Migrate system/core/adb to packages/modules/adb
BUG: 167963357 Test: TH Merged-In: Ie1f82db2fb14e1bdd183bf8d3d93d5e9f974be5d Change-Id: I810a109116247af2af9a8628680620cb683d48a9
This commit is contained in:
parent
7d55df2895
commit
795c2c222c
|
@ -1 +0,0 @@
|
|||
../.clang-format-4
|
914
adb/Android.bp
914
adb/Android.bp
|
@ -1,914 +0,0 @@
|
|||
// Copyright (C) 2017 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
tidy_errors = [
|
||||
"-*",
|
||||
"bugprone-inaccurate-erase",
|
||||
]
|
||||
|
||||
cc_defaults {
|
||||
name: "adb_defaults",
|
||||
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Werror",
|
||||
"-Wexit-time-destructors",
|
||||
"-Wno-non-virtual-dtor",
|
||||
"-Wno-unused-parameter",
|
||||
"-Wno-missing-field-initializers",
|
||||
"-Wthread-safety",
|
||||
"-Wvla",
|
||||
"-DADB_HOST=1", // overridden by adbd_defaults
|
||||
"-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION=1",
|
||||
],
|
||||
cpp_std: "experimental",
|
||||
|
||||
use_version_lib: true,
|
||||
compile_multilib: "first",
|
||||
|
||||
target: {
|
||||
darwin: {
|
||||
host_ldlibs: [
|
||||
"-lpthread",
|
||||
"-framework CoreFoundation",
|
||||
"-framework IOKit",
|
||||
"-lobjc",
|
||||
],
|
||||
},
|
||||
|
||||
windows: {
|
||||
cflags: [
|
||||
// Define windows.h and tchar.h Unicode preprocessor symbols so that
|
||||
// CreateFile(), _tfopen(), etc. map to versions that take wchar_t*, breaking the
|
||||
// build if you accidentally pass char*. Fix by calling like:
|
||||
// std::wstring path_wide;
|
||||
// if (!android::base::UTF8ToWide(path_utf8, &path_wide)) { /* error handling */ }
|
||||
// CreateFileW(path_wide.c_str());
|
||||
"-DUNICODE=1",
|
||||
"-D_UNICODE=1",
|
||||
|
||||
// Unlike on Linux, -std=gnu++ doesn't set _GNU_SOURCE on Windows.
|
||||
"-D_GNU_SOURCE",
|
||||
|
||||
// MinGW hides some things behind _POSIX_SOURCE.
|
||||
"-D_POSIX_SOURCE",
|
||||
|
||||
// libusb uses __stdcall on a variadic function, which gets ignored.
|
||||
"-Wno-ignored-attributes",
|
||||
|
||||
// Not supported yet.
|
||||
"-Wno-thread-safety",
|
||||
],
|
||||
|
||||
host_ldlibs: [
|
||||
"-lws2_32",
|
||||
"-lgdi32",
|
||||
"-luserenv",
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
tidy: true,
|
||||
tidy_checks: tidy_errors,
|
||||
tidy_checks_as_errors: tidy_errors,
|
||||
}
|
||||
|
||||
cc_defaults {
|
||||
name: "adbd_defaults",
|
||||
defaults: ["adb_defaults"],
|
||||
|
||||
cflags: ["-UADB_HOST", "-DADB_HOST=0"],
|
||||
}
|
||||
|
||||
cc_defaults {
|
||||
name: "host_adbd_supported",
|
||||
|
||||
host_supported: true,
|
||||
target: {
|
||||
linux: {
|
||||
enabled: true,
|
||||
host_ldlibs: [
|
||||
"-lresolv", // b64_pton
|
||||
"-lutil", // forkpty
|
||||
],
|
||||
},
|
||||
darwin: {
|
||||
enabled: false,
|
||||
},
|
||||
windows: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cc_defaults {
|
||||
name: "libadbd_binary_dependencies",
|
||||
static_libs: [
|
||||
"libadb_crypto",
|
||||
"libadb_pairing_connection",
|
||||
"libadb_sysdeps",
|
||||
"libadb_tls_connection",
|
||||
"libadbd",
|
||||
"libadbd_core",
|
||||
"libadbconnection_server",
|
||||
"libasyncio",
|
||||
"libbase",
|
||||
"libbrotli",
|
||||
"libcutils_sockets",
|
||||
"libdiagnose_usb",
|
||||
"libmdnssd",
|
||||
"libzstd",
|
||||
|
||||
"libadb_protos",
|
||||
"libapp_processes_protos_lite",
|
||||
"libprotobuf-cpp-lite",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libadbd_auth",
|
||||
"libadbd_fs",
|
||||
"libcrypto",
|
||||
"libcrypto_utils",
|
||||
"liblog",
|
||||
"libselinux",
|
||||
],
|
||||
|
||||
target: {
|
||||
recovery: {
|
||||
exclude_static_libs: [
|
||||
"libadb_pairing_auth",
|
||||
"libadb_pairing_connection",
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// libadb
|
||||
// =========================================================
|
||||
// These files are compiled for both the host and the device.
|
||||
libadb_srcs = [
|
||||
"adb.cpp",
|
||||
"adb_io.cpp",
|
||||
"adb_listeners.cpp",
|
||||
"adb_trace.cpp",
|
||||
"adb_unique_fd.cpp",
|
||||
"adb_utils.cpp",
|
||||
"fdevent/fdevent.cpp",
|
||||
"services.cpp",
|
||||
"sockets.cpp",
|
||||
"socket_spec.cpp",
|
||||
"sysdeps/env.cpp",
|
||||
"sysdeps/errno.cpp",
|
||||
"transport.cpp",
|
||||
"transport_fd.cpp",
|
||||
"types.cpp",
|
||||
]
|
||||
|
||||
libadb_darwin_srcs = [
|
||||
"fdevent/fdevent_poll.cpp",
|
||||
]
|
||||
|
||||
libadb_windows_srcs = [
|
||||
"fdevent/fdevent_poll.cpp",
|
||||
"sysdeps_win32.cpp",
|
||||
"sysdeps/win32/errno.cpp",
|
||||
"sysdeps/win32/stat.cpp",
|
||||
]
|
||||
|
||||
libadb_posix_srcs = [
|
||||
"sysdeps_unix.cpp",
|
||||
"sysdeps/posix/network.cpp",
|
||||
]
|
||||
|
||||
libadb_linux_srcs = [
|
||||
"fdevent/fdevent_epoll.cpp",
|
||||
]
|
||||
|
||||
libadb_test_srcs = [
|
||||
"adb_io_test.cpp",
|
||||
"adb_listeners_test.cpp",
|
||||
"adb_utils_test.cpp",
|
||||
"fdevent/fdevent_test.cpp",
|
||||
"socket_spec_test.cpp",
|
||||
"socket_test.cpp",
|
||||
"sysdeps_test.cpp",
|
||||
"sysdeps/stat_test.cpp",
|
||||
"transport_test.cpp",
|
||||
"types_test.cpp",
|
||||
]
|
||||
|
||||
cc_library_host_static {
|
||||
name: "libadb_host",
|
||||
defaults: ["adb_defaults"],
|
||||
|
||||
srcs: libadb_srcs + [
|
||||
"client/auth.cpp",
|
||||
"client/adb_wifi.cpp",
|
||||
"client/usb_libusb.cpp",
|
||||
"client/usb_dispatch.cpp",
|
||||
"client/transport_local.cpp",
|
||||
"client/transport_mdns.cpp",
|
||||
"client/mdns_utils.cpp",
|
||||
"client/transport_usb.cpp",
|
||||
"client/pairing/pairing_client.cpp",
|
||||
],
|
||||
|
||||
generated_headers: ["platform_tools_version"],
|
||||
|
||||
target: {
|
||||
linux: {
|
||||
srcs: ["client/usb_linux.cpp"] + libadb_linux_srcs,
|
||||
},
|
||||
darwin: {
|
||||
srcs: ["client/usb_osx.cpp"] + libadb_darwin_srcs,
|
||||
},
|
||||
not_windows: {
|
||||
srcs: libadb_posix_srcs,
|
||||
},
|
||||
windows: {
|
||||
enabled: true,
|
||||
srcs: [
|
||||
"client/usb_windows.cpp",
|
||||
] + libadb_windows_srcs,
|
||||
shared_libs: ["AdbWinApi"],
|
||||
},
|
||||
},
|
||||
|
||||
static_libs: [
|
||||
"libadb_crypto",
|
||||
"libadb_protos",
|
||||
"libadb_pairing_connection",
|
||||
"libadb_tls_connection",
|
||||
"libbase",
|
||||
"libcrypto_utils",
|
||||
"libcrypto",
|
||||
"libdiagnose_usb",
|
||||
"libmdnssd",
|
||||
"libusb",
|
||||
"libutils",
|
||||
"liblog",
|
||||
"libcutils",
|
||||
"libprotobuf-cpp-lite",
|
||||
],
|
||||
}
|
||||
|
||||
cc_library {
|
||||
name: "libadb_sysdeps",
|
||||
defaults: ["adb_defaults"],
|
||||
recovery_available: true,
|
||||
host_supported: true,
|
||||
compile_multilib: "both",
|
||||
min_sdk_version: "apex_inherit",
|
||||
// This library doesn't use build::GetBuildNumber()
|
||||
use_version_lib: false,
|
||||
|
||||
srcs: [
|
||||
"sysdeps/env.cpp",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"liblog",
|
||||
],
|
||||
|
||||
target: {
|
||||
windows: {
|
||||
enabled: true,
|
||||
ldflags: ["-municode"],
|
||||
},
|
||||
},
|
||||
|
||||
export_include_dirs: ["."],
|
||||
|
||||
visibility: [
|
||||
"//bootable/recovery/minadbd:__subpackages__",
|
||||
"//packages/modules/adb:__subpackages__",
|
||||
"//system/core/adb:__subpackages__",
|
||||
],
|
||||
|
||||
apex_available: [
|
||||
"com.android.adbd",
|
||||
"test_com.android.adbd",
|
||||
],
|
||||
}
|
||||
|
||||
cc_test_host {
|
||||
name: "adb_test",
|
||||
defaults: ["adb_defaults"],
|
||||
srcs: libadb_test_srcs + [
|
||||
"client/mdns_utils_test.cpp",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libadb_crypto_static",
|
||||
"libadb_host",
|
||||
"libadb_pairing_auth_static",
|
||||
"libadb_pairing_connection_static",
|
||||
"libadb_protos_static",
|
||||
"libadb_sysdeps",
|
||||
"libadb_tls_connection_static",
|
||||
"libbase",
|
||||
"libcutils",
|
||||
"libcrypto_utils",
|
||||
"libcrypto",
|
||||
"liblog",
|
||||
"libmdnssd",
|
||||
"libdiagnose_usb",
|
||||
"libprotobuf-cpp-lite",
|
||||
"libssl",
|
||||
"libusb",
|
||||
],
|
||||
|
||||
target: {
|
||||
windows: {
|
||||
enabled: true,
|
||||
ldflags: ["-municode"],
|
||||
shared_libs: ["AdbWinApi"],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cc_binary_host {
|
||||
name: "adb",
|
||||
|
||||
stl: "libc++_static",
|
||||
defaults: ["adb_defaults"],
|
||||
|
||||
srcs: [
|
||||
"client/adb_client.cpp",
|
||||
"client/bugreport.cpp",
|
||||
"client/commandline.cpp",
|
||||
"client/file_sync_client.cpp",
|
||||
"client/main.cpp",
|
||||
"client/console.cpp",
|
||||
"client/adb_install.cpp",
|
||||
"client/line_printer.cpp",
|
||||
"client/fastdeploy.cpp",
|
||||
"client/fastdeploycallbacks.cpp",
|
||||
"client/incremental.cpp",
|
||||
"client/incremental_server.cpp",
|
||||
"client/incremental_utils.cpp",
|
||||
"shell_service_protocol.cpp",
|
||||
],
|
||||
|
||||
generated_headers: [
|
||||
"bin2c_fastdeployagent",
|
||||
"bin2c_fastdeployagentscript"
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libadb_crypto",
|
||||
"libadb_host",
|
||||
"libadb_pairing_auth",
|
||||
"libadb_pairing_connection",
|
||||
"libadb_protos",
|
||||
"libadb_sysdeps",
|
||||
"libadb_tls_connection",
|
||||
"libandroidfw",
|
||||
"libapp_processes_protos_full",
|
||||
"libbase",
|
||||
"libbrotli",
|
||||
"libcutils",
|
||||
"libcrypto_utils",
|
||||
"libcrypto",
|
||||
"libfastdeploy_host",
|
||||
"libdiagnose_usb",
|
||||
"liblog",
|
||||
"liblz4",
|
||||
"libmdnssd",
|
||||
"libprotobuf-cpp-full",
|
||||
"libssl",
|
||||
"libusb",
|
||||
"libutils",
|
||||
"liblog",
|
||||
"libziparchive",
|
||||
"libz",
|
||||
"libzstd",
|
||||
],
|
||||
|
||||
// Don't add anything here, we don't want additional shared dependencies
|
||||
// on the host adb tool, and shared libraries that link against libc++
|
||||
// will violate ODR
|
||||
shared_libs: [],
|
||||
|
||||
// Archive adb, adb.exe.
|
||||
dist: {
|
||||
targets: [
|
||||
"dist_files",
|
||||
"sdk",
|
||||
"win_sdk",
|
||||
],
|
||||
},
|
||||
|
||||
target: {
|
||||
darwin: {
|
||||
cflags: [
|
||||
"-Wno-sizeof-pointer-memaccess",
|
||||
],
|
||||
},
|
||||
windows: {
|
||||
enabled: true,
|
||||
ldflags: ["-municode"],
|
||||
shared_libs: ["AdbWinApi"],
|
||||
required: [
|
||||
"AdbWinUsbApi",
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// libadbd_core contains the common sources to build libadbd and libadbd_services.
|
||||
cc_library_static {
|
||||
name: "libadbd_core",
|
||||
defaults: ["adbd_defaults", "host_adbd_supported"],
|
||||
recovery_available: true,
|
||||
|
||||
// libminadbd wants both, as it's used to build native tests.
|
||||
compile_multilib: "both",
|
||||
|
||||
srcs: libadb_srcs + libadb_linux_srcs + libadb_posix_srcs + [
|
||||
"daemon/adb_wifi.cpp",
|
||||
"daemon/auth.cpp",
|
||||
"daemon/jdwp_service.cpp",
|
||||
"daemon/logging.cpp",
|
||||
"daemon/transport_local.cpp",
|
||||
],
|
||||
|
||||
generated_headers: ["platform_tools_version"],
|
||||
|
||||
static_libs: [
|
||||
"libdiagnose_usb",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libadbconnection_server",
|
||||
"libadb_crypto",
|
||||
"libadb_pairing_connection",
|
||||
"libadb_protos",
|
||||
"libadb_tls_connection",
|
||||
"libadbd_auth",
|
||||
"libapp_processes_protos_lite",
|
||||
"libasyncio",
|
||||
"libbase",
|
||||
"libcrypto",
|
||||
"libcrypto_utils",
|
||||
"libcutils_sockets",
|
||||
"liblog",
|
||||
],
|
||||
|
||||
proto: {
|
||||
type: "lite",
|
||||
static: true,
|
||||
export_proto_headers: true,
|
||||
},
|
||||
|
||||
target: {
|
||||
android: {
|
||||
whole_static_libs: [
|
||||
"libqemu_pipe",
|
||||
],
|
||||
srcs: [
|
||||
"daemon/transport_qemu.cpp",
|
||||
"daemon/usb.cpp",
|
||||
"daemon/usb_ffs.cpp",
|
||||
]
|
||||
},
|
||||
recovery: {
|
||||
exclude_shared_libs: [
|
||||
"libadb_pairing_auth",
|
||||
"libadb_pairing_connection",
|
||||
"libapp_processes_protos_lite",
|
||||
],
|
||||
}
|
||||
},
|
||||
|
||||
apex_available: [
|
||||
"//apex_available:platform",
|
||||
"com.android.adbd",
|
||||
],
|
||||
visibility: [
|
||||
"//bootable/recovery/minadbd",
|
||||
"//packages/modules/adb:__subpackages__",
|
||||
"//system/core/adb",
|
||||
],
|
||||
}
|
||||
|
||||
cc_library {
|
||||
name: "libadbd_services",
|
||||
defaults: ["adbd_defaults", "host_adbd_supported"],
|
||||
recovery_available: true,
|
||||
compile_multilib: "both",
|
||||
|
||||
srcs: [
|
||||
"daemon/file_sync_service.cpp",
|
||||
"daemon/services.cpp",
|
||||
"daemon/shell_service.cpp",
|
||||
"shell_service_protocol.cpp",
|
||||
],
|
||||
|
||||
cflags: [
|
||||
"-D_GNU_SOURCE",
|
||||
"-Wno-deprecated-declarations",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libadbconnection_server",
|
||||
"libadbd_core",
|
||||
"libbrotli",
|
||||
"libdiagnose_usb",
|
||||
"liblz4",
|
||||
"libzstd",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libadb_crypto",
|
||||
"libadb_pairing_connection",
|
||||
"libadb_protos",
|
||||
"libadb_tls_connection",
|
||||
"libapp_processes_protos_lite",
|
||||
"libasyncio",
|
||||
"libbase",
|
||||
"libcrypto_utils",
|
||||
"libcutils_sockets",
|
||||
"libprotobuf-cpp-lite",
|
||||
|
||||
// APEX dependencies.
|
||||
"libadbd_auth",
|
||||
"libadbd_fs",
|
||||
"libcrypto",
|
||||
"liblog",
|
||||
],
|
||||
|
||||
target: {
|
||||
android: {
|
||||
srcs: [
|
||||
"daemon/abb_service.cpp",
|
||||
"daemon/framebuffer_service.cpp",
|
||||
"daemon/mdns.cpp",
|
||||
"daemon/restart_service.cpp",
|
||||
],
|
||||
shared_libs: [
|
||||
"libmdnssd",
|
||||
"libselinux",
|
||||
],
|
||||
},
|
||||
recovery: {
|
||||
exclude_srcs: [
|
||||
"daemon/abb_service.cpp",
|
||||
],
|
||||
exclude_shared_libs: [
|
||||
"libadb_pairing_auth",
|
||||
"libadb_pairing_connection",
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
apex_available: [
|
||||
"//apex_available:platform",
|
||||
"com.android.adbd",
|
||||
],
|
||||
visibility: [
|
||||
"//packages/modules/adb",
|
||||
"//system/core/adb",
|
||||
],
|
||||
|
||||
}
|
||||
|
||||
cc_library {
|
||||
name: "libadbd",
|
||||
defaults: ["adbd_defaults", "host_adbd_supported"],
|
||||
recovery_available: true,
|
||||
apex_available: ["com.android.adbd"],
|
||||
|
||||
// avoid getting duplicate symbol of android::build::getbuildnumber().
|
||||
use_version_lib: false,
|
||||
|
||||
// libminadbd wants both, as it's used to build native tests.
|
||||
compile_multilib: "both",
|
||||
|
||||
shared_libs: [
|
||||
"libadbconnection_server",
|
||||
"libapp_processes_protos_lite",
|
||||
"libprotobuf-cpp-lite",
|
||||
"libadb_crypto",
|
||||
"libadb_pairing_connection",
|
||||
"libadb_tls_connection",
|
||||
"libasyncio",
|
||||
"libbase",
|
||||
"libcrypto",
|
||||
"libcrypto_utils",
|
||||
"liblog",
|
||||
"libselinux",
|
||||
|
||||
// APEX dependencies on the system image.
|
||||
"libadbd_auth",
|
||||
"libadbd_fs",
|
||||
"libadbd_services",
|
||||
],
|
||||
|
||||
target: {
|
||||
recovery: {
|
||||
exclude_shared_libs: [
|
||||
"libadb_pairing_auth",
|
||||
"libadb_pairing_connection",
|
||||
],
|
||||
}
|
||||
},
|
||||
|
||||
static_libs: [
|
||||
"libadbd_core",
|
||||
"libbrotli",
|
||||
"libcutils_sockets",
|
||||
"libdiagnose_usb",
|
||||
"liblz4",
|
||||
"libmdnssd",
|
||||
"libzstd",
|
||||
],
|
||||
|
||||
visibility: [
|
||||
"//bootable/recovery/minadbd",
|
||||
"//packages/modules/adb",
|
||||
"//system/core/adb",
|
||||
],
|
||||
}
|
||||
|
||||
cc_binary {
|
||||
name: "adbd",
|
||||
defaults: ["adbd_defaults", "host_adbd_supported", "libadbd_binary_dependencies"],
|
||||
recovery_available: true,
|
||||
apex_available: ["com.android.adbd"],
|
||||
|
||||
srcs: [
|
||||
"daemon/main.cpp",
|
||||
],
|
||||
|
||||
cflags: [
|
||||
"-D_GNU_SOURCE",
|
||||
"-Wno-deprecated-declarations",
|
||||
],
|
||||
|
||||
strip: {
|
||||
keep_symbols: true,
|
||||
},
|
||||
|
||||
static_libs: [
|
||||
"libadbd",
|
||||
"libadbd_services",
|
||||
"libasyncio",
|
||||
"libcap",
|
||||
"liblz4",
|
||||
"libminijail",
|
||||
"libssl",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libadb_protos",
|
||||
"libadbd_auth",
|
||||
],
|
||||
|
||||
target: {
|
||||
recovery: {
|
||||
exclude_shared_libs: [
|
||||
"libadb_pairing_auth",
|
||||
"libadb_pairing_connection",
|
||||
],
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
phony {
|
||||
// Interface between adbd in a module and the system.
|
||||
name: "adbd_system_api",
|
||||
required: [
|
||||
"libadbd_auth",
|
||||
"libadbd_fs",
|
||||
"abb",
|
||||
"reboot",
|
||||
"set-verity-state",
|
||||
]
|
||||
}
|
||||
|
||||
phony {
|
||||
name: "adbd_system_api_recovery",
|
||||
required: [
|
||||
"libadbd_auth",
|
||||
"libadbd_fs",
|
||||
"reboot.recovery",
|
||||
],
|
||||
}
|
||||
|
||||
cc_binary {
|
||||
name: "abb",
|
||||
|
||||
defaults: ["adbd_defaults"],
|
||||
stl: "libc++",
|
||||
recovery_available: false,
|
||||
|
||||
srcs: [
|
||||
"daemon/abb.cpp",
|
||||
],
|
||||
|
||||
cflags: [
|
||||
"-D_GNU_SOURCE",
|
||||
"-Wno-deprecated-declarations",
|
||||
],
|
||||
|
||||
strip: {
|
||||
keep_symbols: true,
|
||||
},
|
||||
|
||||
static_libs: [
|
||||
"libadbd_core",
|
||||
"libadbd_services",
|
||||
"libcmd",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libbinder",
|
||||
"liblog",
|
||||
"libutils",
|
||||
"libselinux",
|
||||
],
|
||||
}
|
||||
|
||||
cc_test {
|
||||
name: "adbd_test",
|
||||
|
||||
defaults: ["adbd_defaults", "libadbd_binary_dependencies"],
|
||||
|
||||
recovery_available: false,
|
||||
srcs: libadb_test_srcs + [
|
||||
"daemon/services.cpp",
|
||||
"daemon/shell_service.cpp",
|
||||
"daemon/shell_service_test.cpp",
|
||||
"shell_service_protocol.cpp",
|
||||
"shell_service_protocol_test.cpp",
|
||||
"mdns_test.cpp",
|
||||
],
|
||||
|
||||
test_config: "adb_test.xml",
|
||||
|
||||
shared_libs: [
|
||||
"liblog",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libadbd",
|
||||
"libadbd_auth",
|
||||
"libbase",
|
||||
"libcrypto_utils",
|
||||
"libusb",
|
||||
],
|
||||
test_suites: ["device-tests", "mts"],
|
||||
require_root: true,
|
||||
}
|
||||
|
||||
python_test_host {
|
||||
name: "adb_integration_test_adb",
|
||||
main: "test_adb.py",
|
||||
srcs: [
|
||||
"test_adb.py",
|
||||
],
|
||||
test_config: "adb_integration_test_adb.xml",
|
||||
test_suites: ["general-tests"],
|
||||
version: {
|
||||
py2: {
|
||||
enabled: false,
|
||||
},
|
||||
py3: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
python_test_host {
|
||||
name: "adb_integration_test_device",
|
||||
main: "test_device.py",
|
||||
srcs: [
|
||||
"test_device.py",
|
||||
],
|
||||
libs: [
|
||||
"adb_py",
|
||||
],
|
||||
test_config: "adb_integration_test_device.xml",
|
||||
test_suites: ["general-tests"],
|
||||
version: {
|
||||
py2: {
|
||||
enabled: false,
|
||||
},
|
||||
py3: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Note: using pipe for xxd to control the variable name generated
|
||||
// the default name used by xxd is the path to the input file.
|
||||
java_genrule {
|
||||
name: "bin2c_fastdeployagent",
|
||||
out: ["deployagent.inc"],
|
||||
srcs: [":deployagent"],
|
||||
cmd: "(echo 'unsigned char kDeployAgent[] = {' && xxd -i <$(in) && echo '};') > $(out)",
|
||||
}
|
||||
|
||||
genrule {
|
||||
name: "bin2c_fastdeployagentscript",
|
||||
out: ["deployagentscript.inc"],
|
||||
srcs: ["fastdeploy/deployagent/deployagent.sh"],
|
||||
cmd: "(echo 'unsigned char kDeployAgentScript[] = {' && xxd -i <$(in) && echo '};') > $(out)",
|
||||
}
|
||||
|
||||
cc_library_host_static {
|
||||
name: "libfastdeploy_host",
|
||||
defaults: ["adb_defaults"],
|
||||
srcs: [
|
||||
"fastdeploy/deploypatchgenerator/apk_archive.cpp",
|
||||
"fastdeploy/deploypatchgenerator/deploy_patch_generator.cpp",
|
||||
"fastdeploy/deploypatchgenerator/patch_utils.cpp",
|
||||
"fastdeploy/proto/ApkEntry.proto",
|
||||
],
|
||||
static_libs: [
|
||||
"libadb_host",
|
||||
"libandroidfw",
|
||||
"libbase",
|
||||
"libcutils",
|
||||
"libcrypto_utils",
|
||||
"libcrypto",
|
||||
"libdiagnose_usb",
|
||||
"liblog",
|
||||
"libmdnssd",
|
||||
"libusb",
|
||||
"libutils",
|
||||
"libziparchive",
|
||||
"libz",
|
||||
],
|
||||
proto: {
|
||||
type: "lite",
|
||||
export_proto_headers: true,
|
||||
},
|
||||
target: {
|
||||
windows: {
|
||||
enabled: true,
|
||||
shared_libs: ["AdbWinApi"],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cc_test_host {
|
||||
name: "fastdeploy_test",
|
||||
defaults: ["adb_defaults"],
|
||||
srcs: [
|
||||
"fastdeploy/deploypatchgenerator/apk_archive_test.cpp",
|
||||
"fastdeploy/deploypatchgenerator/deploy_patch_generator_test.cpp",
|
||||
"fastdeploy/deploypatchgenerator/patch_utils_test.cpp",
|
||||
],
|
||||
static_libs: [
|
||||
"libadb_crypto_static",
|
||||
"libadb_host",
|
||||
"libadb_pairing_auth_static",
|
||||
"libadb_pairing_connection_static",
|
||||
"libadb_protos_static",
|
||||
"libadb_sysdeps",
|
||||
"libadb_tls_connection_static",
|
||||
"libandroidfw",
|
||||
"libbase",
|
||||
"libcutils",
|
||||
"libcrypto_utils",
|
||||
"libcrypto",
|
||||
"libdiagnose_usb",
|
||||
"libfastdeploy_host",
|
||||
"liblog",
|
||||
"libmdnssd",
|
||||
"libprotobuf-cpp-lite",
|
||||
"libssl",
|
||||
"libusb",
|
||||
"libutils",
|
||||
"libziparchive",
|
||||
"libz",
|
||||
],
|
||||
target: {
|
||||
windows: {
|
||||
enabled: true,
|
||||
shared_libs: ["AdbWinApi"],
|
||||
},
|
||||
},
|
||||
data: [
|
||||
"fastdeploy/testdata/rotating_cube-metadata-release.data",
|
||||
"fastdeploy/testdata/rotating_cube-release.apk",
|
||||
"fastdeploy/testdata/sample.apk",
|
||||
"fastdeploy/testdata/sample.cd",
|
||||
],
|
||||
}
|
191
adb/NOTICE
191
adb/NOTICE
|
@ -1,191 +0,0 @@
|
|||
|
||||
Copyright (c) 2006-2009, The Android Open Source Project
|
||||
Copyright 2006, Brian Swetland <swetland@frotz.net>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
135
adb/OVERVIEW.TXT
135
adb/OVERVIEW.TXT
|
@ -1,135 +0,0 @@
|
|||
Implementation notes regarding ADB.
|
||||
|
||||
I. General Overview:
|
||||
|
||||
The Android Debug Bridge (ADB) is used to:
|
||||
|
||||
- keep track of all Android devices and emulators instances
|
||||
connected to or running on a given host developer machine
|
||||
|
||||
- implement various control commands (e.g. "adb shell", "adb pull", etc.)
|
||||
for the benefit of clients (command-line users, or helper programs like
|
||||
DDMS). These commands are called 'services' in ADB.
|
||||
|
||||
As a whole, everything works through the following components:
|
||||
|
||||
1. The ADB server
|
||||
|
||||
This is a background process that runs on the host machine. Its purpose
|
||||
is to sense the USB ports to know when devices are attached/removed,
|
||||
as well as when emulator instances start/stop.
|
||||
|
||||
It thus maintains a list of "connected devices" and assigns a 'state'
|
||||
to each one of them: OFFLINE, BOOTLOADER, RECOVERY or ONLINE (more on
|
||||
this below).
|
||||
|
||||
The ADB server is really one giant multiplexing loop whose purpose is
|
||||
to orchestrate the exchange of data (packets, really) between clients,
|
||||
services and devices.
|
||||
|
||||
|
||||
2. The ADB daemon (adbd)
|
||||
|
||||
The 'adbd' program runs as a background process within an Android device
|
||||
or emulated system. Its purpose is to connect to the ADB server
|
||||
(through USB for devices, through TCP for emulators) and provide a
|
||||
few services for clients that run on the host.
|
||||
|
||||
The ADB server considers that a device is ONLINE when it has successfully
|
||||
connected to the adbd program within it. Otherwise, the device is OFFLINE,
|
||||
meaning that the ADB server detected a new device/emulator, but could not
|
||||
connect to the adbd daemon.
|
||||
|
||||
The BOOTLOADER and RECOVERY states correspond to alternate states of
|
||||
devices when they are in the bootloader or recovery mode.
|
||||
|
||||
3. The ADB command-line client
|
||||
|
||||
The 'adb' command-line program is used to run adb commands from a shell
|
||||
or a script. It first tries to locate the ADB server on the host machine,
|
||||
and will start one automatically if none is found.
|
||||
|
||||
Then, the client sends its service requests to the ADB server.
|
||||
|
||||
Currently, a single 'adb' binary is used for both the server and client.
|
||||
this makes distribution and starting the server easier.
|
||||
|
||||
|
||||
4. Services
|
||||
|
||||
There are essentially two kinds of services that a client can talk to.
|
||||
|
||||
Host Services:
|
||||
These services run within the ADB Server and thus do not need to
|
||||
communicate with a device at all. A typical example is "adb devices"
|
||||
which is used to return the list of currently known devices and their
|
||||
states. They are a few other services though.
|
||||
|
||||
Local Services:
|
||||
These services either run within the adbd daemon, or are started by
|
||||
it on the device. The ADB server is used to multiplex streams
|
||||
between the client and the service running in adbd. In this case
|
||||
its role is to initiate the connection, then of being a pass-through
|
||||
for the data.
|
||||
|
||||
|
||||
II. Protocol details:
|
||||
|
||||
1. Client <-> Server protocol:
|
||||
|
||||
This details the protocol used between ADB clients and the ADB
|
||||
server itself. The ADB server listens on TCP:localhost:5037.
|
||||
|
||||
A client sends a request using the following format:
|
||||
|
||||
1. A 4-byte hexadecimal string giving the length of the payload
|
||||
2. Followed by the payload itself.
|
||||
|
||||
For example, to query the ADB server for its internal version number,
|
||||
the client will do the following:
|
||||
|
||||
1. Connect to tcp:localhost:5037
|
||||
2. Send the string "000Chost:version" to the corresponding socket
|
||||
|
||||
The 'host:' prefix is used to indicate that the request is addressed
|
||||
to the server itself (we will talk about other kinds of requests later).
|
||||
The content length is encoded in ASCII for easier debugging.
|
||||
|
||||
The server should answer a request with one of the following:
|
||||
|
||||
1. For success, the 4-byte "OKAY" string
|
||||
|
||||
2. For failure, the 4-byte "FAIL" string, followed by a
|
||||
4-byte hex length, followed by a string giving the reason
|
||||
for failure.
|
||||
|
||||
Note that the connection is still alive after an OKAY, which allows the
|
||||
client to make other requests. But in certain cases, an OKAY will even
|
||||
change the state of the connection.
|
||||
|
||||
For example, the case of the 'host:transport:<serialnumber>' request,
|
||||
where '<serialnumber>' is used to identify a given device/emulator; after
|
||||
the "OKAY" answer, all further requests made by the client will go
|
||||
directly to the corresponding adbd daemon.
|
||||
|
||||
The file SERVICES.TXT lists all services currently implemented by ADB.
|
||||
|
||||
|
||||
2. Transports:
|
||||
|
||||
An ADB transport models a connection between the ADB server and one device
|
||||
or emulator. There are currently two kinds of transports:
|
||||
|
||||
- USB transports, for physical devices through USB
|
||||
|
||||
- Local transports, for emulators running on the host, connected to
|
||||
the server through TCP
|
||||
|
||||
In theory, it should be possible to write a local transport that proxies
|
||||
a connection between an ADB server and a device/emulator connected to/
|
||||
running on another machine. This hasn't been done yet though.
|
||||
|
||||
Each transport can carry one or more multiplexed streams between clients
|
||||
and the device/emulator they point to. The ADB server must handle
|
||||
unexpected transport disconnections (e.g. when a device is physically
|
||||
unplugged) properly.
|
|
@ -1,2 +0,0 @@
|
|||
jmgao@google.com
|
||||
yabinc@google.com
|
|
@ -1,94 +0,0 @@
|
|||
# ADB Internals
|
||||
|
||||
If you are new to adb source code, you should start by reading [OVERVIEW.TXT](OVERVIEW.TXT) which describes the three components of adb pipeline.
|
||||
|
||||
This document is here to boost what can be achieved within a "window of naive interest". You will not find function or class documentation here but rather the "big picture" which should allow you to build a mental map to help navigate the code.
|
||||
|
||||
## Three components of adb pipeline
|
||||
|
||||
As outlined in the overview, this codebase generates three components (Client, Server (a.k.a Host), and Daemon (a.k.a adbd)). The central part is the Server which runs on the Host computer. On one side the Server exposes a "Smart Socket" to Clients such as adb or DDMLIB. On the other side, the Server continuously monitors for connecting Daemons (as USB devices or TCP emulator). Communication with a device is done with a Transport.
|
||||
|
||||
```
|
||||
+----------+ +------------------------+
|
||||
| ADB +----------+ | ADB SERVER | +----------+
|
||||
| CLIENT | | | | (USB)| ADBD |
|
||||
+----------+ | | Transport+-------------+ (DEVICE) |
|
||||
| | | +----------+
|
||||
+----------- | | |
|
||||
| ADB | v + | +----------+
|
||||
| CLIENT +--------->SmartSocket | (USB)| ADBD |
|
||||
+----------+ ^ | (TCP/IP) Transport+-------------+ (DEVICE) |
|
||||
| | | +----------+
|
||||
+----------+ | | |
|
||||
| DDMLIB | | | Transport+--+ +----------+
|
||||
| CLIENT +----------+ | | | (TCP/IP)| ADBD |
|
||||
+----------+ +------------------------+ +----------|(EMULATOR)|
|
||||
+----------+
|
||||
```
|
||||
|
||||
The Client and the Server are contained in the same executable and both run on the Host machine. Code sections specific to the Host is enclosed within `ADB_HOST` guard. adbd runs on the Android Device. Daemon specific code is enclosed in `!ADB_HOST` but also sometimes with-in `__ANDROID__` guard.
|
||||
|
||||
|
||||
## "SMART SOCKET" and TRANSPORT
|
||||
|
||||
A smart socket is a simple TCP socket with a smart protocol built on top of it. This is what Clients connect onto from the Host side. The Client must always initiate communication via a human readable request but the response format varies. The smart protocol is documented in [SERVICES.TXT](SERVICES.TXT).
|
||||
|
||||
On the other side, the Server communicate with a device via a Transport. adb initially targeted devices connecting over USB, which is restricted to a fixed number of data streams. Therefore, adb multiplexes multiple byte streams over a single pipe via Transport. When devices connecting over other mechanisms (e.g. emulators over TCP) were introduced, the existing transport protocol was maintained.
|
||||
|
||||
## THREADING MODEL and FDEVENT system
|
||||
|
||||
At the heart of both the Server and Daemon is a main thread running an fdevent loop, which is an platform-independent abstraction over poll/epoll/WSAPoll monitoring file descriptors events. Requests and services are usually server from the main thread but some service requests result in new threads being spawned.
|
||||
|
||||
To allow for operations to run on the Main thread, fdevent features a RunQueue combined with an interrupt fd to force polling to return.
|
||||
|
||||
```
|
||||
+------------+ +-------------------------^
|
||||
| RUNQUEUE | | |
|
||||
+------------+ | POLLING (Main thread) |
|
||||
| Function<> | | |
|
||||
+------------+ | |
|
||||
| Function<> | ^-^-------^-------^------^^
|
||||
+------------+ | | | |
|
||||
| ... | | | | |
|
||||
+------------+ | | | |
|
||||
| | | | | |
|
||||
|============| | | | |
|
||||
|Interrupt fd+------+ +----+ +----+ +----+
|
||||
+------------+ fd Socket Pipe
|
||||
```
|
||||
|
||||
## ASOCKET, APACKET, and AMESSAGE
|
||||
|
||||
The asocket, apacket, and amessage constructs exist only to wrap data while it transits on a Transport. An asocket handles a stream of apackets. An apacket consists in a amessage header featuring a command (`A_SYNC`, `A_OPEN`, `A_CLSE`, `A_WRTE`, `A_OKAY`, ...) followed by a payload (find more documentation in [protocol.txt](protocol.txt). There is no `A_READ` command because an asocket is unidirectional. To model a bi-directional stream, asocket have a peer which go in the opposite direction.
|
||||
|
||||
An asocket features a buffer where the elemental unit is an apacket. Is traffic is inbound, the buffer stores apacket until they are consumed. If the traffic is oubound, the buffer store apackets until they are sent down the wire (with `A_WRTE` commands).
|
||||
|
||||
```
|
||||
+---------------------ASocket------------------------+
|
||||
| |
|
||||
| +----------------APacket Queue------------------+ |
|
||||
| | | |
|
||||
| | APacket APacket APacket | |
|
||||
| | +--------+ +--------+ +--------+ | |
|
||||
| | |AMessage| |AMessage| |AMessage| | |
|
||||
| | +--------+ +--------+ +--------+ | |
|
||||
| | | | | | | | | |
|
||||
| | ..... | | | | | | | |
|
||||
| | | Data | | Data | | Data | | |
|
||||
| | | | | | | | | |
|
||||
| | | | | | | | | |
|
||||
| | +--------+ +--------+ +--------+ | |
|
||||
| | | |
|
||||
| +-----------------------------------------------+ |
|
||||
+---------------------------------------------------+
|
||||
```
|
||||
|
||||
This system allows to multiplex data streams on an unique byte stream. Without entering too much into details, the amessage fields arg1 and arg2 are used alike in the TCP protocol where local and remote ports identify an unique stream. Note that unlike TCP which feature an "unacknowledged-send window", an apacket is sent only after the previous one has been confirmed to be received.
|
||||
|
||||
The two types of asocket (Remote and Local) differentiate between outbound and inbound traffic.
|
||||
|
||||
## adbd <-> APPPLICATION communication
|
||||
|
||||
This pipeline is detailed in [services.cpp](services.cpp). The JDWP extension implemented by Dalvik/ART are documented in:
|
||||
- platform/dalvik/+/master/docs/debugmon.html
|
||||
- platform/dalvik/+/master/docs/debugger.html
|
255
adb/SERVICES.TXT
255
adb/SERVICES.TXT
|
@ -1,255 +0,0 @@
|
|||
This file tries to document all requests a client can make
|
||||
to the ADB server of an adbd daemon. See the OVERVIEW.TXT document
|
||||
to understand what's going on here.
|
||||
|
||||
HOST SERVICES:
|
||||
|
||||
host:version
|
||||
Ask the ADB server for its internal version number.
|
||||
|
||||
host:kill
|
||||
Ask the ADB server to quit immediately. This is used when the
|
||||
ADB client detects that an obsolete server is running after an
|
||||
upgrade.
|
||||
|
||||
host:devices
|
||||
host:devices-l
|
||||
Ask to return the list of available Android devices and their
|
||||
state. devices-l includes the device paths in the state.
|
||||
After the OKAY, this is followed by a 4-byte hex len,
|
||||
and a string that will be dumped as-is by the client, then
|
||||
the connection is closed
|
||||
|
||||
host:track-devices
|
||||
This is a variant of host:devices which doesn't close the
|
||||
connection. Instead, a new device list description is sent
|
||||
each time a device is added/removed or the state of a given
|
||||
device changes (hex4 + content). This allows tools like DDMS
|
||||
to track the state of connected devices in real-time without
|
||||
polling the server repeatedly.
|
||||
|
||||
host:emulator:<port>
|
||||
This is a special query that is sent to the ADB server when a
|
||||
new emulator starts up. <port> is a decimal number corresponding
|
||||
to the emulator's ADB control port, i.e. the TCP port that the
|
||||
emulator will forward automatically to the adbd daemon running
|
||||
in the emulator system.
|
||||
|
||||
This mechanism allows the ADB server to know when new emulator
|
||||
instances start.
|
||||
|
||||
host:transport:<serial-number>
|
||||
Ask to switch the connection to the device/emulator identified by
|
||||
<serial-number>. After the OKAY response, every client request will
|
||||
be sent directly to the adbd daemon running on the device.
|
||||
(Used to implement the -s option)
|
||||
|
||||
host:transport-usb
|
||||
Ask to switch the connection to one device connected through USB
|
||||
to the host machine. This will fail if there are more than one such
|
||||
devices. (Used to implement the -d convenience option)
|
||||
|
||||
host:transport-local
|
||||
Ask to switch the connection to one emulator connected through TCP.
|
||||
This will fail if there is more than one such emulator instance
|
||||
running. (Used to implement the -e convenience option)
|
||||
|
||||
host:transport-any
|
||||
Another host:transport variant. Ask to switch the connection to
|
||||
either the device or emulator connect to/running on the host.
|
||||
Will fail if there is more than one such device/emulator available.
|
||||
(Used when neither -s, -d or -e are provided)
|
||||
|
||||
host-serial:<serial-number>:<request>
|
||||
This is a special form of query, where the 'host-serial:<serial-number>:'
|
||||
prefix can be used to indicate that the client is asking the ADB server
|
||||
for information related to a specific device. <request> can be in one
|
||||
of the format described below.
|
||||
|
||||
host-usb:<request>
|
||||
A variant of host-serial used to target the single USB device connected
|
||||
to the host. This will fail if there is none or more than one.
|
||||
|
||||
host-local:<request>
|
||||
A variant of host-serial used to target the single emulator instance
|
||||
running on the host. This will fail if there is none or more than one.
|
||||
|
||||
host:<request>
|
||||
When asking for information related to a device, 'host:' can also be
|
||||
interpreted as 'any single device or emulator connected to/running on
|
||||
the host'.
|
||||
|
||||
<host-prefix>:get-product
|
||||
XXX
|
||||
|
||||
<host-prefix>:get-serialno
|
||||
Returns the serial number of the corresponding device/emulator.
|
||||
Note that emulator serial numbers are of the form "emulator-5554"
|
||||
|
||||
<host-prefix>:get-devpath
|
||||
Returns the device path of the corresponding device/emulator.
|
||||
|
||||
<host-prefix>:get-state
|
||||
Returns the state of a given device as a string.
|
||||
|
||||
<host-prefix>:forward:<local>;<remote>
|
||||
Asks the ADB server to forward local connections from <local>
|
||||
to the <remote> address on a given device.
|
||||
|
||||
There, <host-prefix> can be one of the
|
||||
host-serial/host-usb/host-local/host prefixes as described previously
|
||||
and indicates which device/emulator to target.
|
||||
|
||||
the format of <local> is one of:
|
||||
|
||||
tcp:<port> -> TCP connection on localhost:<port>
|
||||
local:<path> -> Unix local domain socket on <path>
|
||||
|
||||
the format of <remote> is one of:
|
||||
|
||||
tcp:<port> -> TCP localhost:<port> on device
|
||||
local:<path> -> Unix local domain socket on device
|
||||
jdwp:<pid> -> JDWP thread on VM process <pid>
|
||||
|
||||
or even any one of the local services described below.
|
||||
|
||||
<host-prefix>:forward:norebind:<local>;<remote>
|
||||
Same as <host-prefix>:forward:<local>;<remote> except that it will
|
||||
fail it there is already a forward connection from <local>.
|
||||
|
||||
Used to implement 'adb forward --no-rebind <local> <remote>'
|
||||
|
||||
<host-prefix>:killforward:<local>
|
||||
Remove any existing forward local connection from <local>.
|
||||
This is used to implement 'adb forward --remove <local>'
|
||||
|
||||
<host-prefix>:killforward-all
|
||||
Remove all forward network connections.
|
||||
This is used to implement 'adb forward --remove-all'.
|
||||
|
||||
<host-prefix>:list-forward
|
||||
List all existing forward connections from this server.
|
||||
This returns something that looks like the following:
|
||||
|
||||
<hex4>: The length of the payload, as 4 hexadecimal chars.
|
||||
<payload>: A series of lines of the following format:
|
||||
|
||||
<serial> " " <local> " " <remote> "\n"
|
||||
|
||||
Where <serial> is a device serial number.
|
||||
<local> is the host-specific endpoint (e.g. tcp:9000).
|
||||
<remote> is the device-specific endpoint.
|
||||
|
||||
Used to implement 'adb forward --list'.
|
||||
|
||||
LOCAL SERVICES:
|
||||
|
||||
All the queries below assumed that you already switched the transport
|
||||
to a real device, or that you have used a query prefix as described
|
||||
above.
|
||||
|
||||
shell:command arg1 arg2 ...
|
||||
Run 'command arg1 arg2 ...' in a shell on the device, and return
|
||||
its output and error streams. Note that arguments must be separated
|
||||
by spaces. If an argument contains a space, it must be quoted with
|
||||
double-quotes. Arguments cannot contain double quotes or things
|
||||
will go very wrong.
|
||||
|
||||
Note that this is the non-interactive version of "adb shell"
|
||||
|
||||
shell:
|
||||
Start an interactive shell session on the device. Redirect
|
||||
stdin/stdout/stderr as appropriate. Note that the ADB server uses
|
||||
this to implement "adb shell", but will also cook the input before
|
||||
sending it to the device (see interactive_shell() in commandline.c)
|
||||
|
||||
remount:
|
||||
Ask adbd to remount the device's filesystem in read-write mode,
|
||||
instead of read-only. This is usually necessary before performing
|
||||
an "adb sync" or "adb push" request.
|
||||
|
||||
This request may not succeed on certain builds which do not allow
|
||||
that.
|
||||
|
||||
dev:<path>
|
||||
Opens a device file and connects the client directly to it for
|
||||
read/write purposes. Useful for debugging, but may require special
|
||||
privileges and thus may not run on all devices. <path> is a full
|
||||
path from the root of the filesystem.
|
||||
|
||||
tcp:<port>
|
||||
Tries to connect to tcp port <port> on localhost.
|
||||
|
||||
tcp:<port>:<server-name>
|
||||
Tries to connect to tcp port <port> on machine <server-name> from
|
||||
the device. This can be useful to debug some networking/proxy
|
||||
issues that can only be revealed on the device itself.
|
||||
|
||||
local:<path>
|
||||
Tries to connect to a Unix domain socket <path> on the device
|
||||
|
||||
localreserved:<path>
|
||||
localabstract:<path>
|
||||
localfilesystem:<path>
|
||||
Variants of local:<path> that are used to access other Android
|
||||
socket namespaces.
|
||||
|
||||
framebuffer:
|
||||
This service is used to send snapshots of the framebuffer to a client.
|
||||
It requires sufficient privileges but works as follow:
|
||||
|
||||
After the OKAY, the service sends 16-byte binary structure
|
||||
containing the following fields (little-endian format):
|
||||
|
||||
depth: uint32_t: framebuffer depth
|
||||
size: uint32_t: framebuffer size in bytes
|
||||
width: uint32_t: framebuffer width in pixels
|
||||
height: uint32_t: framebuffer height in pixels
|
||||
|
||||
With the current implementation, depth is always 16, and
|
||||
size is always width*height*2
|
||||
|
||||
Then, each time the client wants a snapshot, it should send
|
||||
one byte through the channel, which will trigger the service
|
||||
to send it 'size' bytes of framebuffer data.
|
||||
|
||||
If the adbd daemon doesn't have sufficient privileges to open
|
||||
the framebuffer device, the connection is simply closed immediately.
|
||||
|
||||
jdwp:<pid>
|
||||
Connects to the JDWP thread running in the VM of process <pid>.
|
||||
|
||||
track-jdwp
|
||||
This is used to send the list of JDWP pids periodically to the client.
|
||||
The format of the returned data is the following:
|
||||
|
||||
<hex4>: the length of all content as a 4-char hexadecimal string
|
||||
<content>: a series of ASCII lines of the following format:
|
||||
<pid> "\n"
|
||||
|
||||
This service is used by DDMS to know which debuggable processes are running
|
||||
on the device/emulator.
|
||||
|
||||
Note that there is no single-shot service to retrieve the list only once.
|
||||
|
||||
sync:
|
||||
This starts the file synchronization service, used to implement "adb push"
|
||||
and "adb pull". Since this service is pretty complex, it will be detailed
|
||||
in a companion document named SYNC.TXT
|
||||
|
||||
reverse:<forward-command>
|
||||
This implements the 'adb reverse' feature, i.e. the ability to reverse
|
||||
socket connections from a device to the host. <forward-command> is one
|
||||
of the forwarding commands that are described above, as in:
|
||||
|
||||
list-forward
|
||||
forward:<local>;<remote>
|
||||
forward:norebind:<local>;<remote>
|
||||
killforward-all
|
||||
killforward:<local>
|
||||
|
||||
Note that in this case, <local> corresponds to the socket on the device
|
||||
and <remote> corresponds to the socket on the host.
|
||||
|
||||
The output of reverse:list-forward is the same as host:list-forward
|
||||
except that <serial> will be just 'host'.
|
|
@ -1,42 +0,0 @@
|
|||
adb can be configured to work with systemd-style socket activation,
|
||||
allowing the daemon to start automatically when the adb control port
|
||||
is forwarded across a network. You need two files, placed in the usual
|
||||
systemd service directories (e.g., ~/.config/systemd/user for a user
|
||||
service).
|
||||
|
||||
adb.service:
|
||||
|
||||
--- START adb.service CUT HERE ---
|
||||
[Unit]
|
||||
Description=adb
|
||||
After=adb.socket
|
||||
Requires=adb.socket
|
||||
[Service]
|
||||
Type=simple
|
||||
# FD 3 is part of the systemd interface
|
||||
ExecStart=/path/to/adb server nodaemon -L acceptfd:3
|
||||
--- END adb.service CUT HERE ---
|
||||
|
||||
--- START adb.socket CUT HERE ---
|
||||
[Unit]
|
||||
Description=adb
|
||||
PartOf=adb.service
|
||||
[Socket]
|
||||
ListenStream=127.0.0.1:5037
|
||||
Accept=no
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
--- END adb.socket CUT HERE ---
|
||||
|
||||
After installing the adb service, the adb server will be started
|
||||
automatically on any connection to 127.0.0.1:5037 (the default adb
|
||||
control port), even after adb kill-server kills the server.
|
||||
|
||||
Other "superserver" launcher systems (like macOS launchd) can be
|
||||
configured analogously. The important part is that adb be started with
|
||||
"server" and "nodaemon" command line arguments and that the listen
|
||||
address (passed to -L) name a file descriptor that's ready to
|
||||
accept(2) connections and that's already bound to the desired address
|
||||
and listening. inetd-style pre-accepted sockets do _not_ work in this
|
||||
configuration: the file descriptor passed to acceptfd must be the
|
||||
serve socket, not the accepted connection socket.
|
80
adb/SYNC.TXT
80
adb/SYNC.TXT
|
@ -1,80 +0,0 @@
|
|||
This file tries to document file-related requests a client can make
|
||||
to the ADB server of an adbd daemon. See the OVERVIEW.TXT document
|
||||
to understand what's going on here. See the SERVICES.TXT to learn more
|
||||
about the other requests that are possible.
|
||||
|
||||
SYNC SERVICES:
|
||||
|
||||
|
||||
Requesting the sync service ("sync:") using the protocol as described in
|
||||
SERVICES.TXT sets the connection in sync mode. This mode is a binary mode that
|
||||
differs from the regular adb protocol. The connection stays in sync mode until
|
||||
explicitly terminated (see below).
|
||||
|
||||
After the initial "sync:" command is sent the server must respond with either
|
||||
"OKAY" or "FAIL" as per usual.
|
||||
|
||||
In sync mode both the server and the client will frequently use eight-byte
|
||||
packets to communicate. In this document these are called sync requests and sync
|
||||
responses. The first four bytes are an id that specifies the sync request. It is
|
||||
represented by four utf-8 characters. The last four bytes are a Little-Endian
|
||||
integer, with various uses. This number will be called "length" below. In fact
|
||||
all binary integers are Little-Endian in the sync mode. Sync mode is
|
||||
implicitly exited after each sync request, and normal adb communication
|
||||
follows as described in SERVICES.TXT.
|
||||
|
||||
The following sync requests are accepted:
|
||||
LIST - List the files in a folder
|
||||
RECV - Retrieve a file from device
|
||||
SEND - Send a file to device
|
||||
STAT - Stat a file
|
||||
|
||||
All of the sync requests above must be followed by "length": the number of
|
||||
bytes containing a utf-8 string with a remote filename.
|
||||
|
||||
LIST:
|
||||
Lists files in the directory specified by the remote filename. The server will
|
||||
respond with zero or more directory entries or "dents".
|
||||
|
||||
The directory entries will be returned in the following form
|
||||
1. A four-byte sync response id "DENT"
|
||||
2. A four-byte integer representing file mode.
|
||||
3. A four-byte integer representing file size.
|
||||
4. A four-byte integer representing last modified time.
|
||||
5. A four-byte integer representing file name length.
|
||||
6. length number of bytes containing an utf-8 string representing the file
|
||||
name.
|
||||
|
||||
When a sync response "DONE" is received the listing is done.
|
||||
|
||||
SEND:
|
||||
The remote file name is split into two parts separated by the last
|
||||
comma (","). The first part is the actual path, while the second is a decimal
|
||||
encoded file mode containing the permissions of the file on device.
|
||||
|
||||
Note that some file types will be deleted before the copying starts, and if
|
||||
the transfer fails. Some file types will not be deleted, which allows
|
||||
adb push disk_image /some_block_device
|
||||
to work.
|
||||
|
||||
After this the actual file is sent in chunks. Each chunk has the following
|
||||
format.
|
||||
A sync request with id "DATA" and length equal to the chunk size. After
|
||||
follows chunk size number of bytes. This is repeated until the file is
|
||||
transferred. Each chunk must not be larger than 64k.
|
||||
|
||||
When the file is transferred a sync request "DONE" is sent, where length is set
|
||||
to the last modified time for the file. The server responds to this last
|
||||
request (but not to chunk requests) with an "OKAY" sync response (length can
|
||||
be ignored).
|
||||
|
||||
|
||||
RECV:
|
||||
Retrieves a file from device to a local file. The remote path is the path to
|
||||
the file that will be returned. Just as for the SEND sync request the file
|
||||
received is split up into chunks. The sync response id is "DATA" and length is
|
||||
the chunk size. After follows chunk size number of bytes. This is repeated
|
||||
until the file is transferred. Each chunk will not be larger than 64k.
|
||||
|
||||
When the file is transferred a sync response "DONE" is retrieved where the
|
||||
length can be ignored.
|
499
adb/adb.bash
499
adb/adb.bash
|
@ -1,499 +0,0 @@
|
|||
# /* 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 ! check_type "$1" >/dev/null; then
|
||||
return
|
||||
fi
|
||||
|
||||
if check_type _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 [[ $(check_type 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 [[ $(check_type 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 [[ $(check_type compopt) == "builtin" ]]; then
|
||||
complete -F _adb adb
|
||||
else
|
||||
complete -o nospace -F _adb adb
|
||||
fi
|
1394
adb/adb.cpp
1394
adb/adb.cpp
File diff suppressed because it is too large
Load Diff
261
adb/adb.h
261
adb/adb.h
|
@ -1,261 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <android-base/macros.h>
|
||||
|
||||
#include "adb_trace.h"
|
||||
#include "fdevent/fdevent.h"
|
||||
#include "socket.h"
|
||||
#include "types.h"
|
||||
|
||||
constexpr size_t MAX_PAYLOAD_V1 = 4 * 1024;
|
||||
constexpr size_t MAX_PAYLOAD = 1024 * 1024;
|
||||
constexpr size_t MAX_FRAMEWORK_PAYLOAD = 64 * 1024;
|
||||
|
||||
constexpr size_t LINUX_MAX_SOCKET_SIZE = 4194304;
|
||||
|
||||
#define A_SYNC 0x434e5953
|
||||
#define A_CNXN 0x4e584e43
|
||||
#define A_OPEN 0x4e45504f
|
||||
#define A_OKAY 0x59414b4f
|
||||
#define A_CLSE 0x45534c43
|
||||
#define A_WRTE 0x45545257
|
||||
#define A_AUTH 0x48545541
|
||||
#define A_STLS 0x534C5453
|
||||
|
||||
// ADB protocol version.
|
||||
// Version revision:
|
||||
// 0x01000000: original
|
||||
// 0x01000001: skip checksum (Dec 2017)
|
||||
#define A_VERSION_MIN 0x01000000
|
||||
#define A_VERSION_SKIP_CHECKSUM 0x01000001
|
||||
#define A_VERSION 0x01000001
|
||||
|
||||
// Stream-based TLS protocol version
|
||||
#define A_STLS_VERSION_MIN 0x01000000
|
||||
#define A_STLS_VERSION 0x01000000
|
||||
|
||||
// Used for help/version information.
|
||||
#define ADB_VERSION_MAJOR 1
|
||||
#define ADB_VERSION_MINOR 0
|
||||
|
||||
std::string adb_version();
|
||||
|
||||
// Increment this when we want to force users to start a new adb server.
|
||||
#define ADB_SERVER_VERSION 41
|
||||
|
||||
using TransportId = uint64_t;
|
||||
class atransport;
|
||||
|
||||
uint32_t calculate_apacket_checksum(const apacket* packet);
|
||||
|
||||
/* the adisconnect structure is used to record a callback that
|
||||
** will be called whenever a transport is disconnected (e.g. by the user)
|
||||
** this should be used to cleanup objects that depend on the
|
||||
** transport (e.g. remote sockets, listeners, etc...)
|
||||
*/
|
||||
struct adisconnect {
|
||||
void (*func)(void* opaque, atransport* t);
|
||||
void* opaque;
|
||||
};
|
||||
|
||||
// A transport object models the connection to a remote device or emulator there
|
||||
// is one transport per connected device/emulator. A "local transport" connects
|
||||
// through TCP (for the emulator), while a "usb transport" through USB (for real
|
||||
// devices).
|
||||
//
|
||||
// Note that kTransportHost doesn't really correspond to a real transport
|
||||
// object, it's a special value used to indicate that a client wants to connect
|
||||
// to a service implemented within the ADB server itself.
|
||||
enum TransportType {
|
||||
kTransportUsb,
|
||||
kTransportLocal,
|
||||
kTransportAny,
|
||||
kTransportHost,
|
||||
};
|
||||
|
||||
#define TOKEN_SIZE 20
|
||||
|
||||
enum ConnectionState {
|
||||
kCsAny = -1,
|
||||
|
||||
kCsConnecting = 0, // Haven't received a response from the device yet.
|
||||
kCsAuthorizing, // Authorizing with keys from ADB_VENDOR_KEYS.
|
||||
kCsUnauthorized, // ADB_VENDOR_KEYS exhausted, fell back to user prompt.
|
||||
kCsNoPerm, // Insufficient permissions to communicate with the device.
|
||||
kCsOffline,
|
||||
|
||||
kCsBootloader,
|
||||
kCsDevice,
|
||||
kCsHost,
|
||||
kCsRecovery,
|
||||
kCsSideload,
|
||||
kCsRescue,
|
||||
};
|
||||
|
||||
inline bool ConnectionStateIsOnline(ConnectionState state) {
|
||||
switch (state) {
|
||||
case kCsBootloader:
|
||||
case kCsDevice:
|
||||
case kCsHost:
|
||||
case kCsRecovery:
|
||||
case kCsSideload:
|
||||
case kCsRescue:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void print_packet(const char* label, apacket* p);
|
||||
|
||||
void handle_packet(apacket* p, atransport* t);
|
||||
|
||||
int launch_server(const std::string& socket_spec);
|
||||
int adb_server_main(int is_daemon, const std::string& socket_spec, int ack_reply_fd);
|
||||
|
||||
/* initialize a transport object's func pointers and state */
|
||||
int init_socket_transport(atransport* t, unique_fd s, int port, int local);
|
||||
|
||||
std::string getEmulatorSerialString(int console_port);
|
||||
#if ADB_HOST
|
||||
atransport* find_emulator_transport_by_adb_port(int adb_port);
|
||||
atransport* find_emulator_transport_by_console_port(int console_port);
|
||||
#endif
|
||||
|
||||
unique_fd service_to_fd(std::string_view name, atransport* transport);
|
||||
#if !ADB_HOST
|
||||
unique_fd daemon_service_to_fd(std::string_view name, atransport* transport);
|
||||
#endif
|
||||
|
||||
#if ADB_HOST
|
||||
asocket* host_service_to_socket(std::string_view name, std::string_view serial,
|
||||
TransportId transport_id);
|
||||
#endif
|
||||
|
||||
#if !ADB_HOST
|
||||
asocket* daemon_service_to_socket(std::string_view name);
|
||||
#endif
|
||||
|
||||
#if !ADB_HOST
|
||||
unique_fd execute_abb_command(std::string_view command);
|
||||
#endif
|
||||
|
||||
#if !ADB_HOST
|
||||
int init_jdwp(void);
|
||||
asocket* create_jdwp_service_socket();
|
||||
asocket* create_jdwp_tracker_service_socket();
|
||||
asocket* create_app_tracker_service_socket();
|
||||
unique_fd create_jdwp_connection_fd(int jdwp_pid);
|
||||
#endif
|
||||
|
||||
bool handle_forward_request(const char* service, atransport* transport, int reply_fd);
|
||||
bool handle_forward_request(const char* service,
|
||||
std::function<atransport*(std::string* error)> transport_acquirer,
|
||||
int reply_fd);
|
||||
|
||||
/* packet allocator */
|
||||
apacket* get_apacket(void);
|
||||
void put_apacket(apacket* p);
|
||||
|
||||
// Define it if you want to dump packets.
|
||||
#define DEBUG_PACKETS 0
|
||||
|
||||
#if !DEBUG_PACKETS
|
||||
#define print_packet(tag, p) \
|
||||
do { \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#define DEFAULT_ADB_PORT 5037
|
||||
|
||||
#define DEFAULT_ADB_LOCAL_TRANSPORT_PORT 5555
|
||||
|
||||
#define ADB_CLASS 0xff
|
||||
#define ADB_SUBCLASS 0x42
|
||||
#define ADB_PROTOCOL 0x1
|
||||
|
||||
void local_init(const std::string& addr);
|
||||
bool local_connect(int port);
|
||||
int local_connect_arbitrary_ports(int console_port, int adb_port, std::string* error);
|
||||
|
||||
ConnectionState connection_state(atransport* t);
|
||||
|
||||
extern const char* adb_device_banner;
|
||||
|
||||
#define CHUNK_SIZE (64 * 1024)
|
||||
|
||||
// Argument delimeter for adb abb command.
|
||||
#define ABB_ARG_DELIMETER ('\0')
|
||||
|
||||
#if !ADB_HOST
|
||||
#define USB_FFS_ADB_PATH "/dev/usb-ffs/adb/"
|
||||
#define USB_FFS_ADB_EP(x) USB_FFS_ADB_PATH #x
|
||||
|
||||
#define USB_FFS_ADB_EP0 USB_FFS_ADB_EP(ep0)
|
||||
#define USB_FFS_ADB_OUT USB_FFS_ADB_EP(ep1)
|
||||
#define USB_FFS_ADB_IN USB_FFS_ADB_EP(ep2)
|
||||
#endif
|
||||
|
||||
enum class HostRequestResult {
|
||||
Handled,
|
||||
SwitchedTransport,
|
||||
Unhandled,
|
||||
};
|
||||
|
||||
HostRequestResult handle_host_request(std::string_view service, TransportType type,
|
||||
const char* serial, TransportId transport_id, int reply_fd,
|
||||
asocket* s);
|
||||
|
||||
void handle_online(atransport* t);
|
||||
void handle_offline(atransport* t);
|
||||
|
||||
void send_connect(atransport* t);
|
||||
void send_tls_request(atransport* t);
|
||||
|
||||
void parse_banner(const std::string&, atransport* t);
|
||||
|
||||
#if ADB_HOST
|
||||
// On startup, the adb server needs to wait until all of the connected devices are ready.
|
||||
// To do this, we need to know when the scan has identified all of the potential new transports, and
|
||||
// when each transport becomes ready.
|
||||
// TODO: Do this for mDNS as well, instead of just USB?
|
||||
|
||||
// We've found all of the transports we potentially care about.
|
||||
void adb_notify_device_scan_complete();
|
||||
|
||||
// One or more transports have changed status, check to see if we're ready.
|
||||
void update_transport_status();
|
||||
|
||||
// Wait until device scan has completed and every transport is ready, or a timeout elapses.
|
||||
void adb_wait_for_device_initialization();
|
||||
#endif // ADB_HOST
|
||||
|
||||
#if ADB_HOST
|
||||
// When ssh-forwarding to a remote adb server, kill-server is almost never what you actually want,
|
||||
// and unfortunately, many other tools issue it. This adds a knob to reject kill-servers.
|
||||
void adb_set_reject_kill_server(bool reject);
|
||||
#endif
|
||||
|
||||
void usb_init();
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 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.
|
||||
*/
|
||||
|
||||
#ifndef __ADB_AUTH_H
|
||||
#define __ADB_AUTH_H
|
||||
|
||||
#include "adb.h"
|
||||
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
|
||||
#include <openssl/rsa.h>
|
||||
|
||||
/* AUTH packets first argument */
|
||||
/* Request */
|
||||
#define ADB_AUTH_TOKEN 1
|
||||
/* Response */
|
||||
#define ADB_AUTH_SIGNATURE 2
|
||||
#define ADB_AUTH_RSAPUBLICKEY 3
|
||||
|
||||
#if ADB_HOST
|
||||
|
||||
void adb_auth_init();
|
||||
|
||||
int adb_auth_keygen(const char* filename);
|
||||
int adb_auth_pubkey(const char* filename);
|
||||
std::string adb_auth_get_userkey();
|
||||
bssl::UniquePtr<EVP_PKEY> adb_auth_get_user_privkey();
|
||||
std::deque<std::shared_ptr<RSA>> adb_auth_get_private_keys();
|
||||
|
||||
void send_auth_response(const char* token, size_t token_size, atransport* t);
|
||||
|
||||
int adb_tls_set_certificate(SSL* ssl);
|
||||
void adb_auth_tls_handshake(atransport* t);
|
||||
|
||||
#else // !ADB_HOST
|
||||
|
||||
extern bool auth_required;
|
||||
|
||||
void adbd_auth_init(void);
|
||||
void adbd_auth_verified(atransport *t);
|
||||
|
||||
void adbd_cloexec_auth_socket();
|
||||
bool adbd_auth_verify(const char* token, size_t token_size, const std::string& sig,
|
||||
std::string* auth_key);
|
||||
void adbd_auth_confirm_key(atransport* t);
|
||||
void adbd_notify_framework_connected_key(atransport* t);
|
||||
|
||||
void send_auth_request(atransport *t);
|
||||
|
||||
void adbd_auth_tls_handshake(atransport* t);
|
||||
int adbd_tls_verify_cert(X509_STORE_CTX* ctx, std::string* auth_key);
|
||||
bssl::UniquePtr<STACK_OF(X509_NAME)> adbd_tls_client_ca_list();
|
||||
|
||||
#endif // ADB_HOST
|
||||
|
||||
#endif // __ADB_AUTH_H
|
|
@ -1,29 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
<configuration description="Config to run adb integration tests">
|
||||
<option name="test-suite-tag" value="adb_tests" />
|
||||
<option name="test-suite-tag" value="adb_integration" />
|
||||
<target_preparer class="com.android.tradefed.targetprep.SemaphoreTokenTargetPreparer">
|
||||
<option name="disable" value="false" />
|
||||
</target_preparer>
|
||||
|
||||
<target_preparer class="com.android.tradefed.targetprep.adb.AdbStopServerPreparer" />
|
||||
<test class="com.android.tradefed.testtype.python.PythonBinaryHostTest" >
|
||||
<option name="par-file-name" value="adb_integration_test_adb" />
|
||||
<option name="inject-android-serial" value="true" />
|
||||
<option name="test-timeout" value="2m" />
|
||||
</test>
|
||||
</configuration>
|
|
@ -1,29 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
<configuration description="Config to run adb integration tests for device">
|
||||
<option name="test-suite-tag" value="adb_tests" />
|
||||
<option name="test-suite-tag" value="adb_integration_device" />
|
||||
<target_preparer class="com.android.tradefed.targetprep.SemaphoreTokenTargetPreparer">
|
||||
<option name="disable" value="false" />
|
||||
</target_preparer>
|
||||
|
||||
<target_preparer class="com.android.tradefed.targetprep.adb.AdbStopServerPreparer" />
|
||||
<test class="com.android.tradefed.testtype.python.PythonBinaryHostTest" >
|
||||
<option name="par-file-name" value="adb_integration_test_device" />
|
||||
<option name="inject-android-serial" value="true" />
|
||||
<option name="test-timeout" value="2m" />
|
||||
</test>
|
||||
</configuration>
|
189
adb/adb_io.cpp
189
adb/adb_io.cpp
|
@ -1,189 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#define TRACE_TAG RWX
|
||||
|
||||
#include "adb_io.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#if !ADB_HOST
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#endif
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include <android-base/stringprintf.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "adb_trace.h"
|
||||
#include "adb_utils.h"
|
||||
#include "sysdeps.h"
|
||||
|
||||
bool SendProtocolString(borrowed_fd fd, std::string_view s) {
|
||||
unsigned int length = s.size();
|
||||
if (length > MAX_PAYLOAD - 4) {
|
||||
errno = EMSGSIZE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// The cost of sending two strings outweighs the cost of formatting.
|
||||
// "adb sync" performance is affected by this.
|
||||
auto str = android::base::StringPrintf("%04x", length).append(s);
|
||||
return WriteFdExactly(fd, str);
|
||||
}
|
||||
|
||||
bool ReadProtocolString(borrowed_fd fd, std::string* s, std::string* error) {
|
||||
char buf[5];
|
||||
if (!ReadFdExactly(fd, buf, 4)) {
|
||||
*error = perror_str("protocol fault (couldn't read status length)");
|
||||
return false;
|
||||
}
|
||||
buf[4] = 0;
|
||||
|
||||
unsigned long len = strtoul(buf, nullptr, 16);
|
||||
s->resize(len, '\0');
|
||||
if (!ReadFdExactly(fd, &(*s)[0], len)) {
|
||||
*error = perror_str("protocol fault (couldn't read status message)");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SendOkay(borrowed_fd fd) {
|
||||
return WriteFdExactly(fd, "OKAY", 4);
|
||||
}
|
||||
|
||||
bool SendFail(borrowed_fd fd, std::string_view reason) {
|
||||
return WriteFdExactly(fd, "FAIL", 4) && SendProtocolString(fd, reason);
|
||||
}
|
||||
|
||||
bool ReadFdExactly(borrowed_fd fd, void* buf, size_t len) {
|
||||
char* p = reinterpret_cast<char*>(buf);
|
||||
|
||||
size_t len0 = len;
|
||||
|
||||
D("readx: fd=%d wanted=%zu", fd.get(), len);
|
||||
while (len > 0) {
|
||||
int r = adb_read(fd, p, len);
|
||||
if (r > 0) {
|
||||
len -= r;
|
||||
p += r;
|
||||
} else if (r == -1) {
|
||||
D("readx: fd=%d error %d: %s", fd.get(), errno, strerror(errno));
|
||||
return false;
|
||||
} else {
|
||||
D("readx: fd=%d disconnected", fd.get());
|
||||
errno = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
VLOG(RWX) << "readx: fd=" << fd.get() << " wanted=" << len0 << " got=" << (len0 - len) << " "
|
||||
<< dump_hex(reinterpret_cast<const unsigned char*>(buf), len0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteFdExactly(borrowed_fd fd, const void* buf, size_t len) {
|
||||
const char* p = reinterpret_cast<const char*>(buf);
|
||||
int r;
|
||||
|
||||
VLOG(RWX) << "writex: fd=" << fd.get() << " len=" << len << " "
|
||||
<< dump_hex(reinterpret_cast<const unsigned char*>(buf), len);
|
||||
|
||||
while (len > 0) {
|
||||
r = adb_write(fd, p, len);
|
||||
if (r == -1) {
|
||||
D("writex: fd=%d error %d: %s", fd.get(), errno, strerror(errno));
|
||||
if (errno == EAGAIN) {
|
||||
std::this_thread::yield();
|
||||
continue;
|
||||
} else if (errno == EPIPE) {
|
||||
D("writex: fd=%d disconnected", fd.get());
|
||||
errno = 0;
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
len -= r;
|
||||
p += r;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteFdExactly(borrowed_fd fd, const char* str) {
|
||||
return WriteFdExactly(fd, str, strlen(str));
|
||||
}
|
||||
|
||||
bool WriteFdExactly(borrowed_fd fd, const std::string& str) {
|
||||
return WriteFdExactly(fd, str.c_str(), str.size());
|
||||
}
|
||||
|
||||
bool WriteFdFmt(borrowed_fd fd, const char* fmt, ...) {
|
||||
std::string str;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
android::base::StringAppendV(&str, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return WriteFdExactly(fd, str);
|
||||
}
|
||||
|
||||
bool ReadOrderlyShutdown(borrowed_fd fd) {
|
||||
char buf[16];
|
||||
|
||||
// Only call this function if you're sure that the peer does
|
||||
// orderly/graceful shutdown of the socket, closing the socket so that
|
||||
// adb_read() will return 0. If the peer keeps the socket open, adb_read()
|
||||
// will never return.
|
||||
int result = adb_read(fd, buf, sizeof(buf));
|
||||
if (result == -1) {
|
||||
// If errno is EAGAIN, that means this function was called on a
|
||||
// nonblocking socket and it would have blocked (which would be bad
|
||||
// because we'd probably block the main thread where nonblocking IO is
|
||||
// done). Don't do that. If you have a nonblocking socket, use the
|
||||
// fdevent APIs to get called on FDE_READ, and then call this function
|
||||
// if you really need to, but it shouldn't be needed for server sockets.
|
||||
CHECK_NE(errno, EAGAIN);
|
||||
|
||||
// Note that on Windows, orderly shutdown sometimes causes
|
||||
// recv() == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET. That
|
||||
// can be ignored.
|
||||
return false;
|
||||
} else if (result == 0) {
|
||||
// Peer has performed an orderly/graceful shutdown.
|
||||
return true;
|
||||
} else {
|
||||
// Unexpectedly received data. This is essentially a protocol error
|
||||
// because you should not call this function unless you expect no more
|
||||
// data. We don't repeatedly call adb_read() until we get zero because
|
||||
// we don't know how long that would take, but we do know that the
|
||||
// caller wants to close the socket soon.
|
||||
VLOG(RWX) << "ReadOrderlyShutdown(" << fd.get() << ") unexpectedly read "
|
||||
<< dump_hex(buf, result);
|
||||
// Shutdown the socket to prevent the caller from reading or writing to
|
||||
// it which doesn't make sense if we just read and discarded some data.
|
||||
adb_shutdown(fd);
|
||||
errno = EINVAL;
|
||||
return false;
|
||||
}
|
||||
}
|
78
adb/adb_io.h
78
adb/adb_io.h
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#ifndef ADB_IO_H
|
||||
#define ADB_IO_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "adb_unique_fd.h"
|
||||
|
||||
// Sends the protocol "OKAY" message.
|
||||
bool SendOkay(borrowed_fd fd);
|
||||
|
||||
// Sends the protocol "FAIL" message, with the given failure reason.
|
||||
bool SendFail(borrowed_fd fd, std::string_view reason);
|
||||
|
||||
// Writes a protocol-format string; a four hex digit length followed by the string data.
|
||||
bool SendProtocolString(borrowed_fd fd, std::string_view s);
|
||||
|
||||
// Reads a protocol-format string; a four hex digit length followed by the string data.
|
||||
bool ReadProtocolString(borrowed_fd fd, std::string* s, std::string* error);
|
||||
|
||||
// Reads exactly len bytes from fd into buf.
|
||||
//
|
||||
// Returns false if there is an error or if EOF was reached before len bytes
|
||||
// were read. If EOF was found, errno will be set to 0.
|
||||
//
|
||||
// If this function fails, the contents of buf are undefined.
|
||||
bool ReadFdExactly(borrowed_fd fd, void* buf, size_t len);
|
||||
|
||||
// Given a client socket, wait for orderly/graceful shutdown. Call this:
|
||||
//
|
||||
// * Before closing a client socket.
|
||||
// * Only when no more data is expected to come in.
|
||||
// * Only when the server is not waiting for data from the client (because then
|
||||
// the client and server will deadlock waiting for each other).
|
||||
// * Only when the server is expected to close its socket right now.
|
||||
// * Don't call shutdown(SHUT_WR) before calling this because that will shutdown
|
||||
// the client socket early, defeating the purpose of calling this.
|
||||
//
|
||||
// Waiting for orderly/graceful shutdown of the server socket will cause the
|
||||
// server socket to close before the client socket. That prevents the client
|
||||
// socket from staying in TIME_WAIT which eventually causes subsequent
|
||||
// connect()s from the client to fail with WSAEADDRINUSE on Windows.
|
||||
// Returns true if it is sure that orderly/graceful shutdown has occurred with
|
||||
// no additional data read from the server.
|
||||
bool ReadOrderlyShutdown(borrowed_fd fd);
|
||||
|
||||
// Writes exactly len bytes from buf to fd.
|
||||
//
|
||||
// Returns false if there is an error or if the fd was closed before the write
|
||||
// completed. If the other end of the fd (such as in a socket, pipe, or fifo),
|
||||
// is closed, errno will be set to 0.
|
||||
bool WriteFdExactly(borrowed_fd fd, const void* buf, size_t len);
|
||||
|
||||
// Same as above, but for strings.
|
||||
bool WriteFdExactly(borrowed_fd fd, const char* s);
|
||||
bool WriteFdExactly(borrowed_fd fd, const std::string& s);
|
||||
|
||||
// Same as above, but formats the string to send.
|
||||
bool WriteFdFmt(borrowed_fd fd, const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3)));
|
||||
#endif /* ADB_IO_H */
|
|
@ -1,156 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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 "adb_io.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <android-base/file.h>
|
||||
|
||||
// All of these tests fail on Windows because they use the C Runtime open(),
|
||||
// but the adb_io APIs expect file descriptors from adb_open(). This could
|
||||
// theoretically be fixed by making adb_read()/adb_write() fallback to using
|
||||
// read()/write() if an unrecognized fd is used, and by making adb_open() return
|
||||
// fds far from the range that open() returns. But all of that might defeat the
|
||||
// purpose of the tests.
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define POSIX_TEST(x,y) TEST(DISABLED_ ## x,y)
|
||||
#else
|
||||
#define POSIX_TEST TEST
|
||||
#endif
|
||||
|
||||
POSIX_TEST(io, ReadFdExactly_whole) {
|
||||
const char expected[] = "Foobar";
|
||||
TemporaryFile tf;
|
||||
ASSERT_NE(-1, tf.fd);
|
||||
|
||||
ASSERT_TRUE(android::base::WriteStringToFd(expected, tf.fd)) << strerror(errno);
|
||||
ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
|
||||
|
||||
// Test reading the whole file.
|
||||
char buf[sizeof(expected)] = {};
|
||||
ASSERT_TRUE(ReadFdExactly(tf.fd, buf, sizeof(buf) - 1)) << strerror(errno);
|
||||
EXPECT_STREQ(expected, buf);
|
||||
}
|
||||
|
||||
POSIX_TEST(io, ReadFdExactly_eof) {
|
||||
const char expected[] = "Foobar";
|
||||
TemporaryFile tf;
|
||||
ASSERT_NE(-1, tf.fd);
|
||||
|
||||
ASSERT_TRUE(android::base::WriteStringToFd(expected, tf.fd)) << strerror(errno);
|
||||
ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
|
||||
|
||||
// Test that not having enough data will fail.
|
||||
char buf[sizeof(expected) + 1] = {};
|
||||
ASSERT_FALSE(ReadFdExactly(tf.fd, buf, sizeof(buf)));
|
||||
EXPECT_EQ(0, errno) << strerror(errno);
|
||||
}
|
||||
|
||||
POSIX_TEST(io, ReadFdExactly_partial) {
|
||||
const char input[] = "Foobar";
|
||||
TemporaryFile tf;
|
||||
ASSERT_NE(-1, tf.fd);
|
||||
|
||||
ASSERT_TRUE(android::base::WriteStringToFd(input, tf.fd)) << strerror(errno);
|
||||
ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
|
||||
|
||||
// Test reading a partial file.
|
||||
char buf[sizeof(input) - 1] = {};
|
||||
ASSERT_TRUE(ReadFdExactly(tf.fd, buf, sizeof(buf) - 1));
|
||||
|
||||
std::string expected(input);
|
||||
expected.pop_back();
|
||||
EXPECT_STREQ(expected.c_str(), buf);
|
||||
}
|
||||
|
||||
POSIX_TEST(io, WriteFdExactly_whole) {
|
||||
const char expected[] = "Foobar";
|
||||
TemporaryFile tf;
|
||||
ASSERT_NE(-1, tf.fd);
|
||||
|
||||
// Test writing the whole string to the file.
|
||||
ASSERT_TRUE(WriteFdExactly(tf.fd, expected, sizeof(expected)))
|
||||
<< strerror(errno);
|
||||
ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
|
||||
|
||||
std::string s;
|
||||
ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s));
|
||||
EXPECT_STREQ(expected, s.c_str());
|
||||
}
|
||||
|
||||
POSIX_TEST(io, WriteFdExactly_partial) {
|
||||
const char buf[] = "Foobar";
|
||||
TemporaryFile tf;
|
||||
ASSERT_NE(-1, tf.fd);
|
||||
|
||||
// Test writing a partial string to the file.
|
||||
ASSERT_TRUE(WriteFdExactly(tf.fd, buf, sizeof(buf) - 2)) << strerror(errno);
|
||||
ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
|
||||
|
||||
std::string expected(buf);
|
||||
expected.pop_back();
|
||||
|
||||
std::string s;
|
||||
ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s));
|
||||
EXPECT_EQ(expected, s);
|
||||
}
|
||||
|
||||
POSIX_TEST(io, WriteFdExactly_ENOSPC) {
|
||||
int fd = open("/dev/full", O_WRONLY);
|
||||
ASSERT_NE(-1, fd);
|
||||
|
||||
char buf[] = "foo";
|
||||
ASSERT_FALSE(WriteFdExactly(fd, buf, sizeof(buf)));
|
||||
ASSERT_EQ(ENOSPC, errno);
|
||||
}
|
||||
|
||||
POSIX_TEST(io, WriteFdExactly_string) {
|
||||
const char str[] = "Foobar";
|
||||
TemporaryFile tf;
|
||||
ASSERT_NE(-1, tf.fd);
|
||||
|
||||
// Test writing a partial string to the file.
|
||||
ASSERT_TRUE(WriteFdExactly(tf.fd, str)) << strerror(errno);
|
||||
ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
|
||||
|
||||
std::string s;
|
||||
ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s));
|
||||
EXPECT_STREQ(str, s.c_str());
|
||||
}
|
||||
|
||||
POSIX_TEST(io, WriteFdFmt) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_NE(-1, tf.fd);
|
||||
|
||||
// Test writing a partial string to the file.
|
||||
ASSERT_TRUE(WriteFdFmt(tf.fd, "Foo%s%d", "bar", 123)) << strerror(errno);
|
||||
ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
|
||||
|
||||
std::string s;
|
||||
ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s));
|
||||
EXPECT_STREQ("Foobar123", s.c_str());
|
||||
}
|
|
@ -1,256 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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 "adb_listeners.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <android-base/thread_annotations.h>
|
||||
#include <cutils/sockets.h>
|
||||
|
||||
#include "socket_spec.h"
|
||||
#include "sysdeps.h"
|
||||
#include "transport.h"
|
||||
|
||||
// A listener is an entity which binds to a local port and, upon receiving a connection on that
|
||||
// port, creates an asocket to connect the new local connection to a specific remote service.
|
||||
//
|
||||
// TODO: some listeners read from the new connection to determine what exact service to connect to
|
||||
// on the far side.
|
||||
class alistener {
|
||||
public:
|
||||
alistener(const std::string& _local_name, const std::string& _connect_to);
|
||||
~alistener();
|
||||
|
||||
fdevent* fde = nullptr;
|
||||
int fd = -1;
|
||||
|
||||
std::string local_name;
|
||||
std::string connect_to;
|
||||
atransport* transport = nullptr;
|
||||
adisconnect disconnect;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(alistener);
|
||||
};
|
||||
|
||||
alistener::alistener(const std::string& _local_name, const std::string& _connect_to)
|
||||
: local_name(_local_name), connect_to(_connect_to) {
|
||||
}
|
||||
|
||||
alistener::~alistener() {
|
||||
// Closes the corresponding fd.
|
||||
fdevent_destroy(fde);
|
||||
|
||||
if (transport) {
|
||||
transport->RemoveDisconnect(&disconnect);
|
||||
}
|
||||
}
|
||||
|
||||
// listener_list retains ownership of all created alistener objects. Removing an alistener from
|
||||
// this list will cause it to be deleted.
|
||||
static auto& listener_list_mutex = *new std::mutex();
|
||||
typedef std::list<std::unique_ptr<alistener>> ListenerList;
|
||||
static ListenerList& listener_list GUARDED_BY(listener_list_mutex) = *new ListenerList();
|
||||
|
||||
#if ADB_HOST
|
||||
static void ss_listener_event_func(int _fd, unsigned ev, void *_l) {
|
||||
if (ev & FDE_READ) {
|
||||
unique_fd fd(adb_socket_accept(_fd, nullptr, nullptr));
|
||||
if (fd < 0) return;
|
||||
|
||||
int rcv_buf_size = CHUNK_SIZE;
|
||||
adb_setsockopt(fd.get(), SOL_SOCKET, SO_RCVBUF, &rcv_buf_size, sizeof(rcv_buf_size));
|
||||
|
||||
asocket* s = create_local_socket(std::move(fd));
|
||||
if (s) {
|
||||
connect_to_smartsocket(s);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void listener_event_func(int _fd, unsigned ev, void* _l)
|
||||
{
|
||||
alistener* listener = reinterpret_cast<alistener*>(_l);
|
||||
|
||||
if (ev & FDE_READ) {
|
||||
unique_fd fd(adb_socket_accept(_fd, nullptr, nullptr));
|
||||
if (fd < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
asocket* s = create_local_socket(std::move(fd));
|
||||
if (s) {
|
||||
s->transport = listener->transport;
|
||||
connect_to_remote(s, listener->connect_to);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Called as a transport disconnect function. |arg| is the raw alistener*.
|
||||
static void listener_disconnect(void* arg, atransport*) EXCLUDES(listener_list_mutex) {
|
||||
std::lock_guard<std::mutex> lock(listener_list_mutex);
|
||||
for (auto iter = listener_list.begin(); iter != listener_list.end(); ++iter) {
|
||||
if (iter->get() == arg) {
|
||||
(*iter)->transport = nullptr;
|
||||
listener_list.erase(iter);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write the list of current listeners (network redirections) into a string.
|
||||
std::string format_listeners() EXCLUDES(listener_list_mutex) {
|
||||
std::lock_guard<std::mutex> lock(listener_list_mutex);
|
||||
std::string result;
|
||||
for (auto& l : listener_list) {
|
||||
// Ignore special listeners like those for *smartsocket*
|
||||
if (l->connect_to[0] == '*') {
|
||||
continue;
|
||||
}
|
||||
// <device-serial> " " <local-name> " " <remote-name> "\n"
|
||||
// Entries from "adb reverse" have no serial.
|
||||
android::base::StringAppendF(
|
||||
&result, "%s %s %s\n",
|
||||
!l->transport->serial.empty() ? l->transport->serial.c_str() : "(reverse)",
|
||||
l->local_name.c_str(), l->connect_to.c_str());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
InstallStatus remove_listener(const char* local_name, atransport* transport)
|
||||
EXCLUDES(listener_list_mutex) {
|
||||
std::lock_guard<std::mutex> lock(listener_list_mutex);
|
||||
for (auto iter = listener_list.begin(); iter != listener_list.end(); ++iter) {
|
||||
if (local_name == (*iter)->local_name) {
|
||||
listener_list.erase(iter);
|
||||
return INSTALL_STATUS_OK;
|
||||
}
|
||||
}
|
||||
return INSTALL_STATUS_LISTENER_NOT_FOUND;
|
||||
}
|
||||
|
||||
void remove_all_listeners() EXCLUDES(listener_list_mutex) {
|
||||
std::lock_guard<std::mutex> lock(listener_list_mutex);
|
||||
auto iter = listener_list.begin();
|
||||
while (iter != listener_list.end()) {
|
||||
// Never remove smart sockets.
|
||||
if ((*iter)->connect_to[0] == '*') {
|
||||
++iter;
|
||||
} else {
|
||||
iter = listener_list.erase(iter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void enable_server_sockets() EXCLUDES(listener_list_mutex) {
|
||||
std::lock_guard<std::mutex> lock(listener_list_mutex);
|
||||
for (auto& l : listener_list) {
|
||||
if (l->connect_to == "*smartsocket*") {
|
||||
fdevent_set(l->fde, FDE_READ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if ADB_HOST
|
||||
void close_smartsockets() EXCLUDES(listener_list_mutex) {
|
||||
std::lock_guard<std::mutex> lock(listener_list_mutex);
|
||||
auto pred = [](const std::unique_ptr<alistener>& listener) {
|
||||
return listener->local_name == "*smartsocket*";
|
||||
};
|
||||
listener_list.remove_if(pred);
|
||||
}
|
||||
#endif
|
||||
|
||||
InstallStatus install_listener(const std::string& local_name, const char* connect_to,
|
||||
atransport* transport, int flags, int* resolved_tcp_port,
|
||||
std::string* error) EXCLUDES(listener_list_mutex) {
|
||||
std::lock_guard<std::mutex> lock(listener_list_mutex);
|
||||
for (auto& l : listener_list) {
|
||||
if (local_name == l->local_name) {
|
||||
// Can't repurpose a smartsocket.
|
||||
if (l->connect_to[0] == '*') {
|
||||
*error = "cannot repurpose smartsocket";
|
||||
return INSTALL_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
// Can't repurpose a listener if INSTALL_LISTENER_NO_REBIND is set
|
||||
if (flags & INSTALL_LISTENER_NO_REBIND) {
|
||||
*error = "cannot rebind";
|
||||
return INSTALL_STATUS_CANNOT_REBIND;
|
||||
}
|
||||
|
||||
l->connect_to = connect_to;
|
||||
if (l->transport != transport) {
|
||||
l->transport->RemoveDisconnect(&l->disconnect);
|
||||
l->transport = transport;
|
||||
l->transport->AddDisconnect(&l->disconnect);
|
||||
}
|
||||
return INSTALL_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
auto listener = std::make_unique<alistener>(local_name, connect_to);
|
||||
|
||||
int resolved = 0;
|
||||
listener->fd = socket_spec_listen(listener->local_name, error, &resolved);
|
||||
if (listener->fd < 0) {
|
||||
return INSTALL_STATUS_CANNOT_BIND;
|
||||
}
|
||||
|
||||
// If the caller requested port 0, update the listener name with the resolved port.
|
||||
if (resolved != 0) {
|
||||
listener->local_name = android::base::StringPrintf("tcp:%d", resolved);
|
||||
if (resolved_tcp_port) {
|
||||
*resolved_tcp_port = resolved;
|
||||
}
|
||||
}
|
||||
|
||||
close_on_exec(listener->fd);
|
||||
if (listener->connect_to == "*smartsocket*") {
|
||||
#if ADB_HOST
|
||||
listener->fde = fdevent_create(listener->fd, ss_listener_event_func, listener.get());
|
||||
#else
|
||||
LOG(FATAL) << "attempted to connect to *smartsocket* in daemon";
|
||||
#endif
|
||||
} else {
|
||||
listener->fde = fdevent_create(listener->fd, listener_event_func, listener.get());
|
||||
}
|
||||
if ((flags & INSTALL_LISTENER_DISABLED) == 0) {
|
||||
fdevent_set(listener->fde, FDE_READ);
|
||||
}
|
||||
|
||||
listener->transport = transport;
|
||||
|
||||
if (transport) {
|
||||
listener->disconnect.opaque = listener.get();
|
||||
listener->disconnect.func = listener_disconnect;
|
||||
transport->AddDisconnect(&listener->disconnect);
|
||||
}
|
||||
|
||||
listener_list.push_back(std::move(listener));
|
||||
return INSTALL_STATUS_OK;
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "adb.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <android-base/macros.h>
|
||||
|
||||
// error/status codes for install_listener.
|
||||
enum InstallStatus {
|
||||
INSTALL_STATUS_OK = 0,
|
||||
INSTALL_STATUS_INTERNAL_ERROR = -1,
|
||||
INSTALL_STATUS_CANNOT_BIND = -2,
|
||||
INSTALL_STATUS_CANNOT_REBIND = -3,
|
||||
INSTALL_STATUS_LISTENER_NOT_FOUND = -4,
|
||||
};
|
||||
|
||||
inline constexpr int INSTALL_LISTENER_NO_REBIND = 1 << 0;
|
||||
inline constexpr int INSTALL_LISTENER_DISABLED = 1 << 1;
|
||||
|
||||
InstallStatus install_listener(const std::string& local_name, const char* connect_to,
|
||||
atransport* transport, int flags, int* resolved_tcp_port,
|
||||
std::string* error);
|
||||
|
||||
std::string format_listeners();
|
||||
|
||||
InstallStatus remove_listener(const char* local_name, atransport* transport);
|
||||
void remove_all_listeners(void);
|
||||
|
||||
#if ADB_HOST
|
||||
void enable_server_sockets();
|
||||
void close_smartsockets();
|
||||
#endif
|
|
@ -1,166 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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 "adb_listeners.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/strings.h>
|
||||
|
||||
#include "fdevent/fdevent.h"
|
||||
#include "sysdeps.h"
|
||||
#include "transport.h"
|
||||
|
||||
// Returns true if the given listener is present in format_listeners(). Empty parameters will
|
||||
// be ignored.
|
||||
static bool listener_is_installed(const std::string& serial, const std::string& source,
|
||||
const std::string& dest) {
|
||||
// format_listeners() gives lines of "<serial> <source> <dest>\n".
|
||||
for (const std::string& line : android::base::Split(format_listeners(), "\n")) {
|
||||
std::vector<std::string> info = android::base::Split(line, " ");
|
||||
if (info.size() == 3 &&
|
||||
(serial.empty() || info[0] == serial) &&
|
||||
(source.empty() || info[1] == source) &&
|
||||
(dest.empty() || info[2] == dest)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
class AdbListenersTest : public ::testing::Test {
|
||||
public:
|
||||
void SetUp() override {
|
||||
// We don't need an fdevent loop, but adding/removing listeners must be done from the
|
||||
// fdevent thread if one exists. Since previously run tests may have created an fdevent
|
||||
// thread, we need to reset to prevent the thread check.
|
||||
fdevent_reset();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
// Clean up any listeners that may have been installed.
|
||||
remove_all_listeners();
|
||||
|
||||
// Make sure we didn't leave any dangling events.
|
||||
ASSERT_EQ(0u, fdevent_installed_count());
|
||||
}
|
||||
|
||||
protected:
|
||||
atransport transport_;
|
||||
};
|
||||
|
||||
TEST_F(AdbListenersTest, test_install_listener) {
|
||||
std::string error;
|
||||
|
||||
ASSERT_EQ(INSTALL_STATUS_OK,
|
||||
install_listener("tcp:9000", "tcp:9000", &transport_, false, nullptr, &error));
|
||||
ASSERT_TRUE(error.empty());
|
||||
|
||||
ASSERT_TRUE(listener_is_installed("", "tcp:9000", "tcp:9000"));
|
||||
}
|
||||
|
||||
TEST_F(AdbListenersTest, test_install_listener_rebind) {
|
||||
std::string error;
|
||||
|
||||
ASSERT_EQ(INSTALL_STATUS_OK,
|
||||
install_listener("tcp:9000", "tcp:9000", &transport_, false, nullptr, &error));
|
||||
ASSERT_TRUE(error.empty());
|
||||
|
||||
ASSERT_EQ(INSTALL_STATUS_OK,
|
||||
install_listener("tcp:9000", "tcp:9001", &transport_, false, nullptr, &error));
|
||||
ASSERT_TRUE(error.empty());
|
||||
|
||||
ASSERT_TRUE(listener_is_installed("", "tcp:9000", "tcp:9001"));
|
||||
}
|
||||
|
||||
TEST_F(AdbListenersTest, test_install_listener_no_rebind) {
|
||||
std::string error;
|
||||
|
||||
ASSERT_EQ(INSTALL_STATUS_OK,
|
||||
install_listener("tcp:9000", "tcp:9000", &transport_, true, nullptr, &error));
|
||||
ASSERT_TRUE(error.empty());
|
||||
|
||||
ASSERT_EQ(INSTALL_STATUS_CANNOT_REBIND,
|
||||
install_listener("tcp:9000", "tcp:9001", &transport_, true, nullptr, &error));
|
||||
ASSERT_FALSE(error.empty());
|
||||
|
||||
ASSERT_TRUE(listener_is_installed("", "tcp:9000", "tcp:9000"));
|
||||
}
|
||||
|
||||
TEST_F(AdbListenersTest, test_install_listener_tcp_port_0) {
|
||||
int port = 0;
|
||||
std::string error;
|
||||
|
||||
ASSERT_EQ(INSTALL_STATUS_OK,
|
||||
install_listener("tcp:0", "tcp:9000", &transport_, true, &port, &error));
|
||||
ASSERT_TRUE(error.empty());
|
||||
|
||||
ASSERT_TRUE(listener_is_installed("", android::base::StringPrintf("tcp:%d", port), "tcp:9000"));
|
||||
}
|
||||
|
||||
TEST_F(AdbListenersTest, test_remove_listener) {
|
||||
std::string error;
|
||||
|
||||
ASSERT_EQ(INSTALL_STATUS_OK,
|
||||
install_listener("tcp:9000", "tcp:9000", &transport_, false, nullptr, &error));
|
||||
ASSERT_TRUE(error.empty());
|
||||
|
||||
ASSERT_EQ(INSTALL_STATUS_OK, remove_listener("tcp:9000", &transport_));
|
||||
ASSERT_TRUE(format_listeners().empty());
|
||||
}
|
||||
|
||||
TEST_F(AdbListenersTest, test_remove_nonexistent_listener) {
|
||||
std::string error;
|
||||
|
||||
ASSERT_EQ(INSTALL_STATUS_OK,
|
||||
install_listener("tcp:9000", "tcp:9000", &transport_, false, nullptr, &error));
|
||||
ASSERT_TRUE(error.empty());
|
||||
|
||||
ASSERT_EQ(INSTALL_STATUS_LISTENER_NOT_FOUND, remove_listener("tcp:1", &transport_));
|
||||
ASSERT_TRUE(listener_is_installed("", "tcp:9000", "tcp:9000"));
|
||||
}
|
||||
|
||||
TEST_F(AdbListenersTest, test_remove_all_listeners) {
|
||||
std::string error;
|
||||
|
||||
ASSERT_EQ(INSTALL_STATUS_OK,
|
||||
install_listener("tcp:9000", "tcp:9000", &transport_, false, nullptr, &error));
|
||||
ASSERT_TRUE(error.empty());
|
||||
|
||||
ASSERT_EQ(INSTALL_STATUS_OK,
|
||||
install_listener("tcp:9001", "tcp:9001", &transport_, false, nullptr, &error));
|
||||
ASSERT_TRUE(error.empty());
|
||||
|
||||
remove_all_listeners();
|
||||
ASSERT_TRUE(format_listeners().empty());
|
||||
}
|
||||
|
||||
TEST_F(AdbListenersTest, test_transport_disconnect) {
|
||||
std::string error;
|
||||
|
||||
ASSERT_EQ(INSTALL_STATUS_OK,
|
||||
install_listener("tcp:9000", "tcp:9000", &transport_, false, nullptr, &error));
|
||||
ASSERT_TRUE(error.empty());
|
||||
|
||||
ASSERT_EQ(INSTALL_STATUS_OK,
|
||||
install_listener("tcp:9001", "tcp:9001", &transport_, false, nullptr, &error));
|
||||
ASSERT_TRUE(error.empty());
|
||||
|
||||
transport_.RunDisconnects();
|
||||
ASSERT_TRUE(format_listeners().empty());
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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.
|
||||
*/
|
||||
|
||||
#ifndef _ADB_MDNS_H_
|
||||
#define _ADB_MDNS_H_
|
||||
|
||||
#include <android-base/macros.h>
|
||||
|
||||
// The rules for Service Names [RFC6335] state that they may be no more
|
||||
// than fifteen characters long (not counting the mandatory underscore),
|
||||
// consisting of only letters, digits, and hyphens, must begin and end
|
||||
// with a letter or digit, must not contain consecutive hyphens, and
|
||||
// must contain at least one letter.
|
||||
#define ADB_MDNS_SERVICE_TYPE "adb"
|
||||
#define ADB_MDNS_TLS_PAIRING_TYPE "adb-tls-pairing"
|
||||
#define ADB_MDNS_TLS_CONNECT_TYPE "adb-tls-connect"
|
||||
|
||||
const int kADBTransportServiceRefIndex = 0;
|
||||
const int kADBSecurePairingServiceRefIndex = 1;
|
||||
const int kADBSecureConnectServiceRefIndex = 2;
|
||||
|
||||
// Each ADB Secure service advertises with a TXT record indicating the version
|
||||
// using a key/value pair per RFC 6763 (https://tools.ietf.org/html/rfc6763).
|
||||
//
|
||||
// The first key/value pair is always the version of the protocol.
|
||||
// There may be more key/value pairs added after.
|
||||
//
|
||||
// The version is purposely represented as the single letter "v" due to the
|
||||
// need to minimize DNS traffic. The version starts at 1. With each breaking
|
||||
// protocol change, the version is incremented by 1.
|
||||
//
|
||||
// Newer adb clients/daemons need to recognize and either reject
|
||||
// or be backward-compatible with older verseions if there is a mismatch.
|
||||
//
|
||||
// Relevant sections:
|
||||
//
|
||||
// """
|
||||
// 6.4. Rules for Keys in DNS-SD Key/Value Pairs
|
||||
//
|
||||
// The key MUST be at least one character. DNS-SD TXT record strings
|
||||
// beginning with an '=' character (i.e., the key is missing) MUST be
|
||||
// silently ignored.
|
||||
//
|
||||
// ...
|
||||
//
|
||||
// 6.5. Rules for Values in DNS-SD Key/Value Pairs
|
||||
//
|
||||
// If there is an '=' in a DNS-SD TXT record string, then everything
|
||||
// after the first '=' to the end of the string is the value. The value
|
||||
// can contain any eight-bit values including '='.
|
||||
// """
|
||||
|
||||
#define ADB_SECURE_SERVICE_VERSION_TXT_RECORD(ver) ("v=" #ver)
|
||||
|
||||
// Client/service versions are initially defined to be matching,
|
||||
// but may go out of sync as different clients and services
|
||||
// try to talk to each other.
|
||||
#define ADB_SECURE_SERVICE_VERSION 1
|
||||
#define ADB_SECURE_CLIENT_VERSION ADB_SECURE_SERVICE_VERSION
|
||||
|
||||
const char* kADBSecurePairingServiceTxtRecord =
|
||||
ADB_SECURE_SERVICE_VERSION_TXT_RECORD(ADB_SECURE_SERVICE_VERSION);
|
||||
const char* kADBSecureConnectServiceTxtRecord =
|
||||
ADB_SECURE_SERVICE_VERSION_TXT_RECORD(ADB_SECURE_SERVICE_VERSION);
|
||||
|
||||
#define ADB_FULL_MDNS_SERVICE_TYPE(atype) ("_" atype "._tcp")
|
||||
const char* kADBDNSServices[] = {ADB_FULL_MDNS_SERVICE_TYPE(ADB_MDNS_SERVICE_TYPE),
|
||||
ADB_FULL_MDNS_SERVICE_TYPE(ADB_MDNS_TLS_PAIRING_TYPE),
|
||||
ADB_FULL_MDNS_SERVICE_TYPE(ADB_MDNS_TLS_CONNECT_TYPE)};
|
||||
|
||||
const char* kADBDNSServiceTxtRecords[] = {
|
||||
nullptr,
|
||||
kADBSecurePairingServiceTxtRecord,
|
||||
kADBSecureConnectServiceTxtRecord,
|
||||
};
|
||||
|
||||
const int kNumADBDNSServices = arraysize(kADBDNSServices);
|
||||
|
||||
#endif
|
|
@ -1,37 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2020 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.
|
||||
-->
|
||||
<!-- This test config file is auto-generated. -->
|
||||
<configuration description="Runs adbd_test.">
|
||||
<option name="test-suite-tag" value="apct" />
|
||||
<option name="test-suite-tag" value="apct-native" />
|
||||
|
||||
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
|
||||
</target_preparer>
|
||||
|
||||
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
|
||||
<option name="cleanup" value="true" />
|
||||
<option name="push" value="adbd_test->/data/local/tmp/adbd_test" />
|
||||
</target_preparer>
|
||||
|
||||
<test class="com.android.tradefed.testtype.GTest" >
|
||||
<option name="native-test-device-path" value="/data/local/tmp" />
|
||||
<option name="module-name" value="adbd_test" />
|
||||
</test>
|
||||
|
||||
<object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
|
||||
<option name="mainline-module-package-name" value="com.google.android.adbd" />
|
||||
</object>
|
||||
</configuration>
|
|
@ -1,192 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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 "sysdeps.h"
|
||||
#include "adb_trace.h"
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/strings.h>
|
||||
|
||||
#include "adb.h"
|
||||
|
||||
#if !ADB_HOST
|
||||
#include <android-base/properties.h>
|
||||
#endif
|
||||
|
||||
#if !ADB_HOST
|
||||
const char* adb_device_banner = "device";
|
||||
#if defined(__ANDROID__)
|
||||
static android::base::LogdLogger gLogdLogger;
|
||||
#endif
|
||||
#else
|
||||
const char* adb_device_banner = "host";
|
||||
#endif
|
||||
|
||||
void AdbLogger(android::base::LogId id, android::base::LogSeverity severity,
|
||||
const char* tag, const char* file, unsigned int line,
|
||||
const char* message) {
|
||||
android::base::StderrLogger(id, severity, tag, file, line, message);
|
||||
#if defined(_WIN32)
|
||||
// stderr can be buffered on Windows (and setvbuf doesn't seem to work), so explicitly flush.
|
||||
fflush(stderr);
|
||||
#endif
|
||||
|
||||
#if !ADB_HOST && defined(__ANDROID__)
|
||||
// Only print logs of INFO or higher to logcat, so that `adb logcat` with adbd tracing on
|
||||
// doesn't result in exponential logging.
|
||||
if (severity >= android::base::INFO) {
|
||||
gLogdLogger(id, severity, tag, file, line, message);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#if !ADB_HOST
|
||||
static std::string get_log_file_name() {
|
||||
struct tm now;
|
||||
time_t t;
|
||||
tzset();
|
||||
time(&t);
|
||||
localtime_r(&t, &now);
|
||||
|
||||
char timestamp[PATH_MAX];
|
||||
strftime(timestamp, sizeof(timestamp), "%Y-%m-%d-%H-%M-%S", &now);
|
||||
|
||||
return android::base::StringPrintf("/data/adb/adb-%s-%d", timestamp,
|
||||
getpid());
|
||||
}
|
||||
|
||||
void start_device_log(void) {
|
||||
int fd = unix_open(get_log_file_name(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0640);
|
||||
if (fd == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Redirect stdout and stderr to the log file.
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
fprintf(stderr, "--- adb starting (pid %d) ---\n", getpid());
|
||||
unix_close(fd);
|
||||
}
|
||||
#endif
|
||||
|
||||
int adb_trace_mask;
|
||||
|
||||
std::string get_trace_setting() {
|
||||
#if ADB_HOST || !defined(__ANDROID__)
|
||||
const char* setting = getenv("ADB_TRACE");
|
||||
if (setting == nullptr) {
|
||||
setting = "";
|
||||
}
|
||||
return setting;
|
||||
#else
|
||||
return android::base::GetProperty("persist.adb.trace_mask", "");
|
||||
#endif
|
||||
}
|
||||
|
||||
// Split the space separated list of tags from the trace setting and build the
|
||||
// trace mask from it. note that '1' and 'all' are special cases to enable all
|
||||
// tracing.
|
||||
//
|
||||
// adb's trace setting comes from the ADB_TRACE environment variable, whereas
|
||||
// adbd's comes from the system property persist.adb.trace_mask.
|
||||
static void setup_trace_mask() {
|
||||
const std::string trace_setting = get_trace_setting();
|
||||
if (trace_setting.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, int> trace_flags = {{"1", -1},
|
||||
{"all", -1},
|
||||
{"adb", ADB},
|
||||
{"sockets", SOCKETS},
|
||||
{"packets", PACKETS},
|
||||
{"rwx", RWX},
|
||||
{"usb", USB},
|
||||
{"sync", SYNC},
|
||||
{"sysdeps", SYSDEPS},
|
||||
{"transport", TRANSPORT},
|
||||
{"jdwp", JDWP},
|
||||
{"services", SERVICES},
|
||||
{"auth", AUTH},
|
||||
{"fdevent", FDEVENT},
|
||||
{"shell", SHELL},
|
||||
{"incremental", INCREMENTAL}};
|
||||
|
||||
std::vector<std::string> elements = android::base::Split(trace_setting, " ");
|
||||
for (const auto& elem : elements) {
|
||||
const auto& flag = trace_flags.find(elem);
|
||||
if (flag == trace_flags.end()) {
|
||||
LOG(ERROR) << "Unknown trace flag: " << elem;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (flag->second == -1) {
|
||||
// -1 is used for the special values "1" and "all" that enable all
|
||||
// tracing.
|
||||
adb_trace_mask = ~0;
|
||||
break;
|
||||
} else {
|
||||
adb_trace_mask |= 1 << flag->second;
|
||||
}
|
||||
}
|
||||
|
||||
if (adb_trace_mask != 0) {
|
||||
android::base::SetMinimumLogSeverity(android::base::VERBOSE);
|
||||
}
|
||||
}
|
||||
|
||||
void adb_trace_init(char** argv) {
|
||||
#if !ADB_HOST
|
||||
// Don't open log file if no tracing, since this will block
|
||||
// the crypto unmount of /data
|
||||
if (!get_trace_setting().empty()) {
|
||||
if (unix_isatty(STDOUT_FILENO) == 0) {
|
||||
start_device_log();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ADB_HOST && !defined(_WIN32)
|
||||
// adb historically ignored $ANDROID_LOG_TAGS but passed it through to logcat.
|
||||
// If set, move it out of the way so that libbase logging doesn't try to parse it.
|
||||
std::string log_tags;
|
||||
char* ANDROID_LOG_TAGS = getenv("ANDROID_LOG_TAGS");
|
||||
if (ANDROID_LOG_TAGS) {
|
||||
log_tags = ANDROID_LOG_TAGS;
|
||||
unsetenv("ANDROID_LOG_TAGS");
|
||||
}
|
||||
#endif
|
||||
|
||||
android::base::InitLogging(argv, &AdbLogger);
|
||||
|
||||
#if ADB_HOST && !defined(_WIN32)
|
||||
// Put $ANDROID_LOG_TAGS back so we can pass it to logcat.
|
||||
if (!log_tags.empty()) setenv("ANDROID_LOG_TAGS", log_tags.c_str(), 1);
|
||||
#endif
|
||||
|
||||
setup_trace_mask();
|
||||
|
||||
VLOG(ADB) << adb_version();
|
||||
}
|
||||
|
||||
void adb_trace_enable(AdbTrace trace_tag) {
|
||||
adb_trace_mask |= (1 << trace_tag);
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2014 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.
|
||||
*/
|
||||
|
||||
#ifndef __ADB_TRACE_H
|
||||
#define __ADB_TRACE_H
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
|
||||
/* IMPORTANT: if you change the following list, don't
|
||||
* forget to update the corresponding 'tags' table in
|
||||
* the adb_trace_init() function implemented in adb_trace.cpp.
|
||||
*/
|
||||
enum AdbTrace {
|
||||
ADB = 0, /* 0x001 */
|
||||
SOCKETS,
|
||||
PACKETS,
|
||||
TRANSPORT,
|
||||
RWX, /* 0x010 */
|
||||
USB,
|
||||
SYNC,
|
||||
SYSDEPS,
|
||||
JDWP, /* 0x100 */
|
||||
SERVICES,
|
||||
AUTH,
|
||||
FDEVENT,
|
||||
SHELL,
|
||||
INCREMENTAL,
|
||||
};
|
||||
|
||||
#define VLOG_IS_ON(TAG) \
|
||||
((adb_trace_mask & (1 << (TAG))) != 0)
|
||||
|
||||
#define VLOG(TAG) \
|
||||
if (LIKELY(!VLOG_IS_ON(TAG))) \
|
||||
; \
|
||||
else \
|
||||
LOG(DEBUG)
|
||||
|
||||
// You must define TRACE_TAG before using this macro.
|
||||
#define D(...) \
|
||||
VLOG(TRACE_TAG) << android::base::StringPrintf(__VA_ARGS__)
|
||||
|
||||
|
||||
extern int adb_trace_mask;
|
||||
void adb_trace_init(char**);
|
||||
void adb_trace_enable(AdbTrace trace_tag);
|
||||
|
||||
#endif /* __ADB_TRACE_H */
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
* 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 "adb_unique_fd.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
void AdbCloser::Close(int fd) {
|
||||
adb_close(fd);
|
||||
}
|
||||
#endif
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
// Helper to automatically close an FD when it goes out of scope.
|
||||
struct AdbCloser {
|
||||
static void Close(int fd);
|
||||
};
|
||||
|
||||
using unique_fd = android::base::unique_fd_impl<AdbCloser>;
|
||||
#else
|
||||
using unique_fd = android::base::unique_fd;
|
||||
#endif
|
||||
|
||||
using android::base::borrowed_fd;
|
||||
|
||||
template <typename T>
|
||||
int adb_close(const android::base::unique_fd_impl<T>&)
|
||||
__attribute__((__unavailable__("adb_close called on unique_fd")));
|
|
@ -1,380 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#define TRACE_TAG ADB
|
||||
|
||||
#include "adb_utils.h"
|
||||
#include "adb_unique_fd.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/parseint.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/strings.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "adb_trace.h"
|
||||
#include "sysdeps.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
# ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# endif
|
||||
# include "windows.h"
|
||||
# include "shlobj.h"
|
||||
#else
|
||||
#include <pwd.h>
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(_WIN32)
|
||||
static constexpr char kNullFileName[] = "NUL";
|
||||
#else
|
||||
static constexpr char kNullFileName[] = "/dev/null";
|
||||
#endif
|
||||
|
||||
void close_stdin() {
|
||||
int fd = unix_open(kNullFileName, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
PLOG(FATAL) << "failed to open " << kNullFileName;
|
||||
}
|
||||
|
||||
if (TEMP_FAILURE_RETRY(dup2(fd, STDIN_FILENO)) == -1) {
|
||||
PLOG(FATAL) << "failed to redirect stdin to " << kNullFileName;
|
||||
}
|
||||
unix_close(fd);
|
||||
}
|
||||
|
||||
bool getcwd(std::string* s) {
|
||||
char* cwd = getcwd(nullptr, 0);
|
||||
if (cwd != nullptr) *s = cwd;
|
||||
free(cwd);
|
||||
return (cwd != nullptr);
|
||||
}
|
||||
|
||||
bool directory_exists(const std::string& path) {
|
||||
struct stat sb;
|
||||
return stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode);
|
||||
}
|
||||
|
||||
std::string escape_arg(const std::string& s) {
|
||||
// Escape any ' in the string (before we single-quote the whole thing).
|
||||
// The correct way to do this for the shell is to replace ' with '\'' --- that is,
|
||||
// close the existing single-quoted string, escape a single single-quote, and start
|
||||
// a new single-quoted string. Like the C preprocessor, the shell will concatenate
|
||||
// these pieces into one string.
|
||||
|
||||
std::string result;
|
||||
result.push_back('\'');
|
||||
|
||||
size_t base = 0;
|
||||
while (true) {
|
||||
size_t found = s.find('\'', base);
|
||||
result.append(s, base, found - base);
|
||||
if (found == s.npos) break;
|
||||
result.append("'\\''");
|
||||
base = found + 1;
|
||||
}
|
||||
|
||||
result.push_back('\'');
|
||||
return result;
|
||||
}
|
||||
|
||||
// Given a relative or absolute filepath, create the directory hierarchy
|
||||
// as needed. Returns true if the hierarchy is/was setup.
|
||||
bool mkdirs(const std::string& path) {
|
||||
// TODO: all the callers do unlink && mkdirs && adb_creat ---
|
||||
// that's probably the operation we should expose.
|
||||
|
||||
// Implementation Notes:
|
||||
//
|
||||
// Pros:
|
||||
// - Uses dirname, so does not need to deal with OS_PATH_SEPARATOR.
|
||||
// - On Windows, uses mingw dirname which accepts '/' and '\\', drive letters
|
||||
// (C:\foo), UNC paths (\\server\share\dir\dir\file), and Unicode (when
|
||||
// combined with our adb_mkdir() which takes UTF-8).
|
||||
// - Is optimistic wrt thinking that a deep directory hierarchy will exist.
|
||||
// So it does as few stat()s as possible before doing mkdir()s.
|
||||
// Cons:
|
||||
// - Recursive, so it uses stack space relative to number of directory
|
||||
// components.
|
||||
|
||||
// If path points to a symlink to a directory, that's fine.
|
||||
struct stat sb;
|
||||
if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string parent(android::base::Dirname(path));
|
||||
|
||||
// If dirname returned the same path as what we passed in, don't go recursive.
|
||||
// This can happen on Windows when walking up the directory hierarchy and not
|
||||
// finding anything that already exists (unlike POSIX that will eventually
|
||||
// find . or /).
|
||||
if (parent == path) {
|
||||
errno = ENOENT;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Recursively make parent directories of 'path'.
|
||||
if (!mkdirs(parent)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now that the parent directory hierarchy of 'path' has been ensured,
|
||||
// create path itself.
|
||||
if (adb_mkdir(path, 0775) == -1) {
|
||||
const int saved_errno = errno;
|
||||
// If someone else created the directory, that is ok.
|
||||
if (directory_exists(path)) {
|
||||
return true;
|
||||
}
|
||||
// There might be a pre-existing file at 'path', or there might have been some other error.
|
||||
errno = saved_errno;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string dump_hex(const void* data, size_t byte_count) {
|
||||
size_t truncate_len = 16;
|
||||
bool truncated = false;
|
||||
if (byte_count > truncate_len) {
|
||||
byte_count = truncate_len;
|
||||
truncated = true;
|
||||
}
|
||||
|
||||
const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
|
||||
|
||||
std::string line;
|
||||
for (size_t i = 0; i < byte_count; ++i) {
|
||||
android::base::StringAppendF(&line, "%02x", p[i]);
|
||||
}
|
||||
line.push_back(' ');
|
||||
|
||||
for (size_t i = 0; i < byte_count; ++i) {
|
||||
int ch = p[i];
|
||||
line.push_back(isprint(ch) ? ch : '.');
|
||||
}
|
||||
|
||||
if (truncated) {
|
||||
line += " [truncated]";
|
||||
}
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
std::string dump_header(const amessage* msg) {
|
||||
unsigned command = msg->command;
|
||||
int len = msg->data_length;
|
||||
char cmd[9];
|
||||
char arg0[12], arg1[12];
|
||||
int n;
|
||||
|
||||
for (n = 0; n < 4; n++) {
|
||||
int b = (command >> (n * 8)) & 255;
|
||||
if (b < 32 || b >= 127) break;
|
||||
cmd[n] = (char)b;
|
||||
}
|
||||
if (n == 4) {
|
||||
cmd[4] = 0;
|
||||
} else {
|
||||
// There is some non-ASCII name in the command, so dump the hexadecimal value instead
|
||||
snprintf(cmd, sizeof cmd, "%08x", command);
|
||||
}
|
||||
|
||||
if (msg->arg0 < 256U)
|
||||
snprintf(arg0, sizeof arg0, "%d", msg->arg0);
|
||||
else
|
||||
snprintf(arg0, sizeof arg0, "0x%x", msg->arg0);
|
||||
|
||||
if (msg->arg1 < 256U)
|
||||
snprintf(arg1, sizeof arg1, "%d", msg->arg1);
|
||||
else
|
||||
snprintf(arg1, sizeof arg1, "0x%x", msg->arg1);
|
||||
|
||||
return android::base::StringPrintf("[%s] arg0=%s arg1=%s (len=%d) ", cmd, arg0, arg1, len);
|
||||
}
|
||||
|
||||
std::string dump_packet(const char* name, const char* func, const apacket* p) {
|
||||
std::string result = name;
|
||||
result += ": ";
|
||||
result += func;
|
||||
result += ": ";
|
||||
result += dump_header(&p->msg);
|
||||
result += dump_hex(p->payload.data(), p->payload.size());
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string perror_str(const char* msg) {
|
||||
return android::base::StringPrintf("%s: %s", msg, strerror(errno));
|
||||
}
|
||||
|
||||
#if !defined(_WIN32)
|
||||
// Windows version provided in sysdeps_win32.cpp
|
||||
bool set_file_block_mode(borrowed_fd fd, bool block) {
|
||||
int flags = fcntl(fd.get(), F_GETFL, 0);
|
||||
if (flags == -1) {
|
||||
PLOG(ERROR) << "failed to fcntl(F_GETFL) for fd " << fd.get();
|
||||
return false;
|
||||
}
|
||||
flags = block ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
|
||||
if (fcntl(fd.get(), F_SETFL, flags) != 0) {
|
||||
PLOG(ERROR) << "failed to fcntl(F_SETFL) for fd " << fd.get() << ", flags " << flags;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool forward_targets_are_valid(const std::string& source, const std::string& dest,
|
||||
std::string* error) {
|
||||
if (android::base::StartsWith(source, "tcp:")) {
|
||||
// The source port may be 0 to allow the system to select an open port.
|
||||
int port;
|
||||
if (!android::base::ParseInt(&source[4], &port) || port < 0) {
|
||||
*error = android::base::StringPrintf("Invalid source port: '%s'", &source[4]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (android::base::StartsWith(dest, "tcp:")) {
|
||||
// The destination port must be > 0.
|
||||
int port;
|
||||
if (!android::base::ParseInt(&dest[4], &port) || port <= 0) {
|
||||
*error = android::base::StringPrintf("Invalid destination port: '%s'", &dest[4]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string adb_get_homedir_path() {
|
||||
#ifdef _WIN32
|
||||
WCHAR path[MAX_PATH];
|
||||
const HRESULT hr = SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, path);
|
||||
if (FAILED(hr)) {
|
||||
D("SHGetFolderPathW failed: %s", android::base::SystemErrorCodeToString(hr).c_str());
|
||||
return {};
|
||||
}
|
||||
std::string home_str;
|
||||
if (!android::base::WideToUTF8(path, &home_str)) {
|
||||
return {};
|
||||
}
|
||||
return home_str;
|
||||
#else
|
||||
if (const char* const home = getenv("HOME")) {
|
||||
return home;
|
||||
}
|
||||
|
||||
struct passwd pwent;
|
||||
struct passwd* result;
|
||||
int pwent_max = sysconf(_SC_GETPW_R_SIZE_MAX);
|
||||
if (pwent_max == -1) {
|
||||
pwent_max = 16384;
|
||||
}
|
||||
std::vector<char> buf(pwent_max);
|
||||
int rc = getpwuid_r(getuid(), &pwent, buf.data(), buf.size(), &result);
|
||||
if (rc == 0 && result) {
|
||||
return result->pw_dir;
|
||||
}
|
||||
|
||||
LOG(FATAL) << "failed to get user home directory";
|
||||
return {};
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string adb_get_android_dir_path() {
|
||||
std::string user_dir = adb_get_homedir_path();
|
||||
std::string android_dir = user_dir + OS_PATH_SEPARATOR + ".android";
|
||||
struct stat buf;
|
||||
if (stat(android_dir.c_str(), &buf) == -1) {
|
||||
if (adb_mkdir(android_dir, 0750) == -1) {
|
||||
PLOG(FATAL) << "Cannot mkdir '" << android_dir << "'";
|
||||
}
|
||||
}
|
||||
return android_dir;
|
||||
}
|
||||
|
||||
std::string GetLogFilePath() {
|
||||
// https://issuetracker.google.com/112588493
|
||||
const char* path = getenv("ANDROID_ADB_LOG_PATH");
|
||||
if (path) return path;
|
||||
|
||||
#if defined(_WIN32)
|
||||
const char log_name[] = "adb.log";
|
||||
WCHAR temp_path[MAX_PATH];
|
||||
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa364992%28v=vs.85%29.aspx
|
||||
DWORD nchars = GetTempPathW(arraysize(temp_path), temp_path);
|
||||
if (nchars >= arraysize(temp_path) || nchars == 0) {
|
||||
// If string truncation or some other error.
|
||||
LOG(FATAL) << "cannot retrieve temporary file path: "
|
||||
<< android::base::SystemErrorCodeToString(GetLastError());
|
||||
}
|
||||
|
||||
std::string temp_path_utf8;
|
||||
if (!android::base::WideToUTF8(temp_path, &temp_path_utf8)) {
|
||||
PLOG(FATAL) << "cannot convert temporary file path from UTF-16 to UTF-8";
|
||||
}
|
||||
|
||||
return temp_path_utf8 + log_name;
|
||||
#else
|
||||
const char* tmp_dir = getenv("TMPDIR");
|
||||
if (tmp_dir == nullptr) tmp_dir = "/tmp";
|
||||
return android::base::StringPrintf("%s/adb.%u.log", tmp_dir, getuid());
|
||||
#endif
|
||||
}
|
||||
|
||||
[[noreturn]] static void error_exit_va(int error, const char* fmt, va_list va) {
|
||||
fflush(stdout);
|
||||
fprintf(stderr, "%s: ", android::base::Basename(android::base::GetExecutablePath()).c_str());
|
||||
|
||||
vfprintf(stderr, fmt, va);
|
||||
|
||||
if (error != 0) {
|
||||
fprintf(stderr, ": %s", strerror(error));
|
||||
}
|
||||
|
||||
putc('\n', stderr);
|
||||
fflush(stderr);
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
void error_exit(const char* fmt, ...) {
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
error_exit_va(0, fmt, va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
void perror_exit(const char* fmt, ...) {
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
error_exit_va(errno, fmt, va);
|
||||
va_end(va);
|
||||
}
|
129
adb/adb_utils.h
129
adb/adb_utils.h
|
@ -1,129 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <charconv>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/macros.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "adb_unique_fd.h"
|
||||
|
||||
void close_stdin();
|
||||
|
||||
bool getcwd(std::string* cwd);
|
||||
bool directory_exists(const std::string& path);
|
||||
|
||||
// Return the user's home directory.
|
||||
std::string adb_get_homedir_path();
|
||||
|
||||
// Return the adb user directory.
|
||||
std::string adb_get_android_dir_path();
|
||||
|
||||
bool mkdirs(const std::string& path);
|
||||
|
||||
std::string escape_arg(const std::string& s);
|
||||
|
||||
std::string dump_hex(const void* ptr, size_t byte_count);
|
||||
std::string dump_header(const amessage* msg);
|
||||
std::string dump_packet(const char* name, const char* func, const apacket* p);
|
||||
|
||||
std::string perror_str(const char* msg);
|
||||
|
||||
[[noreturn]] void error_exit(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
|
||||
[[noreturn]] void perror_exit(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
|
||||
|
||||
bool set_file_block_mode(borrowed_fd fd, bool block);
|
||||
|
||||
// Given forward/reverse targets, returns true if they look valid. If an error is found, fills
|
||||
// |error| and returns false.
|
||||
// Currently this only checks "tcp:" targets. Additional checking could be added for other targets
|
||||
// if needed.
|
||||
bool forward_targets_are_valid(const std::string& source, const std::string& dest,
|
||||
std::string* error);
|
||||
|
||||
// A thread-safe blocking queue.
|
||||
template <typename T>
|
||||
class BlockingQueue {
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
std::vector<T> queue;
|
||||
|
||||
public:
|
||||
void Push(const T& t) {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
queue.push_back(t);
|
||||
}
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
template <typename Fn>
|
||||
void PopAll(Fn fn) {
|
||||
std::vector<T> popped;
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
cv.wait(lock, [this]() { return !queue.empty(); });
|
||||
popped = std::move(queue);
|
||||
queue.clear();
|
||||
}
|
||||
|
||||
for (const T& t : popped) {
|
||||
fn(t);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::string GetLogFilePath();
|
||||
|
||||
inline std::string_view StripTrailingNulls(std::string_view str) {
|
||||
size_t n = 0;
|
||||
for (auto it = str.rbegin(); it != str.rend(); ++it) {
|
||||
if (*it != '\0') {
|
||||
break;
|
||||
}
|
||||
++n;
|
||||
}
|
||||
|
||||
str.remove_suffix(n);
|
||||
return str;
|
||||
}
|
||||
|
||||
// Base-10 stroll on a string_view.
|
||||
template <typename T>
|
||||
inline bool ParseUint(T* result, std::string_view str, std::string_view* remaining = nullptr) {
|
||||
T value;
|
||||
const auto res = std::from_chars(str.begin(), str.end(), value);
|
||||
if (res.ec != std::errc{}) {
|
||||
return false;
|
||||
}
|
||||
if (res.ptr != str.end() && !remaining) {
|
||||
return false;
|
||||
}
|
||||
if (remaining) {
|
||||
*remaining = std::string_view(res.ptr, str.end() - res.ptr);
|
||||
}
|
||||
*result = value;
|
||||
return true;
|
||||
}
|
|
@ -1,235 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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 "adb_utils.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <userenv.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/macros.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
static std::string subdir(const char* parent, const char* child) {
|
||||
std::string str(parent);
|
||||
str += OS_PATH_SEPARATOR;
|
||||
str += child;
|
||||
return str;
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(adb_utils, directory_exists) {
|
||||
#ifdef _WIN32
|
||||
char profiles_dir[MAX_PATH];
|
||||
DWORD cch = arraysize(profiles_dir);
|
||||
|
||||
// On typical Windows 7, returns C:\Users
|
||||
ASSERT_TRUE(GetProfilesDirectoryA(profiles_dir, &cch));
|
||||
|
||||
ASSERT_TRUE(directory_exists(profiles_dir));
|
||||
|
||||
ASSERT_FALSE(directory_exists(subdir(profiles_dir, "does-not-exist")));
|
||||
#else
|
||||
ASSERT_TRUE(directory_exists("/proc"));
|
||||
ASSERT_FALSE(directory_exists("/proc/does-not-exist"));
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
TEST(adb_utils, directory_exists_win32_symlink_junction) {
|
||||
char profiles_dir[MAX_PATH];
|
||||
DWORD cch = arraysize(profiles_dir);
|
||||
|
||||
// On typical Windows 7, returns C:\Users
|
||||
ASSERT_TRUE(GetProfilesDirectoryA(profiles_dir, &cch));
|
||||
|
||||
// On modern (English?) Windows, this is a directory symbolic link to
|
||||
// C:\ProgramData. Symbolic links are rare on Windows and the user requires
|
||||
// a special permission (by default granted to Administrative users) to
|
||||
// create symbolic links.
|
||||
EXPECT_FALSE(directory_exists(subdir(profiles_dir, "All Users")));
|
||||
|
||||
// On modern (English?) Windows, this is a directory junction to
|
||||
// C:\Users\Default. Junctions are used throughout user profile directories
|
||||
// for backwards compatibility and they don't require any special permissions
|
||||
// to create.
|
||||
EXPECT_FALSE(directory_exists(subdir(profiles_dir, "Default User")));
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(adb_utils, escape_arg) {
|
||||
EXPECT_EQ(R"('')", escape_arg(""));
|
||||
|
||||
EXPECT_EQ(R"('abc')", escape_arg("abc"));
|
||||
|
||||
auto wrap = [](const std::string& x) { return '\'' + x + '\''; };
|
||||
const std::string q = R"('\'')";
|
||||
EXPECT_EQ(wrap(q), escape_arg("'"));
|
||||
EXPECT_EQ(wrap(q + q), escape_arg("''"));
|
||||
EXPECT_EQ(wrap(q + "abc" + q), escape_arg("'abc'"));
|
||||
EXPECT_EQ(wrap(q + "abc"), escape_arg("'abc"));
|
||||
EXPECT_EQ(wrap("abc" + q), escape_arg("abc'"));
|
||||
EXPECT_EQ(wrap("abc" + q + "def"), escape_arg("abc'def"));
|
||||
EXPECT_EQ(wrap("a" + q + "b" + q + "c"), escape_arg("a'b'c"));
|
||||
EXPECT_EQ(wrap("a" + q + "bcde" + q + "f"), escape_arg("a'bcde'f"));
|
||||
|
||||
EXPECT_EQ(R"(' abc')", escape_arg(" abc"));
|
||||
EXPECT_EQ(R"('"abc')", escape_arg("\"abc"));
|
||||
EXPECT_EQ(R"('\abc')", escape_arg("\\abc"));
|
||||
EXPECT_EQ(R"('(abc')", escape_arg("(abc"));
|
||||
EXPECT_EQ(R"(')abc')", escape_arg(")abc"));
|
||||
|
||||
EXPECT_EQ(R"('abc abc')", escape_arg("abc abc"));
|
||||
EXPECT_EQ(R"('abc"abc')", escape_arg("abc\"abc"));
|
||||
EXPECT_EQ(R"('abc\abc')", escape_arg("abc\\abc"));
|
||||
EXPECT_EQ(R"('abc(abc')", escape_arg("abc(abc"));
|
||||
EXPECT_EQ(R"('abc)abc')", escape_arg("abc)abc"));
|
||||
|
||||
EXPECT_EQ(R"('abc ')", escape_arg("abc "));
|
||||
EXPECT_EQ(R"('abc"')", escape_arg("abc\""));
|
||||
EXPECT_EQ(R"('abc\')", escape_arg("abc\\"));
|
||||
EXPECT_EQ(R"('abc(')", escape_arg("abc("));
|
||||
EXPECT_EQ(R"('abc)')", escape_arg("abc)"));
|
||||
}
|
||||
|
||||
void test_mkdirs(const std::string& basepath) {
|
||||
// Test creating a directory hierarchy.
|
||||
ASSERT_TRUE(mkdirs(basepath));
|
||||
// Test finding an existing directory hierarchy.
|
||||
ASSERT_TRUE(mkdirs(basepath));
|
||||
// Test mkdirs on an existing hierarchy with a trailing slash.
|
||||
ASSERT_TRUE(mkdirs(basepath + '/'));
|
||||
#if defined(_WIN32)
|
||||
ASSERT_TRUE(mkdirs(basepath + '\\'));
|
||||
#endif
|
||||
|
||||
const std::string filepath = basepath + "/file";
|
||||
// Verify that the hierarchy was created by trying to create a file in it.
|
||||
ASSERT_NE(-1, adb_creat(filepath.c_str(), 0600));
|
||||
// If a file exists where we want a directory, the operation should fail.
|
||||
ASSERT_FALSE(mkdirs(filepath));
|
||||
}
|
||||
|
||||
TEST(adb_utils, mkdirs) {
|
||||
TemporaryDir td;
|
||||
|
||||
// Absolute paths.
|
||||
test_mkdirs(std::string(td.path) + "/dir/subdir");
|
||||
|
||||
// Relative paths.
|
||||
ASSERT_EQ(0, chdir(td.path)) << strerror(errno);
|
||||
test_mkdirs(std::string("relative/subrel"));
|
||||
}
|
||||
|
||||
#if !defined(_WIN32)
|
||||
TEST(adb_utils, set_file_block_mode) {
|
||||
unique_fd fd(adb_open("/dev/null", O_RDWR | O_APPEND));
|
||||
ASSERT_GE(fd, 0);
|
||||
int flags = fcntl(fd.get(), F_GETFL, 0);
|
||||
ASSERT_EQ(O_RDWR | O_APPEND, (flags & (O_RDWR | O_APPEND)));
|
||||
ASSERT_TRUE(set_file_block_mode(fd, false));
|
||||
int new_flags = fcntl(fd.get(), F_GETFL, 0);
|
||||
ASSERT_EQ(flags | O_NONBLOCK, new_flags);
|
||||
ASSERT_TRUE(set_file_block_mode(fd, true));
|
||||
new_flags = fcntl(fd.get(), F_GETFL, 0);
|
||||
ASSERT_EQ(flags, new_flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(adb_utils, test_forward_targets_are_valid) {
|
||||
std::string error;
|
||||
|
||||
// Source port can be >= 0.
|
||||
EXPECT_FALSE(forward_targets_are_valid("tcp:-1", "tcp:9000", &error));
|
||||
EXPECT_TRUE(forward_targets_are_valid("tcp:0", "tcp:9000", &error));
|
||||
EXPECT_TRUE(forward_targets_are_valid("tcp:8000", "tcp:9000", &error));
|
||||
|
||||
// Destination port must be >0.
|
||||
EXPECT_FALSE(forward_targets_are_valid("tcp:8000", "tcp:-1", &error));
|
||||
EXPECT_FALSE(forward_targets_are_valid("tcp:8000", "tcp:0", &error));
|
||||
|
||||
// Port must be a number.
|
||||
EXPECT_FALSE(forward_targets_are_valid("tcp:", "tcp:9000", &error));
|
||||
EXPECT_FALSE(forward_targets_are_valid("tcp:a", "tcp:9000", &error));
|
||||
EXPECT_FALSE(forward_targets_are_valid("tcp:22x", "tcp:9000", &error));
|
||||
EXPECT_FALSE(forward_targets_are_valid("tcp:8000", "tcp:", &error));
|
||||
EXPECT_FALSE(forward_targets_are_valid("tcp:8000", "tcp:a", &error));
|
||||
EXPECT_FALSE(forward_targets_are_valid("tcp:8000", "tcp:22x", &error));
|
||||
}
|
||||
|
||||
void TestParseUint(std::string_view string, bool expected_success, uint32_t expected_value = 0) {
|
||||
// Standalone.
|
||||
{
|
||||
uint32_t value;
|
||||
std::string_view remaining;
|
||||
bool success = ParseUint(&value, string, &remaining);
|
||||
EXPECT_EQ(success, expected_success);
|
||||
if (expected_success) {
|
||||
EXPECT_EQ(value, expected_value);
|
||||
}
|
||||
EXPECT_TRUE(remaining.empty());
|
||||
}
|
||||
|
||||
// With trailing text.
|
||||
{
|
||||
std::string text = std::string(string) + "foo";
|
||||
uint32_t value;
|
||||
std::string_view remaining;
|
||||
bool success = ParseUint(&value, text, &remaining);
|
||||
EXPECT_EQ(success, expected_success);
|
||||
if (expected_success) {
|
||||
EXPECT_EQ(value, expected_value);
|
||||
EXPECT_EQ(remaining, "foo");
|
||||
}
|
||||
}
|
||||
|
||||
// With trailing text, without remaining.
|
||||
{
|
||||
std::string text = std::string(string) + "foo";
|
||||
uint32_t value;
|
||||
bool success = ParseUint(&value, text, nullptr);
|
||||
EXPECT_EQ(success, false);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(adb_utils, ParseUint) {
|
||||
TestParseUint("", false);
|
||||
TestParseUint("foo", false);
|
||||
TestParseUint("foo123", false);
|
||||
TestParseUint("-1", false);
|
||||
|
||||
TestParseUint("123", true, 123);
|
||||
TestParseUint("9999999999999999999999999", false);
|
||||
TestParseUint(std::to_string(UINT32_MAX), true, UINT32_MAX);
|
||||
TestParseUint("0" + std::to_string(UINT32_MAX), true, UINT32_MAX);
|
||||
TestParseUint(std::to_string(static_cast<uint64_t>(UINT32_MAX) + 1), false);
|
||||
TestParseUint("0" + std::to_string(static_cast<uint64_t>(UINT32_MAX) + 1), false);
|
||||
|
||||
std::string x = std::to_string(UINT32_MAX) + "123";
|
||||
std::string_view substr = std::string_view(x).substr(0, std::to_string(UINT32_MAX).size());
|
||||
TestParseUint(substr, true, UINT32_MAX);
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "adb.h"
|
||||
|
||||
#if ADB_HOST
|
||||
|
||||
void adb_wifi_init(void);
|
||||
void adb_wifi_pair_device(const std::string& host, const std::string& password,
|
||||
std::string& response);
|
||||
bool adb_wifi_is_known_host(const std::string& host);
|
||||
|
||||
std::string mdns_check();
|
||||
std::string mdns_list_discovered_services();
|
||||
|
||||
struct MdnsInfo {
|
||||
std::string service_name;
|
||||
std::string service_type;
|
||||
std::string addr;
|
||||
uint16_t port = 0;
|
||||
|
||||
MdnsInfo(std::string_view name, std::string_view type, std::string_view addr, uint16_t port)
|
||||
: service_name(name), service_type(type), addr(addr), port(port) {}
|
||||
};
|
||||
|
||||
std::optional<MdnsInfo> mdns_get_connect_service_info(std::string_view name);
|
||||
std::optional<MdnsInfo> mdns_get_pairing_service_info(std::string_view name);
|
||||
|
||||
#else // !ADB_HOST
|
||||
|
||||
struct AdbdAuthContext;
|
||||
|
||||
void adbd_wifi_init(AdbdAuthContext* ctx);
|
||||
void adbd_wifi_secure_connect(atransport* t);
|
||||
|
||||
#endif
|
|
@ -1,55 +0,0 @@
|
|||
apex_defaults {
|
||||
name: "com.android.adbd-defaults",
|
||||
updatable: true,
|
||||
min_sdk_version: "R",
|
||||
|
||||
binaries: ["adbd"],
|
||||
compile_multilib: "both",
|
||||
multilib: {
|
||||
both: {
|
||||
native_shared_libs: [
|
||||
"libadb_pairing_auth",
|
||||
"libadb_pairing_connection",
|
||||
"libadb_pairing_server",
|
||||
"libadbconnection_client",
|
||||
],
|
||||
},
|
||||
},
|
||||
prebuilts: ["com.android.adbd.init.rc"],
|
||||
|
||||
key: "com.android.adbd.key",
|
||||
certificate: ":com.android.adbd.certificate",
|
||||
}
|
||||
|
||||
apex {
|
||||
name: "com.android.adbd",
|
||||
defaults: ["com.android.adbd-defaults"],
|
||||
manifest: "apex_manifest.json",
|
||||
}
|
||||
|
||||
// adbd apex with INT_MAX version code, to allow for upgrade/rollback testing.
|
||||
apex_test {
|
||||
name: "test_com.android.adbd",
|
||||
defaults: ["com.android.adbd-defaults"],
|
||||
manifest: "test_apex_manifest.json",
|
||||
file_contexts: ":com.android.adbd-file_contexts",
|
||||
installable: false,
|
||||
}
|
||||
|
||||
prebuilt_etc {
|
||||
name: "com.android.adbd.init.rc",
|
||||
src: "adbd.rc",
|
||||
filename: "init.rc",
|
||||
installable: false,
|
||||
}
|
||||
|
||||
apex_key {
|
||||
name: "com.android.adbd.key",
|
||||
public_key: "com.android.adbd.avbpubkey",
|
||||
private_key: "com.android.adbd.pem",
|
||||
}
|
||||
|
||||
android_app_certificate {
|
||||
name: "com.android.adbd.certificate",
|
||||
certificate: "com.android.adbd",
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
service adbd /apex/com.android.adbd/bin/adbd --root_seclabel=u:r:su:s0
|
||||
class core
|
||||
socket adbd seqpacket 660 system system
|
||||
disabled
|
||||
override
|
||||
seclabel u:r:adbd:s0
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"name": "com.android.adbd",
|
||||
"version": 300900700
|
||||
}
|
Binary file not shown.
|
@ -1,51 +0,0 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIJKQIBAAKCAgEAwUmO4l/ZdLhmBcBtpwDjih6z6bC7iZDPAVgnFVnYuYDRlVDA
|
||||
9OCDwv02Wwc/YCNzON7vt7JBk3o9wyJZpqY9HR1PUjk2DJa/wHtxbskmLcqsvcoh
|
||||
wZxmMkgx1mFyni/vQ0tCjjxYmDcnpoVmSntoPG4LBTZRwbgE2roYSuEi7q88Z9+t
|
||||
cFiQ5x7MqVTzUFsi1E+rpsxRaTt6Ly9DO71yR1gMTqONsSgmFm8f2HhUCiQzRh7H
|
||||
qLwk8eN5ZLPLVc1JBqo8swuH5pR9whR8HaYyQtK1VANRR9oVj3JpRXmyFUk8QjEn
|
||||
91I3sFV1lErdP1uh6xi6ewMBp+mQ+ccNFiNJs8PHVprzbEgX2ah45Tnge95ZwnkR
|
||||
V/5G/EwGBsggk/BcZjQyj0PExG6LmygR7lq8q4m9ODJj3cmNLZsZu8ukMBxf4Fim
|
||||
4/Y7lyaelW0FL+x3CR27wlIxLyIf/JfUNv/cFO/O2MHrDHYdHtCbvg8vpq1MZtDN
|
||||
+gJIkYQNUfBEtGS4SkH3WWfNet3bcL5yFx5IVdwCY+n635jPA1fvr1vcIiKnyGUm
|
||||
zNE+jMOZkgk6lPPuDwllAX0D8nYTm1eBMCTAWCePO0QlcFHCT9j1/xKbFbjt/xYI
|
||||
0pXuOc8/1n61F5ybzH/91cS66gqmYUAekUiP0osTIZ7idVFJMoqpc9m7+rECAwEA
|
||||
AQKCAgEAkjg9WU89SCk/NNavnQj1GUXEwOKr3JOppdC0MFi5tQuYgSaH8jfuNZIs
|
||||
joxbCzWGMt2j5wl4xkJRes7/lyxnSyEjIoaZNsjL4qb/1tlggn+yUhkZlEfmn98x
|
||||
pIYvmS+WBwhmHwfT1cLTwgtkqK/W2PA+cgD3tF6rfXQOcIcEUCBMyB/UKws1A0Kv
|
||||
fOIA9ycaoBZtOk+SvtL5ybwtVoIoc4ROOydLR1uiBJKoOrA8kzdzenZKgIFkSYDW
|
||||
ErJY/l3AAsTCCoiMlIh84ldw1VUm7JpOBnJECOEYMl5Q+PfpGmU+qqxZGaYe7syX
|
||||
mElSOl3tjdY1LF3H4Oi2fd5xLfAgDgQjXcawKRYpImEgbqNfEUHW4BE/uVp0hHn+
|
||||
W0tCq9hvWoizhjxVq7oEfpdCXJBH0bTg9h3Ho2nuJMHTrUVbSWPTqNJn1xOi4Oxl
|
||||
vWsD5qjOOVw1e0P1dtxQ+6a8+rCL8LDvIthQC9Wpt0yXduEi/vUWiMFx2VbcSpNn
|
||||
5PB9HK7vvCpR/k0IocaTKt80D3m2svJCnfrekRx/7n//x8imrvtvaYNpoToTSN0q
|
||||
hPOpTNc77R4aARJNXm4sVHzGs6HUXsJfODJdjFtTuaDHjLvRoXZi2wFUVWBvIaFg
|
||||
/4+PHXjsfMkY15KULKn3f7Xs7K6rmINAb853zti3Qkllv1EeYoECggEBAP9t1Jxe
|
||||
hLKnVrJ5jJ0zCT0/ez6qM5cQG8YvXbVICmoAOQ+/NV6qjPABg5j8FuNhpyr45OuJ
|
||||
m1oISLgZPVCbIvYx3oZS4ekWUp9Z7jlDGzsWiBCkEUFLRzDLQRUl4bQMI2SWM+vD
|
||||
RL9AAM+NHJQ8LJN7ASNdSQw9ZinNCSByCZ52QjPCfRON0OPY4l1FJKHHymzBNXpe
|
||||
R5e9a1o9KEIhd7j+3YX9y8SOVrbUe6U8me5LZ6RY+pLB+cA/UHcSQK23hYAkMcvL
|
||||
MQny6B57P6rquzFZDG/OUOZWzWub2FSYTTmiYSHPAuB15FyWShs7h7+wK8y2xrSM
|
||||
Lq3FWHxzR1OK2HkCggEBAMG4KsAU/lp9rQhNpdw2NQXqbDLgHy09BFMOOWhyp2/Z
|
||||
2lbDo9aP746Q56HAfRRgx5oAAtr3SxeN/R/uEJLYzzDU+SrG4TQO/TZ3DPZOAVYM
|
||||
oESWG/HXLN4Hw6j4iWt2NvqpnSVJrvYr6zar/QxRHOMwnUoUV3ugmzUkqFC/Nwmm
|
||||
nMGJbTQbEha8OyatfwejmhrCkbQMBiCk0AQmgLybUxs2ckGs5jibau7VqXVxly0f
|
||||
WkAsWE/qfybQl4oyBhGCFNObr3Co/PHTaD4ACFQQvaEEF4bTuh6wP+MIgJKxL8IB
|
||||
SkrKWO5PFbJWY5lacnNMe7ITrWy60HukLlJe5or5lfkCggEBAP3Rwghw1CRDrR9F
|
||||
Mbm0UWYPgwTOVN20ICVcRB40LEURW6KOOxaLG+oTVxXay1PAYkGNes2jvEBHIxvt
|
||||
2MQUpTVIcPvBuMPKbufykYtNZ+3bgfInVw4vI9sU3uOI9TPZLAJ0T7vkGpiBnUyh
|
||||
yNh0w0b6YDMoK8KB8Ndw67TWHUDd+wM8LNYVgpInnylX4ALzae+QPvgOX84laFwP
|
||||
kcXFRBcNDExt2uLDHuAnXYbhJYVqYN8rnDPhlbC4OdlYxfTZ/UtMrD769wwP2SER
|
||||
ED9jagirmHQx7Ko3b4GTJ/FINtUiyqqx7wXloLtwjMtq6IZPJfcTWXloI6qCBGAG
|
||||
ncYinuECggEAfZeiF8BEm3RpTz3QL3HxdHFkTqOhctnhSNuq+n2C8nBCLwhN21ic
|
||||
DkkB84txTFnmboBdWYsEYzQKDL5yflIUGeup00L3VKH3Jm2OuM0f7qLm8TCE04kW
|
||||
rKhKAO2JYmNVB7QZjsgzp6QXre1ZdLfNy7mD8Dg584vPtGecvCUMULR1YsBvTV3T
|
||||
n2vPyaan+dLmoTzN6/XzrwxLVLWFt0HYYoctEkk/RSn17PwXDm5jfbya7YoSg1Vb
|
||||
tFV+Oflul8FHMV35I0hcHYhbR/8LZz0nRBH8EsyIGUdZVB76BKDdfqEJgm2ntHEP
|
||||
dvytPAo4s2m9tFkvkZOYgOCTq5GdVDK2OQKCAQAsz+y9rDcqFciCESu4IHzmtckT
|
||||
0kwP2W5ds5hzUjbY0Y2AKTx2oHNOFak6WW5vxN0+OIn37SNK3RBStPWJiigut4R4
|
||||
rGrZM4pijm53s3cWzd0h8XyLGisl2zORu8gD2IQLkQf79F3lEZHGA+J0mkSHB85N
|
||||
IuqReFzL6cfOToNd+8WYjMgJcXmVuKiCV1FRK3jrqNpXO2cLtnhFvQMxRUAYU4j+
|
||||
2MIdBFVeMq5ftMMOBS21hM9cNLlOzoSN2HmK+ZkmrlTCK0G9lmF427/lqXTmWLed
|
||||
sspOUbOLfBOwxdCCc9JUxz5KggKWcDcTqX25M0mv09rpuCxIEg8lez1Ax0if
|
||||
-----END RSA PRIVATE KEY-----
|
Binary file not shown.
|
@ -1,35 +0,0 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIGHzCCBAegAwIBAgIUW8npFHXBP+wsEAesGMBxaV7TScAwDQYJKoZIhvcNAQEL
|
||||
BQAwgZ0xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
|
||||
DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy
|
||||
b2lkMRkwFwYDVQQDDBBjb20uYW5kcm9pZC5hZGJkMSIwIAYJKoZIhvcNAQkBFhNh
|
||||
bmRyb2lkQGFuZHJvaWQuY29tMCAXDTE5MDgxNTE5MzkxM1oYDzQ3NTcwNzExMTkz
|
||||
OTEzWjCBnTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNV
|
||||
BAcMDU1vdW50YWluIFZpZXcxEDAOBgNVBAoMB0FuZHJvaWQxEDAOBgNVBAsMB0Fu
|
||||
ZHJvaWQxGTAXBgNVBAMMEGNvbS5hbmRyb2lkLmFkYmQxIjAgBgkqhkiG9w0BCQEW
|
||||
E2FuZHJvaWRAYW5kcm9pZC5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
|
||||
AoICAQC6zbUeWi5vNA6vCC4FBrJQ9re4UexP6TabsDYvWpFBoCluvMkT2ZRmpXMF
|
||||
W7EzQ5VmuUvZgLYVHuJmnvHIV3uaRc2VE1SV+spjWTRt+6DtsAN7irR5K66POWMp
|
||||
+tr5hASdQBVOJdebimsepy0pH6sXREvanrrFzkSM/2Ho0unlwWJ5Y4jcnvdkVHI5
|
||||
Ks0vifLmX4y5mYgv1dcXYWzyYx39f8HyePv0cjRhYXiIEYZ49KWU4MjryvQe/mAu
|
||||
MQuMp901BLps2W1+oKyPPA4DV69KUXgF66RFfsjjkJJ/CSeQGzTuez+UWzFk3Duc
|
||||
6MmbiL1LTki3vyyVtjW1rYFO2s+M6Pa5NZWHgA55uUxiJ987WPyK9lWnMsY6YeKa
|
||||
FDBfS1JUzXGPzVncgM7LLvzAEibLdhjII88NsJvzPoHK0SluSn+E7t7iGO1fTjkD
|
||||
Js94iUJAp8OQ4GwkcTVgtEAR+NXzownNjHJ6qpiq6tXRqXdBqSat/glf01AgNDtz
|
||||
9AGeW7Mz6FqTdOzg3U4lu77+CGd3SZTuQk8C8PUDNhqhQX5H2qhr90bakGaXuYfE
|
||||
rWFzIjrVdJIznV1BimOCay5HyyHab4FWlVhAvslEQb2BpHRyi2lhe0laupOpmN44
|
||||
LzfjFM18bi2GashIi2OQuYDyAeT5mGtR2g8mC7g44H6dH+wTfQIDAQABo1MwUTAd
|
||||
BgNVHQ4EFgQU7lyyxPO5SOOh9a5O0l4+RjckcgcwHwYDVR0jBBgwFoAU7lyyxPO5
|
||||
SOOh9a5O0l4+RjckcgcwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC
|
||||
AgEAStsOy8bkbZg/Ygx47bPkLSz0cJIvATxTChUGOabkz+brLis88ABVWVP0IXps
|
||||
tlLlZR5cjXBJguE7GJXzKPWzQZuB8+YwcGHG6QDFpfdMeGrxPDhwNfGy236ArVnx
|
||||
K0v1IIxoZRZ0P7aubk3xwUAPgsmT5ayZCKu+dqlEy5B6ioKEsr7Y2RRT/8ifERNm
|
||||
cjS9AhcyWrp4R3cjy2iA/RpdsPFwE5ac3I+GtUB4D2up5aDMsy85i9t2/1kuTUaA
|
||||
9UHwGXCpcqP8f8BqeLzuxDzYkAvkntlNxbXn1cbn+dTRIOCBoDbtSeqtxhWooOUH
|
||||
RQROeRsB7iicdYJJRge0+WyR+216AKUSQPE6/rT0Ifr06ZRwi22/YyySpwuO3SNA
|
||||
+yWffh+f4h31Dz+p6pu8wjbMDkq4LnCWyjLwfF/yhvWhwwm5+KPAEhvJABeHQc+3
|
||||
cslOC9dlXJm9sPoUC7ghmUiFsCmN2hIzQrr2QoK0Obh0AGexOvOAw9cqtOdZQncB
|
||||
bqC8c4sVYScVxwDWkg0lNfRMC5boPjBsl7+M2CC1ukgVpXTyDOEjMWILrBXfYCDX
|
||||
unBH3kbKQOfL5RT0nE1Lkt1rn5qAWMJg4mvS4QuIurbRtEoj3QYQadF9md4qJXs0
|
||||
TvqvY8iEC4xrWU2SQn1K3PutXgaLP9/b6Cy1SBrhBX+AC5s=
|
||||
-----END CERTIFICATE-----
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"name": "com.android.adbd",
|
||||
"version": 2147483647
|
||||
}
|
|
@ -1,156 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
import os
|
||||
import statistics
|
||||
import subprocess
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
import adb
|
||||
|
||||
def lock_min(device):
|
||||
device.shell_nocheck(["""
|
||||
for x in /sys/devices/system/cpu/cpu?/cpufreq; do
|
||||
echo userspace > $x/scaling_governor
|
||||
cat $x/scaling_min_freq > $x/scaling_setspeed
|
||||
done
|
||||
"""])
|
||||
|
||||
def lock_max(device):
|
||||
device.shell_nocheck(["""
|
||||
for x in /sys/devices/system/cpu/cpu?/cpufreq; do
|
||||
echo userspace > $x/scaling_governor
|
||||
cat $x/scaling_max_freq > $x/scaling_setspeed
|
||||
done
|
||||
"""])
|
||||
|
||||
def unlock(device):
|
||||
device.shell_nocheck(["""
|
||||
for x in /sys/devices/system/cpu/cpu?/cpufreq; do
|
||||
echo ondemand > $x/scaling_governor
|
||||
echo sched > $x/scaling_governor
|
||||
echo schedutil > $x/scaling_governor
|
||||
done
|
||||
"""])
|
||||
|
||||
def harmonic_mean(xs):
|
||||
return 1.0 / statistics.mean([1.0 / x for x in xs])
|
||||
|
||||
def analyze(name, speeds):
|
||||
median = statistics.median(speeds)
|
||||
mean = harmonic_mean(speeds)
|
||||
stddev = statistics.stdev(speeds)
|
||||
msg = "%s: %d runs: median %.2f MiB/s, mean %.2f MiB/s, stddev: %.2f MiB/s"
|
||||
print(msg % (name, len(speeds), median, mean, stddev))
|
||||
|
||||
def benchmark_sink(device=None, size_mb=100):
|
||||
if device == None:
|
||||
device = adb.get_device()
|
||||
|
||||
speeds = list()
|
||||
cmd = device.adb_cmd + ["raw", "sink:%d" % (size_mb * 1024 * 1024)]
|
||||
|
||||
with tempfile.TemporaryFile() as tmpfile:
|
||||
tmpfile.truncate(size_mb * 1024 * 1024)
|
||||
|
||||
for _ in range(0, 10):
|
||||
tmpfile.seek(0)
|
||||
begin = time.time()
|
||||
subprocess.check_call(cmd, stdin=tmpfile)
|
||||
end = time.time()
|
||||
speeds.append(size_mb / float(end - begin))
|
||||
|
||||
analyze("sink %dMiB" % size_mb, speeds)
|
||||
|
||||
def benchmark_source(device=None, size_mb=100):
|
||||
if device == None:
|
||||
device = adb.get_device()
|
||||
|
||||
speeds = list()
|
||||
cmd = device.adb_cmd + ["raw", "source:%d" % (size_mb * 1024 * 1024)]
|
||||
|
||||
with open(os.devnull, 'w') as devnull:
|
||||
for _ in range(0, 10):
|
||||
begin = time.time()
|
||||
subprocess.check_call(cmd, stdout=devnull)
|
||||
end = time.time()
|
||||
speeds.append(size_mb / float(end - begin))
|
||||
|
||||
analyze("source %dMiB" % size_mb, speeds)
|
||||
|
||||
def benchmark_push(device=None, file_size_mb=100):
|
||||
if device == None:
|
||||
device = adb.get_device()
|
||||
|
||||
remote_path = "/dev/null"
|
||||
local_path = "/tmp/adb_benchmark_temp"
|
||||
|
||||
with open(local_path, "wb") as f:
|
||||
f.truncate(file_size_mb * 1024 * 1024)
|
||||
|
||||
speeds = list()
|
||||
for _ in range(0, 10):
|
||||
begin = time.time()
|
||||
device.push(local=local_path, remote=remote_path)
|
||||
end = time.time()
|
||||
speeds.append(file_size_mb / float(end - begin))
|
||||
|
||||
analyze("push %dMiB" % file_size_mb, speeds)
|
||||
|
||||
def benchmark_pull(device=None, file_size_mb=100):
|
||||
if device == None:
|
||||
device = adb.get_device()
|
||||
|
||||
remote_path = "/data/local/tmp/adb_benchmark_temp"
|
||||
local_path = "/tmp/adb_benchmark_temp"
|
||||
|
||||
device.shell(["dd", "if=/dev/zero", "of=" + remote_path, "bs=1m",
|
||||
"count=" + str(file_size_mb)])
|
||||
speeds = list()
|
||||
for _ in range(0, 10):
|
||||
begin = time.time()
|
||||
device.pull(remote=remote_path, local=local_path)
|
||||
end = time.time()
|
||||
speeds.append(file_size_mb / float(end - begin))
|
||||
|
||||
analyze("pull %dMiB" % file_size_mb, speeds)
|
||||
|
||||
def benchmark_shell(device=None, file_size_mb=100):
|
||||
if device == None:
|
||||
device = adb.get_device()
|
||||
|
||||
speeds = list()
|
||||
for _ in range(0, 10):
|
||||
begin = time.time()
|
||||
device.shell(["dd", "if=/dev/zero", "bs=1m",
|
||||
"count=" + str(file_size_mb)])
|
||||
end = time.time()
|
||||
speeds.append(file_size_mb / float(end - begin))
|
||||
|
||||
analyze("shell %dMiB" % file_size_mb, speeds)
|
||||
|
||||
def main():
|
||||
device = adb.get_device()
|
||||
unlock(device)
|
||||
benchmark_sink(device)
|
||||
benchmark_source(device)
|
||||
benchmark_push(device)
|
||||
benchmark_pull(device)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,466 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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 "bugreport.h"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <android-base/strings.h>
|
||||
#include <android-base/test_utils.h>
|
||||
|
||||
#include "sysdeps.h"
|
||||
#include "adb_utils.h"
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::Action;
|
||||
using ::testing::ActionInterface;
|
||||
using ::testing::DoAll;
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::HasSubstr;
|
||||
using ::testing::MakeAction;
|
||||
using ::testing::Return;
|
||||
using ::testing::StrEq;
|
||||
using ::testing::WithArg;
|
||||
using ::testing::internal::CaptureStderr;
|
||||
using ::testing::internal::CaptureStdout;
|
||||
using ::testing::internal::GetCapturedStderr;
|
||||
using ::testing::internal::GetCapturedStdout;
|
||||
|
||||
// Empty function so tests don't need to be linked against file_sync_service.cpp, which requires
|
||||
// SELinux and its transitive dependencies...
|
||||
bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
|
||||
const char* name) {
|
||||
ADD_FAILURE() << "do_sync_pull() should have been mocked";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Empty functions so tests don't need to be linked against commandline.cpp
|
||||
DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK(nullptr, nullptr);
|
||||
|
||||
int send_shell_command(const std::string& command, bool disable_shell_protocol,
|
||||
StandardStreamsCallbackInterface* callback) {
|
||||
ADD_FAILURE() << "send_shell_command() should have been mocked";
|
||||
return -42;
|
||||
}
|
||||
|
||||
enum StreamType {
|
||||
kStreamStdout,
|
||||
kStreamStderr,
|
||||
};
|
||||
|
||||
// gmock black magic to provide a WithArg<2>(WriteOnStdout(output)) matcher
|
||||
typedef void OnStandardStreamsCallbackFunction(StandardStreamsCallbackInterface*);
|
||||
|
||||
class OnStandardStreamsCallbackAction : public ActionInterface<OnStandardStreamsCallbackFunction> {
|
||||
public:
|
||||
explicit OnStandardStreamsCallbackAction(StreamType type, const std::string& output)
|
||||
: type_(type), output_(output) {
|
||||
}
|
||||
virtual Result Perform(const ArgumentTuple& args) {
|
||||
if (type_ == kStreamStdout) {
|
||||
::std::tr1::get<0>(args)->OnStdout(output_.c_str(), output_.size());
|
||||
}
|
||||
if (type_ == kStreamStderr) {
|
||||
::std::tr1::get<0>(args)->OnStderr(output_.c_str(), output_.size());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
StreamType type_;
|
||||
std::string output_;
|
||||
};
|
||||
|
||||
// Matcher used to emulated StandardStreamsCallbackInterface.OnStdout(buffer,
|
||||
// length)
|
||||
Action<OnStandardStreamsCallbackFunction> WriteOnStdout(const std::string& output) {
|
||||
return MakeAction(new OnStandardStreamsCallbackAction(kStreamStdout, output));
|
||||
}
|
||||
|
||||
// Matcher used to emulated StandardStreamsCallbackInterface.OnStderr(buffer,
|
||||
// length)
|
||||
Action<OnStandardStreamsCallbackFunction> WriteOnStderr(const std::string& output) {
|
||||
return MakeAction(new OnStandardStreamsCallbackAction(kStreamStderr, output));
|
||||
}
|
||||
|
||||
typedef int CallbackDoneFunction(StandardStreamsCallbackInterface*);
|
||||
|
||||
class CallbackDoneAction : public ActionInterface<CallbackDoneFunction> {
|
||||
public:
|
||||
explicit CallbackDoneAction(int status) : status_(status) {
|
||||
}
|
||||
virtual Result Perform(const ArgumentTuple& args) {
|
||||
int status = ::std::tr1::get<0>(args)->Done(status_);
|
||||
return status;
|
||||
}
|
||||
|
||||
private:
|
||||
int status_;
|
||||
};
|
||||
|
||||
// Matcher used to emulated StandardStreamsCallbackInterface.Done(status)
|
||||
Action<CallbackDoneFunction> ReturnCallbackDone(int status = -1337) {
|
||||
return MakeAction(new CallbackDoneAction(status));
|
||||
}
|
||||
|
||||
class BugreportMock : public Bugreport {
|
||||
public:
|
||||
MOCK_METHOD3(SendShellCommand, int(const std::string& command, bool disable_shell_protocol,
|
||||
StandardStreamsCallbackInterface* callback));
|
||||
MOCK_METHOD4(DoSyncPull, bool(const std::vector<const char*>& srcs, const char* dst,
|
||||
bool copy_attrs, const char* name));
|
||||
MOCK_METHOD2(UpdateProgress, void(const std::string&, int));
|
||||
};
|
||||
|
||||
class BugreportTest : public ::testing::Test {
|
||||
public:
|
||||
void SetUp() {
|
||||
if (!getcwd(&cwd_)) {
|
||||
ADD_FAILURE() << "getcwd failed: " << strerror(errno);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void ExpectBugreportzVersion(const std::string& version) {
|
||||
EXPECT_CALL(br_, SendShellCommand("bugreportz -v", false, _))
|
||||
.WillOnce(DoAll(WithArg<2>(WriteOnStderr(version)),
|
||||
WithArg<2>(ReturnCallbackDone(0))));
|
||||
}
|
||||
|
||||
void ExpectProgress(int progress_percentage, const std::string& file = "file.zip") {
|
||||
EXPECT_CALL(br_, UpdateProgress(StrEq("generating " + file), progress_percentage));
|
||||
}
|
||||
|
||||
BugreportMock br_;
|
||||
std::string cwd_; // TODO: make it static
|
||||
};
|
||||
|
||||
// Tests when called with invalid number of arguments
|
||||
TEST_F(BugreportTest, InvalidNumberArgs) {
|
||||
const char* args[] = {"bugreport", "to", "principal"};
|
||||
ASSERT_EQ(1, br_.DoIt(3, args));
|
||||
}
|
||||
|
||||
// Tests the 'adb bugreport' option when the device does not support 'bugreportz' - it falls back
|
||||
// to the flat-file format ('bugreport' binary on device)
|
||||
TEST_F(BugreportTest, NoArgumentsPreNDevice) {
|
||||
// clang-format off
|
||||
EXPECT_CALL(br_, SendShellCommand("bugreportz -v", false, _))
|
||||
.WillOnce(DoAll(WithArg<2>(WriteOnStderr("")),
|
||||
// Write some bogus output on stdout to make sure it's ignored
|
||||
WithArg<2>(WriteOnStdout("Dude, where is my bugreportz?")),
|
||||
WithArg<2>(ReturnCallbackDone(0))));
|
||||
// clang-format on
|
||||
std::string bugreport = "Reported the bug was.";
|
||||
CaptureStdout();
|
||||
EXPECT_CALL(br_, SendShellCommand("bugreport", false, _))
|
||||
.WillOnce(DoAll(WithArg<2>(WriteOnStdout(bugreport)), Return(0)));
|
||||
|
||||
const char* args[] = {"bugreport"};
|
||||
ASSERT_EQ(0, br_.DoIt(1, args));
|
||||
ASSERT_THAT(GetCapturedStdout(), StrEq(bugreport));
|
||||
}
|
||||
|
||||
// Tests the 'adb bugreport' option when the device supports 'bugreportz' version 1.0 - it will
|
||||
// save the bugreport in the current directory with the name provided by the device.
|
||||
TEST_F(BugreportTest, NoArgumentsNDevice) {
|
||||
ExpectBugreportzVersion("1.0");
|
||||
|
||||
std::string dest_file =
|
||||
android::base::StringPrintf("%s%cda_bugreport.zip", cwd_.c_str(), OS_PATH_SEPARATOR);
|
||||
EXPECT_CALL(br_, SendShellCommand("bugreportz", false, _))
|
||||
.WillOnce(DoAll(WithArg<2>(WriteOnStdout("OK:/device/da_bugreport.zip")),
|
||||
WithArg<2>(ReturnCallbackDone())));
|
||||
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
|
||||
false, StrEq("pulling da_bugreport.zip")))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
const char* args[] = {"bugreport"};
|
||||
ASSERT_EQ(0, br_.DoIt(1, args));
|
||||
}
|
||||
|
||||
// Tests the 'adb bugreport' option when the device supports 'bugreportz' version 1.1 - it will
|
||||
// save the bugreport in the current directory with the name provided by the device.
|
||||
TEST_F(BugreportTest, NoArgumentsPostNDevice) {
|
||||
ExpectBugreportzVersion("1.1");
|
||||
std::string dest_file =
|
||||
android::base::StringPrintf("%s%cda_bugreport.zip", cwd_.c_str(), OS_PATH_SEPARATOR);
|
||||
ExpectProgress(50, "da_bugreport.zip");
|
||||
EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
|
||||
.WillOnce(DoAll(WithArg<2>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
|
||||
WithArg<2>(WriteOnStdout("PROGRESS:50/100\n")),
|
||||
WithArg<2>(WriteOnStdout("OK:/device/da_bugreport.zip\n")),
|
||||
WithArg<2>(ReturnCallbackDone())));
|
||||
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
|
||||
false, StrEq("pulling da_bugreport.zip")))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
const char* args[] = {"bugreport"};
|
||||
ASSERT_EQ(0, br_.DoIt(1, args));
|
||||
}
|
||||
|
||||
// Tests 'adb bugreport file.zip' when it succeeds and device does not support progress.
|
||||
TEST_F(BugreportTest, OkNDevice) {
|
||||
ExpectBugreportzVersion("1.0");
|
||||
EXPECT_CALL(br_, SendShellCommand("bugreportz", false, _))
|
||||
.WillOnce(DoAll(WithArg<2>(WriteOnStdout("OK:/device/bugreport.zip")),
|
||||
WithArg<2>(ReturnCallbackDone())));
|
||||
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
|
||||
false, StrEq("pulling file.zip")))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
const char* args[] = {"bugreport", "file.zip"};
|
||||
ASSERT_EQ(0, br_.DoIt(2, args));
|
||||
}
|
||||
|
||||
// Tests 'adb bugreport file.zip' when it succeeds but response was sent in
|
||||
// multiple buffer writers and without progress updates.
|
||||
TEST_F(BugreportTest, OkNDeviceSplitBuffer) {
|
||||
ExpectBugreportzVersion("1.0");
|
||||
EXPECT_CALL(br_, SendShellCommand("bugreportz", false, _))
|
||||
.WillOnce(DoAll(WithArg<2>(WriteOnStdout("OK:/device")),
|
||||
WithArg<2>(WriteOnStdout("/bugreport.zip")),
|
||||
WithArg<2>(ReturnCallbackDone())));
|
||||
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
|
||||
false, StrEq("pulling file.zip")))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
const char* args[] = {"bugreport", "file.zip"};
|
||||
ASSERT_EQ(0, br_.DoIt(2, args));
|
||||
}
|
||||
|
||||
// Tests 'adb bugreport file.zip' when it succeeds and displays progress.
|
||||
TEST_F(BugreportTest, OkProgress) {
|
||||
ExpectBugreportzVersion("1.1");
|
||||
ExpectProgress(1);
|
||||
ExpectProgress(10);
|
||||
ExpectProgress(50);
|
||||
ExpectProgress(99);
|
||||
// clang-format off
|
||||
EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
|
||||
// NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit...
|
||||
.WillOnce(DoAll(
|
||||
// Name might change on OK, so make sure the right one is picked.
|
||||
WithArg<2>(WriteOnStdout("BEGIN:/device/bugreport___NOT.zip\n")),
|
||||
// Progress line in one write
|
||||
WithArg<2>(WriteOnStdout("PROGRESS:1/100\n")),
|
||||
// Add some bogus lines
|
||||
WithArg<2>(WriteOnStdout("\nDUDE:SWEET\n\nBLA\n\nBLA\nBLA\n\n")),
|
||||
// Multiple progress lines in one write
|
||||
WithArg<2>(WriteOnStdout("PROGRESS:10/100\nPROGRESS:50/100\n")),
|
||||
// Progress line in multiple writes
|
||||
WithArg<2>(WriteOnStdout("PROG")),
|
||||
WithArg<2>(WriteOnStdout("RESS:99")),
|
||||
WithArg<2>(WriteOnStdout("/100\n")),
|
||||
// Split last message as well, just in case
|
||||
WithArg<2>(WriteOnStdout("OK:/device/bugreport")),
|
||||
WithArg<2>(WriteOnStdout(".zip")),
|
||||
WithArg<2>(ReturnCallbackDone())));
|
||||
// clang-format on
|
||||
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
|
||||
false, StrEq("pulling file.zip")))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
const char* args[] = {"bugreport", "file.zip"};
|
||||
ASSERT_EQ(0, br_.DoIt(2, args));
|
||||
}
|
||||
|
||||
// Tests 'adb bugreport file.zip' when it succeeds and displays progress, even if progress recedes.
|
||||
TEST_F(BugreportTest, OkProgressAlwaysForward) {
|
||||
ExpectBugreportzVersion("1.1");
|
||||
ExpectProgress(1);
|
||||
ExpectProgress(50);
|
||||
ExpectProgress(75);
|
||||
// clang-format off
|
||||
EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
|
||||
// NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit...
|
||||
.WillOnce(DoAll(
|
||||
WithArg<2>(WriteOnStdout("BEGIN:/device/bugreport.zip\n")),
|
||||
WithArg<2>(WriteOnStdout("PROGRESS:1/100\n")), // 1%
|
||||
WithArg<2>(WriteOnStdout("PROGRESS:50/100\n")), // 50%
|
||||
// 25% should be ignored becaused it receded.
|
||||
WithArg<2>(WriteOnStdout("PROGRESS:25/100\n")), // 25%
|
||||
WithArg<2>(WriteOnStdout("PROGRESS:75/100\n")), // 75%
|
||||
// 75% should be ignored becaused it didn't change.
|
||||
WithArg<2>(WriteOnStdout("PROGRESS:75/100\n")), // 75%
|
||||
// Try a receeding percentage with a different max progress
|
||||
WithArg<2>(WriteOnStdout("PROGRESS:700/1000\n")), // 70%
|
||||
WithArg<2>(WriteOnStdout("OK:/device/bugreport.zip")),
|
||||
WithArg<2>(ReturnCallbackDone())));
|
||||
// clang-format on
|
||||
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
|
||||
false, StrEq("pulling file.zip")))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
const char* args[] = {"bugreport", "file.zip"};
|
||||
ASSERT_EQ(0, br_.DoIt(2, args));
|
||||
}
|
||||
|
||||
// Tests 'adb bugreport file.zip' when it succeeds and displays the initial progress of 0%
|
||||
TEST_F(BugreportTest, OkProgressZeroPercentIsNotIgnored) {
|
||||
ExpectBugreportzVersion("1.1");
|
||||
ExpectProgress(0);
|
||||
ExpectProgress(1);
|
||||
// clang-format off
|
||||
EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
|
||||
// NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit...
|
||||
.WillOnce(DoAll(
|
||||
WithArg<2>(WriteOnStdout("BEGIN:/device/bugreport.zip\n")),
|
||||
WithArg<2>(WriteOnStdout("PROGRESS:1/100000\n")),
|
||||
WithArg<2>(WriteOnStdout("PROGRESS:1/100\n")), // 1%
|
||||
WithArg<2>(WriteOnStdout("OK:/device/bugreport.zip")),
|
||||
WithArg<2>(ReturnCallbackDone())));
|
||||
// clang-format on
|
||||
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
|
||||
false, StrEq("pulling file.zip")))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
const char* args[] = {"bugreport", "file.zip"};
|
||||
ASSERT_EQ(0, br_.DoIt(2, args));
|
||||
}
|
||||
|
||||
// Tests 'adb bugreport dir' when it succeeds and destination is a directory.
|
||||
TEST_F(BugreportTest, OkDirectory) {
|
||||
ExpectBugreportzVersion("1.1");
|
||||
TemporaryDir td;
|
||||
std::string dest_file =
|
||||
android::base::StringPrintf("%s%cda_bugreport.zip", td.path, OS_PATH_SEPARATOR);
|
||||
|
||||
EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
|
||||
.WillOnce(DoAll(WithArg<2>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
|
||||
WithArg<2>(WriteOnStdout("OK:/device/da_bugreport.zip")),
|
||||
WithArg<2>(ReturnCallbackDone())));
|
||||
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
|
||||
false, StrEq("pulling da_bugreport.zip")))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
const char* args[] = {"bugreport", td.path};
|
||||
ASSERT_EQ(0, br_.DoIt(2, args));
|
||||
}
|
||||
|
||||
// Tests 'adb bugreport file' when it succeeds
|
||||
TEST_F(BugreportTest, OkNoExtension) {
|
||||
ExpectBugreportzVersion("1.1");
|
||||
EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
|
||||
.WillOnce(DoAll(WithArg<2>(WriteOnStdout("OK:/device/bugreport.zip\n")),
|
||||
WithArg<2>(ReturnCallbackDone())));
|
||||
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
|
||||
false, StrEq("pulling file.zip")))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
const char* args[] = {"bugreport", "file"};
|
||||
ASSERT_EQ(0, br_.DoIt(2, args));
|
||||
}
|
||||
|
||||
// Tests 'adb bugreport dir' when it succeeds and destination is a directory and device runs N.
|
||||
TEST_F(BugreportTest, OkNDeviceDirectory) {
|
||||
ExpectBugreportzVersion("1.0");
|
||||
TemporaryDir td;
|
||||
std::string dest_file =
|
||||
android::base::StringPrintf("%s%cda_bugreport.zip", td.path, OS_PATH_SEPARATOR);
|
||||
|
||||
EXPECT_CALL(br_, SendShellCommand("bugreportz", false, _))
|
||||
.WillOnce(DoAll(WithArg<2>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
|
||||
WithArg<2>(WriteOnStdout("OK:/device/da_bugreport.zip")),
|
||||
WithArg<2>(ReturnCallbackDone())));
|
||||
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
|
||||
false, StrEq("pulling da_bugreport.zip")))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
const char* args[] = {"bugreport", td.path};
|
||||
ASSERT_EQ(0, br_.DoIt(2, args));
|
||||
}
|
||||
|
||||
// Tests 'adb bugreport file.zip' when the bugreport itself failed
|
||||
TEST_F(BugreportTest, BugreportzReturnedFail) {
|
||||
ExpectBugreportzVersion("1.1");
|
||||
EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
|
||||
.WillOnce(
|
||||
DoAll(WithArg<2>(WriteOnStdout("FAIL:D'OH!\n")), WithArg<2>(ReturnCallbackDone())));
|
||||
|
||||
CaptureStderr();
|
||||
const char* args[] = {"bugreport", "file.zip"};
|
||||
ASSERT_EQ(-1, br_.DoIt(2, args));
|
||||
ASSERT_THAT(GetCapturedStderr(), HasSubstr("D'OH!"));
|
||||
}
|
||||
|
||||
// Tests 'adb bugreport file.zip' when the bugreport itself failed but response
|
||||
// was sent in
|
||||
// multiple buffer writes
|
||||
TEST_F(BugreportTest, BugreportzReturnedFailSplitBuffer) {
|
||||
ExpectBugreportzVersion("1.1");
|
||||
EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
|
||||
.WillOnce(DoAll(WithArg<2>(WriteOnStdout("FAIL")), WithArg<2>(WriteOnStdout(":D'OH!\n")),
|
||||
WithArg<2>(ReturnCallbackDone())));
|
||||
|
||||
CaptureStderr();
|
||||
const char* args[] = {"bugreport", "file.zip"};
|
||||
ASSERT_EQ(-1, br_.DoIt(2, args));
|
||||
ASSERT_THAT(GetCapturedStderr(), HasSubstr("D'OH!"));
|
||||
}
|
||||
|
||||
// Tests 'adb bugreport file.zip' when the bugreportz returned an unsupported
|
||||
// response.
|
||||
TEST_F(BugreportTest, BugreportzReturnedUnsupported) {
|
||||
ExpectBugreportzVersion("1.1");
|
||||
EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
|
||||
.WillOnce(DoAll(WithArg<2>(WriteOnStdout("bugreportz? What am I, a zombie?")),
|
||||
WithArg<2>(ReturnCallbackDone())));
|
||||
|
||||
CaptureStderr();
|
||||
const char* args[] = {"bugreport", "file.zip"};
|
||||
ASSERT_EQ(-1, br_.DoIt(2, args));
|
||||
ASSERT_THAT(GetCapturedStderr(), HasSubstr("bugreportz? What am I, a zombie?"));
|
||||
}
|
||||
|
||||
// Tests 'adb bugreport file.zip' when the bugreportz -v command failed
|
||||
TEST_F(BugreportTest, BugreportzVersionFailed) {
|
||||
EXPECT_CALL(br_, SendShellCommand("bugreportz -v", false, _)).WillOnce(Return(666));
|
||||
|
||||
const char* args[] = {"bugreport", "file.zip"};
|
||||
ASSERT_EQ(666, br_.DoIt(2, args));
|
||||
}
|
||||
|
||||
// Tests 'adb bugreport file.zip' when the bugreportz -v returns status 0 but with no output.
|
||||
TEST_F(BugreportTest, BugreportzVersionEmpty) {
|
||||
ExpectBugreportzVersion("");
|
||||
|
||||
const char* args[] = {"bugreport", "file.zip"};
|
||||
ASSERT_EQ(-1, br_.DoIt(2, args));
|
||||
}
|
||||
|
||||
// Tests 'adb bugreport file.zip' when the main bugreportz command failed
|
||||
TEST_F(BugreportTest, BugreportzFailed) {
|
||||
ExpectBugreportzVersion("1.1");
|
||||
EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _)).WillOnce(Return(666));
|
||||
|
||||
const char* args[] = {"bugreport", "file.zip"};
|
||||
ASSERT_EQ(666, br_.DoIt(2, args));
|
||||
}
|
||||
|
||||
// Tests 'adb bugreport file.zip' when the bugreport could not be pulled
|
||||
TEST_F(BugreportTest, PullFails) {
|
||||
ExpectBugreportzVersion("1.1");
|
||||
EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
|
||||
.WillOnce(DoAll(WithArg<2>(WriteOnStdout("OK:/device/bugreport.zip")),
|
||||
WithArg<2>(ReturnCallbackDone())));
|
||||
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
|
||||
false, HasSubstr("file.zip")))
|
||||
.WillOnce(Return(false));
|
||||
|
||||
const char* args[] = {"bugreport", "file.zip"};
|
||||
ASSERT_EQ(1, br_.DoIt(2, args));
|
||||
}
|
|
@ -1,435 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#define TRACE_TAG ADB
|
||||
|
||||
#include "sysdeps.h"
|
||||
#include "adb_client.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/no_destructor.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <android-base/thread_annotations.h>
|
||||
#include <cutils/sockets.h>
|
||||
|
||||
#include "adb_io.h"
|
||||
#include "adb_utils.h"
|
||||
#include "socket_spec.h"
|
||||
#include "sysdeps/chrono.h"
|
||||
|
||||
static TransportType __adb_transport = kTransportAny;
|
||||
static const char* __adb_serial = nullptr;
|
||||
static TransportId __adb_transport_id = 0;
|
||||
|
||||
static const char* __adb_server_socket_spec;
|
||||
|
||||
void adb_set_transport(TransportType type, const char* serial, TransportId transport_id) {
|
||||
__adb_transport = type;
|
||||
__adb_serial = serial;
|
||||
__adb_transport_id = transport_id;
|
||||
}
|
||||
|
||||
void adb_get_transport(TransportType* type, const char** serial, TransportId* transport_id) {
|
||||
if (type) *type = __adb_transport;
|
||||
if (serial) *serial = __adb_serial;
|
||||
if (transport_id) *transport_id = __adb_transport_id;
|
||||
}
|
||||
|
||||
void adb_set_socket_spec(const char* socket_spec) {
|
||||
if (__adb_server_socket_spec) {
|
||||
LOG(FATAL) << "attempted to reinitialize adb_server_socket_spec " << socket_spec << " (was " << __adb_server_socket_spec << ")";
|
||||
}
|
||||
__adb_server_socket_spec = socket_spec;
|
||||
}
|
||||
|
||||
static std::optional<TransportId> switch_socket_transport(int fd, std::string* error) {
|
||||
TransportId result;
|
||||
bool read_transport = true;
|
||||
|
||||
std::string service;
|
||||
if (__adb_transport_id) {
|
||||
read_transport = false;
|
||||
service += "host:transport-id:";
|
||||
service += std::to_string(__adb_transport_id);
|
||||
result = __adb_transport_id;
|
||||
} else if (__adb_serial) {
|
||||
service += "host:tport:serial:";
|
||||
service += __adb_serial;
|
||||
} else {
|
||||
const char* transport_type = "???";
|
||||
switch (__adb_transport) {
|
||||
case kTransportUsb:
|
||||
transport_type = "usb";
|
||||
break;
|
||||
case kTransportLocal:
|
||||
transport_type = "local";
|
||||
break;
|
||||
case kTransportAny:
|
||||
transport_type = "any";
|
||||
break;
|
||||
case kTransportHost:
|
||||
// no switch necessary
|
||||
return 0;
|
||||
}
|
||||
service += "host:tport:";
|
||||
service += transport_type;
|
||||
}
|
||||
|
||||
if (!SendProtocolString(fd, service)) {
|
||||
*error = perror_str("write failure during connection");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
LOG(DEBUG) << "Switch transport in progress: " << service;
|
||||
|
||||
if (!adb_status(fd, error)) {
|
||||
D("Switch transport failed: %s", error->c_str());
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (read_transport) {
|
||||
if (!ReadFdExactly(fd, &result, sizeof(result))) {
|
||||
*error = "failed to read transport id from server";
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
D("Switch transport success");
|
||||
return result;
|
||||
}
|
||||
|
||||
bool adb_status(borrowed_fd fd, std::string* error) {
|
||||
char buf[5];
|
||||
if (!ReadFdExactly(fd, buf, 4)) {
|
||||
*error = perror_str("protocol fault (couldn't read status)");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!memcmp(buf, "OKAY", 4)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (memcmp(buf, "FAIL", 4)) {
|
||||
*error = android::base::StringPrintf("protocol fault (status %02x %02x %02x %02x?!)",
|
||||
buf[0], buf[1], buf[2], buf[3]);
|
||||
return false;
|
||||
}
|
||||
|
||||
ReadProtocolString(fd, error, error);
|
||||
return false;
|
||||
}
|
||||
|
||||
static int _adb_connect(std::string_view service, TransportId* transport, std::string* error,
|
||||
bool force_switch = false) {
|
||||
LOG(DEBUG) << "_adb_connect: " << service;
|
||||
if (service.empty() || service.size() > MAX_PAYLOAD) {
|
||||
*error = android::base::StringPrintf("bad service name length (%zd)", service.size());
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string reason;
|
||||
unique_fd fd;
|
||||
if (!socket_spec_connect(&fd, __adb_server_socket_spec, nullptr, nullptr, &reason)) {
|
||||
*error = android::base::StringPrintf("cannot connect to daemon at %s: %s",
|
||||
__adb_server_socket_spec, reason.c_str());
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (!service.starts_with("host") || force_switch) {
|
||||
std::optional<TransportId> transport_result = switch_socket_transport(fd.get(), error);
|
||||
if (!transport_result) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (transport) {
|
||||
*transport = *transport_result;
|
||||
}
|
||||
}
|
||||
|
||||
if (!SendProtocolString(fd.get(), service)) {
|
||||
*error = perror_str("write failure during connection");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!adb_status(fd.get(), error)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
D("_adb_connect: return fd %d", fd.get());
|
||||
return fd.release();
|
||||
}
|
||||
|
||||
bool adb_kill_server() {
|
||||
D("adb_kill_server");
|
||||
std::string reason;
|
||||
unique_fd fd;
|
||||
if (!socket_spec_connect(&fd, __adb_server_socket_spec, nullptr, nullptr, &reason)) {
|
||||
fprintf(stderr, "cannot connect to daemon at %s: %s\n", __adb_server_socket_spec,
|
||||
reason.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!SendProtocolString(fd.get(), "host:kill")) {
|
||||
fprintf(stderr, "error: write failure during connection: %s\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
char buf[4];
|
||||
if (!ReadFdExactly(fd.get(), buf, 4)) {
|
||||
fprintf(stderr, "error: failed to read response from server\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (memcmp(buf, "OKAY", 4) == 0) {
|
||||
// Nothing to do.
|
||||
} else if (memcmp(buf, "FAIL", 4) == 0) {
|
||||
std::string output, error;
|
||||
if (!ReadProtocolString(fd.get(), &output, &error)) {
|
||||
fprintf(stderr, "error: %s\n", error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
fprintf(stderr, "error: %s\n", output.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now that no more data is expected, wait for socket orderly shutdown or error, indicating
|
||||
// server death.
|
||||
ReadOrderlyShutdown(fd.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
int adb_connect(std::string_view service, std::string* error) {
|
||||
return adb_connect(nullptr, service, error);
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
std::optional<std::string> adb_get_server_executable_path() {
|
||||
int port;
|
||||
std::string error;
|
||||
if (!parse_tcp_socket_spec(__adb_server_socket_spec, nullptr, &port, nullptr, &error)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return adb_get_android_dir_path() + OS_PATH_SEPARATOR + "adb." + std::to_string(port);
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool __adb_check_server_version(std::string* error) {
|
||||
unique_fd fd(_adb_connect("host:version", nullptr, error));
|
||||
|
||||
bool local = is_local_socket_spec(__adb_server_socket_spec);
|
||||
if (fd == -2 && !local) {
|
||||
fprintf(stderr, "* cannot start server on remote host\n");
|
||||
// error is the original network connection error
|
||||
return false;
|
||||
} else if (fd == -2) {
|
||||
fprintf(stderr, "* daemon not running; starting now at %s\n", __adb_server_socket_spec);
|
||||
start_server:
|
||||
if (launch_server(__adb_server_socket_spec)) {
|
||||
fprintf(stderr, "* failed to start daemon\n");
|
||||
// launch_server() has already printed detailed error info, so just
|
||||
// return a generic error string about the overall adb_connect()
|
||||
// that the caller requested.
|
||||
*error = "cannot connect to daemon";
|
||||
return false;
|
||||
} else {
|
||||
fprintf(stderr, "* daemon started successfully\n");
|
||||
}
|
||||
// The server will wait until it detects all of its connected devices before acking.
|
||||
// Fall through to _adb_connect.
|
||||
} else {
|
||||
// If a server is already running, check its version matches.
|
||||
int version = 0;
|
||||
|
||||
// If we have a file descriptor, then parse version result.
|
||||
if (fd >= 0) {
|
||||
std::string version_string;
|
||||
if (!ReadProtocolString(fd, &version_string, error)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ReadOrderlyShutdown(fd);
|
||||
|
||||
if (sscanf(&version_string[0], "%04x", &version) != 1) {
|
||||
*error = android::base::StringPrintf("cannot parse version string: %s",
|
||||
version_string.c_str());
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// If fd is -1 check for "unknown host service" which would
|
||||
// indicate a version of adb that does not support the
|
||||
// version command, in which case we should fall-through to kill it.
|
||||
if (*error != "unknown host service") {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (version != ADB_SERVER_VERSION) {
|
||||
#if defined(__linux__)
|
||||
if (version > ADB_SERVER_VERSION && local) {
|
||||
// Try to re-exec the existing adb server's binary.
|
||||
constexpr const char* adb_reexeced = "adb (re-execed)";
|
||||
if (strcmp(adb_reexeced, *__adb_argv) != 0) {
|
||||
__adb_argv[0] = adb_reexeced;
|
||||
std::optional<std::string> server_path_path = adb_get_server_executable_path();
|
||||
std::string server_path;
|
||||
if (server_path_path &&
|
||||
android::base::ReadFileToString(*server_path_path, &server_path)) {
|
||||
if (execve(server_path.c_str(), const_cast<char**>(__adb_argv),
|
||||
const_cast<char**>(__adb_envp)) == -1) {
|
||||
LOG(ERROR) << "failed to exec newer version at " << server_path;
|
||||
}
|
||||
|
||||
// Fall-through to restarting the server.
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
fprintf(stderr, "adb server version (%d) doesn't match this client (%d); killing...\n",
|
||||
version, ADB_SERVER_VERSION);
|
||||
adb_kill_server();
|
||||
goto start_server;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool adb_check_server_version(std::string* error) {
|
||||
// Only check the version once per process, since this isn't atomic anyway.
|
||||
static std::once_flag once;
|
||||
static bool result;
|
||||
static std::string* err;
|
||||
std::call_once(once, []() {
|
||||
err = new std::string();
|
||||
result = __adb_check_server_version(err);
|
||||
});
|
||||
*error = *err;
|
||||
return result;
|
||||
}
|
||||
|
||||
int adb_connect(TransportId* transport, std::string_view service, std::string* error,
|
||||
bool force_switch_device) {
|
||||
LOG(DEBUG) << "adb_connect: service: " << service;
|
||||
|
||||
// Query the adb server's version.
|
||||
if (!adb_check_server_version(error)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// if the command is start-server, we are done.
|
||||
if (service == "host:start-server") {
|
||||
return 0;
|
||||
}
|
||||
|
||||
unique_fd fd(_adb_connect(service, transport, error, force_switch_device));
|
||||
if (fd == -1) {
|
||||
D("_adb_connect error: %s", error->c_str());
|
||||
} else if(fd == -2) {
|
||||
fprintf(stderr, "* daemon still not running\n");
|
||||
}
|
||||
D("adb_connect: return fd %d", fd.get());
|
||||
|
||||
return fd.release();
|
||||
}
|
||||
|
||||
bool adb_command(const std::string& service) {
|
||||
std::string error;
|
||||
unique_fd fd(adb_connect(service, &error));
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "error: %s\n", error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!adb_status(fd.get(), &error)) {
|
||||
fprintf(stderr, "error: %s\n", error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
ReadOrderlyShutdown(fd.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool adb_query(const std::string& service, std::string* result, std::string* error) {
|
||||
D("adb_query: %s", service.c_str());
|
||||
unique_fd fd(adb_connect(service, error));
|
||||
if (fd < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
result->clear();
|
||||
if (!ReadProtocolString(fd.get(), result, error)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ReadOrderlyShutdown(fd.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string format_host_command(const char* command) {
|
||||
if (__adb_transport_id) {
|
||||
return android::base::StringPrintf("host-transport-id:%" PRIu64 ":%s", __adb_transport_id,
|
||||
command);
|
||||
} else if (__adb_serial) {
|
||||
return android::base::StringPrintf("host-serial:%s:%s", __adb_serial, command);
|
||||
}
|
||||
|
||||
const char* prefix = "host";
|
||||
if (__adb_transport == kTransportUsb) {
|
||||
prefix = "host-usb";
|
||||
} else if (__adb_transport == kTransportLocal) {
|
||||
prefix = "host-local";
|
||||
}
|
||||
return android::base::StringPrintf("%s:%s", prefix, command);
|
||||
}
|
||||
|
||||
const std::optional<FeatureSet>& adb_get_feature_set(std::string* error) {
|
||||
static std::mutex feature_mutex [[clang::no_destroy]];
|
||||
static std::optional<FeatureSet> features [[clang::no_destroy]] GUARDED_BY(feature_mutex);
|
||||
std::lock_guard<std::mutex> lock(feature_mutex);
|
||||
if (!features) {
|
||||
std::string result;
|
||||
std::string err;
|
||||
if (adb_query(format_host_command("features"), &result, &err)) {
|
||||
features = StringToFeatureSet(result);
|
||||
} else {
|
||||
if (error) {
|
||||
*error = err;
|
||||
}
|
||||
}
|
||||
}
|
||||
return features;
|
||||
}
|
|
@ -1,106 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "adb.h"
|
||||
#include "adb_unique_fd.h"
|
||||
#include "sysdeps.h"
|
||||
#include "transport.h"
|
||||
|
||||
// Explicitly check the adb server version.
|
||||
// All of the commands below do this implicitly.
|
||||
// Only the first invocation of this function will check the server version.
|
||||
bool adb_check_server_version(std::string* _Nonnull error);
|
||||
|
||||
// Connect to adb, connect to the named service, and return a valid fd for
|
||||
// interacting with that service upon success or a negative number on failure.
|
||||
int adb_connect(std::string_view service, std::string* _Nonnull error);
|
||||
|
||||
// Same as above, except returning the TransportId for the service that we've connected to.
|
||||
// force_switch_device forces the function to attempt to select a device, even if the service
|
||||
// string appears to be a host: service (for use with host services that are device specific, like
|
||||
// forward).
|
||||
int adb_connect(TransportId* _Nullable id, std::string_view service, std::string* _Nonnull error,
|
||||
bool force_switch_device = false);
|
||||
|
||||
// Kill the currently running adb server, if it exists.
|
||||
bool adb_kill_server();
|
||||
|
||||
// Connect to adb, connect to the named service, returns true if the connection
|
||||
// succeeded AND the service returned OKAY. Outputs any returned error otherwise.
|
||||
bool adb_command(const std::string& service);
|
||||
|
||||
// Connects to the named adb service and fills 'result' with the response.
|
||||
// Returns true on success; returns false and fills 'error' on failure.
|
||||
bool adb_query(const std::string& service, std::string* _Nonnull result,
|
||||
std::string* _Nonnull error);
|
||||
|
||||
// Set the preferred transport to connect to.
|
||||
void adb_set_transport(TransportType type, const char* _Nullable serial, TransportId transport_id);
|
||||
void adb_get_transport(TransportType* _Nullable type, const char* _Nullable* _Nullable serial,
|
||||
TransportId* _Nullable transport_id);
|
||||
|
||||
// Set the socket specification for the adb server.
|
||||
// This function can only be called once, and the argument must live to the end of the process.
|
||||
void adb_set_socket_spec(const char* _Nonnull socket_spec);
|
||||
|
||||
// Send commands to the current emulator instance. Will fail if there is not
|
||||
// exactly one emulator connected (or if you use -s <serial> with a <serial>
|
||||
// that does not designate an emulator).
|
||||
int adb_send_emulator_command(int argc, const char* _Nonnull* _Nonnull argv,
|
||||
const char* _Nullable serial);
|
||||
|
||||
// Reads a standard adb status response (OKAY|FAIL) and returns true in the
|
||||
// event of OKAY, false in the event of FAIL or protocol error.
|
||||
bool adb_status(borrowed_fd fd, std::string* _Nonnull error);
|
||||
|
||||
// Create a host command corresponding to selected transport type/serial.
|
||||
std::string format_host_command(const char* _Nonnull command);
|
||||
|
||||
// Get the feature set of the current preferred transport.
|
||||
const std::optional<FeatureSet>& adb_get_feature_set(std::string* _Nullable error);
|
||||
|
||||
#if defined(__linux__)
|
||||
// Get the path of a file containing the path to the server executable, if the socket spec set via
|
||||
// adb_set_socket_spec is a local one.
|
||||
std::optional<std::string> adb_get_server_executable_path();
|
||||
#endif
|
||||
|
||||
// Globally acccesible argv/envp, for the purpose of re-execing adb.
|
||||
extern const char* _Nullable * _Nullable __adb_argv;
|
||||
extern const char* _Nullable * _Nullable __adb_envp;
|
||||
|
||||
// ADB Secure DNS service interface. Used to query what ADB Secure DNS services have been
|
||||
// resolved, and to run some kind of callback for each one.
|
||||
using adb_secure_foreach_service_callback =
|
||||
std::function<void(const char* _Nonnull service_name, const char* _Nonnull reg_type,
|
||||
const char* _Nonnull ip_address, uint16_t port)>;
|
||||
|
||||
// Queries pairing/connect services that have been discovered and resolved.
|
||||
// If |host_name| is not null, run |cb| only for services
|
||||
// matching |host_name|. Otherwise, run for all services.
|
||||
void adb_secure_foreach_pairing_service(const char* _Nullable service_name,
|
||||
adb_secure_foreach_service_callback cb);
|
||||
void adb_secure_foreach_connect_service(const char* _Nullable service_name,
|
||||
adb_secure_foreach_service_callback cb);
|
||||
// Tries to connect to a |service_name| if found. Returns true if found and
|
||||
// connected, false otherwise.
|
||||
bool adb_secure_connect_by_service_name(const char* _Nonnull service_name);
|
|
@ -1,994 +0,0 @@
|
|||
/*
|
||||
* 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 "adb_install.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/parsebool.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/strings.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "adb_client.h"
|
||||
#include "adb_unique_fd.h"
|
||||
#include "adb_utils.h"
|
||||
#include "client/file_sync_client.h"
|
||||
#include "commandline.h"
|
||||
#include "fastdeploy.h"
|
||||
#include "incremental.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
static constexpr int kFastDeployMinApi = 24;
|
||||
|
||||
namespace {
|
||||
|
||||
enum InstallMode {
|
||||
INSTALL_DEFAULT,
|
||||
INSTALL_PUSH,
|
||||
INSTALL_STREAM,
|
||||
INSTALL_INCREMENTAL,
|
||||
};
|
||||
|
||||
enum class CmdlineOption { None, Enable, Disable };
|
||||
}
|
||||
|
||||
static bool can_use_feature(const char* feature) {
|
||||
// We ignore errors here, if the device is missing, we'll notice when we try to push install.
|
||||
auto&& features = adb_get_feature_set(nullptr);
|
||||
if (!features) {
|
||||
return false;
|
||||
}
|
||||
return CanUseFeature(*features, feature);
|
||||
}
|
||||
|
||||
static InstallMode best_install_mode() {
|
||||
if (can_use_feature(kFeatureCmd)) {
|
||||
return INSTALL_STREAM;
|
||||
}
|
||||
return INSTALL_PUSH;
|
||||
}
|
||||
|
||||
static bool is_apex_supported() {
|
||||
return can_use_feature(kFeatureApex);
|
||||
}
|
||||
|
||||
static bool is_abb_exec_supported() {
|
||||
return can_use_feature(kFeatureAbbExec);
|
||||
}
|
||||
|
||||
static int pm_command(int argc, const char** argv) {
|
||||
std::string cmd = "pm";
|
||||
|
||||
while (argc-- > 0) {
|
||||
cmd += " " + escape_arg(*argv++);
|
||||
}
|
||||
|
||||
return send_shell_command(cmd);
|
||||
}
|
||||
|
||||
static int uninstall_app_streamed(int argc, const char** argv) {
|
||||
// 'adb uninstall' takes the same arguments as 'cmd package uninstall' on device
|
||||
std::string cmd = "cmd package";
|
||||
while (argc-- > 0) {
|
||||
// deny the '-k' option until the remaining data/cache can be removed with adb/UI
|
||||
if (strcmp(*argv, "-k") == 0) {
|
||||
printf("The -k option uninstalls the application while retaining the "
|
||||
"data/cache.\n"
|
||||
"At the moment, there is no way to remove the remaining data.\n"
|
||||
"You will have to reinstall the application with the same "
|
||||
"signature, and fully "
|
||||
"uninstall it.\n"
|
||||
"If you truly wish to continue, execute 'adb shell cmd package "
|
||||
"uninstall -k'.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
cmd += " " + escape_arg(*argv++);
|
||||
}
|
||||
|
||||
return send_shell_command(cmd);
|
||||
}
|
||||
|
||||
static int uninstall_app_legacy(int argc, const char** argv) {
|
||||
/* if the user choose the -k option, we refuse to do it until devices are
|
||||
out with the option to uninstall the remaining data somehow (adb/ui) */
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (!strcmp(argv[i], "-k")) {
|
||||
printf("The -k option uninstalls the application while retaining the "
|
||||
"data/cache.\n"
|
||||
"At the moment, there is no way to remove the remaining data.\n"
|
||||
"You will have to reinstall the application with the same "
|
||||
"signature, and fully "
|
||||
"uninstall it.\n"
|
||||
"If you truly wish to continue, execute 'adb shell pm uninstall "
|
||||
"-k'\n.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/* 'adb uninstall' takes the same arguments as 'pm uninstall' on device */
|
||||
return pm_command(argc, argv);
|
||||
}
|
||||
|
||||
int uninstall_app(int argc, const char** argv) {
|
||||
if (best_install_mode() == INSTALL_PUSH) {
|
||||
return uninstall_app_legacy(argc, argv);
|
||||
}
|
||||
return uninstall_app_streamed(argc, argv);
|
||||
}
|
||||
|
||||
static void read_status_line(int fd, char* buf, size_t count) {
|
||||
count--;
|
||||
while (count > 0) {
|
||||
int len = adb_read(fd, buf, count);
|
||||
if (len <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
buf += len;
|
||||
count -= len;
|
||||
}
|
||||
*buf = '\0';
|
||||
}
|
||||
|
||||
static unique_fd send_command(const std::vector<std::string>& cmd_args, std::string* error) {
|
||||
if (is_abb_exec_supported()) {
|
||||
return send_abb_exec_command(cmd_args, error);
|
||||
} else {
|
||||
return unique_fd(adb_connect(android::base::Join(cmd_args, " "), error));
|
||||
}
|
||||
}
|
||||
|
||||
static int install_app_streamed(int argc, const char** argv, bool use_fastdeploy) {
|
||||
printf("Performing Streamed Install\n");
|
||||
|
||||
// The last argument must be the APK file
|
||||
const char* file = argv[argc - 1];
|
||||
if (!android::base::EndsWithIgnoreCase(file, ".apk") &&
|
||||
!android::base::EndsWithIgnoreCase(file, ".apex")) {
|
||||
error_exit("filename doesn't end .apk or .apex: %s", file);
|
||||
}
|
||||
|
||||
bool is_apex = false;
|
||||
if (android::base::EndsWithIgnoreCase(file, ".apex")) {
|
||||
is_apex = true;
|
||||
}
|
||||
if (is_apex && !is_apex_supported()) {
|
||||
error_exit(".apex is not supported on the target device");
|
||||
}
|
||||
|
||||
if (is_apex && use_fastdeploy) {
|
||||
error_exit("--fastdeploy doesn't support .apex files");
|
||||
}
|
||||
|
||||
if (use_fastdeploy) {
|
||||
auto metadata = extract_metadata(file);
|
||||
if (metadata.has_value()) {
|
||||
// pass all but 1st (command) and last (apk path) parameters through to pm for
|
||||
// session creation
|
||||
std::vector<const char*> pm_args{argv + 1, argv + argc - 1};
|
||||
auto patchFd = install_patch(pm_args.size(), pm_args.data());
|
||||
return stream_patch(file, std::move(metadata.value()), std::move(patchFd));
|
||||
}
|
||||
}
|
||||
|
||||
struct stat sb;
|
||||
if (stat(file, &sb) == -1) {
|
||||
fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
unique_fd local_fd(adb_open(file, O_RDONLY | O_CLOEXEC));
|
||||
if (local_fd < 0) {
|
||||
fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
posix_fadvise(local_fd.get(), 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE);
|
||||
#endif
|
||||
|
||||
const bool use_abb_exec = is_abb_exec_supported();
|
||||
std::string error;
|
||||
std::vector<std::string> cmd_args = {use_abb_exec ? "package" : "exec:cmd package"};
|
||||
cmd_args.reserve(argc + 3);
|
||||
|
||||
// don't copy the APK name, but, copy the rest of the arguments as-is
|
||||
while (argc-- > 1) {
|
||||
if (use_abb_exec) {
|
||||
cmd_args.push_back(*argv++);
|
||||
} else {
|
||||
cmd_args.push_back(escape_arg(*argv++));
|
||||
}
|
||||
}
|
||||
|
||||
// add size parameter [required for streaming installs]
|
||||
// do last to override any user specified value
|
||||
cmd_args.push_back("-S");
|
||||
cmd_args.push_back(android::base::StringPrintf("%" PRIu64, static_cast<uint64_t>(sb.st_size)));
|
||||
|
||||
if (is_apex) {
|
||||
cmd_args.push_back("--apex");
|
||||
}
|
||||
|
||||
unique_fd remote_fd = send_command(cmd_args, &error);
|
||||
if (remote_fd < 0) {
|
||||
fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!copy_to_file(local_fd.get(), remote_fd.get())) {
|
||||
fprintf(stderr, "adb: failed to install: copy_to_file: %s: %s", file, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
char buf[BUFSIZ];
|
||||
read_status_line(remote_fd.get(), buf, sizeof(buf));
|
||||
if (strncmp("Success", buf, 7) != 0) {
|
||||
fprintf(stderr, "adb: failed to install %s: %s", file, buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fputs(buf, stdout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int install_app_legacy(int argc, const char** argv, bool use_fastdeploy) {
|
||||
printf("Performing Push Install\n");
|
||||
|
||||
// Find last APK argument.
|
||||
// All other arguments passed through verbatim.
|
||||
int last_apk = -1;
|
||||
for (int i = argc - 1; i >= 0; i--) {
|
||||
if (android::base::EndsWithIgnoreCase(argv[i], ".apex")) {
|
||||
error_exit("APEX packages are only compatible with Streamed Install");
|
||||
}
|
||||
if (android::base::EndsWithIgnoreCase(argv[i], ".apk")) {
|
||||
last_apk = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (last_apk == -1) error_exit("need APK file on command line");
|
||||
|
||||
int result = -1;
|
||||
std::vector<const char*> apk_file = {argv[last_apk]};
|
||||
std::string apk_dest = "/data/local/tmp/" + android::base::Basename(argv[last_apk]);
|
||||
argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
|
||||
|
||||
if (use_fastdeploy) {
|
||||
auto metadata = extract_metadata(apk_file[0]);
|
||||
if (metadata.has_value()) {
|
||||
auto patchFd = apply_patch_on_device(apk_dest.c_str());
|
||||
int status = stream_patch(apk_file[0], std::move(metadata.value()), std::move(patchFd));
|
||||
|
||||
result = pm_command(argc, argv);
|
||||
delete_device_file(apk_dest);
|
||||
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
if (do_sync_push(apk_file, apk_dest.c_str(), false, CompressionType::Any, false)) {
|
||||
result = pm_command(argc, argv);
|
||||
delete_device_file(apk_dest);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class TimePoint>
|
||||
static int ms_between(TimePoint start, TimePoint end) {
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
|
||||
}
|
||||
|
||||
static int install_app_incremental(int argc, const char** argv, bool wait, bool silent) {
|
||||
using clock = std::chrono::high_resolution_clock;
|
||||
const auto start = clock::now();
|
||||
int first_apk = -1;
|
||||
int last_apk = -1;
|
||||
incremental::Args passthrough_args = {};
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
const auto arg = std::string_view(argv[i]);
|
||||
if (android::base::EndsWithIgnoreCase(arg, ".apk"sv)) {
|
||||
last_apk = i;
|
||||
if (first_apk == -1) {
|
||||
first_apk = i;
|
||||
}
|
||||
} else if (arg.starts_with("install"sv)) {
|
||||
// incremental installation command on the device is the same for all its variations in
|
||||
// the adb, e.g. install-multiple or install-multi-package
|
||||
} else {
|
||||
passthrough_args.push_back(arg);
|
||||
}
|
||||
}
|
||||
|
||||
if (first_apk == -1) {
|
||||
if (!silent) {
|
||||
fprintf(stderr, "error: need at least one APK file on command line\n");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto files = incremental::Files{argv + first_apk, argv + last_apk + 1};
|
||||
if (silent) {
|
||||
// For a silent installation we want to do the lightweight check first and bail early and
|
||||
// quietly if it fails.
|
||||
if (!incremental::can_install(files)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Performing Incremental Install\n");
|
||||
auto server_process = incremental::install(files, passthrough_args, silent);
|
||||
if (!server_process) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const auto end = clock::now();
|
||||
printf("Install command complete in %d ms\n", ms_between(start, end));
|
||||
|
||||
if (wait) {
|
||||
(*server_process).wait();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static std::pair<InstallMode, std::optional<InstallMode>> calculate_install_mode(
|
||||
InstallMode modeFromArgs, bool fastdeploy, CmdlineOption incremental_request) {
|
||||
if (incremental_request == CmdlineOption::Enable) {
|
||||
if (fastdeploy) {
|
||||
error_exit(
|
||||
"--incremental and --fast-deploy options are incompatible. "
|
||||
"Please choose one");
|
||||
}
|
||||
}
|
||||
|
||||
if (modeFromArgs != INSTALL_DEFAULT) {
|
||||
if (incremental_request == CmdlineOption::Enable) {
|
||||
error_exit("--incremental is not compatible with other installation modes");
|
||||
}
|
||||
return {modeFromArgs, std::nullopt};
|
||||
}
|
||||
|
||||
if (incremental_request != CmdlineOption::Disable && !is_abb_exec_supported()) {
|
||||
if (incremental_request == CmdlineOption::None) {
|
||||
incremental_request = CmdlineOption::Disable;
|
||||
} else {
|
||||
error_exit("Device doesn't support incremental installations");
|
||||
}
|
||||
}
|
||||
if (incremental_request == CmdlineOption::None) {
|
||||
// check if the host is ok with incremental by default
|
||||
if (const char* incrementalFromEnv = getenv("ADB_INSTALL_DEFAULT_INCREMENTAL")) {
|
||||
using namespace android::base;
|
||||
auto val = ParseBool(incrementalFromEnv);
|
||||
if (val == ParseBoolResult::kFalse) {
|
||||
incremental_request = CmdlineOption::Disable;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (incremental_request == CmdlineOption::None) {
|
||||
// still ok: let's see if the device allows using incremental by default
|
||||
// it starts feeling like we're looking for an excuse to not to use incremental...
|
||||
std::string error;
|
||||
std::vector<std::string> args = {"settings", "get",
|
||||
"enable_adb_incremental_install_default"};
|
||||
auto fd = send_abb_exec_command(args, &error);
|
||||
if (!fd.ok()) {
|
||||
fprintf(stderr, "adb: retrieving the default device installation mode failed: %s",
|
||||
error.c_str());
|
||||
} else {
|
||||
char buf[BUFSIZ] = {};
|
||||
read_status_line(fd.get(), buf, sizeof(buf));
|
||||
using namespace android::base;
|
||||
auto val = ParseBool(buf);
|
||||
if (val == ParseBoolResult::kFalse) {
|
||||
incremental_request = CmdlineOption::Disable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (incremental_request == CmdlineOption::Enable) {
|
||||
// explicitly requested - no fallback
|
||||
return {INSTALL_INCREMENTAL, std::nullopt};
|
||||
}
|
||||
const auto bestMode = best_install_mode();
|
||||
if (incremental_request == CmdlineOption::None) {
|
||||
// no opinion - use incremental, fallback to regular on a failure.
|
||||
return {INSTALL_INCREMENTAL, bestMode};
|
||||
}
|
||||
// incremental turned off - use the regular best mode without a fallback.
|
||||
return {bestMode, std::nullopt};
|
||||
}
|
||||
|
||||
static std::vector<const char*> parse_install_mode(std::vector<const char*> argv,
|
||||
InstallMode* install_mode,
|
||||
CmdlineOption* incremental_request,
|
||||
bool* incremental_wait) {
|
||||
*install_mode = INSTALL_DEFAULT;
|
||||
*incremental_request = CmdlineOption::None;
|
||||
*incremental_wait = false;
|
||||
|
||||
std::vector<const char*> passthrough;
|
||||
for (auto&& arg : argv) {
|
||||
if (arg == "--streaming"sv) {
|
||||
*install_mode = INSTALL_STREAM;
|
||||
} else if (arg == "--no-streaming"sv) {
|
||||
*install_mode = INSTALL_PUSH;
|
||||
} else if (strlen(arg) >= "--incr"sv.size() && "--incremental"sv.starts_with(arg)) {
|
||||
*incremental_request = CmdlineOption::Enable;
|
||||
} else if (strlen(arg) >= "--no-incr"sv.size() && "--no-incremental"sv.starts_with(arg)) {
|
||||
*incremental_request = CmdlineOption::Disable;
|
||||
} else if (arg == "--wait"sv) {
|
||||
*incremental_wait = true;
|
||||
} else {
|
||||
passthrough.push_back(arg);
|
||||
}
|
||||
}
|
||||
return passthrough;
|
||||
}
|
||||
|
||||
static std::vector<const char*> parse_fast_deploy_mode(
|
||||
std::vector<const char*> argv, bool* use_fastdeploy,
|
||||
FastDeploy_AgentUpdateStrategy* agent_update_strategy) {
|
||||
*use_fastdeploy = false;
|
||||
*agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
|
||||
|
||||
std::vector<const char*> passthrough;
|
||||
for (auto&& arg : argv) {
|
||||
if (arg == "--fastdeploy"sv) {
|
||||
*use_fastdeploy = true;
|
||||
} else if (arg == "--no-fastdeploy"sv) {
|
||||
*use_fastdeploy = false;
|
||||
} else if (arg == "--force-agent"sv) {
|
||||
*agent_update_strategy = FastDeploy_AgentUpdateAlways;
|
||||
} else if (arg == "--date-check-agent"sv) {
|
||||
*agent_update_strategy = FastDeploy_AgentUpdateNewerTimeStamp;
|
||||
} else if (arg == "--version-check-agent"sv) {
|
||||
*agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
|
||||
} else {
|
||||
passthrough.push_back(arg);
|
||||
}
|
||||
}
|
||||
return passthrough;
|
||||
}
|
||||
|
||||
int install_app(int argc, const char** argv) {
|
||||
InstallMode install_mode = INSTALL_DEFAULT;
|
||||
auto incremental_request = CmdlineOption::None;
|
||||
bool incremental_wait = false;
|
||||
|
||||
bool use_fastdeploy = false;
|
||||
FastDeploy_AgentUpdateStrategy agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
|
||||
|
||||
auto unused_argv = parse_install_mode({argv, argv + argc}, &install_mode, &incremental_request,
|
||||
&incremental_wait);
|
||||
auto passthrough_argv =
|
||||
parse_fast_deploy_mode(std::move(unused_argv), &use_fastdeploy, &agent_update_strategy);
|
||||
|
||||
auto [primary_mode, fallback_mode] =
|
||||
calculate_install_mode(install_mode, use_fastdeploy, incremental_request);
|
||||
if ((primary_mode == INSTALL_STREAM ||
|
||||
fallback_mode.value_or(INSTALL_PUSH) == INSTALL_STREAM) &&
|
||||
best_install_mode() == INSTALL_PUSH) {
|
||||
error_exit("Attempting to use streaming install on unsupported device");
|
||||
}
|
||||
|
||||
if (use_fastdeploy && get_device_api_level() < kFastDeployMinApi) {
|
||||
fprintf(stderr,
|
||||
"Fast Deploy is only compatible with devices of API version %d or higher, "
|
||||
"ignoring.\n",
|
||||
kFastDeployMinApi);
|
||||
use_fastdeploy = false;
|
||||
}
|
||||
fastdeploy_set_agent_update_strategy(agent_update_strategy);
|
||||
|
||||
if (passthrough_argv.size() < 2) {
|
||||
error_exit("install requires an apk argument");
|
||||
}
|
||||
|
||||
auto run_install_mode = [&](InstallMode install_mode, bool silent) {
|
||||
switch (install_mode) {
|
||||
case INSTALL_PUSH:
|
||||
return install_app_legacy(passthrough_argv.size(), passthrough_argv.data(),
|
||||
use_fastdeploy);
|
||||
case INSTALL_STREAM:
|
||||
return install_app_streamed(passthrough_argv.size(), passthrough_argv.data(),
|
||||
use_fastdeploy);
|
||||
case INSTALL_INCREMENTAL:
|
||||
return install_app_incremental(passthrough_argv.size(), passthrough_argv.data(),
|
||||
incremental_wait, silent);
|
||||
case INSTALL_DEFAULT:
|
||||
default:
|
||||
error_exit("invalid install mode");
|
||||
}
|
||||
};
|
||||
auto res = run_install_mode(primary_mode, fallback_mode.has_value());
|
||||
if (res && fallback_mode.value_or(primary_mode) != primary_mode) {
|
||||
res = run_install_mode(*fallback_mode, false);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int install_multiple_app_streamed(int argc, const char** argv) {
|
||||
// Find all APK arguments starting at end.
|
||||
// All other arguments passed through verbatim.
|
||||
int first_apk = -1;
|
||||
uint64_t total_size = 0;
|
||||
for (int i = argc - 1; i >= 0; i--) {
|
||||
const char* file = argv[i];
|
||||
if (android::base::EndsWithIgnoreCase(argv[i], ".apex")) {
|
||||
error_exit("APEX packages are not compatible with install-multiple");
|
||||
}
|
||||
|
||||
if (android::base::EndsWithIgnoreCase(file, ".apk") ||
|
||||
android::base::EndsWithIgnoreCase(file, ".dm") ||
|
||||
android::base::EndsWithIgnoreCase(file, ".fsv_sig")) {
|
||||
struct stat sb;
|
||||
if (stat(file, &sb) == -1) perror_exit("failed to stat \"%s\"", file);
|
||||
total_size += sb.st_size;
|
||||
first_apk = i;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (first_apk == -1) error_exit("need APK file on command line");
|
||||
|
||||
const bool use_abb_exec = is_abb_exec_supported();
|
||||
const std::string install_cmd =
|
||||
use_abb_exec ? "package"
|
||||
: best_install_mode() == INSTALL_PUSH ? "exec:pm" : "exec:cmd package";
|
||||
|
||||
std::vector<std::string> cmd_args = {install_cmd, "install-create", "-S",
|
||||
std::to_string(total_size)};
|
||||
cmd_args.reserve(first_apk + 4);
|
||||
for (int i = 1; i < first_apk; i++) {
|
||||
if (use_abb_exec) {
|
||||
cmd_args.push_back(argv[i]);
|
||||
} else {
|
||||
cmd_args.push_back(escape_arg(argv[i]));
|
||||
}
|
||||
}
|
||||
|
||||
// Create install session
|
||||
std::string error;
|
||||
char buf[BUFSIZ];
|
||||
{
|
||||
unique_fd fd = send_command(cmd_args, &error);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
read_status_line(fd.get(), buf, sizeof(buf));
|
||||
}
|
||||
|
||||
int session_id = -1;
|
||||
if (!strncmp("Success", buf, 7)) {
|
||||
char* start = strrchr(buf, '[');
|
||||
char* end = strrchr(buf, ']');
|
||||
if (start && end) {
|
||||
*end = '\0';
|
||||
session_id = strtol(start + 1, nullptr, 10);
|
||||
}
|
||||
}
|
||||
if (session_id < 0) {
|
||||
fprintf(stderr, "adb: failed to create session\n");
|
||||
fputs(buf, stderr);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
const auto session_id_str = std::to_string(session_id);
|
||||
|
||||
// Valid session, now stream the APKs
|
||||
bool success = true;
|
||||
for (int i = first_apk; i < argc; i++) {
|
||||
const char* file = argv[i];
|
||||
struct stat sb;
|
||||
if (stat(file, &sb) == -1) {
|
||||
fprintf(stderr, "adb: failed to stat \"%s\": %s\n", file, strerror(errno));
|
||||
success = false;
|
||||
goto finalize_session;
|
||||
}
|
||||
|
||||
std::vector<std::string> cmd_args = {
|
||||
install_cmd,
|
||||
"install-write",
|
||||
"-S",
|
||||
std::to_string(sb.st_size),
|
||||
session_id_str,
|
||||
android::base::Basename(file),
|
||||
"-",
|
||||
};
|
||||
|
||||
unique_fd local_fd(adb_open(file, O_RDONLY | O_CLOEXEC));
|
||||
if (local_fd < 0) {
|
||||
fprintf(stderr, "adb: failed to open \"%s\": %s\n", file, strerror(errno));
|
||||
success = false;
|
||||
goto finalize_session;
|
||||
}
|
||||
|
||||
std::string error;
|
||||
unique_fd remote_fd = send_command(cmd_args, &error);
|
||||
if (remote_fd < 0) {
|
||||
fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
|
||||
success = false;
|
||||
goto finalize_session;
|
||||
}
|
||||
|
||||
if (!copy_to_file(local_fd.get(), remote_fd.get())) {
|
||||
fprintf(stderr, "adb: failed to write \"%s\": %s\n", file, strerror(errno));
|
||||
success = false;
|
||||
goto finalize_session;
|
||||
}
|
||||
|
||||
read_status_line(remote_fd.get(), buf, sizeof(buf));
|
||||
|
||||
if (strncmp("Success", buf, 7)) {
|
||||
fprintf(stderr, "adb: failed to write \"%s\"\n", file);
|
||||
fputs(buf, stderr);
|
||||
success = false;
|
||||
goto finalize_session;
|
||||
}
|
||||
}
|
||||
|
||||
finalize_session:
|
||||
// Commit session if we streamed everything okay; otherwise abandon.
|
||||
std::vector<std::string> service_args = {
|
||||
install_cmd,
|
||||
success ? "install-commit" : "install-abandon",
|
||||
session_id_str,
|
||||
};
|
||||
{
|
||||
unique_fd fd = send_command(service_args, &error);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
read_status_line(fd.get(), buf, sizeof(buf));
|
||||
}
|
||||
if (!success) return EXIT_FAILURE;
|
||||
|
||||
if (strncmp("Success", buf, 7)) {
|
||||
fprintf(stderr, "adb: failed to finalize session\n");
|
||||
fputs(buf, stderr);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
fputs(buf, stdout);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int install_multiple_app(int argc, const char** argv) {
|
||||
InstallMode install_mode = INSTALL_DEFAULT;
|
||||
auto incremental_request = CmdlineOption::None;
|
||||
bool incremental_wait = false;
|
||||
bool use_fastdeploy = false;
|
||||
|
||||
auto passthrough_argv = parse_install_mode({argv + 1, argv + argc}, &install_mode,
|
||||
&incremental_request, &incremental_wait);
|
||||
|
||||
auto [primary_mode, fallback_mode] =
|
||||
calculate_install_mode(install_mode, use_fastdeploy, incremental_request);
|
||||
if ((primary_mode == INSTALL_STREAM ||
|
||||
fallback_mode.value_or(INSTALL_PUSH) == INSTALL_STREAM) &&
|
||||
best_install_mode() == INSTALL_PUSH) {
|
||||
error_exit("Attempting to use streaming install on unsupported device");
|
||||
}
|
||||
|
||||
auto run_install_mode = [&](InstallMode install_mode, bool silent) {
|
||||
switch (install_mode) {
|
||||
case INSTALL_PUSH:
|
||||
case INSTALL_STREAM:
|
||||
return install_multiple_app_streamed(passthrough_argv.size(),
|
||||
passthrough_argv.data());
|
||||
case INSTALL_INCREMENTAL:
|
||||
return install_app_incremental(passthrough_argv.size(), passthrough_argv.data(),
|
||||
incremental_wait, silent);
|
||||
case INSTALL_DEFAULT:
|
||||
default:
|
||||
error_exit("invalid install mode");
|
||||
}
|
||||
};
|
||||
auto res = run_install_mode(primary_mode, fallback_mode.has_value());
|
||||
if (res && fallback_mode.value_or(primary_mode) != primary_mode) {
|
||||
res = run_install_mode(*fallback_mode, false);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int install_multi_package(int argc, const char** argv) {
|
||||
// Find all APK arguments starting at end.
|
||||
// All other arguments passed through verbatim.
|
||||
bool apex_found = false;
|
||||
int first_package = -1;
|
||||
for (int i = argc - 1; i >= 0; i--) {
|
||||
const char* file = argv[i];
|
||||
if (android::base::EndsWithIgnoreCase(file, ".apk") ||
|
||||
android::base::EndsWithIgnoreCase(file, ".apex")) {
|
||||
first_package = i;
|
||||
if (android::base::EndsWithIgnoreCase(file, ".apex")) {
|
||||
apex_found = true;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (first_package == -1) error_exit("need APK or APEX files on command line");
|
||||
|
||||
if (best_install_mode() == INSTALL_PUSH) {
|
||||
fprintf(stderr, "adb: multi-package install is not supported on this device\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const bool use_abb_exec = is_abb_exec_supported();
|
||||
const std::string install_cmd = use_abb_exec ? "package" : "exec:cmd package";
|
||||
|
||||
std::vector<std::string> multi_package_cmd_args = {install_cmd, "install-create",
|
||||
"--multi-package"};
|
||||
|
||||
multi_package_cmd_args.reserve(first_package + 4);
|
||||
for (int i = 1; i < first_package; i++) {
|
||||
if (use_abb_exec) {
|
||||
multi_package_cmd_args.push_back(argv[i]);
|
||||
} else {
|
||||
multi_package_cmd_args.push_back(escape_arg(argv[i]));
|
||||
}
|
||||
}
|
||||
|
||||
if (apex_found) {
|
||||
multi_package_cmd_args.emplace_back("--staged");
|
||||
}
|
||||
|
||||
// Create multi-package install session
|
||||
std::string error;
|
||||
char buf[BUFSIZ];
|
||||
{
|
||||
unique_fd fd = send_command(multi_package_cmd_args, &error);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "adb: connect error for create multi-package: %s\n", error.c_str());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
read_status_line(fd.get(), buf, sizeof(buf));
|
||||
}
|
||||
|
||||
int parent_session_id = -1;
|
||||
if (!strncmp("Success", buf, 7)) {
|
||||
char* start = strrchr(buf, '[');
|
||||
char* end = strrchr(buf, ']');
|
||||
if (start && end) {
|
||||
*end = '\0';
|
||||
parent_session_id = strtol(start + 1, nullptr, 10);
|
||||
}
|
||||
}
|
||||
if (parent_session_id < 0) {
|
||||
fprintf(stderr, "adb: failed to create multi-package session\n");
|
||||
fputs(buf, stderr);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
const auto parent_session_id_str = std::to_string(parent_session_id);
|
||||
|
||||
fprintf(stdout, "Created parent session ID %d.\n", parent_session_id);
|
||||
|
||||
std::vector<int> session_ids;
|
||||
|
||||
// Valid session, now create the individual sessions and stream the APKs
|
||||
int success = EXIT_FAILURE;
|
||||
std::vector<std::string> individual_cmd_args = {install_cmd, "install-create"};
|
||||
for (int i = 1; i < first_package; i++) {
|
||||
if (use_abb_exec) {
|
||||
individual_cmd_args.push_back(argv[i]);
|
||||
} else {
|
||||
individual_cmd_args.push_back(escape_arg(argv[i]));
|
||||
}
|
||||
}
|
||||
if (apex_found) {
|
||||
individual_cmd_args.emplace_back("--staged");
|
||||
}
|
||||
|
||||
std::vector<std::string> individual_apex_cmd_args;
|
||||
if (apex_found) {
|
||||
individual_apex_cmd_args = individual_cmd_args;
|
||||
individual_apex_cmd_args.emplace_back("--apex");
|
||||
}
|
||||
|
||||
std::vector<std::string> add_session_cmd_args = {
|
||||
install_cmd,
|
||||
"install-add-session",
|
||||
parent_session_id_str,
|
||||
};
|
||||
|
||||
for (int i = first_package; i < argc; i++) {
|
||||
const char* file = argv[i];
|
||||
char buf[BUFSIZ];
|
||||
{
|
||||
unique_fd fd;
|
||||
// Create individual install session
|
||||
if (android::base::EndsWithIgnoreCase(file, ".apex")) {
|
||||
fd = send_command(individual_apex_cmd_args, &error);
|
||||
} else {
|
||||
fd = send_command(individual_cmd_args, &error);
|
||||
}
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
|
||||
goto finalize_multi_package_session;
|
||||
}
|
||||
read_status_line(fd.get(), buf, sizeof(buf));
|
||||
}
|
||||
|
||||
int session_id = -1;
|
||||
if (!strncmp("Success", buf, 7)) {
|
||||
char* start = strrchr(buf, '[');
|
||||
char* end = strrchr(buf, ']');
|
||||
if (start && end) {
|
||||
*end = '\0';
|
||||
session_id = strtol(start + 1, nullptr, 10);
|
||||
}
|
||||
}
|
||||
if (session_id < 0) {
|
||||
fprintf(stderr, "adb: failed to create multi-package session\n");
|
||||
fputs(buf, stderr);
|
||||
goto finalize_multi_package_session;
|
||||
}
|
||||
const auto session_id_str = std::to_string(session_id);
|
||||
|
||||
fprintf(stdout, "Created child session ID %d.\n", session_id);
|
||||
session_ids.push_back(session_id);
|
||||
|
||||
// Support splitAPKs by allowing the notation split1.apk:split2.apk:split3.apk as argument.
|
||||
std::vector<std::string> splits = android::base::Split(file, ":");
|
||||
|
||||
for (const std::string& split : splits) {
|
||||
struct stat sb;
|
||||
if (stat(split.c_str(), &sb) == -1) {
|
||||
fprintf(stderr, "adb: failed to stat %s: %s\n", split.c_str(), strerror(errno));
|
||||
goto finalize_multi_package_session;
|
||||
}
|
||||
|
||||
std::vector<std::string> cmd_args = {
|
||||
install_cmd,
|
||||
"install-write",
|
||||
"-S",
|
||||
std::to_string(sb.st_size),
|
||||
session_id_str,
|
||||
android::base::StringPrintf("%d_%s", i, android::base::Basename(split).c_str()),
|
||||
"-",
|
||||
};
|
||||
|
||||
unique_fd local_fd(adb_open(split.c_str(), O_RDONLY | O_CLOEXEC));
|
||||
if (local_fd < 0) {
|
||||
fprintf(stderr, "adb: failed to open %s: %s\n", split.c_str(), strerror(errno));
|
||||
goto finalize_multi_package_session;
|
||||
}
|
||||
|
||||
std::string error;
|
||||
unique_fd remote_fd = send_command(cmd_args, &error);
|
||||
if (remote_fd < 0) {
|
||||
fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
|
||||
goto finalize_multi_package_session;
|
||||
}
|
||||
|
||||
if (!copy_to_file(local_fd.get(), remote_fd.get())) {
|
||||
fprintf(stderr, "adb: failed to write %s: %s\n", split.c_str(), strerror(errno));
|
||||
goto finalize_multi_package_session;
|
||||
}
|
||||
|
||||
read_status_line(remote_fd.get(), buf, sizeof(buf));
|
||||
|
||||
if (strncmp("Success", buf, 7)) {
|
||||
fprintf(stderr, "adb: failed to write %s\n", split.c_str());
|
||||
fputs(buf, stderr);
|
||||
goto finalize_multi_package_session;
|
||||
}
|
||||
}
|
||||
add_session_cmd_args.push_back(std::to_string(session_id));
|
||||
}
|
||||
|
||||
{
|
||||
unique_fd fd = send_command(add_session_cmd_args, &error);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "adb: connect error for install-add-session: %s\n", error.c_str());
|
||||
goto finalize_multi_package_session;
|
||||
}
|
||||
read_status_line(fd.get(), buf, sizeof(buf));
|
||||
}
|
||||
|
||||
if (strncmp("Success", buf, 7)) {
|
||||
fprintf(stderr, "adb: failed to link sessions (%s)\n",
|
||||
android::base::Join(add_session_cmd_args, " ").c_str());
|
||||
fputs(buf, stderr);
|
||||
goto finalize_multi_package_session;
|
||||
}
|
||||
|
||||
// no failures means we can proceed with the assumption of success
|
||||
success = 0;
|
||||
|
||||
finalize_multi_package_session:
|
||||
// Commit session if we streamed everything okay; otherwise abandon
|
||||
std::vector<std::string> service_args = {
|
||||
install_cmd,
|
||||
success == 0 ? "install-commit" : "install-abandon",
|
||||
parent_session_id_str,
|
||||
};
|
||||
|
||||
{
|
||||
unique_fd fd = send_command(service_args, &error);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
read_status_line(fd.get(), buf, sizeof(buf));
|
||||
}
|
||||
|
||||
if (!strncmp("Success", buf, 7)) {
|
||||
fputs(buf, stdout);
|
||||
if (success == 0) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "adb: failed to finalize session\n");
|
||||
fputs(buf, stderr);
|
||||
}
|
||||
|
||||
session_ids.push_back(parent_session_id);
|
||||
// try to abandon all remaining sessions
|
||||
for (std::size_t i = 0; i < session_ids.size(); i++) {
|
||||
std::vector<std::string> service_args = {
|
||||
install_cmd,
|
||||
"install-abandon",
|
||||
std::to_string(session_ids[i]),
|
||||
};
|
||||
fprintf(stderr, "Attempting to abandon session ID %d\n", session_ids[i]);
|
||||
unique_fd fd = send_command(service_args, &error);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
|
||||
continue;
|
||||
}
|
||||
read_status_line(fd.get(), buf, sizeof(buf));
|
||||
}
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
int delete_device_file(const std::string& filename) {
|
||||
// http://b/17339227 "Sideloading a Readonly File Results in a Prompt to
|
||||
// Delete" caused us to add `-f` here, to avoid the equivalent of the `-i`
|
||||
// prompt that you get from BSD rm (used in Android 5) if you have a
|
||||
// non-writable file and stdin is a tty (which is true for old versions of
|
||||
// adbd).
|
||||
//
|
||||
// Unfortunately, `rm -f` requires Android 4.3, so that workaround broke
|
||||
// earlier Android releases. This was reported as http://b/37704384 "adb
|
||||
// install -r passes invalid argument to rm on Android 4.1" and
|
||||
// http://b/37035817 "ADB Fails: rm failed for -f, No such file or
|
||||
// directory".
|
||||
//
|
||||
// Testing on a variety of devices and emulators shows that redirecting
|
||||
// stdin is sufficient to avoid the pseudo-`-i`, and works on toolbox,
|
||||
// BSD, and toybox versions of rm.
|
||||
return send_shell_command("rm " + escape_arg(filename) + " </dev/null");
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
int install_app(int argc, const char** argv);
|
||||
int install_multiple_app(int argc, const char** argv);
|
||||
int install_multi_package(int argc, const char** argv);
|
||||
int uninstall_app(int argc, const char** argv);
|
||||
|
||||
int delete_device_file(const std::string& filename);
|
||||
int delete_host_file(const std::string& filename);
|
||||
|
|
@ -1,254 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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 "adb_wifi.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <random>
|
||||
#include <thread>
|
||||
|
||||
#include <adb/crypto/key.h>
|
||||
#include <adb/crypto/x509_generator.h>
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/parsenetaddress.h>
|
||||
#include "client/pairing/pairing_client.h"
|
||||
|
||||
#include "adb_auth.h"
|
||||
#include "adb_known_hosts.pb.h"
|
||||
#include "adb_utils.h"
|
||||
#include "client/adb_client.h"
|
||||
#include "sysdeps.h"
|
||||
|
||||
using adbwifi::pairing::PairingClient;
|
||||
using namespace adb::crypto;
|
||||
|
||||
struct PairingResultWaiter {
|
||||
std::mutex mutex_;
|
||||
std::condition_variable cv_;
|
||||
std::optional<bool> is_valid_;
|
||||
PeerInfo peer_info_;
|
||||
|
||||
static void OnResult(const PeerInfo* peer_info, void* opaque) {
|
||||
CHECK(opaque);
|
||||
auto* p = reinterpret_cast<PairingResultWaiter*>(opaque);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(p->mutex_);
|
||||
if (peer_info) {
|
||||
memcpy(&(p->peer_info_), peer_info, sizeof(PeerInfo));
|
||||
}
|
||||
p->is_valid_ = (peer_info != nullptr);
|
||||
}
|
||||
p->cv_.notify_one();
|
||||
}
|
||||
}; // PairingResultWaiter
|
||||
|
||||
void adb_wifi_init() {}
|
||||
|
||||
static std::vector<uint8_t> stringToUint8(const std::string& str) {
|
||||
auto* p8 = reinterpret_cast<const uint8_t*>(str.data());
|
||||
return std::vector<uint8_t>(p8, p8 + str.length());
|
||||
}
|
||||
|
||||
// Tries to replace the |old_file| with |new_file|.
|
||||
// On success, then |old_file| has been removed and replaced with the
|
||||
// contents of |new_file|, |new_file| will be removed, and only |old_file| will
|
||||
// remain.
|
||||
// On failure, both files will be unchanged.
|
||||
// |new_file| must exist, but |old_file| does not need to exist.
|
||||
bool SafeReplaceFile(std::string_view old_file, std::string_view new_file) {
|
||||
std::string to_be_deleted(old_file);
|
||||
to_be_deleted += ".tbd";
|
||||
|
||||
bool old_renamed = true;
|
||||
if (adb_rename(old_file.data(), to_be_deleted.c_str()) != 0) {
|
||||
// Don't exit here. This is not necessarily an error, because |old_file|
|
||||
// may not exist.
|
||||
PLOG(INFO) << "Failed to rename " << old_file;
|
||||
old_renamed = false;
|
||||
}
|
||||
|
||||
if (adb_rename(new_file.data(), old_file.data()) != 0) {
|
||||
PLOG(ERROR) << "Unable to rename file (" << new_file << " => " << old_file << ")";
|
||||
if (old_renamed) {
|
||||
// Rename the .tbd file back to it's original name
|
||||
adb_rename(to_be_deleted.c_str(), old_file.data());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
adb_unlink(to_be_deleted.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string get_user_known_hosts_path() {
|
||||
return adb_get_android_dir_path() + OS_PATH_SEPARATOR + "adb_known_hosts.pb";
|
||||
}
|
||||
|
||||
bool load_known_hosts_from_file(const std::string& path, adb::proto::AdbKnownHosts& known_hosts) {
|
||||
// Check for file existence.
|
||||
struct stat buf;
|
||||
if (stat(path.c_str(), &buf) == -1) {
|
||||
LOG(INFO) << "Known hosts file [" << path << "] does not exist...";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::ifstream file(path, std::ios::binary);
|
||||
if (!file) {
|
||||
PLOG(ERROR) << "Unable to open [" << path << "].";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!known_hosts.ParseFromIstream(&file)) {
|
||||
PLOG(ERROR) << "Failed to parse [" << path << "]. Deleting it as it may be corrupted.";
|
||||
adb_unlink(path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool write_known_host_to_file(std::string& known_host) {
|
||||
std::string path = get_user_known_hosts_path();
|
||||
if (path.empty()) {
|
||||
PLOG(ERROR) << "Error getting user known hosts filename";
|
||||
return false;
|
||||
}
|
||||
|
||||
adb::proto::AdbKnownHosts known_hosts;
|
||||
load_known_hosts_from_file(path, known_hosts);
|
||||
auto* host_info = known_hosts.add_host_infos();
|
||||
host_info->set_guid(known_host);
|
||||
|
||||
std::unique_ptr<TemporaryFile> temp_file(new TemporaryFile(adb_get_android_dir_path()));
|
||||
if (temp_file->fd == -1) {
|
||||
PLOG(ERROR) << "Failed to open [" << temp_file->path << "] for writing";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!known_hosts.SerializeToFileDescriptor(temp_file->fd)) {
|
||||
LOG(ERROR) << "Unable to write out adb_knowns_hosts";
|
||||
return false;
|
||||
}
|
||||
temp_file->DoNotRemove();
|
||||
std::string temp_file_name(temp_file->path);
|
||||
temp_file.reset();
|
||||
|
||||
// Replace the existing adb_known_hosts with the new one
|
||||
if (!SafeReplaceFile(path, temp_file_name.c_str())) {
|
||||
LOG(ERROR) << "Failed to replace old adb_known_hosts";
|
||||
adb_unlink(temp_file_name.c_str());
|
||||
return false;
|
||||
}
|
||||
chmod(path.c_str(), S_IRUSR | S_IWUSR | S_IRGRP);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool adb_wifi_is_known_host(const std::string& host) {
|
||||
std::string path = get_user_known_hosts_path();
|
||||
if (path.empty()) {
|
||||
PLOG(ERROR) << "Error getting user known hosts filename";
|
||||
return false;
|
||||
}
|
||||
|
||||
adb::proto::AdbKnownHosts known_hosts;
|
||||
if (!load_known_hosts_from_file(path, known_hosts)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& host_info : known_hosts.host_infos()) {
|
||||
if (host == host_info.guid()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void adb_wifi_pair_device(const std::string& host, const std::string& password,
|
||||
std::string& response) {
|
||||
auto mdns_info = mdns_get_pairing_service_info(host);
|
||||
|
||||
if (!mdns_info.has_value()) {
|
||||
// Check the address for a valid address and port.
|
||||
std::string parsed_host;
|
||||
std::string err;
|
||||
int port = -1;
|
||||
if (!android::base::ParseNetAddress(host, &parsed_host, &port, nullptr, &err)) {
|
||||
response = "Failed to parse address for pairing: " + err;
|
||||
return;
|
||||
}
|
||||
if (port <= 0 || port > 65535) {
|
||||
response = "Invalid port while parsing address [" + host + "]";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto priv_key = adb_auth_get_user_privkey();
|
||||
auto x509_cert = GenerateX509Certificate(priv_key.get());
|
||||
if (!x509_cert) {
|
||||
LOG(ERROR) << "Unable to create X509 certificate for pairing";
|
||||
return;
|
||||
}
|
||||
auto cert_str = X509ToPEMString(x509_cert.get());
|
||||
auto priv_str = Key::ToPEMString(priv_key.get());
|
||||
|
||||
// Send our public key on pairing success
|
||||
PeerInfo system_info = {};
|
||||
system_info.type = ADB_RSA_PUB_KEY;
|
||||
std::string public_key = adb_auth_get_userkey();
|
||||
CHECK_LE(public_key.size(), sizeof(system_info.data) - 1); // -1 for null byte
|
||||
memcpy(system_info.data, public_key.data(), public_key.size());
|
||||
|
||||
auto pswd8 = stringToUint8(password);
|
||||
auto cert8 = stringToUint8(cert_str);
|
||||
auto priv8 = stringToUint8(priv_str);
|
||||
|
||||
auto client = PairingClient::Create(pswd8, system_info, cert8, priv8);
|
||||
if (client == nullptr) {
|
||||
response = "Failed: unable to create pairing client.";
|
||||
return;
|
||||
}
|
||||
|
||||
PairingResultWaiter waiter;
|
||||
std::unique_lock<std::mutex> lock(waiter.mutex_);
|
||||
if (!client->Start(mdns_info.has_value()
|
||||
? android::base::StringPrintf("%s:%d", mdns_info->addr.c_str(),
|
||||
mdns_info->port)
|
||||
: host,
|
||||
waiter.OnResult, &waiter)) {
|
||||
response = "Failed: Unable to start pairing client.";
|
||||
return;
|
||||
}
|
||||
waiter.cv_.wait(lock, [&]() { return waiter.is_valid_.has_value(); });
|
||||
if (!*(waiter.is_valid_)) {
|
||||
response = "Failed: Wrong password or connection was dropped.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (waiter.peer_info_.type != ADB_DEVICE_GUID) {
|
||||
response = "Failed: Successfully paired but server returned unknown response=";
|
||||
response += waiter.peer_info_.type;
|
||||
return;
|
||||
}
|
||||
|
||||
std::string device_guid = reinterpret_cast<const char*>(waiter.peer_info_.data);
|
||||
response = "Successfully paired to " + host + " [guid=" + device_guid + "]";
|
||||
|
||||
// Write to adb_known_hosts
|
||||
write_known_host_to_file(device_guid);
|
||||
// Try to auto-connect.
|
||||
adb_secure_connect_by_service_name(device_guid.c_str());
|
||||
}
|
|
@ -1,559 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 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.
|
||||
*/
|
||||
|
||||
#define TRACE_TAG AUTH
|
||||
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#if defined(__linux__)
|
||||
#include <sys/inotify.h>
|
||||
#endif
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include <adb/crypto/rsa_2048_key.h>
|
||||
#include <adb/crypto/x509_generator.h>
|
||||
#include <adb/tls/adb_ca_list.h>
|
||||
#include <adb/tls/tls_connection.h>
|
||||
#include <android-base/errors.h>
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <crypto_utils/android_pubkey.h>
|
||||
#include <openssl/base64.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/objects.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "adb_auth.h"
|
||||
#include "adb_io.h"
|
||||
#include "adb_utils.h"
|
||||
#include "sysdeps.h"
|
||||
#include "transport.h"
|
||||
|
||||
static std::mutex& g_keys_mutex = *new std::mutex;
|
||||
static std::map<std::string, std::shared_ptr<RSA>>& g_keys =
|
||||
*new std::map<std::string, std::shared_ptr<RSA>>;
|
||||
static std::map<int, std::string>& g_monitored_paths = *new std::map<int, std::string>;
|
||||
|
||||
using namespace adb::crypto;
|
||||
using namespace adb::tls;
|
||||
|
||||
static bool generate_key(const std::string& file) {
|
||||
LOG(INFO) << "generate_key(" << file << ")...";
|
||||
|
||||
auto rsa_2048 = CreateRSA2048Key();
|
||||
if (!rsa_2048) {
|
||||
LOG(ERROR) << "Unable to create key";
|
||||
return false;
|
||||
}
|
||||
std::string pubkey;
|
||||
|
||||
RSA* rsa = EVP_PKEY_get0_RSA(rsa_2048->GetEvpPkey());
|
||||
CHECK(rsa);
|
||||
|
||||
if (!CalculatePublicKey(&pubkey, rsa)) {
|
||||
LOG(ERROR) << "failed to calculate public key";
|
||||
return false;
|
||||
}
|
||||
|
||||
mode_t old_mask = umask(077);
|
||||
|
||||
std::unique_ptr<FILE, decltype(&fclose)> f(nullptr, &fclose);
|
||||
f.reset(fopen(file.c_str(), "w"));
|
||||
if (!f) {
|
||||
PLOG(ERROR) << "Failed to open " << file;
|
||||
umask(old_mask);
|
||||
return false;
|
||||
}
|
||||
|
||||
umask(old_mask);
|
||||
|
||||
if (!PEM_write_PrivateKey(f.get(), rsa_2048->GetEvpPkey(), nullptr, nullptr, 0, nullptr,
|
||||
nullptr)) {
|
||||
LOG(ERROR) << "Failed to write key";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!android::base::WriteStringToFile(pubkey, file + ".pub")) {
|
||||
PLOG(ERROR) << "failed to write public key";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string hash_key(RSA* key) {
|
||||
unsigned char* pubkey = nullptr;
|
||||
int len = i2d_RSA_PUBKEY(key, &pubkey);
|
||||
if (len < 0) {
|
||||
LOG(ERROR) << "failed to encode RSA public key";
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string result;
|
||||
result.resize(SHA256_DIGEST_LENGTH);
|
||||
SHA256(pubkey, len, reinterpret_cast<unsigned char*>(&result[0]));
|
||||
OPENSSL_free(pubkey);
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::shared_ptr<RSA> read_key_file(const std::string& file) {
|
||||
std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(file.c_str(), "r"), fclose);
|
||||
if (!fp) {
|
||||
PLOG(ERROR) << "Failed to open '" << file << "'";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RSA* key = RSA_new();
|
||||
if (!PEM_read_RSAPrivateKey(fp.get(), &key, nullptr, nullptr)) {
|
||||
LOG(ERROR) << "Failed to read key from '" << file << "'";
|
||||
ERR_print_errors_fp(stderr);
|
||||
RSA_free(key);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::shared_ptr<RSA>(key, RSA_free);
|
||||
}
|
||||
|
||||
static bool load_key(const std::string& file) {
|
||||
std::shared_ptr<RSA> key = read_key_file(file);
|
||||
if (!key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(g_keys_mutex);
|
||||
std::string fingerprint = hash_key(key.get());
|
||||
bool already_loaded = (g_keys.find(fingerprint) != g_keys.end());
|
||||
if (!already_loaded) {
|
||||
g_keys[fingerprint] = std::move(key);
|
||||
}
|
||||
LOG(INFO) << (already_loaded ? "ignored already-loaded" : "loaded new") << " key from '" << file
|
||||
<< "' with fingerprint " << SHA256BitsToHexString(fingerprint);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool load_keys(const std::string& path, bool allow_dir = true) {
|
||||
LOG(INFO) << "load_keys '" << path << "'...";
|
||||
|
||||
struct stat st;
|
||||
if (stat(path.c_str(), &st) != 0) {
|
||||
PLOG(ERROR) << "load_keys: failed to stat '" << path << "'";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
return load_key(path);
|
||||
}
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
if (!allow_dir) {
|
||||
// inotify isn't recursive. It would break expectations to load keys in nested
|
||||
// directories but not monitor them for new keys.
|
||||
LOG(WARNING) << "load_keys: refusing to recurse into directory '" << path << "'";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir);
|
||||
if (!dir) {
|
||||
PLOG(ERROR) << "load_keys: failed to open directory '" << path << "'";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
while (struct dirent* dent = readdir(dir.get())) {
|
||||
std::string name = dent->d_name;
|
||||
|
||||
// We can't use dent->d_type here because it's not available on Windows.
|
||||
if (name == "." || name == "..") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!android::base::EndsWith(name, ".adb_key")) {
|
||||
LOG(INFO) << "skipped non-adb_key '" << path << "/" << name << "'";
|
||||
continue;
|
||||
}
|
||||
|
||||
result |= load_key((path + OS_PATH_SEPARATOR + name));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
LOG(ERROR) << "load_keys: unexpected type for '" << path << "': 0x" << std::hex << st.st_mode;
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::string get_user_key_path() {
|
||||
return adb_get_android_dir_path() + OS_PATH_SEPARATOR + "adbkey";
|
||||
}
|
||||
|
||||
static bool load_userkey() {
|
||||
std::string path = get_user_key_path();
|
||||
if (path.empty()) {
|
||||
PLOG(ERROR) << "Error getting user key filename";
|
||||
return false;
|
||||
}
|
||||
|
||||
struct stat buf;
|
||||
if (stat(path.c_str(), &buf) == -1) {
|
||||
LOG(INFO) << "User key '" << path << "' does not exist...";
|
||||
if (!generate_key(path)) {
|
||||
LOG(ERROR) << "Failed to generate new key";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return load_key(path);
|
||||
}
|
||||
|
||||
static std::set<std::string> get_vendor_keys() {
|
||||
const char* adb_keys_path = getenv("ADB_VENDOR_KEYS");
|
||||
if (adb_keys_path == nullptr) {
|
||||
return std::set<std::string>();
|
||||
}
|
||||
|
||||
std::set<std::string> result;
|
||||
for (const auto& path : android::base::Split(adb_keys_path, ENV_PATH_SEPARATOR_STR)) {
|
||||
result.emplace(path);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::deque<std::shared_ptr<RSA>> adb_auth_get_private_keys() {
|
||||
std::deque<std::shared_ptr<RSA>> result;
|
||||
|
||||
// Copy all the currently known keys.
|
||||
std::lock_guard<std::mutex> lock(g_keys_mutex);
|
||||
for (const auto& it : g_keys) {
|
||||
result.push_back(it.second);
|
||||
}
|
||||
|
||||
// Add a sentinel to the list. Our caller uses this to mean "out of private keys,
|
||||
// but try using the public key" (the empty deque could otherwise mean this _or_
|
||||
// that this function hasn't been called yet to request the keys).
|
||||
result.push_back(nullptr);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::string adb_auth_sign(RSA* key, const char* token, size_t token_size) {
|
||||
if (token_size != TOKEN_SIZE) {
|
||||
D("Unexpected token size %zd", token_size);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string result;
|
||||
result.resize(MAX_PAYLOAD);
|
||||
|
||||
unsigned int len;
|
||||
if (!RSA_sign(NID_sha1, reinterpret_cast<const uint8_t*>(token), token_size,
|
||||
reinterpret_cast<uint8_t*>(&result[0]), &len, key)) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
result.resize(len);
|
||||
|
||||
D("adb_auth_sign len=%d", len);
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool pubkey_from_privkey(std::string* out, const std::string& path) {
|
||||
std::shared_ptr<RSA> privkey = read_key_file(path);
|
||||
if (!privkey) {
|
||||
return false;
|
||||
}
|
||||
return CalculatePublicKey(out, privkey.get());
|
||||
}
|
||||
|
||||
bssl::UniquePtr<EVP_PKEY> adb_auth_get_user_privkey() {
|
||||
std::string path = get_user_key_path();
|
||||
if (path.empty()) {
|
||||
PLOG(ERROR) << "Error getting user key filename";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<RSA> rsa_privkey = read_key_file(path);
|
||||
if (!rsa_privkey) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
|
||||
if (!pkey) {
|
||||
LOG(ERROR) << "Failed to allocate key";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
EVP_PKEY_set1_RSA(pkey.get(), rsa_privkey.get());
|
||||
return pkey;
|
||||
}
|
||||
|
||||
std::string adb_auth_get_userkey() {
|
||||
std::string path = get_user_key_path();
|
||||
if (path.empty()) {
|
||||
PLOG(ERROR) << "Error getting user key filename";
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string result;
|
||||
if (!pubkey_from_privkey(&result, path)) {
|
||||
return "";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int adb_auth_keygen(const char* filename) {
|
||||
return !generate_key(filename);
|
||||
}
|
||||
|
||||
int adb_auth_pubkey(const char* filename) {
|
||||
std::string pubkey;
|
||||
if (!pubkey_from_privkey(&pubkey, filename)) {
|
||||
return 1;
|
||||
}
|
||||
pubkey.push_back('\n');
|
||||
|
||||
return WriteFdExactly(STDOUT_FILENO, pubkey.data(), pubkey.size()) ? 0 : 1;
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
static void adb_auth_inotify_update(int fd, unsigned fd_event, void*) {
|
||||
LOG(INFO) << "adb_auth_inotify_update called";
|
||||
if (!(fd_event & FDE_READ)) {
|
||||
return;
|
||||
}
|
||||
|
||||
char buf[sizeof(struct inotify_event) + NAME_MAX + 1];
|
||||
while (true) {
|
||||
ssize_t rc = TEMP_FAILURE_RETRY(unix_read(fd, buf, sizeof(buf)));
|
||||
if (rc == -1) {
|
||||
if (errno == EAGAIN) {
|
||||
LOG(INFO) << "done reading inotify fd";
|
||||
break;
|
||||
}
|
||||
PLOG(FATAL) << "read of inotify event failed";
|
||||
}
|
||||
|
||||
// The read potentially returned multiple events.
|
||||
char* start = buf;
|
||||
char* end = buf + rc;
|
||||
|
||||
while (start < end) {
|
||||
inotify_event* event = reinterpret_cast<inotify_event*>(start);
|
||||
auto root_it = g_monitored_paths.find(event->wd);
|
||||
if (root_it == g_monitored_paths.end()) {
|
||||
LOG(FATAL) << "observed inotify event for unmonitored path, wd = " << event->wd;
|
||||
}
|
||||
|
||||
std::string path = root_it->second;
|
||||
if (event->len > 0) {
|
||||
path += '/';
|
||||
path += event->name;
|
||||
}
|
||||
|
||||
if (event->mask & (IN_CREATE | IN_MOVED_TO)) {
|
||||
if (event->mask & IN_ISDIR) {
|
||||
LOG(INFO) << "ignoring new directory at '" << path << "'";
|
||||
} else {
|
||||
LOG(INFO) << "observed new file at '" << path << "'";
|
||||
load_keys(path, false);
|
||||
}
|
||||
} else {
|
||||
LOG(WARNING) << "unmonitored event for " << path << ": 0x" << std::hex
|
||||
<< event->mask;
|
||||
}
|
||||
|
||||
start += sizeof(struct inotify_event) + event->len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void adb_auth_inotify_init(const std::set<std::string>& paths) {
|
||||
LOG(INFO) << "adb_auth_inotify_init...";
|
||||
|
||||
int infd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK);
|
||||
if (infd < 0) {
|
||||
PLOG(ERROR) << "failed to create inotify fd";
|
||||
return;
|
||||
}
|
||||
|
||||
for (const std::string& path : paths) {
|
||||
int wd = inotify_add_watch(infd, path.c_str(), IN_CREATE | IN_MOVED_TO);
|
||||
if (wd < 0) {
|
||||
PLOG(ERROR) << "failed to inotify_add_watch on path '" << path << "'";
|
||||
continue;
|
||||
}
|
||||
|
||||
g_monitored_paths[wd] = path;
|
||||
LOG(INFO) << "watch descriptor " << wd << " registered for '" << path << "'";
|
||||
}
|
||||
|
||||
fdevent* event = fdevent_create(infd, adb_auth_inotify_update, nullptr);
|
||||
fdevent_add(event, FDE_READ);
|
||||
}
|
||||
#endif
|
||||
|
||||
void adb_auth_init() {
|
||||
LOG(INFO) << "adb_auth_init...";
|
||||
|
||||
if (!load_userkey()) {
|
||||
LOG(ERROR) << "Failed to load (or generate) user key";
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& key_paths = get_vendor_keys();
|
||||
|
||||
#if defined(__linux__)
|
||||
adb_auth_inotify_init(key_paths);
|
||||
#endif
|
||||
|
||||
for (const std::string& path : key_paths) {
|
||||
load_keys(path);
|
||||
}
|
||||
}
|
||||
|
||||
static void send_auth_publickey(atransport* t) {
|
||||
LOG(INFO) << "Calling send_auth_publickey";
|
||||
|
||||
std::string key = adb_auth_get_userkey();
|
||||
if (key.empty()) {
|
||||
D("Failed to get user public key");
|
||||
return;
|
||||
}
|
||||
|
||||
if (key.size() >= MAX_PAYLOAD_V1) {
|
||||
D("User public key too large (%zu B)", key.size());
|
||||
return;
|
||||
}
|
||||
|
||||
apacket* p = get_apacket();
|
||||
p->msg.command = A_AUTH;
|
||||
p->msg.arg0 = ADB_AUTH_RSAPUBLICKEY;
|
||||
|
||||
// adbd expects a null-terminated string.
|
||||
p->payload.assign(key.data(), key.data() + key.size() + 1);
|
||||
p->msg.data_length = p->payload.size();
|
||||
send_packet(p, t);
|
||||
}
|
||||
|
||||
void send_auth_response(const char* token, size_t token_size, atransport* t) {
|
||||
std::shared_ptr<RSA> key = t->NextKey();
|
||||
if (key == nullptr) {
|
||||
// No more private keys to try, send the public key.
|
||||
t->SetConnectionState(kCsUnauthorized);
|
||||
t->SetConnectionEstablished(true);
|
||||
send_auth_publickey(t);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(INFO) << "Calling send_auth_response";
|
||||
apacket* p = get_apacket();
|
||||
|
||||
std::string result = adb_auth_sign(key.get(), token, token_size);
|
||||
if (result.empty()) {
|
||||
D("Error signing the token");
|
||||
put_apacket(p);
|
||||
return;
|
||||
}
|
||||
|
||||
p->msg.command = A_AUTH;
|
||||
p->msg.arg0 = ADB_AUTH_SIGNATURE;
|
||||
p->payload.assign(result.begin(), result.end());
|
||||
p->msg.data_length = p->payload.size();
|
||||
send_packet(p, t);
|
||||
}
|
||||
|
||||
void adb_auth_tls_handshake(atransport* t) {
|
||||
std::thread([t]() {
|
||||
std::shared_ptr<RSA> key = t->Key();
|
||||
if (key == nullptr) {
|
||||
// Can happen if !auth_required
|
||||
LOG(INFO) << "t->auth_key not set before handshake";
|
||||
key = t->NextKey();
|
||||
CHECK(key);
|
||||
}
|
||||
|
||||
LOG(INFO) << "Attempting to TLS handshake";
|
||||
bool success = t->connection()->DoTlsHandshake(key.get());
|
||||
if (success) {
|
||||
LOG(INFO) << "Handshake succeeded. Waiting for CNXN packet...";
|
||||
} else {
|
||||
LOG(INFO) << "Handshake failed. Kicking transport";
|
||||
t->Kick();
|
||||
}
|
||||
}).detach();
|
||||
}
|
||||
|
||||
// Callback given to SSL_set_cert_cb to select a certificate when server requests
|
||||
// for a certificate. This is where the server will give us a CA-issuer list, and
|
||||
// figure out if the server knows any of our public keys. We currently always return
|
||||
// 1 here to indicate success, since we always try a key here (in the case of no auth).
|
||||
// See https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_set_cert_cb
|
||||
// for more details.
|
||||
int adb_tls_set_certificate(SSL* ssl) {
|
||||
LOG(INFO) << __func__;
|
||||
|
||||
const STACK_OF(X509_NAME)* ca_list = SSL_get_client_CA_list(ssl);
|
||||
if (ca_list == nullptr) {
|
||||
// Either the device doesn't know any keys, or !auth_required.
|
||||
// So let's just try with the default certificate and see what happens.
|
||||
LOG(INFO) << "No client CA list. Trying with default certificate.";
|
||||
return 1;
|
||||
}
|
||||
|
||||
const size_t num_cas = sk_X509_NAME_num(ca_list);
|
||||
for (size_t i = 0; i < num_cas; ++i) {
|
||||
auto* x509_name = sk_X509_NAME_value(ca_list, i);
|
||||
auto adbFingerprint = ParseEncodedKeyFromCAIssuer(x509_name);
|
||||
if (!adbFingerprint.has_value()) {
|
||||
// This could be a real CA issuer. Unfortunately, we don't support
|
||||
// it ATM.
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG(INFO) << "Checking for fingerprint match [" << *adbFingerprint << "]";
|
||||
auto encoded_key = SHA256HexStringToBits(*adbFingerprint);
|
||||
if (!encoded_key.has_value()) {
|
||||
continue;
|
||||
}
|
||||
// Check against our list of encoded keys for a match
|
||||
std::lock_guard<std::mutex> lock(g_keys_mutex);
|
||||
auto rsa_priv_key = g_keys.find(*encoded_key);
|
||||
if (rsa_priv_key != g_keys.end()) {
|
||||
LOG(INFO) << "Got SHA256 match on a key";
|
||||
bssl::UniquePtr<EVP_PKEY> evp_pkey(EVP_PKEY_new());
|
||||
CHECK(EVP_PKEY_set1_RSA(evp_pkey.get(), rsa_priv_key->second.get()));
|
||||
auto x509 = GenerateX509Certificate(evp_pkey.get());
|
||||
auto x509_str = X509ToPEMString(x509.get());
|
||||
auto evp_str = Key::ToPEMString(evp_pkey.get());
|
||||
TlsConnection::SetCertAndKey(ssl, x509_str, evp_str);
|
||||
return 1;
|
||||
} else {
|
||||
LOG(INFO) << "No match for [" << *adbFingerprint << "]";
|
||||
}
|
||||
}
|
||||
|
||||
// Let's just try with the default certificate anyways, because daemon might
|
||||
// not require auth, even though it has a list of keys.
|
||||
return 1;
|
||||
}
|
|
@ -1,289 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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.
|
||||
*/
|
||||
|
||||
#define TRACE_TAG ADB
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#include "bugreport.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/strings.h>
|
||||
|
||||
#include "adb_utils.h"
|
||||
#include "client/file_sync_client.h"
|
||||
|
||||
static constexpr char BUGZ_BEGIN_PREFIX[] = "BEGIN:";
|
||||
static constexpr char BUGZ_PROGRESS_PREFIX[] = "PROGRESS:";
|
||||
static constexpr char BUGZ_PROGRESS_SEPARATOR[] = "/";
|
||||
static constexpr char BUGZ_OK_PREFIX[] = "OK:";
|
||||
static constexpr char BUGZ_FAIL_PREFIX[] = "FAIL:";
|
||||
|
||||
// Custom callback used to handle the output of zipped bugreports.
|
||||
class BugreportStandardStreamsCallback : public StandardStreamsCallbackInterface {
|
||||
public:
|
||||
BugreportStandardStreamsCallback(const std::string& dest_dir, const std::string& dest_file,
|
||||
bool show_progress, Bugreport* br)
|
||||
: br_(br),
|
||||
src_file_(),
|
||||
dest_dir_(dest_dir),
|
||||
dest_file_(dest_file),
|
||||
line_message_(),
|
||||
invalid_lines_(),
|
||||
show_progress_(show_progress),
|
||||
status_(0),
|
||||
line_(),
|
||||
last_progress_percentage_(0) {
|
||||
SetLineMessage("generating");
|
||||
}
|
||||
|
||||
void OnStdout(const char* buffer, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
char c = buffer[i];
|
||||
if (c == '\n') {
|
||||
ProcessLine(line_);
|
||||
line_.clear();
|
||||
} else {
|
||||
line_.append(1, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnStderr(const char* buffer, int length) {
|
||||
OnStream(nullptr, stderr, buffer, length);
|
||||
}
|
||||
|
||||
int Done(int unused_) {
|
||||
// Process remaining line, if any.
|
||||
ProcessLine(line_);
|
||||
|
||||
// Warn about invalid lines, if any.
|
||||
if (!invalid_lines_.empty()) {
|
||||
fprintf(stderr,
|
||||
"WARNING: bugreportz generated %zu line(s) with unknown commands, "
|
||||
"device might not support zipped bugreports:\n",
|
||||
invalid_lines_.size());
|
||||
for (const auto& line : invalid_lines_) {
|
||||
fprintf(stderr, "\t%s\n", line.c_str());
|
||||
}
|
||||
fprintf(stderr,
|
||||
"If the zipped bugreport was not generated, try 'adb bugreport' instead.\n");
|
||||
}
|
||||
|
||||
// Pull the generated bug report.
|
||||
if (status_ == 0) {
|
||||
if (src_file_.empty()) {
|
||||
fprintf(stderr, "bugreportz did not return a '%s' or '%s' line\n", BUGZ_OK_PREFIX,
|
||||
BUGZ_FAIL_PREFIX);
|
||||
return -1;
|
||||
}
|
||||
std::string destination;
|
||||
if (dest_dir_.empty()) {
|
||||
destination = dest_file_;
|
||||
} else {
|
||||
destination = android::base::StringPrintf("%s%c%s", dest_dir_.c_str(),
|
||||
OS_PATH_SEPARATOR, dest_file_.c_str());
|
||||
}
|
||||
std::vector<const char*> srcs{src_file_.c_str()};
|
||||
SetLineMessage("pulling");
|
||||
status_ =
|
||||
br_->DoSyncPull(srcs, destination.c_str(), false, line_message_.c_str()) ? 0 : 1;
|
||||
if (status_ == 0) {
|
||||
printf("Bug report copied to %s\n", destination.c_str());
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"Bug report finished but could not be copied to '%s'.\n"
|
||||
"Try to run 'adb pull %s <directory>'\n"
|
||||
"to copy it to a directory that can be written.\n",
|
||||
destination.c_str(), src_file_.c_str());
|
||||
}
|
||||
}
|
||||
return status_;
|
||||
}
|
||||
|
||||
private:
|
||||
void SetLineMessage(const std::string& action) {
|
||||
line_message_ = action + " " + android::base::Basename(dest_file_);
|
||||
}
|
||||
|
||||
void SetSrcFile(const std::string path) {
|
||||
src_file_ = path;
|
||||
if (!dest_dir_.empty()) {
|
||||
// Only uses device-provided name when user passed a directory.
|
||||
dest_file_ = android::base::Basename(path);
|
||||
SetLineMessage("generating");
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessLine(const std::string& line) {
|
||||
if (line.empty()) return;
|
||||
|
||||
if (android::base::StartsWith(line, BUGZ_BEGIN_PREFIX)) {
|
||||
SetSrcFile(&line[strlen(BUGZ_BEGIN_PREFIX)]);
|
||||
} else if (android::base::StartsWith(line, BUGZ_OK_PREFIX)) {
|
||||
SetSrcFile(&line[strlen(BUGZ_OK_PREFIX)]);
|
||||
} else if (android::base::StartsWith(line, BUGZ_FAIL_PREFIX)) {
|
||||
const char* error_message = &line[strlen(BUGZ_FAIL_PREFIX)];
|
||||
fprintf(stderr, "adb: device failed to take a zipped bugreport: %s\n", error_message);
|
||||
status_ = -1;
|
||||
} else if (show_progress_ && android::base::StartsWith(line, BUGZ_PROGRESS_PREFIX)) {
|
||||
// progress_line should have the following format:
|
||||
//
|
||||
// BUGZ_PROGRESS_PREFIX:PROGRESS/TOTAL
|
||||
//
|
||||
size_t idx1 = line.rfind(BUGZ_PROGRESS_PREFIX) + strlen(BUGZ_PROGRESS_PREFIX);
|
||||
size_t idx2 = line.rfind(BUGZ_PROGRESS_SEPARATOR);
|
||||
int progress = std::stoi(line.substr(idx1, (idx2 - idx1)));
|
||||
int total = std::stoi(line.substr(idx2 + 1));
|
||||
int progress_percentage = (progress * 100 / total);
|
||||
if (progress_percentage != 0 && progress_percentage <= last_progress_percentage_) {
|
||||
// Ignore.
|
||||
return;
|
||||
}
|
||||
last_progress_percentage_ = progress_percentage;
|
||||
br_->UpdateProgress(line_message_, progress_percentage);
|
||||
} else {
|
||||
invalid_lines_.push_back(line);
|
||||
}
|
||||
}
|
||||
|
||||
Bugreport* br_;
|
||||
|
||||
// Path of bugreport on device.
|
||||
std::string src_file_;
|
||||
|
||||
// Bugreport destination on host, depending on argument passed on constructor:
|
||||
// - if argument is a directory, dest_dir_ is set with it and dest_file_ will be the name
|
||||
// of the bugreport reported by the device.
|
||||
// - if argument is empty, dest_dir is set as the current directory and dest_file_ will be the
|
||||
// name of the bugreport reported by the device.
|
||||
// - otherwise, dest_dir_ is not set and dest_file_ is set with the value passed on constructor.
|
||||
std::string dest_dir_, dest_file_;
|
||||
|
||||
// Message displayed on LinePrinter, it's updated every time the destination above change.
|
||||
std::string line_message_;
|
||||
|
||||
// Lines sent by bugreportz that contain invalid commands; will be displayed at the end.
|
||||
std::vector<std::string> invalid_lines_;
|
||||
|
||||
// Whether PROGRESS_LINES should be interpreted as progress.
|
||||
bool show_progress_;
|
||||
|
||||
// Overall process of the operation, as returned by Done().
|
||||
int status_;
|
||||
|
||||
// Temporary buffer containing the characters read since the last newline (\n).
|
||||
std::string line_;
|
||||
|
||||
// Last displayed progress.
|
||||
// Since dumpstate progress can recede, only forward progress should be displayed
|
||||
int last_progress_percentage_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(BugreportStandardStreamsCallback);
|
||||
};
|
||||
|
||||
int Bugreport::DoIt(int argc, const char** argv) {
|
||||
if (argc > 2) error_exit("usage: adb bugreport [PATH]");
|
||||
|
||||
// Gets bugreportz version.
|
||||
std::string bugz_stdout, bugz_stderr;
|
||||
DefaultStandardStreamsCallback version_callback(&bugz_stdout, &bugz_stderr);
|
||||
int status = SendShellCommand("bugreportz -v", false, &version_callback);
|
||||
std::string bugz_version = android::base::Trim(bugz_stderr);
|
||||
std::string bugz_output = android::base::Trim(bugz_stdout);
|
||||
|
||||
if (status != 0 || bugz_version.empty()) {
|
||||
D("'bugreportz' -v results: status=%d, stdout='%s', stderr='%s'", status,
|
||||
bugz_output.c_str(), bugz_version.c_str());
|
||||
if (argc == 1) {
|
||||
// Device does not support bugreportz: if called as 'adb bugreport', just falls out to
|
||||
// the flat-file version.
|
||||
fprintf(stderr,
|
||||
"Failed to get bugreportz version, which is only available on devices "
|
||||
"running Android 7.0 or later.\nTrying a plain-text bug report instead.\n");
|
||||
return SendShellCommand("bugreport", false);
|
||||
}
|
||||
|
||||
// But if user explicitly asked for a zipped bug report, fails instead (otherwise calling
|
||||
// 'bugreport' would generate a lot of output the user might not be prepared to handle).
|
||||
fprintf(stderr,
|
||||
"Failed to get bugreportz version: 'bugreportz -v' returned '%s' (code %d).\n"
|
||||
"If the device does not run Android 7.0 or above, try this instead:\n"
|
||||
"\tadb bugreport > bugreport.txt\n",
|
||||
bugz_output.c_str(), status);
|
||||
return status != 0 ? status : -1;
|
||||
}
|
||||
|
||||
std::string dest_file, dest_dir;
|
||||
|
||||
if (argc == 1) {
|
||||
// No args - use current directory
|
||||
if (!getcwd(&dest_dir)) {
|
||||
perror("adb: getcwd failed");
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
// Check whether argument is a directory or file
|
||||
if (directory_exists(argv[1])) {
|
||||
dest_dir = argv[1];
|
||||
} else {
|
||||
dest_file = argv[1];
|
||||
}
|
||||
}
|
||||
|
||||
if (dest_file.empty()) {
|
||||
// Uses a default value until device provides the proper name
|
||||
dest_file = "bugreport.zip";
|
||||
} else {
|
||||
if (!android::base::EndsWithIgnoreCase(dest_file, ".zip")) {
|
||||
dest_file += ".zip";
|
||||
}
|
||||
}
|
||||
|
||||
bool show_progress = true;
|
||||
std::string bugz_command = "bugreportz -p";
|
||||
if (bugz_version == "1.0") {
|
||||
// 1.0 does not support progress notifications, so print a disclaimer
|
||||
// message instead.
|
||||
fprintf(stderr,
|
||||
"Bugreport is in progress and it could take minutes to complete.\n"
|
||||
"Please be patient and do not cancel or disconnect your device "
|
||||
"until it completes.\n");
|
||||
show_progress = false;
|
||||
bugz_command = "bugreportz";
|
||||
}
|
||||
BugreportStandardStreamsCallback bugz_callback(dest_dir, dest_file, show_progress, this);
|
||||
return SendShellCommand(bugz_command, false, &bugz_callback);
|
||||
}
|
||||
|
||||
void Bugreport::UpdateProgress(const std::string& message, int progress_percentage) {
|
||||
line_printer_.Print(
|
||||
android::base::StringPrintf("[%3d%%] %s", progress_percentage, message.c_str()),
|
||||
LinePrinter::INFO);
|
||||
}
|
||||
|
||||
int Bugreport::SendShellCommand(const std::string& command, bool disable_shell_protocol,
|
||||
StandardStreamsCallbackInterface* callback) {
|
||||
return send_shell_command(command, disable_shell_protocol, callback);
|
||||
}
|
||||
|
||||
bool Bugreport::DoSyncPull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
|
||||
const char* name) {
|
||||
return do_sync_pull(srcs, dst, copy_attrs, CompressionType::None, name);
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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.
|
||||
*/
|
||||
|
||||
#ifndef BUGREPORT_H
|
||||
#define BUGREPORT_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "adb.h"
|
||||
#include "commandline.h"
|
||||
#include "line_printer.h"
|
||||
|
||||
class Bugreport {
|
||||
friend class BugreportStandardStreamsCallback;
|
||||
|
||||
public:
|
||||
Bugreport() : line_printer_() {
|
||||
}
|
||||
int DoIt(int argc, const char** argv);
|
||||
|
||||
protected:
|
||||
// Functions below are abstractions of external functions so they can be
|
||||
// mocked on tests.
|
||||
virtual int SendShellCommand(
|
||||
const std::string& command, bool disable_shell_protocol,
|
||||
StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK);
|
||||
|
||||
virtual bool DoSyncPull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
|
||||
const char* name);
|
||||
|
||||
private:
|
||||
virtual void UpdateProgress(const std::string& file_name, int progress_percentage);
|
||||
LinePrinter line_printer_;
|
||||
DISALLOW_COPY_AND_ASSIGN(Bugreport);
|
||||
};
|
||||
|
||||
#endif // BUGREPORT_H
|
File diff suppressed because it is too large
Load Diff
|
@ -1,133 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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.
|
||||
*/
|
||||
|
||||
#ifndef COMMANDLINE_H
|
||||
#define COMMANDLINE_H
|
||||
|
||||
#include <android-base/strings.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "adb_client.h"
|
||||
#include "adb_unique_fd.h"
|
||||
|
||||
// Callback used to handle the standard streams (stdout and stderr) sent by the
|
||||
// device's upon receiving a command.
|
||||
//
|
||||
class StandardStreamsCallbackInterface {
|
||||
public:
|
||||
StandardStreamsCallbackInterface() {
|
||||
}
|
||||
// Handles the stdout output from devices supporting the Shell protocol.
|
||||
virtual void OnStdout(const char* buffer, int length) = 0;
|
||||
|
||||
// Handles the stderr output from devices supporting the Shell protocol.
|
||||
virtual void OnStderr(const char* buffer, int length) = 0;
|
||||
|
||||
// Indicates the communication is finished and returns the appropriate error
|
||||
// code.
|
||||
//
|
||||
// |status| has the status code returning by the underlying communication
|
||||
// channels
|
||||
virtual int Done(int status) = 0;
|
||||
|
||||
protected:
|
||||
static void OnStream(std::string* string, FILE* stream, const char* buffer, int length) {
|
||||
if (string != nullptr) {
|
||||
string->append(buffer, length);
|
||||
} else {
|
||||
fwrite(buffer, 1, length, stream);
|
||||
fflush(stream);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(StandardStreamsCallbackInterface);
|
||||
};
|
||||
|
||||
// Default implementation that redirects the streams to the equilavent host
|
||||
// stream or to a string
|
||||
// passed to the constructor.
|
||||
class DefaultStandardStreamsCallback : public StandardStreamsCallbackInterface {
|
||||
public:
|
||||
// If |stdout_str| is non-null, OnStdout will append to it.
|
||||
// If |stderr_str| is non-null, OnStderr will append to it.
|
||||
DefaultStandardStreamsCallback(std::string* stdout_str, std::string* stderr_str)
|
||||
: stdout_str_(stdout_str), stderr_str_(stderr_str) {
|
||||
}
|
||||
|
||||
void OnStdout(const char* buffer, int length) {
|
||||
OnStream(stdout_str_, stdout, buffer, length);
|
||||
}
|
||||
|
||||
void OnStderr(const char* buffer, int length) {
|
||||
OnStream(stderr_str_, stderr, buffer, length);
|
||||
}
|
||||
|
||||
int Done(int status) {
|
||||
return status;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string* stdout_str_;
|
||||
std::string* stderr_str_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(DefaultStandardStreamsCallback);
|
||||
};
|
||||
|
||||
class SilentStandardStreamsCallbackInterface : public StandardStreamsCallbackInterface {
|
||||
public:
|
||||
SilentStandardStreamsCallbackInterface() = default;
|
||||
void OnStdout(const char*, int) override final {}
|
||||
void OnStderr(const char*, int) override final {}
|
||||
int Done(int status) override final { return status; }
|
||||
};
|
||||
|
||||
// Singleton.
|
||||
extern DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK;
|
||||
|
||||
int adb_commandline(int argc, const char** argv);
|
||||
|
||||
bool copy_to_file(int inFd, int outFd);
|
||||
|
||||
// Connects to the device "shell" service with |command| and prints the
|
||||
// resulting output.
|
||||
// if |callback| is non-null, stdout/stderr output will be handled by it.
|
||||
int send_shell_command(
|
||||
const std::string& command, bool disable_shell_protocol = false,
|
||||
StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK);
|
||||
|
||||
// Reads from |fd| and prints received data. If |use_shell_protocol| is true
|
||||
// this expects that incoming data will use the shell protocol, in which case
|
||||
// stdout/stderr are routed independently and the remote exit code will be
|
||||
// returned.
|
||||
// if |callback| is non-null, stdout/stderr output will be handled by it.
|
||||
int read_and_dump(borrowed_fd fd, bool use_shell_protocol = false,
|
||||
StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK);
|
||||
|
||||
// Connects to the device "abb" service with |command| and returns the fd.
|
||||
template <typename ContainerT>
|
||||
unique_fd send_abb_exec_command(const ContainerT& command_args, std::string* error) {
|
||||
std::string service_string = "abb_exec:" + android::base::Join(command_args, ABB_ARG_DELIMETER);
|
||||
|
||||
unique_fd fd(adb_connect(service_string, error));
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "adb: failed to run abb_exec. Error: %s\n", error->c_str());
|
||||
return unique_fd{};
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
#endif // COMMANDLINE_H
|
|
@ -1,181 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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 "sysdeps.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <cutils/sockets.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "adb_client.h"
|
||||
#include "adb_io.h"
|
||||
#include "adb_utils.h"
|
||||
|
||||
// Return the console authentication command for the emulator, if needed
|
||||
static std::string adb_construct_auth_command() {
|
||||
static const char auth_token_filename[] = ".emulator_console_auth_token";
|
||||
|
||||
std::string auth_token_path = adb_get_homedir_path();
|
||||
auth_token_path += OS_PATH_SEPARATOR;
|
||||
auth_token_path += auth_token_filename;
|
||||
|
||||
// read the token
|
||||
std::string token;
|
||||
if (!android::base::ReadFileToString(auth_token_path, &token)
|
||||
|| token.empty()) {
|
||||
// we either can't read the file, or it doesn't exist, or it's empty -
|
||||
// either way we won't add any authentication command.
|
||||
return {};
|
||||
}
|
||||
|
||||
// now construct and return the actual command: "auth <token>\n"
|
||||
std::string command = "auth ";
|
||||
command += token;
|
||||
command += '\n';
|
||||
return command;
|
||||
}
|
||||
|
||||
// Return the console port of the currently connected emulator (if any) or -1 if
|
||||
// there is no emulator, and -2 if there is more than one.
|
||||
static int adb_get_emulator_console_port(const char* serial) {
|
||||
if (serial) {
|
||||
// The user specified a serial number; is it an emulator?
|
||||
int port;
|
||||
return (sscanf(serial, "emulator-%d", &port) == 1) ? port : -1;
|
||||
}
|
||||
|
||||
// No specific device was given, so get the list of connected devices and
|
||||
// search for emulators. If there's one, we'll take it. If there are more
|
||||
// than one, that's an error.
|
||||
std::string devices;
|
||||
std::string error;
|
||||
if (!adb_query("host:devices", &devices, &error)) {
|
||||
fprintf(stderr, "error: no emulator connected: %s\n", error.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
int port = -1;
|
||||
size_t emulator_count = 0;
|
||||
for (const auto& device : android::base::Split(devices, "\n")) {
|
||||
if (sscanf(device.c_str(), "emulator-%d", &port) == 1) {
|
||||
if (++emulator_count > 1) {
|
||||
fprintf(
|
||||
stderr, "error: more than one emulator detected; use -s\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (emulator_count == 0) {
|
||||
fprintf(stderr, "error: no emulator detected\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
static int connect_to_console(const char* serial) {
|
||||
int port = adb_get_emulator_console_port(serial);
|
||||
if (port == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string error;
|
||||
int fd = network_loopback_client(port, SOCK_STREAM, &error);
|
||||
if (fd == -1) {
|
||||
fprintf(stderr, "error: could not connect to TCP port %d: %s\n", port,
|
||||
error.c_str());
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
int adb_send_emulator_command(int argc, const char** argv, const char* serial) {
|
||||
unique_fd fd(connect_to_console(serial));
|
||||
if (fd == -1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string commands = adb_construct_auth_command();
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
commands.append(argv[i]);
|
||||
commands.push_back(i == argc - 1 ? '\n' : ' ');
|
||||
}
|
||||
|
||||
commands.append("quit\n");
|
||||
|
||||
if (!WriteFdExactly(fd, commands)) {
|
||||
fprintf(stderr, "error: cannot write to emulator: %s\n",
|
||||
strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Drain output that the emulator console has sent us to prevent a problem
|
||||
// on Windows where if adb closes the socket without reading all the data,
|
||||
// the emulator's next call to recv() will have an ECONNABORTED error,
|
||||
// preventing the emulator from reading the command that adb has sent.
|
||||
// https://code.google.com/p/android/issues/detail?id=21021
|
||||
int result;
|
||||
std::string emulator_output;
|
||||
do {
|
||||
char buf[BUFSIZ];
|
||||
result = adb_read(fd, buf, sizeof(buf));
|
||||
// Keep reading until zero bytes (orderly/graceful shutdown) or an
|
||||
// error. If 'adb emu kill' is executed, the emulator calls exit() with
|
||||
// the socket open (and shutdown(SD_SEND) was not called), which causes
|
||||
// Windows to send a TCP RST segment which causes adb to get ECONNRESET.
|
||||
// Any other emu command is followed by the quit command that we
|
||||
// appended above, and that causes the emulator to close the socket
|
||||
// which should cause zero bytes (orderly/graceful shutdown) to be
|
||||
// returned.
|
||||
if (result > 0) emulator_output.append(buf, result);
|
||||
} while (result > 0);
|
||||
|
||||
// Note: the following messages are expected to be quite stable from emulator.
|
||||
//
|
||||
// Emulator console will send the following message upon connection:
|
||||
//
|
||||
// Android Console: Authentication required
|
||||
// Android Console: type 'auth <auth_token>' to authenticate
|
||||
// Android Console: you can find your <auth_token> in
|
||||
// '/<path-to-home>/.emulator_console_auth_token'
|
||||
// OK\r\n
|
||||
//
|
||||
// and the following after authentication:
|
||||
// Android Console: type 'help' for a list of commands
|
||||
// OK\r\n
|
||||
//
|
||||
// So try search and skip first two "OK\r\n", print the rest.
|
||||
//
|
||||
const std::string delims = "OK\r\n";
|
||||
size_t found = 0;
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
const size_t result = emulator_output.find(delims, found);
|
||||
if (result == std::string::npos) {
|
||||
break;
|
||||
} else {
|
||||
found = result + delims.size();
|
||||
}
|
||||
}
|
||||
|
||||
printf("%s", emulator_output.c_str() + found);
|
||||
return 0;
|
||||
}
|
|
@ -1,357 +0,0 @@
|
|||
/*
|
||||
* 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 "fastdeploy.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
#include "android-base/file.h"
|
||||
#include "android-base/strings.h"
|
||||
#include "androidfw/ResourceTypes.h"
|
||||
#include "androidfw/ZipFileRO.h"
|
||||
#include "client/file_sync_client.h"
|
||||
#include "commandline.h"
|
||||
#include "deployagent.inc" // Generated include via build rule.
|
||||
#include "deployagentscript.inc" // Generated include via build rule.
|
||||
#include "fastdeploy/deploypatchgenerator/deploy_patch_generator.h"
|
||||
#include "fastdeploy/deploypatchgenerator/patch_utils.h"
|
||||
#include "fastdeploy/proto/ApkEntry.pb.h"
|
||||
#include "fastdeploycallbacks.h"
|
||||
#include "sysdeps.h"
|
||||
|
||||
#include "adb_utils.h"
|
||||
|
||||
static constexpr long kRequiredAgentVersion = 0x00000003;
|
||||
|
||||
static constexpr int kPackageMissing = 3;
|
||||
static constexpr int kInvalidAgentVersion = 4;
|
||||
|
||||
static constexpr const char* kDeviceAgentFile = "/data/local/tmp/deployagent.jar";
|
||||
static constexpr const char* kDeviceAgentScript = "/data/local/tmp/deployagent";
|
||||
|
||||
static constexpr bool g_verbose_timings = false;
|
||||
static FastDeploy_AgentUpdateStrategy g_agent_update_strategy =
|
||||
FastDeploy_AgentUpdateDifferentVersion;
|
||||
|
||||
using APKMetaData = com::android::fastdeploy::APKMetaData;
|
||||
|
||||
namespace {
|
||||
|
||||
struct TimeReporter {
|
||||
TimeReporter(const char* label) : label_(label) {}
|
||||
~TimeReporter() {
|
||||
if (g_verbose_timings) {
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now() - start_);
|
||||
fprintf(stderr, "%s finished in %lldms\n", label_,
|
||||
static_cast<long long>(duration.count()));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const char* label_;
|
||||
std::chrono::steady_clock::time_point start_ = std::chrono::steady_clock::now();
|
||||
};
|
||||
#define REPORT_FUNC_TIME() TimeReporter reporter(__func__)
|
||||
|
||||
struct FileDeleter {
|
||||
FileDeleter(const char* path) : path_(path) {}
|
||||
~FileDeleter() { adb_unlink(path_); }
|
||||
|
||||
private:
|
||||
const char* const path_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
int get_device_api_level() {
|
||||
static const int api_level = [] {
|
||||
REPORT_FUNC_TIME();
|
||||
std::vector<char> sdk_version_output_buffer;
|
||||
std::vector<char> sdk_version_error_buffer;
|
||||
int api_level = -1;
|
||||
|
||||
int status_code =
|
||||
capture_shell_command("getprop ro.build.version.sdk", &sdk_version_output_buffer,
|
||||
&sdk_version_error_buffer);
|
||||
if (status_code == 0 && sdk_version_output_buffer.size() > 0) {
|
||||
api_level = strtol((char*)sdk_version_output_buffer.data(), nullptr, 10);
|
||||
}
|
||||
|
||||
return api_level;
|
||||
}();
|
||||
return api_level;
|
||||
}
|
||||
|
||||
void fastdeploy_set_agent_update_strategy(FastDeploy_AgentUpdateStrategy agent_update_strategy) {
|
||||
g_agent_update_strategy = agent_update_strategy;
|
||||
}
|
||||
|
||||
static void push_to_device(const void* data, size_t byte_count, const char* dst, bool sync) {
|
||||
std::vector<const char*> srcs;
|
||||
TemporaryFile tf;
|
||||
android::base::WriteFully(tf.fd, data, byte_count);
|
||||
srcs.push_back(tf.path);
|
||||
// On Windows, the file needs to be flushed before pushing to device,
|
||||
// but can't be removed until after the push.
|
||||
unix_close(tf.release());
|
||||
|
||||
if (!do_sync_push(srcs, dst, sync, CompressionType::Any, false)) {
|
||||
error_exit("Failed to push fastdeploy agent to device.");
|
||||
}
|
||||
}
|
||||
|
||||
static bool deploy_agent(bool check_time_stamps) {
|
||||
REPORT_FUNC_TIME();
|
||||
|
||||
push_to_device(kDeployAgent, sizeof(kDeployAgent), kDeviceAgentFile, check_time_stamps);
|
||||
push_to_device(kDeployAgentScript, sizeof(kDeployAgentScript), kDeviceAgentScript,
|
||||
check_time_stamps);
|
||||
|
||||
// on windows the shell script might have lost execute permission
|
||||
// so need to set this explicitly
|
||||
const char* kChmodCommandPattern = "chmod 777 %s";
|
||||
std::string chmod_command =
|
||||
android::base::StringPrintf(kChmodCommandPattern, kDeviceAgentScript);
|
||||
int ret = send_shell_command(chmod_command);
|
||||
if (ret != 0) {
|
||||
error_exit("Error executing %s returncode: %d", chmod_command.c_str(), ret);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string get_string_from_utf16(const char16_t* input, int input_len) {
|
||||
ssize_t utf8_length = utf16_to_utf8_length(input, input_len);
|
||||
if (utf8_length <= 0) {
|
||||
return {};
|
||||
}
|
||||
std::string utf8;
|
||||
utf8.resize(utf8_length);
|
||||
utf16_to_utf8(input, input_len, &*utf8.begin(), utf8_length + 1);
|
||||
return utf8;
|
||||
}
|
||||
|
||||
static std::string get_package_name_from_apk(const char* apk_path) {
|
||||
#undef open
|
||||
std::unique_ptr<android::ZipFileRO> zip_file((android::ZipFileRO::open)(apk_path));
|
||||
#define open ___xxx_unix_open
|
||||
if (zip_file == nullptr) {
|
||||
perror_exit("Could not open %s", apk_path);
|
||||
}
|
||||
android::ZipEntryRO entry = zip_file->findEntryByName("AndroidManifest.xml");
|
||||
if (entry == nullptr) {
|
||||
error_exit("Could not find AndroidManifest.xml inside %s", apk_path);
|
||||
}
|
||||
uint32_t manifest_len = 0;
|
||||
if (!zip_file->getEntryInfo(entry, NULL, &manifest_len, NULL, NULL, NULL, NULL)) {
|
||||
error_exit("Could not read AndroidManifest.xml inside %s", apk_path);
|
||||
}
|
||||
std::vector<char> manifest_data(manifest_len);
|
||||
if (!zip_file->uncompressEntry(entry, manifest_data.data(), manifest_len)) {
|
||||
error_exit("Could not uncompress AndroidManifest.xml inside %s", apk_path);
|
||||
}
|
||||
android::ResXMLTree tree;
|
||||
android::status_t setto_status = tree.setTo(manifest_data.data(), manifest_len, true);
|
||||
if (setto_status != android::OK) {
|
||||
error_exit("Could not parse AndroidManifest.xml inside %s", apk_path);
|
||||
}
|
||||
android::ResXMLParser::event_code_t code;
|
||||
while ((code = tree.next()) != android::ResXMLParser::BAD_DOCUMENT &&
|
||||
code != android::ResXMLParser::END_DOCUMENT) {
|
||||
switch (code) {
|
||||
case android::ResXMLParser::START_TAG: {
|
||||
size_t element_name_length;
|
||||
const char16_t* element_name = tree.getElementName(&element_name_length);
|
||||
if (element_name == nullptr) {
|
||||
continue;
|
||||
}
|
||||
std::u16string element_name_string(element_name, element_name_length);
|
||||
if (element_name_string == u"manifest") {
|
||||
for (size_t i = 0; i < tree.getAttributeCount(); i++) {
|
||||
size_t attribute_name_length;
|
||||
const char16_t* attribute_name_text =
|
||||
tree.getAttributeName(i, &attribute_name_length);
|
||||
if (attribute_name_text == nullptr) {
|
||||
continue;
|
||||
}
|
||||
std::u16string attribute_name_string(attribute_name_text,
|
||||
attribute_name_length);
|
||||
if (attribute_name_string == u"package") {
|
||||
size_t attribute_value_length;
|
||||
const char16_t* attribute_value_text =
|
||||
tree.getAttributeStringValue(i, &attribute_value_length);
|
||||
if (attribute_value_text == nullptr) {
|
||||
continue;
|
||||
}
|
||||
return get_string_from_utf16(attribute_value_text,
|
||||
attribute_value_length);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
error_exit("Could not find package name tag in AndroidManifest.xml inside %s", apk_path);
|
||||
}
|
||||
|
||||
static long parse_agent_version(const std::vector<char>& version_buffer) {
|
||||
long version = -1;
|
||||
if (!version_buffer.empty()) {
|
||||
version = strtol((char*)version_buffer.data(), NULL, 16);
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
static void update_agent_if_necessary() {
|
||||
switch (g_agent_update_strategy) {
|
||||
case FastDeploy_AgentUpdateAlways:
|
||||
deploy_agent(/*check_time_stamps=*/false);
|
||||
break;
|
||||
case FastDeploy_AgentUpdateNewerTimeStamp:
|
||||
deploy_agent(/*check_time_stamps=*/true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<APKMetaData> extract_metadata(const char* apk_path) {
|
||||
// Update agent if there is a command line argument forcing to do so.
|
||||
update_agent_if_necessary();
|
||||
|
||||
REPORT_FUNC_TIME();
|
||||
|
||||
std::string package_name = get_package_name_from_apk(apk_path);
|
||||
|
||||
// Dump apk command checks the required vs current agent version and if they match then returns
|
||||
// the APK dump for package. Doing this in a single call saves round-trip and agent launch time.
|
||||
constexpr const char* kAgentDumpCommandPattern = "/data/local/tmp/deployagent dump %ld %s";
|
||||
std::string dump_command = android::base::StringPrintf(
|
||||
kAgentDumpCommandPattern, kRequiredAgentVersion, package_name.c_str());
|
||||
|
||||
std::vector<char> dump_out_buffer;
|
||||
std::vector<char> dump_error_buffer;
|
||||
int returnCode =
|
||||
capture_shell_command(dump_command.c_str(), &dump_out_buffer, &dump_error_buffer);
|
||||
if (returnCode >= kInvalidAgentVersion) {
|
||||
// Agent has wrong version or missing.
|
||||
long agent_version = parse_agent_version(dump_out_buffer);
|
||||
if (agent_version < 0) {
|
||||
printf("Could not detect agent on device, deploying\n");
|
||||
} else {
|
||||
printf("Device agent version is (%ld), (%ld) is required, re-deploying\n",
|
||||
agent_version, kRequiredAgentVersion);
|
||||
}
|
||||
deploy_agent(/*check_time_stamps=*/false);
|
||||
|
||||
// Retry with new agent.
|
||||
dump_out_buffer.clear();
|
||||
dump_error_buffer.clear();
|
||||
returnCode =
|
||||
capture_shell_command(dump_command.c_str(), &dump_out_buffer, &dump_error_buffer);
|
||||
}
|
||||
if (returnCode != 0) {
|
||||
if (returnCode == kInvalidAgentVersion) {
|
||||
long agent_version = parse_agent_version(dump_out_buffer);
|
||||
error_exit(
|
||||
"After update agent version remains incorrect! Expected %ld but version is %ld",
|
||||
kRequiredAgentVersion, agent_version);
|
||||
}
|
||||
if (returnCode == kPackageMissing) {
|
||||
fprintf(stderr, "Package %s not found, falling back to install\n",
|
||||
package_name.c_str());
|
||||
return {};
|
||||
}
|
||||
fprintf(stderr, "Executing %s returned %d\n", dump_command.c_str(), returnCode);
|
||||
fprintf(stderr, "%*s\n", int(dump_error_buffer.size()), dump_error_buffer.data());
|
||||
error_exit("Aborting");
|
||||
}
|
||||
|
||||
com::android::fastdeploy::APKDump dump;
|
||||
if (!dump.ParseFromArray(dump_out_buffer.data(), dump_out_buffer.size())) {
|
||||
fprintf(stderr, "Can't parse output of %s\n", dump_command.c_str());
|
||||
error_exit("Aborting");
|
||||
}
|
||||
|
||||
return PatchUtils::GetDeviceAPKMetaData(dump);
|
||||
}
|
||||
|
||||
unique_fd install_patch(int argc, const char** argv) {
|
||||
REPORT_FUNC_TIME();
|
||||
constexpr char kAgentApplyServicePattern[] = "shell:/data/local/tmp/deployagent apply - -pm %s";
|
||||
|
||||
std::vector<unsigned char> apply_output_buffer;
|
||||
std::vector<unsigned char> apply_error_buffer;
|
||||
std::string argsString;
|
||||
|
||||
bool rSwitchPresent = false;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
argsString.append(argv[i]);
|
||||
argsString.append(" ");
|
||||
if (!strcmp(argv[i], "-r")) {
|
||||
rSwitchPresent = true;
|
||||
}
|
||||
}
|
||||
if (!rSwitchPresent) {
|
||||
argsString.append("-r");
|
||||
}
|
||||
|
||||
std::string error;
|
||||
std::string apply_patch_service_string =
|
||||
android::base::StringPrintf(kAgentApplyServicePattern, argsString.c_str());
|
||||
unique_fd fd{adb_connect(apply_patch_service_string, &error)};
|
||||
if (fd < 0) {
|
||||
error_exit("Executing %s returned %s", apply_patch_service_string.c_str(), error.c_str());
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
unique_fd apply_patch_on_device(const char* output_path) {
|
||||
REPORT_FUNC_TIME();
|
||||
constexpr char kAgentApplyServicePattern[] = "shell:/data/local/tmp/deployagent apply - -o %s";
|
||||
|
||||
std::string error;
|
||||
std::string apply_patch_service_string =
|
||||
android::base::StringPrintf(kAgentApplyServicePattern, output_path);
|
||||
unique_fd fd{adb_connect(apply_patch_service_string, &error)};
|
||||
if (fd < 0) {
|
||||
error_exit("Executing %s returned %s", apply_patch_service_string.c_str(), error.c_str());
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void create_patch(const char* apk_path, APKMetaData metadata, borrowed_fd patch_fd) {
|
||||
REPORT_FUNC_TIME();
|
||||
DeployPatchGenerator generator(/*is_verbose=*/false);
|
||||
bool success = generator.CreatePatch(apk_path, std::move(metadata), patch_fd);
|
||||
if (!success) {
|
||||
error_exit("Failed to create patch for %s", apk_path);
|
||||
}
|
||||
}
|
||||
|
||||
int stream_patch(const char* apk_path, APKMetaData metadata, unique_fd patch_fd) {
|
||||
create_patch(apk_path, std::move(metadata), patch_fd);
|
||||
|
||||
REPORT_FUNC_TIME();
|
||||
return read_and_dump(patch_fd.get());
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "adb_unique_fd.h"
|
||||
|
||||
#include "fastdeploy/proto/ApkEntry.pb.h"
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
enum FastDeploy_AgentUpdateStrategy {
|
||||
FastDeploy_AgentUpdateAlways,
|
||||
FastDeploy_AgentUpdateNewerTimeStamp,
|
||||
FastDeploy_AgentUpdateDifferentVersion
|
||||
};
|
||||
|
||||
void fastdeploy_set_agent_update_strategy(FastDeploy_AgentUpdateStrategy agent_update_strategy);
|
||||
int get_device_api_level();
|
||||
|
||||
std::optional<com::android::fastdeploy::APKMetaData> extract_metadata(const char* apk_path);
|
||||
unique_fd install_patch(int argc, const char** argv);
|
||||
unique_fd apply_patch_on_device(const char* output_path);
|
||||
int stream_patch(const char* apk_path, com::android::fastdeploy::APKMetaData metadata,
|
||||
unique_fd patch_fd);
|
|
@ -1,71 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define TRACE_TAG ADB
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "client/file_sync_client.h"
|
||||
#include "commandline.h"
|
||||
#include "sysdeps.h"
|
||||
|
||||
#include "fastdeploycallbacks.h"
|
||||
|
||||
static void appendBuffer(std::vector<char>* buffer, const char* input, int length) {
|
||||
if (buffer != NULL) {
|
||||
buffer->insert(buffer->end(), input, input + length);
|
||||
}
|
||||
}
|
||||
|
||||
class DeployAgentBufferCallback : public StandardStreamsCallbackInterface {
|
||||
public:
|
||||
DeployAgentBufferCallback(std::vector<char>* outBuffer, std::vector<char>* errBuffer);
|
||||
|
||||
virtual void OnStdout(const char* buffer, int length);
|
||||
virtual void OnStderr(const char* buffer, int length);
|
||||
virtual int Done(int status);
|
||||
|
||||
private:
|
||||
std::vector<char>* mpOutBuffer;
|
||||
std::vector<char>* mpErrBuffer;
|
||||
};
|
||||
|
||||
int capture_shell_command(const char* command, std::vector<char>* outBuffer,
|
||||
std::vector<char>* errBuffer) {
|
||||
DeployAgentBufferCallback cb(outBuffer, errBuffer);
|
||||
return send_shell_command(command, /*disable_shell_protocol=*/false, &cb);
|
||||
}
|
||||
|
||||
DeployAgentBufferCallback::DeployAgentBufferCallback(std::vector<char>* outBuffer,
|
||||
std::vector<char>* errBuffer) {
|
||||
mpOutBuffer = outBuffer;
|
||||
mpErrBuffer = errBuffer;
|
||||
}
|
||||
|
||||
void DeployAgentBufferCallback::OnStdout(const char* buffer, int length) {
|
||||
appendBuffer(mpOutBuffer, buffer, length);
|
||||
}
|
||||
|
||||
void DeployAgentBufferCallback::OnStderr(const char* buffer, int length) {
|
||||
appendBuffer(mpErrBuffer, buffer, length);
|
||||
}
|
||||
|
||||
int DeployAgentBufferCallback::Done(int status) {
|
||||
return status;
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
int capture_shell_command(const char* command, std::vector<char>* outBuffer,
|
||||
std::vector<char>* errBuffer);
|
File diff suppressed because it is too large
Load Diff
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "file_sync_protocol.h"
|
||||
|
||||
bool do_sync_ls(const char* path);
|
||||
bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync,
|
||||
CompressionType compression, bool dry_run);
|
||||
bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
|
||||
CompressionType compression, const char* name = nullptr);
|
||||
|
||||
bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only,
|
||||
CompressionType compression, bool dry_run);
|
|
@ -1,256 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 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 "incremental.h"
|
||||
|
||||
#include "incremental_utils.h"
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <openssl/base64.h>
|
||||
|
||||
#include "adb_client.h"
|
||||
#include "adb_utils.h"
|
||||
#include "commandline.h"
|
||||
#include "sysdeps.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace incremental {
|
||||
|
||||
using android::base::StringPrintf;
|
||||
|
||||
// Read, verify and return the signature bytes. Keeping fd at the position of start of verity tree.
|
||||
static std::pair<unique_fd, std::vector<char>> read_signature(Size file_size,
|
||||
std::string signature_file,
|
||||
bool silent) {
|
||||
signature_file += IDSIG;
|
||||
|
||||
struct stat st;
|
||||
if (stat(signature_file.c_str(), &st)) {
|
||||
if (!silent) {
|
||||
fprintf(stderr, "Failed to stat signature file %s.\n", signature_file.c_str());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
unique_fd fd(adb_open(signature_file.c_str(), O_RDONLY));
|
||||
if (fd < 0) {
|
||||
if (!silent) {
|
||||
fprintf(stderr, "Failed to open signature file: %s.\n", signature_file.c_str());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
auto [signature, tree_size] = read_id_sig_headers(fd);
|
||||
|
||||
std::vector<char> invalid_signature;
|
||||
if (signature.size() > kMaxSignatureSize) {
|
||||
if (!silent) {
|
||||
fprintf(stderr, "Signature is too long. Max allowed is %d. Abort.\n",
|
||||
kMaxSignatureSize);
|
||||
}
|
||||
return {std::move(fd), std::move(invalid_signature)};
|
||||
}
|
||||
|
||||
if (auto expected = verity_tree_size_for_file(file_size); tree_size != expected) {
|
||||
if (!silent) {
|
||||
fprintf(stderr,
|
||||
"Verity tree size mismatch in signature file: %s [was %lld, expected %lld].\n",
|
||||
signature_file.c_str(), (long long)tree_size, (long long)expected);
|
||||
}
|
||||
return {std::move(fd), std::move(invalid_signature)};
|
||||
}
|
||||
|
||||
return {std::move(fd), std::move(signature)};
|
||||
}
|
||||
|
||||
// Base64-encode signature bytes. Keeping fd at the position of start of verity tree.
|
||||
static std::pair<unique_fd, std::string> read_and_encode_signature(Size file_size,
|
||||
std::string signature_file,
|
||||
bool silent) {
|
||||
std::string encoded_signature;
|
||||
|
||||
auto [fd, signature] = read_signature(file_size, std::move(signature_file), silent);
|
||||
if (!fd.ok() || signature.empty()) {
|
||||
return {std::move(fd), std::move(encoded_signature)};
|
||||
}
|
||||
|
||||
size_t base64_len = 0;
|
||||
if (!EVP_EncodedLength(&base64_len, signature.size())) {
|
||||
if (!silent) {
|
||||
fprintf(stderr, "Fail to estimate base64 encoded length. Abort.\n");
|
||||
}
|
||||
return {std::move(fd), std::move(encoded_signature)};
|
||||
}
|
||||
|
||||
encoded_signature.resize(base64_len, '\0');
|
||||
encoded_signature.resize(EVP_EncodeBlock((uint8_t*)encoded_signature.data(),
|
||||
(const uint8_t*)signature.data(), signature.size()));
|
||||
|
||||
return {std::move(fd), std::move(encoded_signature)};
|
||||
}
|
||||
|
||||
// Send install-incremental to the device along with properly configured file descriptors in
|
||||
// streaming format. Once connection established, send all fs-verity tree bytes.
|
||||
static unique_fd start_install(const Files& files, const Args& passthrough_args, bool silent) {
|
||||
std::vector<std::string> command_args{"package", "install-incremental"};
|
||||
command_args.insert(command_args.end(), passthrough_args.begin(), passthrough_args.end());
|
||||
|
||||
for (int i = 0, size = files.size(); i < size; ++i) {
|
||||
const auto& file = files[i];
|
||||
|
||||
struct stat st;
|
||||
if (stat(file.c_str(), &st)) {
|
||||
if (!silent) {
|
||||
fprintf(stderr, "Failed to stat input file %s. Abort.\n", file.c_str());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
auto [signature_fd, signature] = read_and_encode_signature(st.st_size, file, silent);
|
||||
if (signature_fd.ok() && signature.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto file_desc = StringPrintf("%s:%lld:%d:%s:1", android::base::Basename(file).c_str(),
|
||||
(long long)st.st_size, i, signature.c_str());
|
||||
command_args.push_back(std::move(file_desc));
|
||||
}
|
||||
|
||||
std::string error;
|
||||
auto connection_fd = unique_fd(send_abb_exec_command(command_args, &error));
|
||||
if (connection_fd < 0) {
|
||||
if (!silent) {
|
||||
fprintf(stderr, "Failed to run: %s, error: %s\n",
|
||||
android::base::Join(command_args, " ").c_str(), error.c_str());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
return connection_fd;
|
||||
}
|
||||
|
||||
bool can_install(const Files& files) {
|
||||
for (const auto& file : files) {
|
||||
struct stat st;
|
||||
if (stat(file.c_str(), &st)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (android::base::EndsWithIgnoreCase(file, ".apk")) {
|
||||
// Signature has to be present for APKs.
|
||||
auto [fd, _] = read_signature(st.st_size, file, /*silent=*/true);
|
||||
if (!fd.ok()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<Process> install(const Files& files, const Args& passthrough_args, bool silent) {
|
||||
auto connection_fd = start_install(files, passthrough_args, silent);
|
||||
if (connection_fd < 0) {
|
||||
if (!silent) {
|
||||
fprintf(stderr, "adb: failed to initiate installation on device.\n");
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string adb_path = android::base::GetExecutablePath();
|
||||
|
||||
auto osh = cast_handle_to_int(adb_get_os_handle(connection_fd.get()));
|
||||
auto fd_param = std::to_string(osh);
|
||||
|
||||
// pipe for child process to write output
|
||||
int print_fds[2];
|
||||
if (adb_socketpair(print_fds) != 0) {
|
||||
if (!silent) {
|
||||
fprintf(stderr, "adb: failed to create socket pair for child to print to parent\n");
|
||||
}
|
||||
return {};
|
||||
}
|
||||
auto [pipe_read_fd, pipe_write_fd] = print_fds;
|
||||
auto pipe_write_fd_param = std::to_string(cast_handle_to_int(adb_get_os_handle(pipe_write_fd)));
|
||||
close_on_exec(pipe_read_fd);
|
||||
|
||||
std::vector<std::string> args(std::move(files));
|
||||
args.insert(args.begin(), {"inc-server", fd_param, pipe_write_fd_param});
|
||||
auto child =
|
||||
adb_launch_process(adb_path, std::move(args), {connection_fd.get(), pipe_write_fd});
|
||||
if (!child) {
|
||||
if (!silent) {
|
||||
fprintf(stderr, "adb: failed to fork: %s\n", strerror(errno));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
adb_close(pipe_write_fd);
|
||||
|
||||
auto killOnExit = [](Process* p) { p->kill(); };
|
||||
std::unique_ptr<Process, decltype(killOnExit)> serverKiller(&child, killOnExit);
|
||||
|
||||
Result result = wait_for_installation(pipe_read_fd);
|
||||
adb_close(pipe_read_fd);
|
||||
|
||||
if (result != Result::Success) {
|
||||
if (!silent) {
|
||||
fprintf(stderr, "adb: install command failed");
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
// adb client exits now but inc-server can continue
|
||||
serverKiller.release();
|
||||
return child;
|
||||
}
|
||||
|
||||
Result wait_for_installation(int read_fd) {
|
||||
static constexpr int maxMessageSize = 256;
|
||||
std::vector<char> child_stdout(CHUNK_SIZE);
|
||||
int bytes_read;
|
||||
int buf_size = 0;
|
||||
// TODO(b/150865433): optimize child's output parsing
|
||||
while ((bytes_read = adb_read(read_fd, child_stdout.data() + buf_size,
|
||||
child_stdout.size() - buf_size)) > 0) {
|
||||
// print to parent's stdout
|
||||
fprintf(stdout, "%.*s", bytes_read, child_stdout.data() + buf_size);
|
||||
|
||||
buf_size += bytes_read;
|
||||
const std::string_view stdout_str(child_stdout.data(), buf_size);
|
||||
// wait till installation either succeeds or fails
|
||||
if (stdout_str.find("Success") != std::string::npos) {
|
||||
return Result::Success;
|
||||
}
|
||||
// on failure, wait for full message
|
||||
static constexpr auto failure_msg_head = "Failure ["sv;
|
||||
if (const auto begin_itr = stdout_str.find(failure_msg_head);
|
||||
begin_itr != std::string::npos) {
|
||||
if (buf_size >= maxMessageSize) {
|
||||
return Result::Failure;
|
||||
}
|
||||
const auto end_itr = stdout_str.rfind("]");
|
||||
if (end_itr != std::string::npos && end_itr >= begin_itr + failure_msg_head.size()) {
|
||||
return Result::Failure;
|
||||
}
|
||||
}
|
||||
child_stdout.resize(buf_size + CHUNK_SIZE);
|
||||
}
|
||||
return Result::None;
|
||||
}
|
||||
|
||||
} // namespace incremental
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "adb_unique_fd.h"
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
namespace incremental {
|
||||
|
||||
using Files = std::vector<std::string>;
|
||||
using Args = std::vector<std::string_view>;
|
||||
|
||||
bool can_install(const Files& files);
|
||||
std::optional<Process> install(const Files& files, const Args& passthrough_args, bool silent);
|
||||
|
||||
enum class Result { Success, Failure, None };
|
||||
Result wait_for_installation(int read_fd);
|
||||
|
||||
} // namespace incremental
|
|
@ -1,722 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 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.
|
||||
*/
|
||||
|
||||
#define TRACE_TAG INCREMENTAL
|
||||
|
||||
#include "incremental_server.h"
|
||||
|
||||
#include <android-base/endian.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <inttypes.h>
|
||||
#include <lz4.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <array>
|
||||
#include <deque>
|
||||
#include <fstream>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "adb.h"
|
||||
#include "adb_io.h"
|
||||
#include "adb_trace.h"
|
||||
#include "adb_unique_fd.h"
|
||||
#include "adb_utils.h"
|
||||
#include "incremental_utils.h"
|
||||
#include "sysdeps.h"
|
||||
|
||||
namespace incremental {
|
||||
|
||||
static constexpr int kHashesPerBlock = kBlockSize / kDigestSize;
|
||||
static constexpr int kCompressedSizeMax = kBlockSize * 0.95;
|
||||
static constexpr int8_t kTypeData = 0;
|
||||
static constexpr int8_t kTypeHash = 1;
|
||||
static constexpr int8_t kCompressionNone = 0;
|
||||
static constexpr int8_t kCompressionLZ4 = 1;
|
||||
static constexpr int kCompressBound = std::max(kBlockSize, LZ4_COMPRESSBOUND(kBlockSize));
|
||||
static constexpr auto kReadBufferSize = 128 * 1024;
|
||||
static constexpr int kPollTimeoutMillis = 300000; // 5 minutes
|
||||
|
||||
using BlockSize = int16_t;
|
||||
using FileId = int16_t;
|
||||
using BlockIdx = int32_t;
|
||||
using NumBlocks = int32_t;
|
||||
using BlockType = int8_t;
|
||||
using CompressionType = int8_t;
|
||||
using RequestType = int16_t;
|
||||
using ChunkHeader = int32_t;
|
||||
using MagicType = uint32_t;
|
||||
|
||||
static constexpr MagicType INCR = 0x494e4352; // LE INCR
|
||||
|
||||
static constexpr RequestType SERVING_COMPLETE = 0;
|
||||
static constexpr RequestType BLOCK_MISSING = 1;
|
||||
static constexpr RequestType PREFETCH = 2;
|
||||
static constexpr RequestType DESTROY = 3;
|
||||
|
||||
static constexpr inline int64_t roundDownToBlockOffset(int64_t val) {
|
||||
return val & ~(kBlockSize - 1);
|
||||
}
|
||||
|
||||
static constexpr inline int64_t roundUpToBlockOffset(int64_t val) {
|
||||
return roundDownToBlockOffset(val + kBlockSize - 1);
|
||||
}
|
||||
|
||||
static constexpr inline NumBlocks numBytesToNumBlocks(int64_t bytes) {
|
||||
return roundUpToBlockOffset(bytes) / kBlockSize;
|
||||
}
|
||||
|
||||
static constexpr inline off64_t blockIndexToOffset(BlockIdx blockIdx) {
|
||||
return static_cast<off64_t>(blockIdx) * kBlockSize;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline constexpr T toBigEndian(T t) {
|
||||
using unsigned_type = std::make_unsigned_t<T>;
|
||||
if constexpr (std::is_same_v<T, int16_t>) {
|
||||
return htobe16(static_cast<unsigned_type>(t));
|
||||
} else if constexpr (std::is_same_v<T, int32_t>) {
|
||||
return htobe32(static_cast<unsigned_type>(t));
|
||||
} else if constexpr (std::is_same_v<T, int64_t>) {
|
||||
return htobe64(static_cast<unsigned_type>(t));
|
||||
} else {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline constexpr T readBigEndian(void* data) {
|
||||
using unsigned_type = std::make_unsigned_t<T>;
|
||||
if constexpr (std::is_same_v<T, int16_t>) {
|
||||
return static_cast<T>(be16toh(*reinterpret_cast<unsigned_type*>(data)));
|
||||
} else if constexpr (std::is_same_v<T, int32_t>) {
|
||||
return static_cast<T>(be32toh(*reinterpret_cast<unsigned_type*>(data)));
|
||||
} else if constexpr (std::is_same_v<T, int64_t>) {
|
||||
return static_cast<T>(be64toh(*reinterpret_cast<unsigned_type*>(data)));
|
||||
} else {
|
||||
return T();
|
||||
}
|
||||
}
|
||||
|
||||
// Received from device
|
||||
// !Does not include magic!
|
||||
struct RequestCommand {
|
||||
RequestType request_type; // 2 bytes
|
||||
FileId file_id; // 2 bytes
|
||||
union {
|
||||
BlockIdx block_idx;
|
||||
NumBlocks num_blocks;
|
||||
}; // 4 bytes
|
||||
} __attribute__((packed));
|
||||
|
||||
// Placed before actual data bytes of each block
|
||||
struct ResponseHeader {
|
||||
FileId file_id; // 2 bytes
|
||||
BlockType block_type; // 1 byte
|
||||
CompressionType compression_type; // 1 byte
|
||||
BlockIdx block_idx; // 4 bytes
|
||||
BlockSize block_size; // 2 bytes
|
||||
|
||||
static constexpr size_t responseSizeFor(size_t dataSize) {
|
||||
return dataSize + sizeof(ResponseHeader);
|
||||
}
|
||||
} __attribute__((packed));
|
||||
|
||||
template <size_t Size = kBlockSize>
|
||||
struct BlockBuffer {
|
||||
ResponseHeader header;
|
||||
char data[Size];
|
||||
} __attribute__((packed));
|
||||
|
||||
// Holds streaming state for a file
|
||||
class File {
|
||||
public:
|
||||
// Plain file
|
||||
File(const char* filepath, FileId id, int64_t size, unique_fd fd, int64_t tree_offset,
|
||||
unique_fd tree_fd)
|
||||
: File(filepath, id, size, tree_offset) {
|
||||
this->fd_ = std::move(fd);
|
||||
this->tree_fd_ = std::move(tree_fd);
|
||||
priority_blocks_ = PriorityBlocksForFile(filepath, fd_.get(), size);
|
||||
}
|
||||
int64_t ReadDataBlock(BlockIdx block_idx, void* buf, bool* is_zip_compressed) const {
|
||||
int64_t bytes_read = -1;
|
||||
const off64_t offsetStart = blockIndexToOffset(block_idx);
|
||||
bytes_read = adb_pread(fd_, buf, kBlockSize, offsetStart);
|
||||
return bytes_read;
|
||||
}
|
||||
int64_t ReadTreeBlock(BlockIdx block_idx, void* buf) const {
|
||||
int64_t bytes_read = -1;
|
||||
const off64_t offsetStart = tree_offset_ + blockIndexToOffset(block_idx);
|
||||
bytes_read = adb_pread(tree_fd_, buf, kBlockSize, offsetStart);
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
const std::vector<BlockIdx>& PriorityBlocks() const { return priority_blocks_; }
|
||||
|
||||
bool hasTree() const { return tree_fd_.ok(); }
|
||||
|
||||
std::vector<bool> sentBlocks;
|
||||
NumBlocks sentBlocksCount = 0;
|
||||
|
||||
std::vector<bool> sentTreeBlocks;
|
||||
|
||||
const char* const filepath;
|
||||
const FileId id;
|
||||
const int64_t size;
|
||||
|
||||
private:
|
||||
File(const char* filepath, FileId id, int64_t size, int64_t tree_offset)
|
||||
: filepath(filepath), id(id), size(size), tree_offset_(tree_offset) {
|
||||
sentBlocks.resize(numBytesToNumBlocks(size));
|
||||
sentTreeBlocks.resize(verity_tree_blocks_for_file(size));
|
||||
}
|
||||
unique_fd fd_;
|
||||
std::vector<BlockIdx> priority_blocks_;
|
||||
|
||||
unique_fd tree_fd_;
|
||||
const int64_t tree_offset_;
|
||||
};
|
||||
|
||||
class IncrementalServer {
|
||||
public:
|
||||
IncrementalServer(unique_fd adb_fd, unique_fd output_fd, std::vector<File> files)
|
||||
: adb_fd_(std::move(adb_fd)), output_fd_(std::move(output_fd)), files_(std::move(files)) {
|
||||
buffer_.reserve(kReadBufferSize);
|
||||
pendingBlocksBuffer_.resize(kChunkFlushSize + 2 * kBlockSize);
|
||||
pendingBlocks_ = pendingBlocksBuffer_.data() + sizeof(ChunkHeader);
|
||||
}
|
||||
|
||||
bool Serve();
|
||||
|
||||
private:
|
||||
struct PrefetchState {
|
||||
const File* file;
|
||||
BlockIdx overallIndex = 0;
|
||||
BlockIdx overallEnd = 0;
|
||||
BlockIdx priorityIndex = 0;
|
||||
|
||||
explicit PrefetchState(const File& f, BlockIdx start, int count)
|
||||
: file(&f),
|
||||
overallIndex(start),
|
||||
overallEnd(std::min<BlockIdx>(start + count, f.sentBlocks.size())) {}
|
||||
|
||||
explicit PrefetchState(const File& f)
|
||||
: PrefetchState(f, 0, (BlockIdx)f.sentBlocks.size()) {}
|
||||
|
||||
bool done() const {
|
||||
const bool overallSent = (overallIndex >= overallEnd);
|
||||
if (file->PriorityBlocks().empty()) {
|
||||
return overallSent;
|
||||
}
|
||||
return overallSent && (priorityIndex >= (BlockIdx)file->PriorityBlocks().size());
|
||||
}
|
||||
};
|
||||
|
||||
bool SkipToRequest(void* buffer, size_t* size, bool blocking);
|
||||
std::optional<RequestCommand> ReadRequest(bool blocking);
|
||||
|
||||
void erase_buffer_head(int count) { buffer_.erase(buffer_.begin(), buffer_.begin() + count); }
|
||||
|
||||
enum class SendResult { Sent, Skipped, Error };
|
||||
SendResult SendDataBlock(FileId fileId, BlockIdx blockIdx, bool flush = false);
|
||||
|
||||
bool SendTreeBlock(FileId fileId, int32_t fileBlockIdx, BlockIdx blockIdx);
|
||||
bool SendTreeBlocksForDataBlock(FileId fileId, BlockIdx blockIdx);
|
||||
|
||||
bool SendDone();
|
||||
void RunPrefetching();
|
||||
|
||||
void Send(const void* data, size_t size, bool flush);
|
||||
void Flush();
|
||||
using TimePoint = decltype(std::chrono::high_resolution_clock::now());
|
||||
bool ServingComplete(std::optional<TimePoint> startTime, int missesCount, int missesSent);
|
||||
|
||||
unique_fd const adb_fd_;
|
||||
unique_fd const output_fd_;
|
||||
std::vector<File> files_;
|
||||
|
||||
// Incoming data buffer.
|
||||
std::vector<char> buffer_;
|
||||
|
||||
std::deque<PrefetchState> prefetches_;
|
||||
int compressed_ = 0, uncompressed_ = 0;
|
||||
long long sentSize_ = 0;
|
||||
|
||||
static constexpr auto kChunkFlushSize = 31 * kBlockSize;
|
||||
|
||||
std::vector<char> pendingBlocksBuffer_;
|
||||
char* pendingBlocks_ = nullptr;
|
||||
|
||||
// True when client notifies that all the data has been received
|
||||
bool servingComplete_ = false;
|
||||
};
|
||||
|
||||
bool IncrementalServer::SkipToRequest(void* buffer, size_t* size, bool blocking) {
|
||||
while (true) {
|
||||
// Looking for INCR magic.
|
||||
bool magic_found = false;
|
||||
int bcur = 0;
|
||||
int bsize = buffer_.size();
|
||||
for (bcur = 0; bcur + 4 < bsize; ++bcur) {
|
||||
uint32_t magic = be32toh(*(uint32_t*)(buffer_.data() + bcur));
|
||||
if (magic == INCR) {
|
||||
magic_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bcur > 0) {
|
||||
// output the rest.
|
||||
(void)WriteFdExactly(output_fd_, buffer_.data(), bcur);
|
||||
erase_buffer_head(bcur);
|
||||
}
|
||||
|
||||
if (magic_found && buffer_.size() >= *size + sizeof(INCR)) {
|
||||
// fine, return
|
||||
memcpy(buffer, buffer_.data() + sizeof(INCR), *size);
|
||||
erase_buffer_head(*size + sizeof(INCR));
|
||||
return true;
|
||||
}
|
||||
|
||||
adb_pollfd pfd = {adb_fd_.get(), POLLIN, 0};
|
||||
auto res = adb_poll(&pfd, 1, blocking ? kPollTimeoutMillis : 0);
|
||||
|
||||
if (res != 1) {
|
||||
auto err = errno;
|
||||
(void)WriteFdExactly(output_fd_, buffer_.data(), buffer_.size());
|
||||
if (res < 0) {
|
||||
D("Failed to poll: %s", strerror(err));
|
||||
return false;
|
||||
}
|
||||
if (blocking) {
|
||||
fprintf(stderr, "Timed out waiting for data from device.\n");
|
||||
}
|
||||
if (blocking && servingComplete_) {
|
||||
// timeout waiting from client. Serving is complete, so quit.
|
||||
return false;
|
||||
}
|
||||
*size = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bsize = buffer_.size();
|
||||
buffer_.resize(kReadBufferSize);
|
||||
int r = adb_read(adb_fd_, buffer_.data() + bsize, kReadBufferSize - bsize);
|
||||
if (r > 0) {
|
||||
buffer_.resize(bsize + r);
|
||||
continue;
|
||||
}
|
||||
|
||||
D("Failed to read from fd %d: %d. Exit", adb_fd_.get(), errno);
|
||||
break;
|
||||
}
|
||||
// socket is closed. print remaining messages
|
||||
WriteFdExactly(output_fd_, buffer_.data(), buffer_.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<RequestCommand> IncrementalServer::ReadRequest(bool blocking) {
|
||||
uint8_t commandBuf[sizeof(RequestCommand)];
|
||||
auto size = sizeof(commandBuf);
|
||||
if (!SkipToRequest(&commandBuf, &size, blocking)) {
|
||||
return {{DESTROY}};
|
||||
}
|
||||
if (size < sizeof(RequestCommand)) {
|
||||
return {};
|
||||
}
|
||||
RequestCommand request;
|
||||
request.request_type = readBigEndian<RequestType>(&commandBuf[0]);
|
||||
request.file_id = readBigEndian<FileId>(&commandBuf[2]);
|
||||
request.block_idx = readBigEndian<BlockIdx>(&commandBuf[4]);
|
||||
return request;
|
||||
}
|
||||
|
||||
bool IncrementalServer::SendTreeBlocksForDataBlock(const FileId fileId, const BlockIdx blockIdx) {
|
||||
auto& file = files_[fileId];
|
||||
if (!file.hasTree()) {
|
||||
return true;
|
||||
}
|
||||
const int32_t data_block_count = numBytesToNumBlocks(file.size);
|
||||
|
||||
const int32_t total_nodes_count(file.sentTreeBlocks.size());
|
||||
const int32_t leaf_nodes_count = (data_block_count + kHashesPerBlock - 1) / kHashesPerBlock;
|
||||
|
||||
const int32_t leaf_nodes_offset = total_nodes_count - leaf_nodes_count;
|
||||
|
||||
// Leaf level, sending only 1 block.
|
||||
const int32_t leaf_idx = leaf_nodes_offset + blockIdx / kHashesPerBlock;
|
||||
if (file.sentTreeBlocks[leaf_idx]) {
|
||||
return true;
|
||||
}
|
||||
if (!SendTreeBlock(fileId, blockIdx, leaf_idx)) {
|
||||
return false;
|
||||
}
|
||||
file.sentTreeBlocks[leaf_idx] = true;
|
||||
|
||||
// Non-leaf, sending EVERYTHING. This should be done only once.
|
||||
if (leaf_nodes_offset == 0 || file.sentTreeBlocks[0]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < leaf_nodes_offset; ++i) {
|
||||
if (!SendTreeBlock(fileId, blockIdx, i)) {
|
||||
return false;
|
||||
}
|
||||
file.sentTreeBlocks[i] = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IncrementalServer::SendTreeBlock(FileId fileId, int32_t fileBlockIdx, BlockIdx blockIdx) {
|
||||
const auto& file = files_[fileId];
|
||||
|
||||
BlockBuffer buffer;
|
||||
const int64_t bytesRead = file.ReadTreeBlock(blockIdx, buffer.data);
|
||||
if (bytesRead <= 0) {
|
||||
fprintf(stderr, "Failed to get data for %s.idsig at blockIdx=%d.\n", file.filepath,
|
||||
blockIdx);
|
||||
return false;
|
||||
}
|
||||
|
||||
buffer.header.compression_type = kCompressionNone;
|
||||
buffer.header.block_type = kTypeHash;
|
||||
buffer.header.file_id = toBigEndian(fileId);
|
||||
buffer.header.block_size = toBigEndian(int16_t(bytesRead));
|
||||
buffer.header.block_idx = toBigEndian(blockIdx);
|
||||
|
||||
Send(&buffer, ResponseHeader::responseSizeFor(bytesRead), /*flush=*/false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto IncrementalServer::SendDataBlock(FileId fileId, BlockIdx blockIdx, bool flush) -> SendResult {
|
||||
auto& file = files_[fileId];
|
||||
if (blockIdx >= static_cast<long>(file.sentBlocks.size())) {
|
||||
// may happen as we schedule some extra blocks for reported page misses
|
||||
D("Skipped reading file %s at block %" PRId32 " (past end).", file.filepath, blockIdx);
|
||||
return SendResult::Skipped;
|
||||
}
|
||||
if (file.sentBlocks[blockIdx]) {
|
||||
return SendResult::Skipped;
|
||||
}
|
||||
|
||||
if (!SendTreeBlocksForDataBlock(fileId, blockIdx)) {
|
||||
return SendResult::Error;
|
||||
}
|
||||
|
||||
BlockBuffer raw;
|
||||
bool isZipCompressed = false;
|
||||
const int64_t bytesRead = file.ReadDataBlock(blockIdx, raw.data, &isZipCompressed);
|
||||
if (bytesRead < 0) {
|
||||
fprintf(stderr, "Failed to get data for %s at blockIdx=%d (%d).\n", file.filepath, blockIdx,
|
||||
errno);
|
||||
return SendResult::Error;
|
||||
}
|
||||
|
||||
BlockBuffer<kCompressBound> compressed;
|
||||
int16_t compressedSize = 0;
|
||||
if (!isZipCompressed) {
|
||||
compressedSize = LZ4_compress_default(raw.data, compressed.data, bytesRead, kCompressBound);
|
||||
}
|
||||
int16_t blockSize;
|
||||
ResponseHeader* header;
|
||||
if (compressedSize > 0 && compressedSize < kCompressedSizeMax) {
|
||||
++compressed_;
|
||||
blockSize = compressedSize;
|
||||
header = &compressed.header;
|
||||
header->compression_type = kCompressionLZ4;
|
||||
} else {
|
||||
++uncompressed_;
|
||||
blockSize = bytesRead;
|
||||
header = &raw.header;
|
||||
header->compression_type = kCompressionNone;
|
||||
}
|
||||
|
||||
header->block_type = kTypeData;
|
||||
header->file_id = toBigEndian(fileId);
|
||||
header->block_size = toBigEndian(blockSize);
|
||||
header->block_idx = toBigEndian(blockIdx);
|
||||
|
||||
file.sentBlocks[blockIdx] = true;
|
||||
file.sentBlocksCount += 1;
|
||||
Send(header, ResponseHeader::responseSizeFor(blockSize), flush);
|
||||
|
||||
return SendResult::Sent;
|
||||
}
|
||||
|
||||
bool IncrementalServer::SendDone() {
|
||||
ResponseHeader header;
|
||||
header.file_id = -1;
|
||||
header.block_type = 0;
|
||||
header.compression_type = 0;
|
||||
header.block_idx = 0;
|
||||
header.block_size = 0;
|
||||
Send(&header, sizeof(header), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
void IncrementalServer::RunPrefetching() {
|
||||
constexpr auto kPrefetchBlocksPerIteration = 128;
|
||||
|
||||
int blocksToSend = kPrefetchBlocksPerIteration;
|
||||
while (!prefetches_.empty() && blocksToSend > 0) {
|
||||
auto& prefetch = prefetches_.front();
|
||||
const auto& file = *prefetch.file;
|
||||
const auto& priority_blocks = file.PriorityBlocks();
|
||||
if (!priority_blocks.empty()) {
|
||||
for (auto& i = prefetch.priorityIndex;
|
||||
blocksToSend > 0 && i < (BlockIdx)priority_blocks.size(); ++i) {
|
||||
if (auto res = SendDataBlock(file.id, priority_blocks[i]);
|
||||
res == SendResult::Sent) {
|
||||
--blocksToSend;
|
||||
} else if (res == SendResult::Error) {
|
||||
fprintf(stderr, "Failed to send priority block %" PRId32 "\n", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto& i = prefetch.overallIndex; blocksToSend > 0 && i < prefetch.overallEnd; ++i) {
|
||||
if (auto res = SendDataBlock(file.id, i); res == SendResult::Sent) {
|
||||
--blocksToSend;
|
||||
} else if (res == SendResult::Error) {
|
||||
fprintf(stderr, "Failed to send block %" PRId32 "\n", i);
|
||||
}
|
||||
}
|
||||
if (prefetch.done()) {
|
||||
prefetches_.pop_front();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IncrementalServer::Send(const void* data, size_t size, bool flush) {
|
||||
pendingBlocks_ = std::copy_n(static_cast<const char*>(data), size, pendingBlocks_);
|
||||
if (flush || pendingBlocks_ - pendingBlocksBuffer_.data() > kChunkFlushSize) {
|
||||
Flush();
|
||||
}
|
||||
}
|
||||
|
||||
void IncrementalServer::Flush() {
|
||||
auto dataBytes = pendingBlocks_ - (pendingBlocksBuffer_.data() + sizeof(ChunkHeader));
|
||||
if (dataBytes == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
*(ChunkHeader*)pendingBlocksBuffer_.data() = toBigEndian<int32_t>(dataBytes);
|
||||
auto totalBytes = sizeof(ChunkHeader) + dataBytes;
|
||||
if (!WriteFdExactly(adb_fd_, pendingBlocksBuffer_.data(), totalBytes)) {
|
||||
fprintf(stderr, "Failed to write %d bytes\n", int(totalBytes));
|
||||
}
|
||||
sentSize_ += totalBytes;
|
||||
pendingBlocks_ = pendingBlocksBuffer_.data() + sizeof(ChunkHeader);
|
||||
}
|
||||
|
||||
bool IncrementalServer::ServingComplete(std::optional<TimePoint> startTime, int missesCount,
|
||||
int missesSent) {
|
||||
servingComplete_ = true;
|
||||
using namespace std::chrono;
|
||||
auto endTime = high_resolution_clock::now();
|
||||
D("Streaming completed.\n"
|
||||
"Misses: %d, of those unique: %d; sent compressed: %d, uncompressed: "
|
||||
"%d, mb: %.3f\n"
|
||||
"Total time taken: %.3fms",
|
||||
missesCount, missesSent, compressed_, uncompressed_, sentSize_ / 1024.0 / 1024.0,
|
||||
duration_cast<microseconds>(endTime - (startTime ? *startTime : endTime)).count() / 1000.0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IncrementalServer::Serve() {
|
||||
// Initial handshake to verify connection is still alive
|
||||
if (!SendOkay(adb_fd_)) {
|
||||
fprintf(stderr, "Connection is dead. Abort.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unordered_set<FileId> prefetchedFiles;
|
||||
bool doneSent = false;
|
||||
int missesCount = 0;
|
||||
int missesSent = 0;
|
||||
|
||||
using namespace std::chrono;
|
||||
std::optional<TimePoint> startTime;
|
||||
|
||||
while (true) {
|
||||
if (!doneSent && prefetches_.empty() &&
|
||||
std::all_of(files_.begin(), files_.end(), [](const File& f) {
|
||||
return f.sentBlocksCount == NumBlocks(f.sentBlocks.size());
|
||||
})) {
|
||||
fprintf(stderr, "All files should be loaded. Notifying the device.\n");
|
||||
SendDone();
|
||||
doneSent = true;
|
||||
}
|
||||
|
||||
const bool blocking = prefetches_.empty();
|
||||
if (blocking) {
|
||||
// We've no idea how long the blocking call is, so let's flush whatever is still unsent.
|
||||
Flush();
|
||||
}
|
||||
auto request = ReadRequest(blocking);
|
||||
|
||||
if (!startTime) {
|
||||
startTime = high_resolution_clock::now();
|
||||
}
|
||||
|
||||
if (request) {
|
||||
FileId fileId = request->file_id;
|
||||
BlockIdx blockIdx = request->block_idx;
|
||||
|
||||
switch (request->request_type) {
|
||||
case DESTROY: {
|
||||
// Stop everything.
|
||||
return true;
|
||||
}
|
||||
case SERVING_COMPLETE: {
|
||||
// Not stopping the server here.
|
||||
ServingComplete(startTime, missesCount, missesSent);
|
||||
break;
|
||||
}
|
||||
case BLOCK_MISSING: {
|
||||
++missesCount;
|
||||
// Sends one single block ASAP.
|
||||
if (fileId < 0 || fileId >= (FileId)files_.size() || blockIdx < 0 ||
|
||||
blockIdx >= (BlockIdx)files_[fileId].sentBlocks.size()) {
|
||||
fprintf(stderr,
|
||||
"Received invalid data request for file_id %" PRId16
|
||||
" block_idx %" PRId32 ".\n",
|
||||
fileId, blockIdx);
|
||||
break;
|
||||
}
|
||||
|
||||
if (VLOG_IS_ON(INCREMENTAL)) {
|
||||
auto& file = files_[fileId];
|
||||
auto posP = std::find(file.PriorityBlocks().begin(),
|
||||
file.PriorityBlocks().end(), blockIdx);
|
||||
D("\tMISSING BLOCK: reading file %d block %04d (in priority: %d of %d)",
|
||||
(int)fileId, (int)blockIdx,
|
||||
posP == file.PriorityBlocks().end()
|
||||
? -1
|
||||
: int(posP - file.PriorityBlocks().begin()),
|
||||
int(file.PriorityBlocks().size()));
|
||||
}
|
||||
|
||||
if (auto res = SendDataBlock(fileId, blockIdx, true);
|
||||
res == SendResult::Error) {
|
||||
fprintf(stderr, "Failed to send block %" PRId32 ".\n", blockIdx);
|
||||
} else if (res == SendResult::Sent) {
|
||||
++missesSent;
|
||||
// Make sure we send more pages from this place onward, in case if the OS is
|
||||
// reading a bigger block.
|
||||
prefetches_.emplace_front(files_[fileId], blockIdx + 1, 7);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PREFETCH: {
|
||||
// Start prefetching for a file
|
||||
if (fileId < 0) {
|
||||
fprintf(stderr,
|
||||
"Received invalid prefetch request for file_id %" PRId16 "\n",
|
||||
fileId);
|
||||
break;
|
||||
}
|
||||
if (!prefetchedFiles.insert(fileId).second) {
|
||||
fprintf(stderr,
|
||||
"Received duplicate prefetch request for file_id %" PRId16 "\n",
|
||||
fileId);
|
||||
break;
|
||||
}
|
||||
D("Received prefetch request for file_id %" PRId16 ".", fileId);
|
||||
prefetches_.emplace_back(files_[fileId]);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
fprintf(stderr, "Invalid request %" PRId16 ",%" PRId16 ",%" PRId32 ".\n",
|
||||
request->request_type, fileId, blockIdx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
RunPrefetching();
|
||||
}
|
||||
}
|
||||
|
||||
static std::pair<unique_fd, int64_t> open_fd(const char* filepath) {
|
||||
struct stat st;
|
||||
if (stat(filepath, &st)) {
|
||||
error_exit("inc-server: failed to stat input file '%s'.", filepath);
|
||||
}
|
||||
|
||||
unique_fd fd(adb_open(filepath, O_RDONLY));
|
||||
if (fd < 0) {
|
||||
error_exit("inc-server: failed to open file '%s'.", filepath);
|
||||
}
|
||||
|
||||
return {std::move(fd), st.st_size};
|
||||
}
|
||||
|
||||
static std::pair<unique_fd, int64_t> open_signature(int64_t file_size, const char* filepath) {
|
||||
std::string signature_file(filepath);
|
||||
signature_file += IDSIG;
|
||||
|
||||
unique_fd fd(adb_open(signature_file.c_str(), O_RDONLY));
|
||||
if (fd < 0) {
|
||||
D("No signature file found for '%s'('%s')", filepath, signature_file.c_str());
|
||||
return {};
|
||||
}
|
||||
|
||||
auto [tree_offset, tree_size] = skip_id_sig_headers(fd);
|
||||
if (auto expected = verity_tree_size_for_file(file_size); tree_size != expected) {
|
||||
error_exit("Verity tree size mismatch in signature file: %s [was %lld, expected %lld].\n",
|
||||
signature_file.c_str(), (long long)tree_size, (long long)expected);
|
||||
}
|
||||
|
||||
int32_t data_block_count = numBytesToNumBlocks(file_size);
|
||||
int32_t leaf_nodes_count = (data_block_count + kHashesPerBlock - 1) / kHashesPerBlock;
|
||||
D("Verity tree loaded: %s, tree size: %d (%d blocks, %d leafs)", signature_file.c_str(),
|
||||
int(tree_size), int(numBytesToNumBlocks(tree_size)), int(leaf_nodes_count));
|
||||
|
||||
return {std::move(fd), tree_offset};
|
||||
}
|
||||
|
||||
bool serve(int connection_fd, int output_fd, int argc, const char** argv) {
|
||||
auto connection_ufd = unique_fd(connection_fd);
|
||||
auto output_ufd = unique_fd(output_fd);
|
||||
if (argc <= 0) {
|
||||
error_exit("inc-server: must specify at least one file.");
|
||||
}
|
||||
|
||||
std::vector<File> files;
|
||||
files.reserve(argc);
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
auto filepath = argv[i];
|
||||
|
||||
auto [file_fd, file_size] = open_fd(filepath);
|
||||
auto [sign_fd, sign_offset] = open_signature(file_size, filepath);
|
||||
|
||||
files.emplace_back(filepath, i, file_size, std::move(file_fd), sign_offset,
|
||||
std::move(sign_fd));
|
||||
}
|
||||
|
||||
IncrementalServer server(std::move(connection_ufd), std::move(output_ufd), std::move(files));
|
||||
printf("Serving...\n");
|
||||
fclose(stdin);
|
||||
fclose(stdout);
|
||||
return server.Serve();
|
||||
}
|
||||
|
||||
} // namespace incremental
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace incremental {
|
||||
|
||||
// Expecting arguments like:
|
||||
// {FILE1 FILE2 ...}
|
||||
// Where FILE* are files to serve.
|
||||
bool serve(int connection_fd, int output_fd, int argc, const char** argv);
|
||||
|
||||
} // namespace incremental
|
|
@ -1,379 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 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.
|
||||
*/
|
||||
|
||||
#define TRACE_TAG INCREMENTAL
|
||||
|
||||
#include "incremental_utils.h"
|
||||
|
||||
#include <android-base/endian.h>
|
||||
#include <android-base/mapped_file.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <ziparchive/zip_archive.h>
|
||||
#include <ziparchive/zip_writer.h>
|
||||
|
||||
#include <array>
|
||||
#include <cinttypes>
|
||||
#include <numeric>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "adb_io.h"
|
||||
#include "adb_trace.h"
|
||||
#include "sysdeps.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace incremental {
|
||||
|
||||
static constexpr inline int32_t offsetToBlockIndex(int64_t offset) {
|
||||
return (offset & ~(kBlockSize - 1)) >> 12;
|
||||
}
|
||||
|
||||
Size verity_tree_blocks_for_file(Size fileSize) {
|
||||
if (fileSize == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
constexpr int hash_per_block = kBlockSize / kDigestSize;
|
||||
|
||||
Size total_tree_block_count = 0;
|
||||
|
||||
auto block_count = 1 + (fileSize - 1) / kBlockSize;
|
||||
auto hash_block_count = block_count;
|
||||
for (auto i = 0; hash_block_count > 1; i++) {
|
||||
hash_block_count = (hash_block_count + hash_per_block - 1) / hash_per_block;
|
||||
total_tree_block_count += hash_block_count;
|
||||
}
|
||||
return total_tree_block_count;
|
||||
}
|
||||
|
||||
Size verity_tree_size_for_file(Size fileSize) {
|
||||
return verity_tree_blocks_for_file(fileSize) * kBlockSize;
|
||||
}
|
||||
|
||||
static inline int32_t read_int32(borrowed_fd fd) {
|
||||
int32_t result;
|
||||
return ReadFdExactly(fd, &result, sizeof(result)) ? result : -1;
|
||||
}
|
||||
|
||||
static inline int32_t skip_int(borrowed_fd fd) {
|
||||
return adb_lseek(fd, 4, SEEK_CUR);
|
||||
}
|
||||
|
||||
static inline void append_int(borrowed_fd fd, std::vector<char>* bytes) {
|
||||
int32_t le_val = read_int32(fd);
|
||||
auto old_size = bytes->size();
|
||||
bytes->resize(old_size + sizeof(le_val));
|
||||
memcpy(bytes->data() + old_size, &le_val, sizeof(le_val));
|
||||
}
|
||||
|
||||
static inline void append_bytes_with_size(borrowed_fd fd, std::vector<char>* bytes) {
|
||||
int32_t le_size = read_int32(fd);
|
||||
if (le_size < 0) {
|
||||
return;
|
||||
}
|
||||
int32_t size = int32_t(le32toh(le_size));
|
||||
auto old_size = bytes->size();
|
||||
bytes->resize(old_size + sizeof(le_size) + size);
|
||||
memcpy(bytes->data() + old_size, &le_size, sizeof(le_size));
|
||||
ReadFdExactly(fd, bytes->data() + old_size + sizeof(le_size), size);
|
||||
}
|
||||
|
||||
static inline int32_t skip_bytes_with_size(borrowed_fd fd) {
|
||||
int32_t le_size = read_int32(fd);
|
||||
if (le_size < 0) {
|
||||
return -1;
|
||||
}
|
||||
int32_t size = int32_t(le32toh(le_size));
|
||||
return (int32_t)adb_lseek(fd, size, SEEK_CUR);
|
||||
}
|
||||
|
||||
std::pair<std::vector<char>, int32_t> read_id_sig_headers(borrowed_fd fd) {
|
||||
std::vector<char> result;
|
||||
append_int(fd, &result); // version
|
||||
append_bytes_with_size(fd, &result); // hashingInfo
|
||||
append_bytes_with_size(fd, &result); // signingInfo
|
||||
auto le_tree_size = read_int32(fd);
|
||||
auto tree_size = int32_t(le32toh(le_tree_size)); // size of the verity tree
|
||||
return {std::move(result), tree_size};
|
||||
}
|
||||
|
||||
std::pair<off64_t, ssize_t> skip_id_sig_headers(borrowed_fd fd) {
|
||||
skip_int(fd); // version
|
||||
skip_bytes_with_size(fd); // hashingInfo
|
||||
auto offset = skip_bytes_with_size(fd); // signingInfo
|
||||
auto le_tree_size = read_int32(fd);
|
||||
auto tree_size = int32_t(le32toh(le_tree_size)); // size of the verity tree
|
||||
return {offset + sizeof(le_tree_size), tree_size};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static T valueAt(borrowed_fd fd, off64_t offset) {
|
||||
T t;
|
||||
memset(&t, 0, sizeof(T));
|
||||
if (adb_pread(fd, &t, sizeof(T), offset) != sizeof(T)) {
|
||||
memset(&t, -1, sizeof(T));
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static void appendBlocks(int32_t start, int count, std::vector<int32_t>* blocks) {
|
||||
if (count == 1) {
|
||||
blocks->push_back(start);
|
||||
} else {
|
||||
auto oldSize = blocks->size();
|
||||
blocks->resize(oldSize + count);
|
||||
std::iota(blocks->begin() + oldSize, blocks->end(), start);
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static void unduplicate(std::vector<T>& v) {
|
||||
std::unordered_set<T> uniques(v.size());
|
||||
v.erase(std::remove_if(v.begin(), v.end(),
|
||||
[&uniques](T t) { return !uniques.insert(t).second; }),
|
||||
v.end());
|
||||
}
|
||||
|
||||
static off64_t CentralDirOffset(borrowed_fd fd, Size fileSize) {
|
||||
static constexpr int kZipEocdRecMinSize = 22;
|
||||
static constexpr int32_t kZipEocdRecSig = 0x06054b50;
|
||||
static constexpr int kZipEocdCentralDirSizeFieldOffset = 12;
|
||||
static constexpr int kZipEocdCommentLengthFieldOffset = 20;
|
||||
|
||||
int32_t sigBuf = 0;
|
||||
off64_t eocdOffset = -1;
|
||||
off64_t maxEocdOffset = fileSize - kZipEocdRecMinSize;
|
||||
int16_t commentLenBuf = 0;
|
||||
|
||||
// Search from the end of zip, backward to find beginning of EOCD
|
||||
for (int16_t commentLen = 0; commentLen < fileSize; ++commentLen) {
|
||||
sigBuf = valueAt<int32_t>(fd, maxEocdOffset - commentLen);
|
||||
if (sigBuf == kZipEocdRecSig) {
|
||||
commentLenBuf = valueAt<int16_t>(
|
||||
fd, maxEocdOffset - commentLen + kZipEocdCommentLengthFieldOffset);
|
||||
if (commentLenBuf == commentLen) {
|
||||
eocdOffset = maxEocdOffset - commentLen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (eocdOffset < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
off64_t cdLen = static_cast<int64_t>(
|
||||
valueAt<int32_t>(fd, eocdOffset + kZipEocdCentralDirSizeFieldOffset));
|
||||
|
||||
return eocdOffset - cdLen;
|
||||
}
|
||||
|
||||
// Does not support APKs larger than 4GB
|
||||
static off64_t SignerBlockOffset(borrowed_fd fd, Size fileSize) {
|
||||
static constexpr int kApkSigBlockMinSize = 32;
|
||||
static constexpr int kApkSigBlockFooterSize = 24;
|
||||
static constexpr int64_t APK_SIG_BLOCK_MAGIC_HI = 0x3234206b636f6c42l;
|
||||
static constexpr int64_t APK_SIG_BLOCK_MAGIC_LO = 0x20676953204b5041l;
|
||||
|
||||
off64_t cdOffset = CentralDirOffset(fd, fileSize);
|
||||
if (cdOffset < 0) {
|
||||
return -1;
|
||||
}
|
||||
// CD offset is where original signer block ends. Search backwards for magic and footer.
|
||||
if (cdOffset < kApkSigBlockMinSize ||
|
||||
valueAt<int64_t>(fd, cdOffset - 2 * sizeof(int64_t)) != APK_SIG_BLOCK_MAGIC_LO ||
|
||||
valueAt<int64_t>(fd, cdOffset - sizeof(int64_t)) != APK_SIG_BLOCK_MAGIC_HI) {
|
||||
return -1;
|
||||
}
|
||||
int32_t signerSizeInFooter = valueAt<int32_t>(fd, cdOffset - kApkSigBlockFooterSize);
|
||||
off64_t signerBlockOffset = cdOffset - signerSizeInFooter - sizeof(int64_t);
|
||||
if (signerBlockOffset < 0) {
|
||||
return -1;
|
||||
}
|
||||
int32_t signerSizeInHeader = valueAt<int32_t>(fd, signerBlockOffset);
|
||||
if (signerSizeInFooter != signerSizeInHeader) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return signerBlockOffset;
|
||||
}
|
||||
|
||||
static std::vector<int32_t> ZipPriorityBlocks(off64_t signerBlockOffset, Size fileSize) {
|
||||
int32_t signerBlockIndex = offsetToBlockIndex(signerBlockOffset);
|
||||
int32_t lastBlockIndex = offsetToBlockIndex(fileSize);
|
||||
const auto numPriorityBlocks = lastBlockIndex - signerBlockIndex + 1;
|
||||
|
||||
std::vector<int32_t> zipPriorityBlocks;
|
||||
|
||||
// Some magic here: most of zip libraries perform a scan for EOCD record starting at the offset
|
||||
// of a maximum comment size from the end of the file. This means the last 65-ish KBs will be
|
||||
// accessed first, followed by the rest of the central directory blocks. Make sure we
|
||||
// send the data in the proper order, as central directory can be quite big by itself.
|
||||
static constexpr auto kMaxZipCommentSize = 64 * 1024;
|
||||
static constexpr auto kNumBlocksInEocdSearch = kMaxZipCommentSize / kBlockSize + 1;
|
||||
if (numPriorityBlocks > kNumBlocksInEocdSearch) {
|
||||
appendBlocks(lastBlockIndex - kNumBlocksInEocdSearch + 1, kNumBlocksInEocdSearch,
|
||||
&zipPriorityBlocks);
|
||||
appendBlocks(signerBlockIndex, numPriorityBlocks - kNumBlocksInEocdSearch,
|
||||
&zipPriorityBlocks);
|
||||
} else {
|
||||
appendBlocks(signerBlockIndex, numPriorityBlocks, &zipPriorityBlocks);
|
||||
}
|
||||
|
||||
// Somehow someone keeps accessing the start of the archive, even if there's nothing really
|
||||
// interesting there...
|
||||
appendBlocks(0, 1, &zipPriorityBlocks);
|
||||
return zipPriorityBlocks;
|
||||
}
|
||||
|
||||
[[maybe_unused]] static ZipArchiveHandle openZipArchiveFd(borrowed_fd fd) {
|
||||
bool transferFdOwnership = false;
|
||||
#ifdef _WIN32
|
||||
//
|
||||
// Need to create a special CRT FD here as the current one is not compatible with
|
||||
// normal read()/write() calls that libziparchive uses.
|
||||
// To make this work we have to create a copy of the file handle, as CRT doesn't care
|
||||
// and closes it together with the new descriptor.
|
||||
//
|
||||
// Note: don't move this into a helper function, it's better to be hard to reuse because
|
||||
// the code is ugly and won't work unless it's a last resort.
|
||||
//
|
||||
auto handle = adb_get_os_handle(fd);
|
||||
HANDLE dupedHandle;
|
||||
if (!::DuplicateHandle(::GetCurrentProcess(), handle, ::GetCurrentProcess(), &dupedHandle, 0,
|
||||
false, DUPLICATE_SAME_ACCESS)) {
|
||||
D("%s failed at DuplicateHandle: %d", __func__, (int)::GetLastError());
|
||||
return {};
|
||||
}
|
||||
int osfd = _open_osfhandle((intptr_t)dupedHandle, _O_RDONLY | _O_BINARY);
|
||||
if (osfd < 0) {
|
||||
D("%s failed at _open_osfhandle: %d", __func__, errno);
|
||||
::CloseHandle(handle);
|
||||
return {};
|
||||
}
|
||||
transferFdOwnership = true;
|
||||
#else
|
||||
int osfd = fd.get();
|
||||
#endif
|
||||
ZipArchiveHandle zip;
|
||||
if (OpenArchiveFd(osfd, "apk_fd", &zip, transferFdOwnership) != 0) {
|
||||
D("%s failed at OpenArchiveFd: %d", __func__, errno);
|
||||
#ifdef _WIN32
|
||||
// "_close()" is a secret WinCRT name for the regular close() function.
|
||||
_close(osfd);
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
return zip;
|
||||
}
|
||||
|
||||
static std::pair<ZipArchiveHandle, std::unique_ptr<android::base::MappedFile>> openZipArchive(
|
||||
borrowed_fd fd, Size fileSize) {
|
||||
#ifndef __LP64__
|
||||
if (fileSize >= INT_MAX) {
|
||||
return {openZipArchiveFd(fd), nullptr};
|
||||
}
|
||||
#endif
|
||||
auto mapping =
|
||||
android::base::MappedFile::FromOsHandle(adb_get_os_handle(fd), 0, fileSize, PROT_READ);
|
||||
if (!mapping) {
|
||||
D("%s failed at FromOsHandle: %d", __func__, errno);
|
||||
return {};
|
||||
}
|
||||
ZipArchiveHandle zip;
|
||||
if (OpenArchiveFromMemory(mapping->data(), mapping->size(), "apk_mapping", &zip) != 0) {
|
||||
D("%s failed at OpenArchiveFromMemory: %d", __func__, errno);
|
||||
return {};
|
||||
}
|
||||
return {zip, std::move(mapping)};
|
||||
}
|
||||
|
||||
static std::vector<int32_t> InstallationPriorityBlocks(borrowed_fd fd, Size fileSize) {
|
||||
static constexpr std::array<std::string_view, 3> additional_matches = {
|
||||
"resources.arsc"sv, "AndroidManifest.xml"sv, "classes.dex"sv};
|
||||
|
||||
auto [zip, _] = openZipArchive(fd, fileSize);
|
||||
if (!zip) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto matcher = [](std::string_view entry_name) {
|
||||
if (entry_name.starts_with("lib/"sv) && entry_name.ends_with(".so"sv)) {
|
||||
return true;
|
||||
}
|
||||
return std::any_of(additional_matches.begin(), additional_matches.end(),
|
||||
[entry_name](std::string_view i) { return i == entry_name; });
|
||||
};
|
||||
|
||||
void* cookie = nullptr;
|
||||
if (StartIteration(zip, &cookie, std::move(matcher)) != 0) {
|
||||
D("%s failed at StartIteration: %d", __func__, errno);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<int32_t> installationPriorityBlocks;
|
||||
ZipEntry64 entry;
|
||||
std::string_view entryName;
|
||||
while (Next(cookie, &entry, &entryName) == 0) {
|
||||
if (entryName == "classes.dex"sv) {
|
||||
// Only the head is needed for installation
|
||||
int32_t startBlockIndex = offsetToBlockIndex(entry.offset);
|
||||
appendBlocks(startBlockIndex, 2, &installationPriorityBlocks);
|
||||
D("\tadding to priority blocks: '%.*s' (%d)", (int)entryName.size(), entryName.data(),
|
||||
2);
|
||||
} else {
|
||||
// Full entries are needed for installation
|
||||
off64_t entryStartOffset = entry.offset;
|
||||
off64_t entryEndOffset =
|
||||
entryStartOffset +
|
||||
(entry.method == kCompressStored ? entry.uncompressed_length
|
||||
: entry.compressed_length) +
|
||||
(entry.has_data_descriptor ? 16 /* sizeof(DataDescriptor) */ : 0);
|
||||
int32_t startBlockIndex = offsetToBlockIndex(entryStartOffset);
|
||||
int32_t endBlockIndex = offsetToBlockIndex(entryEndOffset);
|
||||
int32_t numNewBlocks = endBlockIndex - startBlockIndex + 1;
|
||||
appendBlocks(startBlockIndex, numNewBlocks, &installationPriorityBlocks);
|
||||
D("\tadding to priority blocks: '%.*s' (%d)", (int)entryName.size(), entryName.data(),
|
||||
numNewBlocks);
|
||||
}
|
||||
}
|
||||
|
||||
EndIteration(cookie);
|
||||
CloseArchive(zip);
|
||||
return installationPriorityBlocks;
|
||||
}
|
||||
|
||||
std::vector<int32_t> PriorityBlocksForFile(const std::string& filepath, borrowed_fd fd,
|
||||
Size fileSize) {
|
||||
if (!android::base::EndsWithIgnoreCase(filepath, ".apk")) {
|
||||
return {};
|
||||
}
|
||||
off64_t signerOffset = SignerBlockOffset(fd, fileSize);
|
||||
if (signerOffset < 0) {
|
||||
// No signer block? not a valid APK
|
||||
return {};
|
||||
}
|
||||
std::vector<int32_t> priorityBlocks = ZipPriorityBlocks(signerOffset, fileSize);
|
||||
std::vector<int32_t> installationPriorityBlocks = InstallationPriorityBlocks(fd, fileSize);
|
||||
|
||||
priorityBlocks.insert(priorityBlocks.end(), installationPriorityBlocks.begin(),
|
||||
installationPriorityBlocks.end());
|
||||
unduplicate(priorityBlocks);
|
||||
return priorityBlocks;
|
||||
}
|
||||
|
||||
} // namespace incremental
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "adb_unique_fd.h"
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <android-base/off64_t.h>
|
||||
|
||||
namespace incremental {
|
||||
|
||||
using Size = int64_t;
|
||||
constexpr int kBlockSize = 4096;
|
||||
constexpr int kSha256DigestSize = 32;
|
||||
constexpr int kDigestSize = kSha256DigestSize;
|
||||
constexpr int kMaxSignatureSize = 8096; // incrementalfs.h
|
||||
|
||||
constexpr std::string_view IDSIG = ".idsig";
|
||||
|
||||
std::vector<int32_t> PriorityBlocksForFile(const std::string& filepath, borrowed_fd fd,
|
||||
Size fileSize);
|
||||
|
||||
Size verity_tree_blocks_for_file(Size fileSize);
|
||||
Size verity_tree_size_for_file(Size fileSize);
|
||||
|
||||
std::pair<std::vector<char>, int32_t> read_id_sig_headers(borrowed_fd fd);
|
||||
std::pair<off64_t, ssize_t> skip_id_sig_headers(borrowed_fd fd);
|
||||
|
||||
} // namespace incremental
|
|
@ -1,138 +0,0 @@
|
|||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 "line_printer.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <termios.h>
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
// Make sure printf is really adb_printf which works for UTF-8 on Windows.
|
||||
#include <sysdeps.h>
|
||||
|
||||
// Stuff from ninja's util.h that's needed below.
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
// This does not account for multiple UTF-8 bytes corresponding to a single Unicode code point, or
|
||||
// multiple code points corresponding to a single grapheme cluster (user-perceived character).
|
||||
string ElideMiddle(const string& str, size_t width) {
|
||||
const int kMargin = 3; // Space for "...".
|
||||
string result = str;
|
||||
if (result.size() + kMargin > width) {
|
||||
size_t elide_size = (width - kMargin) / 2;
|
||||
result = result.substr(0, elide_size)
|
||||
+ "..."
|
||||
+ result.substr(result.size() - elide_size, elide_size);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
LinePrinter::LinePrinter() : have_blank_line_(true) {
|
||||
#ifndef _WIN32
|
||||
const char* term = getenv("TERM");
|
||||
smart_terminal_ = unix_isatty(1) && term && string(term) != "dumb";
|
||||
#else
|
||||
// Disable output buffer. It'd be nice to use line buffering but
|
||||
// MSDN says: "For some systems, [_IOLBF] provides line
|
||||
// buffering. However, for Win32, the behavior is the same as _IOFBF
|
||||
// - Full Buffering."
|
||||
setvbuf(stdout, nullptr, _IONBF, 0);
|
||||
console_ = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
smart_terminal_ = GetConsoleScreenBufferInfo(console_, &csbi);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void Out(const std::string& s) {
|
||||
// Avoid printf and C strings, since the actual output might contain null
|
||||
// bytes like UTF-16 does (yuck).
|
||||
fwrite(s.data(), 1, s.size(), stdout);
|
||||
}
|
||||
|
||||
void LinePrinter::Print(string to_print, LineType type) {
|
||||
if (!smart_terminal_) {
|
||||
if (type == LineType::INFO) {
|
||||
info_line_ = to_print + "\n";
|
||||
} else {
|
||||
Out(to_print + "\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Print over previous line, if any.
|
||||
// On Windows, calling a C library function writing to stdout also handles
|
||||
// pausing the executable when the "Pause" key or Ctrl-S is pressed.
|
||||
printf("\r");
|
||||
|
||||
if (type == INFO) {
|
||||
#ifdef _WIN32
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
GetConsoleScreenBufferInfo(console_, &csbi);
|
||||
|
||||
to_print = ElideMiddle(to_print, static_cast<size_t>(csbi.dwSize.X));
|
||||
std::wstring to_print_wide;
|
||||
// ElideMiddle may create invalid UTF-8, so ignore conversion errors.
|
||||
(void)android::base::UTF8ToWide(to_print, &to_print_wide);
|
||||
// We don't want to have the cursor spamming back and forth, so instead of
|
||||
// printf use WriteConsoleOutput which updates the contents of the buffer,
|
||||
// but doesn't move the cursor position.
|
||||
COORD buf_size = { csbi.dwSize.X, 1 };
|
||||
COORD zero_zero = { 0, 0 };
|
||||
SMALL_RECT target = {
|
||||
csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y,
|
||||
static_cast<SHORT>(csbi.dwCursorPosition.X + csbi.dwSize.X - 1),
|
||||
csbi.dwCursorPosition.Y
|
||||
};
|
||||
vector<CHAR_INFO> char_data(csbi.dwSize.X);
|
||||
for (size_t i = 0; i < static_cast<size_t>(csbi.dwSize.X); ++i) {
|
||||
char_data[i].Char.UnicodeChar = i < to_print_wide.size() ? to_print_wide[i] : L' ';
|
||||
char_data[i].Attributes = csbi.wAttributes;
|
||||
}
|
||||
WriteConsoleOutputW(console_, &char_data[0], buf_size, zero_zero, &target);
|
||||
#else
|
||||
// Limit output to width of the terminal if provided so we don't cause
|
||||
// line-wrapping.
|
||||
winsize size;
|
||||
if ((ioctl(0, TIOCGWINSZ, &size) == 0) && size.ws_col) {
|
||||
to_print = ElideMiddle(to_print, size.ws_col);
|
||||
}
|
||||
Out(to_print);
|
||||
printf("\x1B[K"); // Clear to end of line.
|
||||
fflush(stdout);
|
||||
#endif
|
||||
|
||||
have_blank_line_ = false;
|
||||
} else {
|
||||
Out(to_print);
|
||||
Out("\n");
|
||||
have_blank_line_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void LinePrinter::KeepInfoLine() {
|
||||
if (smart_terminal_) {
|
||||
if (!have_blank_line_) Out("\n");
|
||||
have_blank_line_ = true;
|
||||
} else {
|
||||
Out(info_line_);
|
||||
info_line_.clear();
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef NINJA_LINE_PRINTER_H_
|
||||
#define NINJA_LINE_PRINTER_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string>
|
||||
|
||||
/// Prints lines of text, possibly overprinting previously printed lines
|
||||
/// if the terminal supports it.
|
||||
struct LinePrinter {
|
||||
LinePrinter();
|
||||
|
||||
bool is_smart_terminal() const { return smart_terminal_; }
|
||||
void set_smart_terminal(bool smart) { smart_terminal_ = smart; }
|
||||
|
||||
enum LineType { INFO, WARNING, ERROR };
|
||||
|
||||
/// Outputs the given line. INFO output will be overwritten.
|
||||
/// WARNING and ERROR appear on a line to themselves.
|
||||
void Print(std::string to_print, LineType type);
|
||||
|
||||
/// If there's an INFO line, keep it. If not, do nothing.
|
||||
void KeepInfoLine();
|
||||
|
||||
private:
|
||||
/// Whether we can do fancy terminal control codes.
|
||||
bool smart_terminal_;
|
||||
|
||||
/// Whether the caret is at the beginning of a blank line.
|
||||
bool have_blank_line_;
|
||||
|
||||
/// The last printed info line when printing to a dumb terminal.
|
||||
std::string info_line_;
|
||||
|
||||
#ifdef _WIN32
|
||||
void* console_;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // NINJA_LINE_PRINTER_H_
|
|
@ -1,238 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#define TRACE_TAG ADB
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include <android-base/errors.h>
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "adb_auth.h"
|
||||
#include "adb_client.h"
|
||||
#include "adb_listeners.h"
|
||||
#include "adb_utils.h"
|
||||
#include "adb_wifi.h"
|
||||
#include "client/usb.h"
|
||||
#include "commandline.h"
|
||||
#include "sysdeps/chrono.h"
|
||||
#include "transport.h"
|
||||
|
||||
const char** __adb_argv;
|
||||
const char** __adb_envp;
|
||||
|
||||
static void setup_daemon_logging() {
|
||||
const std::string log_file_path(GetLogFilePath());
|
||||
int fd = unix_open(log_file_path, O_WRONLY | O_CREAT | O_APPEND, 0640);
|
||||
if (fd == -1) {
|
||||
PLOG(FATAL) << "cannot open " << log_file_path;
|
||||
}
|
||||
if (dup2(fd, STDOUT_FILENO) == -1) {
|
||||
PLOG(FATAL) << "cannot redirect stdout";
|
||||
}
|
||||
if (dup2(fd, STDERR_FILENO) == -1) {
|
||||
PLOG(FATAL) << "cannot redirect stderr";
|
||||
}
|
||||
unix_close(fd);
|
||||
|
||||
fprintf(stderr, "--- adb starting (pid %d) ---\n", getpid());
|
||||
LOG(INFO) << adb_version();
|
||||
}
|
||||
|
||||
void adb_server_cleanup() {
|
||||
// Upon exit, we want to clean up in the following order:
|
||||
// 1. close_smartsockets, so that we don't get any new clients
|
||||
// 2. kick_all_transports, to avoid writing only part of a packet to a transport.
|
||||
// 3. usb_cleanup, to tear down the USB stack.
|
||||
close_smartsockets();
|
||||
kick_all_transports();
|
||||
usb_cleanup();
|
||||
}
|
||||
|
||||
static void intentionally_leak() {
|
||||
void* p = ::operator new(1);
|
||||
// The analyzer is upset about this leaking. NOLINTNEXTLINE
|
||||
LOG(INFO) << "leaking pointer " << p;
|
||||
}
|
||||
|
||||
int adb_server_main(int is_daemon, const std::string& socket_spec, int ack_reply_fd) {
|
||||
#if defined(_WIN32)
|
||||
// adb start-server starts us up with stdout and stderr hooked up to
|
||||
// anonymous pipes. When the C Runtime sees this, it makes stderr and
|
||||
// stdout buffered, but to improve the chance that error output is seen,
|
||||
// unbuffer stdout and stderr just like if we were run at the console.
|
||||
// This also keeps stderr unbuffered when it is redirected to adb.log.
|
||||
if (is_daemon) {
|
||||
if (setvbuf(stdout, nullptr, _IONBF, 0) == -1) {
|
||||
PLOG(FATAL) << "cannot make stdout unbuffered";
|
||||
}
|
||||
if (setvbuf(stderr, nullptr, _IONBF, 0) == -1) {
|
||||
PLOG(FATAL) << "cannot make stderr unbuffered";
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: On Ctrl-C, consider trying to kill a starting up adb server (if we're in
|
||||
// launch_server) by calling GenerateConsoleCtrlEvent().
|
||||
|
||||
// On Windows, SIGBREAK is when Ctrl-Break is pressed or the console window is closed. It should
|
||||
// act like Ctrl-C.
|
||||
signal(SIGBREAK, [](int) { raise(SIGINT); });
|
||||
#endif
|
||||
signal(SIGINT, [](int) {
|
||||
fdevent_run_on_main_thread([]() { exit(0); });
|
||||
});
|
||||
|
||||
const char* reject_kill_server = getenv("ADB_REJECT_KILL_SERVER");
|
||||
if (reject_kill_server && strcmp(reject_kill_server, "1") == 0) {
|
||||
adb_set_reject_kill_server(true);
|
||||
}
|
||||
|
||||
const char* leak = getenv("ADB_LEAK");
|
||||
if (leak && strcmp(leak, "1") == 0) {
|
||||
intentionally_leak();
|
||||
}
|
||||
|
||||
if (is_daemon) {
|
||||
close_stdin();
|
||||
setup_daemon_logging();
|
||||
}
|
||||
|
||||
atexit(adb_server_cleanup);
|
||||
|
||||
init_transport_registration();
|
||||
init_reconnect_handler();
|
||||
|
||||
adb_wifi_init();
|
||||
if (!getenv("ADB_MDNS") || strcmp(getenv("ADB_MDNS"), "0") != 0) {
|
||||
init_mdns_transport_discovery();
|
||||
}
|
||||
|
||||
if (!getenv("ADB_USB") || strcmp(getenv("ADB_USB"), "0") != 0) {
|
||||
usb_init();
|
||||
} else {
|
||||
adb_notify_device_scan_complete();
|
||||
}
|
||||
|
||||
if (!getenv("ADB_EMU") || strcmp(getenv("ADB_EMU"), "0") != 0) {
|
||||
local_init(android::base::StringPrintf("tcp:%d", DEFAULT_ADB_LOCAL_TRANSPORT_PORT));
|
||||
}
|
||||
|
||||
std::string error;
|
||||
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
|
||||
// If we told a previous adb server to quit because of version mismatch, we can get to this
|
||||
// point before it's finished exiting. Retry for a while to give it some time. Don't actually
|
||||
// accept any connections until adb_wait_for_device_initialization finishes below.
|
||||
while (install_listener(socket_spec, "*smartsocket*", nullptr, INSTALL_LISTENER_DISABLED,
|
||||
nullptr, &error) != INSTALL_STATUS_OK) {
|
||||
if (std::chrono::steady_clock::now() - start > 0.5s) {
|
||||
LOG(FATAL) << "could not install *smartsocket* listener: " << error;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(100ms);
|
||||
}
|
||||
|
||||
adb_auth_init();
|
||||
|
||||
if (is_daemon) {
|
||||
#if !defined(_WIN32)
|
||||
// Start a new session for the daemon. Do this here instead of after the fork so
|
||||
// that a ctrl-c between the "starting server" and "done starting server" messages
|
||||
// gets a chance to terminate the server.
|
||||
// setsid will fail with EPERM if it's already been a lead process of new session.
|
||||
// Ignore such error.
|
||||
if (setsid() == -1 && errno != EPERM) {
|
||||
PLOG(FATAL) << "setsid() failed";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Wait for the USB scan to complete before notifying the parent that we're up.
|
||||
// We need to perform this in a thread, because we would otherwise block the event loop.
|
||||
std::thread notify_thread([ack_reply_fd]() {
|
||||
adb_wait_for_device_initialization();
|
||||
|
||||
if (ack_reply_fd >= 0) {
|
||||
// Any error output written to stderr now goes to adb.log. We could
|
||||
// keep around a copy of the stderr fd and use that to write any errors
|
||||
// encountered by the following code, but that is probably overkill.
|
||||
#if defined(_WIN32)
|
||||
const HANDLE ack_reply_handle = cast_int_to_handle(ack_reply_fd);
|
||||
const CHAR ack[] = "OK\n";
|
||||
const DWORD bytes_to_write = arraysize(ack) - 1;
|
||||
DWORD written = 0;
|
||||
if (!WriteFile(ack_reply_handle, ack, bytes_to_write, &written, NULL)) {
|
||||
LOG(FATAL) << "cannot write ACK to handle " << ack_reply_handle
|
||||
<< android::base::SystemErrorCodeToString(GetLastError());
|
||||
}
|
||||
if (written != bytes_to_write) {
|
||||
LOG(FATAL) << "cannot write " << bytes_to_write << " bytes of ACK: only wrote "
|
||||
<< written << " bytes";
|
||||
}
|
||||
CloseHandle(ack_reply_handle);
|
||||
#else
|
||||
// TODO(danalbert): Can't use SendOkay because we're sending "OK\n", not
|
||||
// "OKAY".
|
||||
if (!android::base::WriteStringToFd("OK\n", ack_reply_fd)) {
|
||||
PLOG(FATAL) << "error writing ACK to fd " << ack_reply_fd;
|
||||
}
|
||||
unix_close(ack_reply_fd);
|
||||
#endif
|
||||
}
|
||||
// We don't accept() client connections until this point: this way, clients
|
||||
// can't see wonky state early in startup even if they're connecting directly
|
||||
// to the server instead of going through the adb program.
|
||||
fdevent_run_on_main_thread([] { enable_server_sockets(); });
|
||||
});
|
||||
notify_thread.detach();
|
||||
|
||||
#if defined(__linux__)
|
||||
// Write our location to .android/adb.$PORT, so that older clients can exec us.
|
||||
std::string path;
|
||||
if (!android::base::Readlink("/proc/self/exe", &path)) {
|
||||
PLOG(ERROR) << "failed to readlink /proc/self/exe";
|
||||
}
|
||||
|
||||
std::optional<std::string> server_executable_path = adb_get_server_executable_path();
|
||||
if (server_executable_path) {
|
||||
if (!android::base::WriteStringToFile(path, *server_executable_path)) {
|
||||
PLOG(ERROR) << "failed to write server path to " << path;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
D("Event loop starting");
|
||||
fdevent_loop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[], char* envp[]) {
|
||||
__adb_argv = const_cast<const char**>(argv);
|
||||
__adb_envp = const_cast<const char**>(envp);
|
||||
adb_trace_init(argv);
|
||||
return adb_commandline(argc - 1, const_cast<const char**>(argv + 1));
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 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 "client/mdns_utils.h"
|
||||
|
||||
#include <android-base/strings.h>
|
||||
|
||||
namespace mdns {
|
||||
|
||||
// <Instance>.<Service>.<Domain>
|
||||
std::optional<MdnsInstance> mdns_parse_instance_name(std::string_view name) {
|
||||
CHECK(!name.empty());
|
||||
|
||||
// Return the whole name if it doesn't fall under <Instance>.<Service>.<Domain> or
|
||||
// <Instance>.<Service>
|
||||
bool has_local_suffix = false;
|
||||
// Strip the local suffix, if any
|
||||
{
|
||||
std::string local_suffix = ".local";
|
||||
local_suffix += android::base::EndsWith(name, ".") ? "." : "";
|
||||
|
||||
if (android::base::ConsumeSuffix(&name, local_suffix)) {
|
||||
if (name.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
has_local_suffix = true;
|
||||
}
|
||||
}
|
||||
|
||||
std::string transport;
|
||||
// Strip the transport suffix, if any
|
||||
{
|
||||
std::string add_dot = (!has_local_suffix && android::base::EndsWith(name, ".")) ? "." : "";
|
||||
std::array<std::string, 2> transport_suffixes{"._tcp", "._udp"};
|
||||
|
||||
for (const auto& t : transport_suffixes) {
|
||||
if (android::base::ConsumeSuffix(&name, t + add_dot)) {
|
||||
if (name.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
transport = t.substr(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_local_suffix && transport.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_local_suffix && transport.empty()) {
|
||||
return std::make_optional<MdnsInstance>(name, "", "");
|
||||
}
|
||||
|
||||
// Split the service name from the instance name
|
||||
auto pos = name.rfind(".");
|
||||
if (pos == 0 || pos == std::string::npos || pos == name.size() - 1) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return std::make_optional<MdnsInstance>(name.substr(0, pos), name.substr(pos + 1), transport);
|
||||
}
|
||||
|
||||
} // namespace mdns
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
#include "adb_wifi.h"
|
||||
|
||||
namespace mdns {
|
||||
|
||||
struct MdnsInstance {
|
||||
std::string instance_name; // "my name"
|
||||
std::string service_name; // "_adb-tls-connect"
|
||||
std::string transport_type; // either "_tcp" or "_udp"
|
||||
|
||||
MdnsInstance(std::string_view inst, std::string_view serv, std::string_view trans)
|
||||
: instance_name(inst), service_name(serv), transport_type(trans) {}
|
||||
};
|
||||
|
||||
// This parser is based on https://tools.ietf.org/html/rfc6763#section-4.1 for
|
||||
// structured service instance names, where the whole name is in the format
|
||||
// <Instance>.<Service>.<Domain>.
|
||||
//
|
||||
// In our case, we ignore <Domain> portion of the name, which
|
||||
// we always assume to be ".local", or link-local mDNS.
|
||||
//
|
||||
// The string can be in one of the following forms:
|
||||
// - <Instance>.<Service>.<Domain>.?
|
||||
// - e.g. "instance._service._tcp.local" (or "...local.")
|
||||
// - <Instance>.<Service>.? (must contain either "_tcp" or "_udp" at the end)
|
||||
// - e.g. "instance._service._tcp" (or "..._tcp.)
|
||||
// - <Instance> (can contain dots '.')
|
||||
// - e.g. "myname", "name.", "my.name."
|
||||
//
|
||||
// Returns an MdnsInstance with the appropriate fields filled in (instance name is never empty),
|
||||
// otherwise returns std::nullopt.
|
||||
std::optional<MdnsInstance> mdns_parse_instance_name(std::string_view name);
|
||||
|
||||
} // namespace mdns
|
|
@ -1,173 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 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 "client/mdns_utils.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace mdns {
|
||||
|
||||
TEST(mdns_utils, mdns_parse_instance_name) {
|
||||
// Just the instance name
|
||||
{
|
||||
std::string str = ".";
|
||||
auto res = mdns_parse_instance_name(str);
|
||||
ASSERT_TRUE(res.has_value());
|
||||
EXPECT_EQ(str, res->instance_name);
|
||||
EXPECT_TRUE(res->service_name.empty());
|
||||
EXPECT_TRUE(res->transport_type.empty());
|
||||
}
|
||||
{
|
||||
std::string str = "my.name";
|
||||
auto res = mdns_parse_instance_name(str);
|
||||
ASSERT_TRUE(res.has_value());
|
||||
EXPECT_EQ(str, res->instance_name);
|
||||
EXPECT_TRUE(res->service_name.empty());
|
||||
EXPECT_TRUE(res->transport_type.empty());
|
||||
}
|
||||
{
|
||||
std::string str = "my.name.";
|
||||
auto res = mdns_parse_instance_name(str);
|
||||
ASSERT_TRUE(res.has_value());
|
||||
EXPECT_EQ(str, res->instance_name);
|
||||
EXPECT_TRUE(res->service_name.empty());
|
||||
EXPECT_TRUE(res->transport_type.empty());
|
||||
}
|
||||
|
||||
// With "_tcp", "_udp" transport type
|
||||
for (const std::string_view transport : {"._tcp", "._udp"}) {
|
||||
{
|
||||
std::string str = android::base::StringPrintf("%s", transport.data());
|
||||
auto res = mdns_parse_instance_name(str);
|
||||
EXPECT_FALSE(res.has_value());
|
||||
}
|
||||
{
|
||||
std::string str = android::base::StringPrintf("%s.", transport.data());
|
||||
auto res = mdns_parse_instance_name(str);
|
||||
EXPECT_FALSE(res.has_value());
|
||||
}
|
||||
{
|
||||
std::string str = android::base::StringPrintf("service%s", transport.data());
|
||||
auto res = mdns_parse_instance_name(str);
|
||||
EXPECT_FALSE(res.has_value());
|
||||
}
|
||||
{
|
||||
std::string str = android::base::StringPrintf(".service%s", transport.data());
|
||||
auto res = mdns_parse_instance_name(str);
|
||||
EXPECT_FALSE(res.has_value());
|
||||
}
|
||||
{
|
||||
std::string str = android::base::StringPrintf("service.%s", transport.data());
|
||||
auto res = mdns_parse_instance_name(str);
|
||||
EXPECT_FALSE(res.has_value());
|
||||
}
|
||||
{
|
||||
std::string str = android::base::StringPrintf("my.service%s", transport.data());
|
||||
auto res = mdns_parse_instance_name(str);
|
||||
ASSERT_TRUE(res.has_value());
|
||||
EXPECT_EQ(res->instance_name, "my");
|
||||
EXPECT_EQ(res->service_name, "service");
|
||||
EXPECT_EQ(res->transport_type, transport.substr(1));
|
||||
}
|
||||
{
|
||||
std::string str = android::base::StringPrintf("my.service%s.", transport.data());
|
||||
auto res = mdns_parse_instance_name(str);
|
||||
ASSERT_TRUE(res.has_value());
|
||||
EXPECT_EQ(res->instance_name, "my");
|
||||
EXPECT_EQ(res->service_name, "service");
|
||||
EXPECT_EQ(res->transport_type, transport.substr(1));
|
||||
}
|
||||
{
|
||||
std::string str = android::base::StringPrintf("my..service%s", transport.data());
|
||||
auto res = mdns_parse_instance_name(str);
|
||||
ASSERT_TRUE(res.has_value());
|
||||
EXPECT_EQ(res->instance_name, "my.");
|
||||
EXPECT_EQ(res->service_name, "service");
|
||||
EXPECT_EQ(res->transport_type, transport.substr(1));
|
||||
}
|
||||
{
|
||||
std::string str = android::base::StringPrintf("my.name.service%s.", transport.data());
|
||||
auto res = mdns_parse_instance_name(str);
|
||||
ASSERT_TRUE(res.has_value());
|
||||
EXPECT_EQ(res->instance_name, "my.name");
|
||||
EXPECT_EQ(res->service_name, "service");
|
||||
EXPECT_EQ(res->transport_type, transport.substr(1));
|
||||
}
|
||||
{
|
||||
std::string str = android::base::StringPrintf("name.service.%s.", transport.data());
|
||||
auto res = mdns_parse_instance_name(str);
|
||||
EXPECT_FALSE(res.has_value());
|
||||
}
|
||||
|
||||
// With ".local" domain
|
||||
{
|
||||
std::string str = ".local";
|
||||
auto res = mdns_parse_instance_name(str);
|
||||
EXPECT_FALSE(res.has_value());
|
||||
}
|
||||
{
|
||||
std::string str = ".local.";
|
||||
auto res = mdns_parse_instance_name(str);
|
||||
EXPECT_FALSE(res.has_value());
|
||||
}
|
||||
{
|
||||
std::string str = "name.local";
|
||||
auto res = mdns_parse_instance_name(str);
|
||||
EXPECT_FALSE(res.has_value());
|
||||
}
|
||||
{
|
||||
std::string str = android::base::StringPrintf("%s.local", transport.data());
|
||||
auto res = mdns_parse_instance_name(str);
|
||||
EXPECT_FALSE(res.has_value());
|
||||
}
|
||||
{
|
||||
std::string str = android::base::StringPrintf("service%s.local", transport.data());
|
||||
auto res = mdns_parse_instance_name(str);
|
||||
EXPECT_FALSE(res.has_value());
|
||||
}
|
||||
{
|
||||
std::string str = android::base::StringPrintf("name.service%s.local", transport.data());
|
||||
auto res = mdns_parse_instance_name(str);
|
||||
ASSERT_TRUE(res.has_value());
|
||||
EXPECT_EQ(res->instance_name, "name");
|
||||
EXPECT_EQ(res->service_name, "service");
|
||||
EXPECT_EQ(res->transport_type, transport.substr(1));
|
||||
}
|
||||
{
|
||||
std::string str =
|
||||
android::base::StringPrintf("name.service%s.local.", transport.data());
|
||||
auto res = mdns_parse_instance_name(str);
|
||||
ASSERT_TRUE(res.has_value());
|
||||
EXPECT_EQ(res->instance_name, "name");
|
||||
EXPECT_EQ(res->service_name, "service");
|
||||
EXPECT_EQ(res->transport_type, transport.substr(1));
|
||||
}
|
||||
{
|
||||
std::string str =
|
||||
android::base::StringPrintf("name.service%s..local.", transport.data());
|
||||
auto res = mdns_parse_instance_name(str);
|
||||
EXPECT_FALSE(res.has_value());
|
||||
}
|
||||
{
|
||||
std::string str =
|
||||
android::base::StringPrintf("name.service.%s.local.", transport.data());
|
||||
auto res = mdns_parse_instance_name(str);
|
||||
EXPECT_FALSE(res.has_value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mdns
|
|
@ -1,173 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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 "client/pairing/pairing_client.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <iomanip>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/parsenetaddress.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/thread_annotations.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <cutils/sockets.h>
|
||||
#include "sysdeps.h"
|
||||
|
||||
namespace adbwifi {
|
||||
namespace pairing {
|
||||
|
||||
using android::base::unique_fd;
|
||||
|
||||
namespace {
|
||||
|
||||
struct ConnectionDeleter {
|
||||
void operator()(PairingConnectionCtx* p) { pairing_connection_destroy(p); }
|
||||
}; // ConnectionDeleter
|
||||
using ConnectionPtr = std::unique_ptr<PairingConnectionCtx, ConnectionDeleter>;
|
||||
|
||||
class PairingClientImpl : public PairingClient {
|
||||
public:
|
||||
virtual ~PairingClientImpl();
|
||||
|
||||
explicit PairingClientImpl(const Data& pswd, const PeerInfo& peer_info, const Data& cert,
|
||||
const Data& priv_key);
|
||||
|
||||
// Starts the pairing client. This call is non-blocking. Upon pairing
|
||||
// completion, |cb| will be called with the PeerInfo on success,
|
||||
// or an empty value on failure.
|
||||
//
|
||||
// Returns true if PairingClient was successfully started. Otherwise,
|
||||
// return false.
|
||||
virtual bool Start(std::string_view ip_addr, pairing_client_result_cb cb,
|
||||
void* opaque) override;
|
||||
|
||||
static void OnPairingResult(const PeerInfo* peer_info, int fd, void* opaque);
|
||||
|
||||
private:
|
||||
// Setup and start the PairingConnection
|
||||
bool StartConnection();
|
||||
|
||||
enum class State {
|
||||
Ready,
|
||||
Running,
|
||||
Stopped,
|
||||
};
|
||||
|
||||
State state_ = State::Ready;
|
||||
Data pswd_;
|
||||
PeerInfo peer_info_;
|
||||
Data cert_;
|
||||
Data priv_key_;
|
||||
std::string host_;
|
||||
int port_;
|
||||
|
||||
ConnectionPtr connection_;
|
||||
pairing_client_result_cb cb_;
|
||||
void* opaque_ = nullptr;
|
||||
}; // PairingClientImpl
|
||||
|
||||
PairingClientImpl::PairingClientImpl(const Data& pswd, const PeerInfo& peer_info, const Data& cert,
|
||||
const Data& priv_key)
|
||||
: pswd_(pswd), peer_info_(peer_info), cert_(cert), priv_key_(priv_key) {
|
||||
CHECK(!pswd_.empty() && !cert_.empty() && !priv_key_.empty());
|
||||
|
||||
state_ = State::Ready;
|
||||
}
|
||||
|
||||
PairingClientImpl::~PairingClientImpl() {
|
||||
// Make sure to kill the PairingConnection before terminating the fdevent
|
||||
// looper.
|
||||
if (connection_ != nullptr) {
|
||||
connection_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
bool PairingClientImpl::Start(std::string_view ip_addr, pairing_client_result_cb cb, void* opaque) {
|
||||
CHECK(!ip_addr.empty());
|
||||
cb_ = cb;
|
||||
opaque_ = opaque;
|
||||
|
||||
if (state_ != State::Ready) {
|
||||
LOG(ERROR) << "PairingClient already running or finished";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to parse the host address
|
||||
std::string err;
|
||||
CHECK(android::base::ParseNetAddress(std::string(ip_addr), &host_, &port_, nullptr, &err));
|
||||
CHECK(port_ > 0 && port_ <= 65535);
|
||||
|
||||
if (!StartConnection()) {
|
||||
LOG(ERROR) << "Unable to start PairingClient connection";
|
||||
state_ = State::Stopped;
|
||||
return false;
|
||||
}
|
||||
|
||||
state_ = State::Running;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PairingClientImpl::StartConnection() {
|
||||
std::string err;
|
||||
const int timeout = 10; // seconds
|
||||
unique_fd fd(network_connect(host_, port_, SOCK_STREAM, timeout, &err));
|
||||
if (fd.get() == -1) {
|
||||
LOG(ERROR) << "Failed to start pairing connection client [" << err << "]";
|
||||
return false;
|
||||
}
|
||||
int off = 1;
|
||||
adb_setsockopt(fd.get(), IPPROTO_TCP, TCP_NODELAY, &off, sizeof(off));
|
||||
|
||||
connection_ = ConnectionPtr(
|
||||
pairing_connection_client_new(pswd_.data(), pswd_.size(), &peer_info_, cert_.data(),
|
||||
cert_.size(), priv_key_.data(), priv_key_.size()));
|
||||
CHECK(connection_);
|
||||
|
||||
int osh = cast_handle_to_int(adb_get_os_handle(fd.release()));
|
||||
if (!pairing_connection_start(connection_.get(), osh, OnPairingResult, this)) {
|
||||
LOG(ERROR) << "PairingClient failed to start the PairingConnection";
|
||||
state_ = State::Stopped;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
void PairingClientImpl::OnPairingResult(const PeerInfo* peer_info, int /* fd */, void* opaque) {
|
||||
auto* p = reinterpret_cast<PairingClientImpl*>(opaque);
|
||||
p->cb_(peer_info, p->opaque_);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
std::unique_ptr<PairingClient> PairingClient::Create(const Data& pswd, const PeerInfo& peer_info,
|
||||
const Data& cert, const Data& priv_key) {
|
||||
CHECK(!pswd.empty());
|
||||
CHECK(!cert.empty());
|
||||
CHECK(!priv_key.empty());
|
||||
|
||||
return std::unique_ptr<PairingClient>(new PairingClientImpl(pswd, peer_info, cert, priv_key));
|
||||
}
|
||||
|
||||
} // namespace pairing
|
||||
} // namespace adbwifi
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "adb/pairing/pairing_connection.h"
|
||||
|
||||
namespace adbwifi {
|
||||
namespace pairing {
|
||||
|
||||
typedef void (*pairing_client_result_cb)(const PeerInfo*, void*);
|
||||
|
||||
// PairingClient is the client side of the PairingConnection protocol. It will
|
||||
// attempt to connect to a PairingServer specified at |host| and |port|, and
|
||||
// allocate a new PairingConnection for processing.
|
||||
//
|
||||
// See pairing_connection_test.cpp for example usage.
|
||||
//
|
||||
class PairingClient {
|
||||
public:
|
||||
using Data = std::vector<uint8_t>;
|
||||
|
||||
virtual ~PairingClient() = default;
|
||||
|
||||
// Starts the pairing client. This call is non-blocking. Upon completion,
|
||||
// if the pairing was successful, then |cb| will be called with the PeerInfo
|
||||
// containing the info of the trusted peer. Otherwise, |cb| will be
|
||||
// called with an empty value. Start can only be called once in the lifetime
|
||||
// of this object.
|
||||
//
|
||||
// Returns true if PairingClient was successfully started. Otherwise,
|
||||
// returns false.
|
||||
virtual bool Start(std::string_view ip_addr, pairing_client_result_cb cb, void* opaque) = 0;
|
||||
|
||||
// Creates a new PairingClient instance. May return null if unable
|
||||
// to create an instance. |pswd|, |certificate|, |priv_key| and
|
||||
// |ip_addr| cannot be empty. |peer_info| must contain non-empty strings for
|
||||
// the guid and name fields.
|
||||
static std::unique_ptr<PairingClient> Create(const Data& pswd, const PeerInfo& peer_info,
|
||||
const Data& certificate, const Data& priv_key);
|
||||
|
||||
protected:
|
||||
PairingClient() = default;
|
||||
}; // class PairingClient
|
||||
|
||||
} // namespace pairing
|
||||
} // namespace adbwifi
|
|
@ -1,473 +0,0 @@
|
|||
/*
|
||||
* Copyright 2019 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "AdbWifiPairingConnectionTest"
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include <adbwifi/pairing/pairing_server.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "adb/client/pairing/tests/pairing_client.h"
|
||||
|
||||
namespace adbwifi {
|
||||
namespace pairing {
|
||||
|
||||
static const std::string kTestServerCert =
|
||||
"-----BEGIN CERTIFICATE-----\n"
|
||||
"MIIBljCCAT2gAwIBAgIBATAKBggqhkjOPQQDAjAzMQswCQYDVQQGEwJVUzEQMA4G\n"
|
||||
"A1UECgwHQW5kcm9pZDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTE5MTEwNzAyMDkx\n"
|
||||
"NVoXDTI5MTEwNDAyMDkxNVowMzELMAkGA1UEBhMCVVMxEDAOBgNVBAoMB0FuZHJv\n"
|
||||
"aWQxEjAQBgNVBAMMCWxvY2FsaG9zdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA\n"
|
||||
"BCXRovy3RhtK0Khle48vUmkcuI0OF7K8o9sVPE4oVnp24l+cCYr3BtrgifoHPgj4\n"
|
||||
"vq7n105qzK7ngBHH+LBmYIijQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/\n"
|
||||
"BAQDAgGGMB0GA1UdDgQWBBQi4eskzqVG3SCX2CwJF/aTZqUcuTAKBggqhkjOPQQD\n"
|
||||
"AgNHADBEAiBPYvLOCIvPDtq3vMF7A2z7t7JfcCmbC7g8ftEVJucJBwIgepf+XjTb\n"
|
||||
"L7RCE16p7iVkpHUrWAOl7zDxqD+jaji5MkQ=\n"
|
||||
"-----END CERTIFICATE-----\n";
|
||||
|
||||
static const std::string kTestServerPrivKey =
|
||||
"-----BEGIN PRIVATE KEY-----\n"
|
||||
"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgSCaskWPtutIgh8uQ\n"
|
||||
"UBH6ZIea5Kxm7m6kkGNkd8FYPSOhRANCAAQl0aL8t0YbStCoZXuPL1JpHLiNDhey\n"
|
||||
"vKPbFTxOKFZ6duJfnAmK9wba4In6Bz4I+L6u59dOasyu54ARx/iwZmCI\n"
|
||||
"-----END PRIVATE KEY-----\n";
|
||||
|
||||
static const std::string kTestClientCert =
|
||||
"-----BEGIN CERTIFICATE-----\n"
|
||||
"MIIBlzCCAT2gAwIBAgIBATAKBggqhkjOPQQDAjAzMQswCQYDVQQGEwJVUzEQMA4G\n"
|
||||
"A1UECgwHQW5kcm9pZDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTE5MTEwOTAxNTAy\n"
|
||||
"OFoXDTI5MTEwNjAxNTAyOFowMzELMAkGA1UEBhMCVVMxEDAOBgNVBAoMB0FuZHJv\n"
|
||||
"aWQxEjAQBgNVBAMMCWxvY2FsaG9zdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA\n"
|
||||
"BGW+RuoEIzbt42zAuZzbXaC0bvh8n4OLFDnqkkW6kWA43GYg/mUMVc9vg/nuxyuM\n"
|
||||
"aT0KqbTaLhm+NjCXVRnxBrajQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/\n"
|
||||
"BAQDAgGGMB0GA1UdDgQWBBTjCaC8/NXgdBz9WlMVCNwhx7jn0jAKBggqhkjOPQQD\n"
|
||||
"AgNIADBFAiB/xp2boj7b1KK2saS6BL59deo/TvfgZ+u8HPq4k4VP3gIhAMXswp9W\n"
|
||||
"XdlziccQdj+0KpbUojDKeHOr4fIj/+LxsWPa\n"
|
||||
"-----END CERTIFICATE-----\n";
|
||||
|
||||
static const std::string kTestClientPrivKey =
|
||||
"-----BEGIN PRIVATE KEY-----\n"
|
||||
"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgFw/CWY1f6TSB70AF\n"
|
||||
"yVe8n6QdYFu8HW5t/tij2SrXx42hRANCAARlvkbqBCM27eNswLmc212gtG74fJ+D\n"
|
||||
"ixQ56pJFupFgONxmIP5lDFXPb4P57scrjGk9Cqm02i4ZvjYwl1UZ8Qa2\n"
|
||||
"-----END PRIVATE KEY-----\n";
|
||||
|
||||
class AdbWifiPairingConnectionTest : public testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() override {}
|
||||
|
||||
virtual void TearDown() override {}
|
||||
|
||||
void initPairing(const std::vector<uint8_t> server_pswd,
|
||||
const std::vector<uint8_t> client_pswd) {
|
||||
std::vector<uint8_t> cert;
|
||||
std::vector<uint8_t> key;
|
||||
// Include the null-byte as well.
|
||||
cert.assign(reinterpret_cast<const uint8_t*>(kTestServerCert.data()),
|
||||
reinterpret_cast<const uint8_t*>(kTestServerCert.data()) +
|
||||
kTestServerCert.size() + 1);
|
||||
key.assign(reinterpret_cast<const uint8_t*>(kTestServerPrivKey.data()),
|
||||
reinterpret_cast<const uint8_t*>(kTestServerPrivKey.data()) +
|
||||
kTestServerPrivKey.size() + 1);
|
||||
server_ = PairingServer::create(server_pswd, server_info_, cert, key, kDefaultPairingPort);
|
||||
cert.assign(reinterpret_cast<const uint8_t*>(kTestClientCert.data()),
|
||||
reinterpret_cast<const uint8_t*>(kTestClientCert.data()) +
|
||||
kTestClientCert.size() + 1);
|
||||
key.assign(reinterpret_cast<const uint8_t*>(kTestClientPrivKey.data()),
|
||||
reinterpret_cast<const uint8_t*>(kTestClientPrivKey.data()) +
|
||||
kTestClientPrivKey.size() + 1);
|
||||
client_ = PairingClient::create(client_pswd, client_info_, cert, key, "127.0.0.1");
|
||||
}
|
||||
|
||||
std::unique_ptr<PairingServer> createServer(const std::vector<uint8_t> pswd) {
|
||||
std::vector<uint8_t> cert;
|
||||
std::vector<uint8_t> key;
|
||||
// Include the null-byte as well.
|
||||
cert.assign(reinterpret_cast<const uint8_t*>(kTestServerCert.data()),
|
||||
reinterpret_cast<const uint8_t*>(kTestServerCert.data()) +
|
||||
kTestServerCert.size() + 1);
|
||||
key.assign(reinterpret_cast<const uint8_t*>(kTestServerPrivKey.data()),
|
||||
reinterpret_cast<const uint8_t*>(kTestServerPrivKey.data()) +
|
||||
kTestServerPrivKey.size() + 1);
|
||||
return PairingServer::create(pswd, server_info_, cert, key, kDefaultPairingPort);
|
||||
}
|
||||
|
||||
std::unique_ptr<PairingClient> createClient(const std::vector<uint8_t> pswd) {
|
||||
std::vector<uint8_t> cert;
|
||||
std::vector<uint8_t> key;
|
||||
// Include the null-byte as well.
|
||||
cert.assign(reinterpret_cast<const uint8_t*>(kTestClientCert.data()),
|
||||
reinterpret_cast<const uint8_t*>(kTestClientCert.data()) +
|
||||
kTestClientCert.size() + 1);
|
||||
key.assign(reinterpret_cast<const uint8_t*>(kTestClientPrivKey.data()),
|
||||
reinterpret_cast<const uint8_t*>(kTestClientPrivKey.data()) +
|
||||
kTestClientPrivKey.size() + 1);
|
||||
return PairingClient::create(pswd, client_info_, cert, key, "127.0.0.1");
|
||||
}
|
||||
|
||||
std::unique_ptr<PairingServer> server_;
|
||||
const PeerInfo server_info_ = {
|
||||
.name = "my_server_name",
|
||||
.guid = "my_server_guid",
|
||||
};
|
||||
std::unique_ptr<PairingClient> client_;
|
||||
const PeerInfo client_info_ = {
|
||||
.name = "my_client_name",
|
||||
.guid = "my_client_guid",
|
||||
};
|
||||
};
|
||||
|
||||
TEST_F(AdbWifiPairingConnectionTest, ServerCreation) {
|
||||
// All parameters bad
|
||||
auto server = PairingServer::create({}, {}, {}, {}, -1);
|
||||
EXPECT_EQ(nullptr, server);
|
||||
// Bad password
|
||||
server = PairingServer::create({}, server_info_, {0x01}, {0x01}, -1);
|
||||
EXPECT_EQ(nullptr, server);
|
||||
// Bad peer_info
|
||||
server = PairingServer::create({0x01}, {}, {0x01}, {0x01}, -1);
|
||||
EXPECT_EQ(nullptr, server);
|
||||
// Bad certificate
|
||||
server = PairingServer::create({0x01}, server_info_, {}, {0x01}, -1);
|
||||
EXPECT_EQ(nullptr, server);
|
||||
// Bad private key
|
||||
server = PairingServer::create({0x01}, server_info_, {0x01}, {}, -1);
|
||||
EXPECT_EQ(nullptr, server);
|
||||
// Bad port
|
||||
server = PairingServer::create({0x01}, server_info_, {0x01}, {0x01}, -1);
|
||||
EXPECT_EQ(nullptr, server);
|
||||
// Valid params
|
||||
server = PairingServer::create({0x01}, server_info_, {0x01}, {0x01}, 7776);
|
||||
EXPECT_NE(nullptr, server);
|
||||
}
|
||||
|
||||
TEST_F(AdbWifiPairingConnectionTest, ClientCreation) {
|
||||
// All parameters bad
|
||||
auto client = PairingClient::create({}, client_info_, {}, {}, "");
|
||||
EXPECT_EQ(nullptr, client);
|
||||
// Bad password
|
||||
client = PairingClient::create({}, client_info_, {0x01}, {0x01}, "127.0.0.1");
|
||||
EXPECT_EQ(nullptr, client);
|
||||
// Bad peer_info
|
||||
client = PairingClient::create({0x01}, {}, {0x01}, {0x01}, "127.0.0.1");
|
||||
EXPECT_EQ(nullptr, client);
|
||||
// Bad certificate
|
||||
client = PairingClient::create({0x01}, client_info_, {}, {0x01}, "127.0.0.1");
|
||||
EXPECT_EQ(nullptr, client);
|
||||
// Bad private key
|
||||
client = PairingClient::create({0x01}, client_info_, {0x01}, {}, "127.0.0.1");
|
||||
EXPECT_EQ(nullptr, client);
|
||||
// Bad ip address
|
||||
client = PairingClient::create({0x01}, client_info_, {0x01}, {0x01}, "");
|
||||
EXPECT_EQ(nullptr, client);
|
||||
// Valid params
|
||||
client = PairingClient::create({0x01}, client_info_, {0x01}, {0x01}, "127.0.0.1");
|
||||
EXPECT_NE(nullptr, client);
|
||||
}
|
||||
|
||||
TEST_F(AdbWifiPairingConnectionTest, SmokeValidPairing) {
|
||||
std::vector<uint8_t> pswd{0x01, 0x03, 0x05, 0x07};
|
||||
initPairing(pswd, pswd);
|
||||
|
||||
// Start the server first, to open the port for connections
|
||||
std::mutex server_mutex;
|
||||
std::condition_variable server_cv;
|
||||
std::unique_lock<std::mutex> server_lock(server_mutex);
|
||||
|
||||
auto server_callback = [&](const PeerInfo* peer_info, const std::vector<uint8_t>* cert,
|
||||
void* opaque) {
|
||||
ASSERT_NE(nullptr, peer_info);
|
||||
ASSERT_NE(nullptr, cert);
|
||||
EXPECT_FALSE(cert->empty());
|
||||
EXPECT_EQ(nullptr, opaque);
|
||||
|
||||
// Verify the peer_info and cert
|
||||
ASSERT_EQ(strlen(peer_info->name), strlen(client_info_.name));
|
||||
EXPECT_EQ(::memcmp(peer_info->name, client_info_.name, strlen(client_info_.name)), 0);
|
||||
ASSERT_EQ(strlen(peer_info->guid), strlen(client_info_.guid));
|
||||
EXPECT_EQ(::memcmp(peer_info->guid, client_info_.guid, strlen(client_info_.guid)), 0);
|
||||
ASSERT_EQ(cert->size(), kTestClientCert.size() + 1);
|
||||
EXPECT_EQ(::memcmp(cert->data(), kTestClientCert.data(), kTestClientCert.size() + 1), 0);
|
||||
|
||||
std::lock_guard<std::mutex> lock(server_mutex);
|
||||
server_cv.notify_one();
|
||||
};
|
||||
ASSERT_TRUE(server_->start(server_callback, nullptr));
|
||||
|
||||
// Start the client
|
||||
bool got_valid_pairing = false;
|
||||
std::mutex client_mutex;
|
||||
std::condition_variable client_cv;
|
||||
std::unique_lock<std::mutex> client_lock(client_mutex);
|
||||
auto client_callback = [&](const PeerInfo* peer_info, const std::vector<uint8_t>* cert,
|
||||
void* opaque) {
|
||||
ASSERT_NE(nullptr, peer_info);
|
||||
ASSERT_NE(nullptr, cert);
|
||||
EXPECT_FALSE(cert->empty());
|
||||
EXPECT_EQ(nullptr, opaque);
|
||||
|
||||
// Verify the peer_info and cert
|
||||
ASSERT_EQ(strlen(peer_info->name), strlen(server_info_.name));
|
||||
EXPECT_EQ(::memcmp(peer_info->name, server_info_.name, strlen(server_info_.name)), 0);
|
||||
ASSERT_EQ(strlen(peer_info->guid), strlen(server_info_.guid));
|
||||
EXPECT_EQ(::memcmp(peer_info->guid, server_info_.guid, strlen(server_info_.guid)), 0);
|
||||
ASSERT_EQ(cert->size(), kTestServerCert.size() + 1);
|
||||
EXPECT_EQ(::memcmp(cert->data(), kTestServerCert.data(), kTestServerCert.size() + 1), 0);
|
||||
|
||||
got_valid_pairing = (peer_info != nullptr && cert != nullptr && !cert->empty());
|
||||
std::lock_guard<std::mutex> lock(client_mutex);
|
||||
client_cv.notify_one();
|
||||
};
|
||||
ASSERT_TRUE(client_->start(client_callback, nullptr));
|
||||
client_cv.wait(client_lock);
|
||||
|
||||
// Kill server if the pairing failed, since server only shuts down when
|
||||
// it gets a valid pairing.
|
||||
if (!got_valid_pairing) {
|
||||
server_lock.unlock();
|
||||
server_.reset();
|
||||
} else {
|
||||
server_cv.wait(server_lock);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(AdbWifiPairingConnectionTest, CancelPairing) {
|
||||
std::vector<uint8_t> pswd{0x01, 0x03, 0x05, 0x07};
|
||||
std::vector<uint8_t> pswd2{0x01, 0x03, 0x05, 0x06};
|
||||
initPairing(pswd, pswd2);
|
||||
|
||||
// Start the server first, to open the port for connections
|
||||
std::mutex server_mutex;
|
||||
std::condition_variable server_cv;
|
||||
std::unique_lock<std::mutex> server_lock(server_mutex);
|
||||
|
||||
bool server_got_valid_pairing = true;
|
||||
auto server_callback = [&](const PeerInfo* peer_info, const std::vector<uint8_t>* cert,
|
||||
void* opaque) {
|
||||
// Pairing will be cancelled, which should initiate this callback with
|
||||
// empty values.
|
||||
ASSERT_EQ(nullptr, peer_info);
|
||||
ASSERT_EQ(nullptr, cert);
|
||||
EXPECT_EQ(nullptr, opaque);
|
||||
std::lock_guard<std::mutex> lock(server_mutex);
|
||||
server_cv.notify_one();
|
||||
server_got_valid_pairing = false;
|
||||
};
|
||||
ASSERT_TRUE(server_->start(server_callback, nullptr));
|
||||
|
||||
// Start the client (should fail because of different passwords).
|
||||
bool got_valid_pairing = false;
|
||||
std::mutex client_mutex;
|
||||
std::condition_variable client_cv;
|
||||
std::unique_lock<std::mutex> client_lock(client_mutex);
|
||||
auto client_callback = [&](const PeerInfo* peer_info, const std::vector<uint8_t>* cert,
|
||||
void* opaque) {
|
||||
ASSERT_EQ(nullptr, peer_info);
|
||||
ASSERT_EQ(nullptr, cert);
|
||||
EXPECT_EQ(nullptr, opaque);
|
||||
|
||||
got_valid_pairing = (peer_info != nullptr && cert != nullptr && !cert->empty());
|
||||
std::lock_guard<std::mutex> lock(client_mutex);
|
||||
client_cv.notify_one();
|
||||
};
|
||||
ASSERT_TRUE(client_->start(client_callback, nullptr));
|
||||
client_cv.wait(client_lock);
|
||||
|
||||
server_lock.unlock();
|
||||
// This should trigger the callback to be on the same thread.
|
||||
server_.reset();
|
||||
EXPECT_FALSE(server_got_valid_pairing);
|
||||
}
|
||||
|
||||
TEST_F(AdbWifiPairingConnectionTest, MultipleClientsAllFail) {
|
||||
std::vector<uint8_t> pswd{0x01, 0x03, 0x05, 0x07};
|
||||
std::vector<uint8_t> pswd2{0x01, 0x03, 0x05, 0x06};
|
||||
|
||||
auto server = createServer(pswd);
|
||||
ASSERT_NE(nullptr, server);
|
||||
// Start the server first, to open the port for connections
|
||||
std::mutex server_mutex;
|
||||
std::condition_variable server_cv;
|
||||
std::unique_lock<std::mutex> server_lock(server_mutex);
|
||||
|
||||
bool server_got_valid_pairing = true;
|
||||
auto server_callback = [&](const PeerInfo* peer_info, const std::vector<uint8_t>* cert,
|
||||
void* opaque) {
|
||||
// Pairing will be cancelled, which should initiate this callback with
|
||||
// empty values.
|
||||
ASSERT_EQ(nullptr, peer_info);
|
||||
ASSERT_EQ(nullptr, cert);
|
||||
EXPECT_EQ(nullptr, opaque);
|
||||
std::lock_guard<std::mutex> lock(server_mutex);
|
||||
server_cv.notify_one();
|
||||
server_got_valid_pairing = false;
|
||||
};
|
||||
ASSERT_TRUE(server->start(server_callback, nullptr));
|
||||
|
||||
// Start multiple clients, all with bad passwords
|
||||
std::vector<std::unique_ptr<PairingClient>> clients;
|
||||
int num_clients_done = 0;
|
||||
int test_num_clients = 5;
|
||||
std::mutex client_mutex;
|
||||
std::condition_variable client_cv;
|
||||
std::unique_lock<std::mutex> client_lock(client_mutex);
|
||||
while (clients.size() < test_num_clients) {
|
||||
auto client = createClient(pswd2);
|
||||
ASSERT_NE(nullptr, client);
|
||||
auto callback = [&](const PeerInfo* peer_info, const std::vector<uint8_t>* cert,
|
||||
void* opaque) {
|
||||
ASSERT_EQ(nullptr, peer_info);
|
||||
ASSERT_EQ(nullptr, cert);
|
||||
EXPECT_EQ(nullptr, opaque);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(client_mutex);
|
||||
num_clients_done++;
|
||||
}
|
||||
client_cv.notify_one();
|
||||
};
|
||||
ASSERT_TRUE(client->start(callback, nullptr));
|
||||
clients.push_back(std::move(client));
|
||||
}
|
||||
|
||||
client_cv.wait(client_lock, [&]() { return (num_clients_done == test_num_clients); });
|
||||
EXPECT_EQ(num_clients_done, test_num_clients);
|
||||
|
||||
server_lock.unlock();
|
||||
// This should trigger the callback to be on the same thread.
|
||||
server.reset();
|
||||
EXPECT_FALSE(server_got_valid_pairing);
|
||||
}
|
||||
|
||||
TEST_F(AdbWifiPairingConnectionTest, MultipleClientsOnePass) {
|
||||
// Send multiple clients with bad passwords, but send the last one with the
|
||||
// correct password.
|
||||
std::vector<uint8_t> pswd{0x01, 0x03, 0x05, 0x07};
|
||||
std::vector<uint8_t> pswd2{0x01, 0x03, 0x05, 0x06};
|
||||
|
||||
auto server = createServer(pswd);
|
||||
ASSERT_NE(nullptr, server);
|
||||
// Start the server first, to open the port for connections
|
||||
std::mutex server_mutex;
|
||||
std::condition_variable server_cv;
|
||||
std::unique_lock<std::mutex> server_lock(server_mutex);
|
||||
|
||||
bool server_got_valid_pairing = false;
|
||||
auto server_callback = [&](const PeerInfo* peer_info, const std::vector<uint8_t>* cert,
|
||||
void* opaque) {
|
||||
// Pairing will be cancelled, which should initiate this callback with
|
||||
// empty values.
|
||||
|
||||
ASSERT_NE(nullptr, peer_info);
|
||||
ASSERT_NE(nullptr, cert);
|
||||
EXPECT_FALSE(cert->empty());
|
||||
EXPECT_EQ(nullptr, opaque);
|
||||
|
||||
// Verify the peer_info and cert
|
||||
ASSERT_EQ(strlen(peer_info->name), strlen(client_info_.name));
|
||||
EXPECT_EQ(::memcmp(peer_info->name, client_info_.name, strlen(client_info_.name)), 0);
|
||||
ASSERT_EQ(strlen(peer_info->guid), strlen(client_info_.guid));
|
||||
EXPECT_EQ(::memcmp(peer_info->guid, client_info_.guid, strlen(client_info_.guid)), 0);
|
||||
ASSERT_EQ(cert->size(), kTestClientCert.size() + 1);
|
||||
EXPECT_EQ(::memcmp(cert->data(), kTestClientCert.data(), kTestClientCert.size() + 1), 0);
|
||||
|
||||
std::lock_guard<std::mutex> lock(server_mutex);
|
||||
server_got_valid_pairing = true;
|
||||
server_cv.notify_one();
|
||||
};
|
||||
ASSERT_TRUE(server->start(server_callback, nullptr));
|
||||
|
||||
// Start multiple clients, all with bad passwords (except for the last one)
|
||||
std::vector<std::unique_ptr<PairingClient>> clients;
|
||||
int num_clients_done = 0;
|
||||
int test_num_clients = 5;
|
||||
std::mutex client_mutex;
|
||||
std::condition_variable client_cv;
|
||||
std::unique_lock<std::mutex> client_lock(client_mutex);
|
||||
bool got_valid_pairing = false;
|
||||
while (clients.size() < test_num_clients) {
|
||||
std::unique_ptr<PairingClient> client;
|
||||
if (clients.size() == test_num_clients - 1) {
|
||||
// Make this one have the valid password
|
||||
client = createClient(pswd);
|
||||
ASSERT_NE(nullptr, client);
|
||||
auto callback = [&](const PeerInfo* peer_info, const std::vector<uint8_t>* cert,
|
||||
void* opaque) {
|
||||
ASSERT_NE(nullptr, peer_info);
|
||||
ASSERT_NE(nullptr, cert);
|
||||
EXPECT_FALSE(cert->empty());
|
||||
EXPECT_EQ(nullptr, opaque);
|
||||
|
||||
// Verify the peer_info and cert
|
||||
ASSERT_EQ(strlen(peer_info->name), strlen(server_info_.name));
|
||||
EXPECT_EQ(::memcmp(peer_info->name, server_info_.name, strlen(server_info_.name)),
|
||||
0);
|
||||
ASSERT_EQ(strlen(peer_info->guid), strlen(server_info_.guid));
|
||||
EXPECT_EQ(::memcmp(peer_info->guid, server_info_.guid, strlen(server_info_.guid)),
|
||||
0);
|
||||
ASSERT_EQ(cert->size(), kTestServerCert.size() + 1);
|
||||
EXPECT_EQ(
|
||||
::memcmp(cert->data(), kTestServerCert.data(), kTestServerCert.size() + 1),
|
||||
0);
|
||||
got_valid_pairing = (peer_info != nullptr && cert != nullptr && !cert->empty());
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(client_mutex);
|
||||
num_clients_done++;
|
||||
}
|
||||
client_cv.notify_one();
|
||||
};
|
||||
ASSERT_TRUE(client->start(callback, nullptr));
|
||||
} else {
|
||||
client = createClient(pswd2);
|
||||
ASSERT_NE(nullptr, client);
|
||||
auto callback = [&](const PeerInfo* peer_info, const std::vector<uint8_t>* cert,
|
||||
void* opaque) {
|
||||
ASSERT_EQ(nullptr, peer_info);
|
||||
ASSERT_EQ(nullptr, cert);
|
||||
EXPECT_EQ(nullptr, opaque);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(client_mutex);
|
||||
num_clients_done++;
|
||||
}
|
||||
client_cv.notify_one();
|
||||
};
|
||||
ASSERT_TRUE(client->start(callback, nullptr));
|
||||
}
|
||||
clients.push_back(std::move(client));
|
||||
}
|
||||
|
||||
client_cv.wait(client_lock, [&]() { return (num_clients_done == test_num_clients); });
|
||||
EXPECT_EQ(num_clients_done, test_num_clients);
|
||||
|
||||
// Kill server if the pairing failed, since server only shuts down when
|
||||
// it gets a valid pairing.
|
||||
if (!got_valid_pairing) {
|
||||
server_lock.unlock();
|
||||
server_.reset();
|
||||
} else {
|
||||
server_cv.wait(server_lock);
|
||||
}
|
||||
EXPECT_TRUE(server_got_valid_pairing);
|
||||
}
|
||||
|
||||
} // namespace pairing
|
||||
} // namespace adbwifi
|
|
@ -1,426 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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 "adbwifi/pairing/pairing_server.h"
|
||||
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/eventfd.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <iomanip>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include <adbwifi/pairing/pairing_connection.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/parsenetaddress.h>
|
||||
#include <android-base/thread_annotations.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <cutils/sockets.h>
|
||||
|
||||
namespace adbwifi {
|
||||
namespace pairing {
|
||||
|
||||
using android::base::ScopedLockAssertion;
|
||||
using android::base::unique_fd;
|
||||
|
||||
namespace {
|
||||
|
||||
// The implimentation has two background threads running: one to handle and
|
||||
// accept any new pairing connection requests (socket accept), and the other to
|
||||
// handle connection events (connection started, connection finished).
|
||||
class PairingServerImpl : public PairingServer {
|
||||
public:
|
||||
virtual ~PairingServerImpl();
|
||||
|
||||
// All parameters must be non-empty.
|
||||
explicit PairingServerImpl(const Data& pswd, const PeerInfo& peer_info, const Data& cert,
|
||||
const Data& priv_key, int port);
|
||||
|
||||
// Starts the pairing server. This call is non-blocking. Upon completion,
|
||||
// if the pairing was successful, then |cb| will be called with the PublicKeyHeader
|
||||
// containing the info of the trusted peer. Otherwise, |cb| will be
|
||||
// called with an empty value. Start can only be called once in the lifetime
|
||||
// of this object.
|
||||
//
|
||||
// Returns true if PairingServer was successfully started. Otherwise,
|
||||
// returns false.
|
||||
virtual bool start(PairingConnection::ResultCallback cb, void* opaque) override;
|
||||
|
||||
private:
|
||||
// Setup the server socket to accept incoming connections
|
||||
bool setupServer();
|
||||
// Force stop the server thread.
|
||||
void stopServer();
|
||||
|
||||
// handles a new pairing client connection
|
||||
bool handleNewClientConnection(int fd) EXCLUDES(conn_mutex_);
|
||||
|
||||
// ======== connection events thread =============
|
||||
std::mutex conn_mutex_;
|
||||
std::condition_variable conn_cv_;
|
||||
|
||||
using FdVal = int;
|
||||
using ConnectionPtr = std::unique_ptr<PairingConnection>;
|
||||
using NewConnectionEvent = std::tuple<unique_fd, ConnectionPtr>;
|
||||
// <fd, PeerInfo.name, PeerInfo.guid, certificate>
|
||||
using ConnectionFinishedEvent = std::tuple<FdVal, std::optional<std::string>,
|
||||
std::optional<std::string>, std::optional<Data>>;
|
||||
using ConnectionEvent = std::variant<NewConnectionEvent, ConnectionFinishedEvent>;
|
||||
// Queue for connections to write into. We have a separate queue to read
|
||||
// from, in order to minimize the time the server thread is blocked.
|
||||
std::deque<ConnectionEvent> conn_write_queue_ GUARDED_BY(conn_mutex_);
|
||||
std::deque<ConnectionEvent> conn_read_queue_;
|
||||
// Map of fds to their PairingConnections currently running.
|
||||
std::unordered_map<FdVal, ConnectionPtr> connections_;
|
||||
|
||||
// Two threads launched when starting the pairing server:
|
||||
// 1) A server thread that waits for incoming client connections, and
|
||||
// 2) A connection events thread that synchonizes events from all of the
|
||||
// clients, since each PairingConnection is running in it's own thread.
|
||||
void startConnectionEventsThread();
|
||||
void startServerThread();
|
||||
|
||||
std::thread conn_events_thread_;
|
||||
void connectionEventsWorker();
|
||||
std::thread server_thread_;
|
||||
void serverWorker();
|
||||
bool is_terminate_ GUARDED_BY(conn_mutex_) = false;
|
||||
|
||||
enum class State {
|
||||
Ready,
|
||||
Running,
|
||||
Stopped,
|
||||
};
|
||||
State state_ = State::Ready;
|
||||
Data pswd_;
|
||||
PeerInfo peer_info_;
|
||||
Data cert_;
|
||||
Data priv_key_;
|
||||
int port_ = -1;
|
||||
|
||||
PairingConnection::ResultCallback cb_;
|
||||
void* opaque_ = nullptr;
|
||||
bool got_valid_pairing_ = false;
|
||||
|
||||
static const int kEpollConstSocket = 0;
|
||||
// Used to break the server thread from epoll_wait
|
||||
static const int kEpollConstEventFd = 1;
|
||||
unique_fd epoll_fd_;
|
||||
unique_fd server_fd_;
|
||||
unique_fd event_fd_;
|
||||
}; // PairingServerImpl
|
||||
|
||||
PairingServerImpl::PairingServerImpl(const Data& pswd, const PeerInfo& peer_info, const Data& cert,
|
||||
const Data& priv_key, int port)
|
||||
: pswd_(pswd), peer_info_(peer_info), cert_(cert), priv_key_(priv_key), port_(port) {
|
||||
CHECK(!pswd_.empty() && !cert_.empty() && !priv_key_.empty() && port_ > 0);
|
||||
CHECK('\0' == peer_info.name[kPeerNameLength - 1] &&
|
||||
'\0' == peer_info.guid[kPeerGuidLength - 1] && strlen(peer_info.name) > 0 &&
|
||||
strlen(peer_info.guid) > 0);
|
||||
}
|
||||
|
||||
PairingServerImpl::~PairingServerImpl() {
|
||||
// Since these connections have references to us, let's make sure they
|
||||
// destruct before us.
|
||||
if (server_thread_.joinable()) {
|
||||
stopServer();
|
||||
server_thread_.join();
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(conn_mutex_);
|
||||
is_terminate_ = true;
|
||||
}
|
||||
conn_cv_.notify_one();
|
||||
if (conn_events_thread_.joinable()) {
|
||||
conn_events_thread_.join();
|
||||
}
|
||||
|
||||
// Notify the cb_ if it hasn't already.
|
||||
if (!got_valid_pairing_ && cb_ != nullptr) {
|
||||
cb_(nullptr, nullptr, opaque_);
|
||||
}
|
||||
}
|
||||
|
||||
bool PairingServerImpl::start(PairingConnection::ResultCallback cb, void* opaque) {
|
||||
cb_ = cb;
|
||||
opaque_ = opaque;
|
||||
|
||||
if (state_ != State::Ready) {
|
||||
LOG(ERROR) << "PairingServer already running or stopped";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!setupServer()) {
|
||||
LOG(ERROR) << "Unable to start PairingServer";
|
||||
state_ = State::Stopped;
|
||||
return false;
|
||||
}
|
||||
|
||||
state_ = State::Running;
|
||||
return true;
|
||||
}
|
||||
|
||||
void PairingServerImpl::stopServer() {
|
||||
if (event_fd_.get() == -1) {
|
||||
return;
|
||||
}
|
||||
uint64_t value = 1;
|
||||
ssize_t rc = write(event_fd_.get(), &value, sizeof(value));
|
||||
if (rc == -1) {
|
||||
// This can happen if the server didn't start.
|
||||
PLOG(ERROR) << "write to eventfd failed";
|
||||
} else if (rc != sizeof(value)) {
|
||||
LOG(FATAL) << "write to event returned short (" << rc << ")";
|
||||
}
|
||||
}
|
||||
|
||||
bool PairingServerImpl::setupServer() {
|
||||
epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC));
|
||||
if (epoll_fd_ == -1) {
|
||||
PLOG(ERROR) << "failed to create epoll fd";
|
||||
return false;
|
||||
}
|
||||
|
||||
event_fd_.reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
|
||||
if (event_fd_ == -1) {
|
||||
PLOG(ERROR) << "failed to create eventfd";
|
||||
return false;
|
||||
}
|
||||
|
||||
server_fd_.reset(socket_inaddr_any_server(port_, SOCK_STREAM));
|
||||
if (server_fd_.get() == -1) {
|
||||
PLOG(ERROR) << "Failed to start pairing connection server";
|
||||
return false;
|
||||
} else if (fcntl(server_fd_.get(), F_SETFD, FD_CLOEXEC) != 0) {
|
||||
PLOG(ERROR) << "Failed to make server socket cloexec";
|
||||
return false;
|
||||
} else if (fcntl(server_fd_.get(), F_SETFD, O_NONBLOCK) != 0) {
|
||||
PLOG(ERROR) << "Failed to make server socket nonblocking";
|
||||
return false;
|
||||
}
|
||||
|
||||
startConnectionEventsThread();
|
||||
startServerThread();
|
||||
return true;
|
||||
}
|
||||
|
||||
void PairingServerImpl::startServerThread() {
|
||||
server_thread_ = std::thread([this]() { serverWorker(); });
|
||||
}
|
||||
|
||||
void PairingServerImpl::startConnectionEventsThread() {
|
||||
conn_events_thread_ = std::thread([this]() { connectionEventsWorker(); });
|
||||
}
|
||||
|
||||
void PairingServerImpl::serverWorker() {
|
||||
{
|
||||
struct epoll_event event;
|
||||
event.events = EPOLLIN;
|
||||
event.data.u64 = kEpollConstSocket;
|
||||
CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, server_fd_.get(), &event));
|
||||
}
|
||||
|
||||
{
|
||||
struct epoll_event event;
|
||||
event.events = EPOLLIN;
|
||||
event.data.u64 = kEpollConstEventFd;
|
||||
CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, event_fd_.get(), &event));
|
||||
}
|
||||
|
||||
while (true) {
|
||||
struct epoll_event events[2];
|
||||
int rc = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd_.get(), events, 2, -1));
|
||||
if (rc == -1) {
|
||||
PLOG(ERROR) << "epoll_wait failed";
|
||||
return;
|
||||
} else if (rc == 0) {
|
||||
LOG(ERROR) << "epoll_wait returned 0";
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rc; ++i) {
|
||||
struct epoll_event& event = events[i];
|
||||
switch (event.data.u64) {
|
||||
case kEpollConstSocket:
|
||||
handleNewClientConnection(server_fd_.get());
|
||||
break;
|
||||
case kEpollConstEventFd:
|
||||
uint64_t dummy;
|
||||
int rc = TEMP_FAILURE_RETRY(read(event_fd_.get(), &dummy, sizeof(dummy)));
|
||||
if (rc != sizeof(dummy)) {
|
||||
PLOG(FATAL) << "failed to read from eventfd (rc=" << rc << ")";
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PairingServerImpl::connectionEventsWorker() {
|
||||
for (;;) {
|
||||
// Transfer the write queue to the read queue.
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(conn_mutex_);
|
||||
ScopedLockAssertion assume_locked(conn_mutex_);
|
||||
|
||||
if (is_terminate_) {
|
||||
// We check |is_terminate_| twice because condition_variable's
|
||||
// notify() only wakes up a thread if it is in the wait state
|
||||
// prior to notify(). Furthermore, we aren't holding the mutex
|
||||
// when processing the events in |conn_read_queue_|.
|
||||
return;
|
||||
}
|
||||
if (conn_write_queue_.empty()) {
|
||||
// We need to wait for new events, or the termination signal.
|
||||
conn_cv_.wait(lock, [this]() REQUIRES(conn_mutex_) {
|
||||
return (is_terminate_ || !conn_write_queue_.empty());
|
||||
});
|
||||
}
|
||||
if (is_terminate_) {
|
||||
// We're done.
|
||||
return;
|
||||
}
|
||||
// Move all events into the read queue.
|
||||
conn_read_queue_ = std::move(conn_write_queue_);
|
||||
conn_write_queue_.clear();
|
||||
}
|
||||
|
||||
// Process all events in the read queue.
|
||||
while (conn_read_queue_.size() > 0) {
|
||||
auto& event = conn_read_queue_.front();
|
||||
if (auto* p = std::get_if<NewConnectionEvent>(&event)) {
|
||||
// Ignore if we are already at the max number of connections
|
||||
if (connections_.size() >= internal::kMaxConnections) {
|
||||
conn_read_queue_.pop_front();
|
||||
continue;
|
||||
}
|
||||
auto [ufd, connection] = std::move(*p);
|
||||
int fd = ufd.release();
|
||||
bool started = connection->start(
|
||||
fd,
|
||||
[fd](const PeerInfo* peer_info, const Data* cert, void* opaque) {
|
||||
auto* p = reinterpret_cast<PairingServerImpl*>(opaque);
|
||||
|
||||
ConnectionFinishedEvent event;
|
||||
if (peer_info != nullptr && cert != nullptr) {
|
||||
event = std::make_tuple(fd, std::string(peer_info->name),
|
||||
std::string(peer_info->guid), Data(*cert));
|
||||
} else {
|
||||
event = std::make_tuple(fd, std::nullopt, std::nullopt,
|
||||
std::nullopt);
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(p->conn_mutex_);
|
||||
p->conn_write_queue_.push_back(std::move(event));
|
||||
}
|
||||
p->conn_cv_.notify_one();
|
||||
},
|
||||
this);
|
||||
if (!started) {
|
||||
LOG(ERROR) << "PairingServer unable to start a PairingConnection fd=" << fd;
|
||||
ufd.reset(fd);
|
||||
} else {
|
||||
connections_[fd] = std::move(connection);
|
||||
}
|
||||
} else if (auto* p = std::get_if<ConnectionFinishedEvent>(&event)) {
|
||||
auto [fd, name, guid, cert] = std::move(*p);
|
||||
if (name.has_value() && guid.has_value() && cert.has_value() && !name->empty() &&
|
||||
!guid->empty() && !cert->empty()) {
|
||||
// Valid pairing. Let's shutdown the server and close any
|
||||
// pairing connections in progress.
|
||||
stopServer();
|
||||
connections_.clear();
|
||||
|
||||
CHECK_LE(name->size(), kPeerNameLength);
|
||||
CHECK_LE(guid->size(), kPeerGuidLength);
|
||||
PeerInfo info = {};
|
||||
strncpy(info.name, name->data(), name->size());
|
||||
strncpy(info.guid, guid->data(), guid->size());
|
||||
|
||||
cb_(&info, &*cert, opaque_);
|
||||
|
||||
got_valid_pairing_ = true;
|
||||
return;
|
||||
}
|
||||
// Invalid pairing. Close the invalid connection.
|
||||
if (connections_.find(fd) != connections_.end()) {
|
||||
connections_.erase(fd);
|
||||
}
|
||||
}
|
||||
conn_read_queue_.pop_front();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool PairingServerImpl::handleNewClientConnection(int fd) {
|
||||
unique_fd ufd(TEMP_FAILURE_RETRY(accept4(fd, nullptr, nullptr, SOCK_CLOEXEC)));
|
||||
if (ufd == -1) {
|
||||
PLOG(WARNING) << "adb_socket_accept failed fd=" << fd;
|
||||
return false;
|
||||
}
|
||||
auto connection = PairingConnection::create(PairingConnection::Role::Server, pswd_, peer_info_,
|
||||
cert_, priv_key_);
|
||||
if (connection == nullptr) {
|
||||
LOG(ERROR) << "PairingServer unable to create a PairingConnection fd=" << fd;
|
||||
return false;
|
||||
}
|
||||
// send the new connection to the connection thread for further processing
|
||||
NewConnectionEvent event = std::make_tuple(std::move(ufd), std::move(connection));
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(conn_mutex_);
|
||||
conn_write_queue_.push_back(std::move(event));
|
||||
}
|
||||
conn_cv_.notify_one();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
std::unique_ptr<PairingServer> PairingServer::create(const Data& pswd, const PeerInfo& peer_info,
|
||||
const Data& cert, const Data& priv_key,
|
||||
int port) {
|
||||
if (pswd.empty() || cert.empty() || priv_key.empty() || port <= 0) {
|
||||
return nullptr;
|
||||
}
|
||||
// Make sure peer_info has a non-empty, null-terminated string for guid and
|
||||
// name.
|
||||
if ('\0' != peer_info.name[kPeerNameLength - 1] ||
|
||||
'\0' != peer_info.guid[kPeerGuidLength - 1] || strlen(peer_info.name) == 0 ||
|
||||
strlen(peer_info.guid) == 0) {
|
||||
LOG(ERROR) << "The GUID/short name fields are empty or not null-terminated";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (port != kDefaultPairingPort) {
|
||||
LOG(WARNING) << "Starting server with non-default pairing port=" << port;
|
||||
}
|
||||
|
||||
return std::unique_ptr<PairingServer>(
|
||||
new PairingServerImpl(pswd, peer_info, cert, priv_key, port));
|
||||
}
|
||||
|
||||
} // namespace pairing
|
||||
} // namespace adbwifi
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <adbwifi/pairing/pairing_connection.h>
|
||||
|
||||
namespace adbwifi {
|
||||
namespace pairing {
|
||||
|
||||
// PairingServer is the server side of the PairingConnection protocol. It will
|
||||
// listen for incoming PairingClient connections, and allocate a new
|
||||
// PairingConnection per client for processing. PairingServer can handle multiple
|
||||
// connections, but the first one to establish the pairing will be the only one
|
||||
// to succeed. All others will be disconnected.
|
||||
//
|
||||
// See pairing_connection_test.cpp for example usage.
|
||||
//
|
||||
class PairingServer {
|
||||
public:
|
||||
using Data = std::vector<uint8_t>;
|
||||
|
||||
virtual ~PairingServer() = default;
|
||||
|
||||
// Starts the pairing server. This call is non-blocking. Upon completion,
|
||||
// if the pairing was successful, then |cb| will be called with the PeerInfo
|
||||
// containing the info of the trusted peer. Otherwise, |cb| will be
|
||||
// called with an empty value. Start can only be called once in the lifetime
|
||||
// of this object.
|
||||
//
|
||||
// Returns true if PairingServer was successfully started. Otherwise,
|
||||
// returns false.
|
||||
virtual bool start(PairingConnection::ResultCallback cb, void* opaque) = 0;
|
||||
|
||||
// Creates a new PairingServer instance. May return null if unable
|
||||
// to create an instance. |pswd|, |certificate| and |priv_key| cannot
|
||||
// be empty. |port| is the port PairingServer will listen to PairingClient
|
||||
// connections on. |peer_info| must contain non-empty strings for the guid
|
||||
// and name fields.
|
||||
static std::unique_ptr<PairingServer> create(const Data& pswd, const PeerInfo& peer_info,
|
||||
const Data& certificate, const Data& priv_key,
|
||||
int port);
|
||||
|
||||
protected:
|
||||
PairingServer() = default;
|
||||
}; // class PairingServer
|
||||
|
||||
} // namespace pairing
|
||||
} // namespace adbwifi
|
|
@ -1,310 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
|
||||
#define TRACE_TAG TRANSPORT
|
||||
|
||||
#include "sysdeps.h"
|
||||
#include "transport.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/parsenetaddress.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/thread_annotations.h>
|
||||
#include <cutils/sockets.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "adb_io.h"
|
||||
#include "adb_unique_fd.h"
|
||||
#include "adb_utils.h"
|
||||
#include "socket_spec.h"
|
||||
#include "sysdeps/chrono.h"
|
||||
|
||||
// Android Wear has been using port 5601 in all of its documentation/tooling,
|
||||
// but we search for emulators on ports [5554, 5555 + ADB_LOCAL_TRANSPORT_MAX].
|
||||
// Avoid stomping on their port by restricting the active scanning range.
|
||||
// Once emulators self-(re-)register, they'll have to avoid 5601 in their own way.
|
||||
static int adb_local_transport_max_port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT + 16 * 2 - 1;
|
||||
|
||||
static std::mutex& local_transports_lock = *new std::mutex();
|
||||
|
||||
static void adb_local_transport_max_port_env_override() {
|
||||
const char* env_max_s = getenv("ADB_LOCAL_TRANSPORT_MAX_PORT");
|
||||
if (env_max_s != nullptr) {
|
||||
size_t env_max;
|
||||
if (ParseUint(&env_max, env_max_s, nullptr) && env_max < 65536) {
|
||||
// < DEFAULT_ADB_LOCAL_TRANSPORT_PORT harmlessly mimics ADB_EMU=0
|
||||
adb_local_transport_max_port = env_max;
|
||||
D("transport: ADB_LOCAL_TRANSPORT_MAX_PORT read as %d", adb_local_transport_max_port);
|
||||
} else {
|
||||
D("transport: ADB_LOCAL_TRANSPORT_MAX_PORT '%s' invalid or >= 65536, so ignored",
|
||||
env_max_s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We keep a map from emulator port to transport.
|
||||
// TODO: weak_ptr?
|
||||
static std::unordered_map<int, atransport*> local_transports
|
||||
[[clang::no_destroy]] GUARDED_BY(local_transports_lock);
|
||||
|
||||
bool local_connect(int port) {
|
||||
std::string dummy;
|
||||
return local_connect_arbitrary_ports(port - 1, port, &dummy) == 0;
|
||||
}
|
||||
|
||||
void connect_device(const std::string& address, std::string* response) {
|
||||
if (address.empty()) {
|
||||
*response = "empty address";
|
||||
return;
|
||||
}
|
||||
|
||||
D("connection requested to '%s'", address.c_str());
|
||||
unique_fd fd;
|
||||
int port;
|
||||
std::string serial, prefix_addr;
|
||||
|
||||
// If address does not match any socket type, it should default to TCP.
|
||||
if (address.starts_with("vsock:") || address.starts_with("localfilesystem:")) {
|
||||
prefix_addr = address;
|
||||
} else {
|
||||
prefix_addr = "tcp:" + address;
|
||||
}
|
||||
|
||||
socket_spec_connect(&fd, prefix_addr, &port, &serial, response);
|
||||
if (fd.get() == -1) {
|
||||
return;
|
||||
}
|
||||
auto reconnect = [prefix_addr](atransport* t) {
|
||||
std::string response;
|
||||
unique_fd fd;
|
||||
int port;
|
||||
std::string serial;
|
||||
socket_spec_connect(&fd, prefix_addr, &port, &serial, &response);
|
||||
if (fd == -1) {
|
||||
D("reconnect failed: %s", response.c_str());
|
||||
return ReconnectResult::Retry;
|
||||
}
|
||||
// This invokes the part of register_socket_transport() that needs to be
|
||||
// invoked if the atransport* has already been setup. This eventually
|
||||
// calls atransport->SetConnection() with a newly created Connection*
|
||||
// that will in turn send the CNXN packet.
|
||||
return init_socket_transport(t, std::move(fd), port, 0) >= 0 ? ReconnectResult::Success
|
||||
: ReconnectResult::Retry;
|
||||
};
|
||||
|
||||
int error;
|
||||
if (!register_socket_transport(std::move(fd), serial, port, 0, std::move(reconnect), false,
|
||||
&error)) {
|
||||
if (error == EALREADY) {
|
||||
*response = android::base::StringPrintf("already connected to %s", serial.c_str());
|
||||
} else if (error == EPERM) {
|
||||
*response = android::base::StringPrintf("failed to authenticate to %s", serial.c_str());
|
||||
} else {
|
||||
*response = android::base::StringPrintf("failed to connect to %s", serial.c_str());
|
||||
}
|
||||
} else {
|
||||
*response = android::base::StringPrintf("connected to %s", serial.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
int local_connect_arbitrary_ports(int console_port, int adb_port, std::string* error) {
|
||||
unique_fd fd;
|
||||
|
||||
if (find_emulator_transport_by_adb_port(adb_port) != nullptr ||
|
||||
find_emulator_transport_by_console_port(console_port) != nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char* host = getenv("ADBHOST");
|
||||
if (host) {
|
||||
fd.reset(network_connect(host, adb_port, SOCK_STREAM, 0, error));
|
||||
}
|
||||
|
||||
if (fd < 0) {
|
||||
fd.reset(network_loopback_client(adb_port, SOCK_STREAM, error));
|
||||
}
|
||||
|
||||
if (fd >= 0) {
|
||||
D("client: connected on remote on fd %d", fd.get());
|
||||
close_on_exec(fd.get());
|
||||
disable_tcp_nagle(fd.get());
|
||||
std::string serial = getEmulatorSerialString(console_port);
|
||||
if (register_socket_transport(
|
||||
std::move(fd), std::move(serial), adb_port, 1,
|
||||
[](atransport*) { return ReconnectResult::Abort; }, false)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void PollAllLocalPortsForEmulator() {
|
||||
// Try to connect to any number of running emulator instances.
|
||||
for (int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT; port <= adb_local_transport_max_port;
|
||||
port += 2) {
|
||||
local_connect(port); // Note, uses port and port-1, so '=max_port' is OK.
|
||||
}
|
||||
}
|
||||
|
||||
// Retry the disconnected local port for 60 times, and sleep 1 second between two retries.
|
||||
static constexpr uint32_t LOCAL_PORT_RETRY_COUNT = 60;
|
||||
static constexpr auto LOCAL_PORT_RETRY_INTERVAL = 1s;
|
||||
|
||||
struct RetryPort {
|
||||
int port;
|
||||
uint32_t retry_count;
|
||||
};
|
||||
|
||||
// Retry emulators just kicked.
|
||||
static std::vector<RetryPort>& retry_ports = *new std::vector<RetryPort>;
|
||||
std::mutex& retry_ports_lock = *new std::mutex;
|
||||
std::condition_variable& retry_ports_cond = *new std::condition_variable;
|
||||
|
||||
static void client_socket_thread(std::string_view) {
|
||||
adb_thread_setname("client_socket_thread");
|
||||
D("transport: client_socket_thread() starting");
|
||||
PollAllLocalPortsForEmulator();
|
||||
while (true) {
|
||||
std::vector<RetryPort> ports;
|
||||
// Collect retry ports.
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(retry_ports_lock);
|
||||
while (retry_ports.empty()) {
|
||||
retry_ports_cond.wait(lock);
|
||||
}
|
||||
retry_ports.swap(ports);
|
||||
}
|
||||
// Sleep here instead of the end of loop, because if we immediately try to reconnect
|
||||
// the emulator just kicked, the adbd on the emulator may not have time to remove the
|
||||
// just kicked transport.
|
||||
std::this_thread::sleep_for(LOCAL_PORT_RETRY_INTERVAL);
|
||||
|
||||
// Try connecting retry ports.
|
||||
std::vector<RetryPort> next_ports;
|
||||
for (auto& port : ports) {
|
||||
VLOG(TRANSPORT) << "retry port " << port.port << ", last retry_count "
|
||||
<< port.retry_count;
|
||||
if (local_connect(port.port)) {
|
||||
VLOG(TRANSPORT) << "retry port " << port.port << " successfully";
|
||||
continue;
|
||||
}
|
||||
if (--port.retry_count > 0) {
|
||||
next_ports.push_back(port);
|
||||
} else {
|
||||
VLOG(TRANSPORT) << "stop retrying port " << port.port;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy back left retry ports.
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(retry_ports_lock);
|
||||
retry_ports.insert(retry_ports.end(), next_ports.begin(), next_ports.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void local_init(const std::string& addr) {
|
||||
D("transport: local client init");
|
||||
std::thread(client_socket_thread, addr).detach();
|
||||
adb_local_transport_max_port_env_override();
|
||||
}
|
||||
|
||||
struct EmulatorConnection : public FdConnection {
|
||||
EmulatorConnection(unique_fd fd, int local_port)
|
||||
: FdConnection(std::move(fd)), local_port_(local_port) {}
|
||||
|
||||
~EmulatorConnection() {
|
||||
VLOG(TRANSPORT) << "remote_close, local_port = " << local_port_;
|
||||
std::unique_lock<std::mutex> lock(retry_ports_lock);
|
||||
RetryPort port;
|
||||
port.port = local_port_;
|
||||
port.retry_count = LOCAL_PORT_RETRY_COUNT;
|
||||
retry_ports.push_back(port);
|
||||
retry_ports_cond.notify_one();
|
||||
}
|
||||
|
||||
void Close() override {
|
||||
std::lock_guard<std::mutex> lock(local_transports_lock);
|
||||
local_transports.erase(local_port_);
|
||||
FdConnection::Close();
|
||||
}
|
||||
|
||||
int local_port_;
|
||||
};
|
||||
|
||||
/* Only call this function if you already hold local_transports_lock. */
|
||||
static atransport* find_emulator_transport_by_adb_port_locked(int adb_port)
|
||||
REQUIRES(local_transports_lock) {
|
||||
auto it = local_transports.find(adb_port);
|
||||
if (it == local_transports.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
atransport* find_emulator_transport_by_adb_port(int adb_port) {
|
||||
std::lock_guard<std::mutex> lock(local_transports_lock);
|
||||
return find_emulator_transport_by_adb_port_locked(adb_port);
|
||||
}
|
||||
|
||||
atransport* find_emulator_transport_by_console_port(int console_port) {
|
||||
return find_transport(getEmulatorSerialString(console_port).c_str());
|
||||
}
|
||||
|
||||
std::string getEmulatorSerialString(int console_port) {
|
||||
return android::base::StringPrintf("emulator-%d", console_port);
|
||||
}
|
||||
|
||||
int init_socket_transport(atransport* t, unique_fd fd, int adb_port, int local) {
|
||||
int fail = 0;
|
||||
|
||||
t->type = kTransportLocal;
|
||||
|
||||
// Emulator connection.
|
||||
if (local) {
|
||||
auto emulator_connection = std::make_unique<EmulatorConnection>(std::move(fd), adb_port);
|
||||
t->SetConnection(
|
||||
std::make_unique<BlockingConnectionAdapter>(std::move(emulator_connection)));
|
||||
std::lock_guard<std::mutex> lock(local_transports_lock);
|
||||
atransport* existing_transport = find_emulator_transport_by_adb_port_locked(adb_port);
|
||||
if (existing_transport != nullptr) {
|
||||
D("local transport for port %d already registered (%p)?", adb_port, existing_transport);
|
||||
fail = -1;
|
||||
} else {
|
||||
local_transports[adb_port] = t;
|
||||
}
|
||||
|
||||
return fail;
|
||||
}
|
||||
|
||||
// Regular tcp connection.
|
||||
auto fd_connection = std::make_unique<FdConnection>(std::move(fd));
|
||||
t->SetConnection(std::make_unique<BlockingConnectionAdapter>(std::move(fd_connection)));
|
||||
return fail;
|
||||
}
|
|
@ -1,761 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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.
|
||||
*/
|
||||
|
||||
#define TRACE_TAG TRANSPORT
|
||||
|
||||
#include "transport.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <dns_sd.h>
|
||||
|
||||
#include "adb_client.h"
|
||||
#include "adb_mdns.h"
|
||||
#include "adb_trace.h"
|
||||
#include "adb_utils.h"
|
||||
#include "adb_wifi.h"
|
||||
#include "client/mdns_utils.h"
|
||||
#include "fdevent/fdevent.h"
|
||||
#include "sysdeps.h"
|
||||
|
||||
static DNSServiceRef service_refs[kNumADBDNSServices];
|
||||
static fdevent* service_ref_fdes[kNumADBDNSServices];
|
||||
static auto& g_autoconn_whitelist = *new std::unordered_set<int>();
|
||||
|
||||
static int adb_DNSServiceIndexByName(std::string_view regType) {
|
||||
for (int i = 0; i < kNumADBDNSServices; ++i) {
|
||||
if (!strncmp(regType.data(), kADBDNSServices[i], strlen(kADBDNSServices[i]))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void config_auto_connect_services() {
|
||||
// ADB_MDNS_AUTO_CONNECT is a comma-delimited list of mdns services
|
||||
// that are allowed to auto-connect. By default, only allow "adb-tls-connect"
|
||||
// to auto-connect, since this is filtered down to auto-connect only to paired
|
||||
// devices.
|
||||
g_autoconn_whitelist.insert(kADBSecureConnectServiceRefIndex);
|
||||
const char* srvs = getenv("ADB_MDNS_AUTO_CONNECT");
|
||||
if (!srvs) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(srvs, "0") == 0) {
|
||||
D("Disabling all auto-connecting");
|
||||
g_autoconn_whitelist.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(srvs, "1") == 0) {
|
||||
D("Allow all auto-connecting");
|
||||
g_autoconn_whitelist.insert(kADBTransportServiceRefIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
// Selectively choose which services to allow auto-connect.
|
||||
// E.g. ADB_MDNS_AUTO_CONNECT=adb,adb-tls-connect would allow
|
||||
// _adb._tcp and _adb-tls-connnect._tcp services to auto-connect.
|
||||
auto srvs_list = android::base::Split(srvs, ",");
|
||||
std::unordered_set<int> new_whitelist;
|
||||
for (const auto& item : srvs_list) {
|
||||
auto full_srv = android::base::StringPrintf("_%s._tcp", item.data());
|
||||
int idx = adb_DNSServiceIndexByName(full_srv);
|
||||
if (idx >= 0) {
|
||||
new_whitelist.insert(idx);
|
||||
}
|
||||
}
|
||||
|
||||
if (!new_whitelist.empty()) {
|
||||
g_autoconn_whitelist = std::move(new_whitelist);
|
||||
}
|
||||
}
|
||||
|
||||
static bool adb_DNSServiceShouldAutoConnect(const char* regType, const char* serviceName) {
|
||||
// Try to auto-connect to any "_adb" or "_adb-tls-connect" services excluding emulator services.
|
||||
int index = adb_DNSServiceIndexByName(regType);
|
||||
if (index != kADBTransportServiceRefIndex && index != kADBSecureConnectServiceRefIndex) {
|
||||
return false;
|
||||
}
|
||||
if (g_autoconn_whitelist.find(index) == g_autoconn_whitelist.end()) {
|
||||
D("Auto-connect for regType '%s' disabled", regType);
|
||||
return false;
|
||||
}
|
||||
// Ignore adb-EMULATOR* service names, as it interferes with the
|
||||
// emulator ports that are already connected.
|
||||
if (android::base::StartsWith(serviceName, "adb-EMULATOR")) {
|
||||
LOG(INFO) << "Ignoring emulator transport service [" << serviceName << "]";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Use adb_DNSServiceRefSockFD() instead of calling DNSServiceRefSockFD()
|
||||
// directly so that the socket is put through the appropriate compatibility
|
||||
// layers to work with the rest of ADB's internal APIs.
|
||||
static inline int adb_DNSServiceRefSockFD(DNSServiceRef ref) {
|
||||
return adb_register_socket(DNSServiceRefSockFD(ref));
|
||||
}
|
||||
#define DNSServiceRefSockFD ___xxx_DNSServiceRefSockFD
|
||||
|
||||
static void DNSSD_API register_service_ip(DNSServiceRef sdRef,
|
||||
DNSServiceFlags flags,
|
||||
uint32_t interfaceIndex,
|
||||
DNSServiceErrorType errorCode,
|
||||
const char* hostname,
|
||||
const sockaddr* address,
|
||||
uint32_t ttl,
|
||||
void* context);
|
||||
|
||||
static void pump_service_ref(int /*fd*/, unsigned ev, void* data) {
|
||||
DNSServiceRef* ref = reinterpret_cast<DNSServiceRef*>(data);
|
||||
|
||||
if (ev & FDE_READ)
|
||||
DNSServiceProcessResult(*ref);
|
||||
}
|
||||
|
||||
class AsyncServiceRef {
|
||||
public:
|
||||
bool Initialized() {
|
||||
return initialized_;
|
||||
}
|
||||
|
||||
void DestroyServiceRef() {
|
||||
if (!initialized_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Order matters here! Must destroy the fdevent first since it has a
|
||||
// reference to |sdRef_|.
|
||||
fdevent_destroy(fde_);
|
||||
D("DNSServiceRefDeallocate(sdRef=%p)", sdRef_);
|
||||
DNSServiceRefDeallocate(sdRef_);
|
||||
initialized_ = false;
|
||||
}
|
||||
|
||||
virtual ~AsyncServiceRef() { DestroyServiceRef(); }
|
||||
|
||||
protected:
|
||||
DNSServiceRef sdRef_;
|
||||
|
||||
void Initialize() {
|
||||
fde_ = fdevent_create(adb_DNSServiceRefSockFD(sdRef_), pump_service_ref, &sdRef_);
|
||||
if (fde_ == nullptr) {
|
||||
D("Unable to create fdevent");
|
||||
return;
|
||||
}
|
||||
fdevent_set(fde_, FDE_READ);
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool initialized_ = false;
|
||||
fdevent* fde_;
|
||||
};
|
||||
|
||||
class ResolvedService : public AsyncServiceRef {
|
||||
public:
|
||||
virtual ~ResolvedService() = default;
|
||||
|
||||
ResolvedService(std::string serviceName, std::string regType, uint32_t interfaceIndex,
|
||||
const char* hosttarget, uint16_t port, int version)
|
||||
: serviceName_(serviceName),
|
||||
regType_(regType),
|
||||
hosttarget_(hosttarget),
|
||||
port_(port),
|
||||
sa_family_(0),
|
||||
ip_addr_data_(NULL),
|
||||
serviceVersion_(version) {
|
||||
memset(ip_addr_, 0, sizeof(ip_addr_));
|
||||
|
||||
/* TODO: We should be able to get IPv6 support by adding
|
||||
* kDNSServiceProtocol_IPv6 to the flags below. However, when we do
|
||||
* this, we get served link-local addresses that are usually useless to
|
||||
* connect to. What's more, we seem to /only/ get those and nothing else.
|
||||
* If we want IPv6 in the future we'll have to figure out why.
|
||||
*/
|
||||
DNSServiceErrorType ret =
|
||||
DNSServiceGetAddrInfo(
|
||||
&sdRef_, 0, interfaceIndex,
|
||||
kDNSServiceProtocol_IPv4, hosttarget,
|
||||
register_service_ip, reinterpret_cast<void*>(this));
|
||||
|
||||
if (ret != kDNSServiceErr_NoError) {
|
||||
D("Got %d from DNSServiceGetAddrInfo.", ret);
|
||||
} else {
|
||||
D("DNSServiceGetAddrInfo(sdRef=%p, hosttarget=%s)", sdRef_, hosttarget);
|
||||
Initialize();
|
||||
}
|
||||
|
||||
D("Client version: %d Service version: %d\n", clientVersion_, serviceVersion_);
|
||||
}
|
||||
|
||||
bool ConnectSecureWifiDevice() {
|
||||
if (!adb_wifi_is_known_host(serviceName_)) {
|
||||
LOG(INFO) << "serviceName=" << serviceName_ << " not in keystore";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string response;
|
||||
connect_device(android::base::StringPrintf("%s.%s", serviceName_.c_str(), regType_.c_str()),
|
||||
&response);
|
||||
D("Secure connect to %s regtype %s (%s:%hu) : %s", serviceName_.c_str(), regType_.c_str(),
|
||||
ip_addr_, port_, response.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AddToServiceRegistry(const sockaddr* address) {
|
||||
sa_family_ = address->sa_family;
|
||||
|
||||
if (sa_family_ == AF_INET) {
|
||||
ip_addr_data_ = &reinterpret_cast<const sockaddr_in*>(address)->sin_addr;
|
||||
addr_format_ = "%s:%hu";
|
||||
} else if (sa_family_ == AF_INET6) {
|
||||
ip_addr_data_ = &reinterpret_cast<const sockaddr_in6*>(address)->sin6_addr;
|
||||
addr_format_ = "[%s]:%hu";
|
||||
} else { // Should be impossible
|
||||
D("mDNS resolved non-IP address.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Winsock version requires the const cast Because Microsoft.
|
||||
if (!inet_ntop(sa_family_, const_cast<void*>(ip_addr_data_), ip_addr_, sizeof(ip_addr_))) {
|
||||
D("Could not convert IP address to string.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove any services with the same instance name, as it may be a stale registration.
|
||||
removeDNSService(regType_.c_str(), serviceName_.c_str());
|
||||
|
||||
// Add to the service registry before trying to auto-connect, since socket_spec_connect will
|
||||
// check these registries for the ip address when connecting via mdns instance name.
|
||||
int adbSecureServiceType = serviceIndex();
|
||||
ServiceRegistry* services = nullptr;
|
||||
switch (adbSecureServiceType) {
|
||||
case kADBTransportServiceRefIndex:
|
||||
services = sAdbTransportServices;
|
||||
break;
|
||||
case kADBSecurePairingServiceRefIndex:
|
||||
services = sAdbSecurePairingServices;
|
||||
break;
|
||||
case kADBSecureConnectServiceRefIndex:
|
||||
services = sAdbSecureConnectServices;
|
||||
break;
|
||||
default:
|
||||
LOG(WARNING) << "No registry available for reg_type=[" << regType_ << "]";
|
||||
return false;
|
||||
}
|
||||
|
||||
services->push_back(std::unique_ptr<ResolvedService>(this));
|
||||
|
||||
if (adb_DNSServiceShouldAutoConnect(regType_.c_str(), serviceName_.c_str())) {
|
||||
std::string response;
|
||||
D("Attempting to connect serviceName=[%s], regtype=[%s] ipaddr=(%s:%hu)",
|
||||
serviceName_.c_str(), regType_.c_str(), ip_addr_, port_);
|
||||
int index = adb_DNSServiceIndexByName(regType_.c_str());
|
||||
if (index == kADBSecureConnectServiceRefIndex) {
|
||||
ConnectSecureWifiDevice();
|
||||
} else {
|
||||
connect_device(android::base::StringPrintf("%s.%s", serviceName_.c_str(),
|
||||
regType_.c_str()),
|
||||
&response);
|
||||
D("Connect to %s regtype %s (%s:%hu) : %s", serviceName_.c_str(), regType_.c_str(),
|
||||
ip_addr_, port_, response.c_str());
|
||||
}
|
||||
} else {
|
||||
D("Not immediately connecting to serviceName=[%s], regtype=[%s] ipaddr=(%s:%hu)",
|
||||
serviceName_.c_str(), regType_.c_str(), ip_addr_, port_);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int serviceIndex() const { return adb_DNSServiceIndexByName(regType_.c_str()); }
|
||||
|
||||
std::string hostTarget() const { return hosttarget_; }
|
||||
|
||||
std::string serviceName() const { return serviceName_; }
|
||||
|
||||
std::string regType() const { return regType_; }
|
||||
|
||||
std::string ipAddress() const { return ip_addr_; }
|
||||
|
||||
uint16_t port() const { return port_; }
|
||||
|
||||
using ServiceRegistry = std::vector<std::unique_ptr<ResolvedService>>;
|
||||
|
||||
// unencrypted tcp connections
|
||||
static ServiceRegistry* sAdbTransportServices;
|
||||
|
||||
static ServiceRegistry* sAdbSecurePairingServices;
|
||||
static ServiceRegistry* sAdbSecureConnectServices;
|
||||
|
||||
static void initAdbServiceRegistries();
|
||||
|
||||
static void forEachService(const ServiceRegistry& services, std::string_view hostname,
|
||||
adb_secure_foreach_service_callback cb);
|
||||
|
||||
static bool connectByServiceName(const ServiceRegistry& services,
|
||||
const std::string& service_name);
|
||||
|
||||
static void removeDNSService(const char* regType, const char* serviceName);
|
||||
|
||||
private:
|
||||
int clientVersion_ = ADB_SECURE_CLIENT_VERSION;
|
||||
std::string addr_format_;
|
||||
std::string serviceName_;
|
||||
std::string regType_;
|
||||
std::string hosttarget_;
|
||||
const uint16_t port_;
|
||||
int sa_family_;
|
||||
const void* ip_addr_data_;
|
||||
char ip_addr_[INET6_ADDRSTRLEN];
|
||||
int serviceVersion_;
|
||||
};
|
||||
|
||||
// static
|
||||
ResolvedService::ServiceRegistry* ResolvedService::sAdbTransportServices = NULL;
|
||||
|
||||
// static
|
||||
ResolvedService::ServiceRegistry* ResolvedService::sAdbSecurePairingServices = NULL;
|
||||
|
||||
// static
|
||||
ResolvedService::ServiceRegistry* ResolvedService::sAdbSecureConnectServices = NULL;
|
||||
|
||||
// static
|
||||
void ResolvedService::initAdbServiceRegistries() {
|
||||
if (!sAdbTransportServices) {
|
||||
sAdbTransportServices = new ServiceRegistry;
|
||||
}
|
||||
if (!sAdbSecurePairingServices) {
|
||||
sAdbSecurePairingServices = new ServiceRegistry;
|
||||
}
|
||||
if (!sAdbSecureConnectServices) {
|
||||
sAdbSecureConnectServices = new ServiceRegistry;
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void ResolvedService::forEachService(const ServiceRegistry& services,
|
||||
std::string_view wanted_service_name,
|
||||
adb_secure_foreach_service_callback cb) {
|
||||
initAdbServiceRegistries();
|
||||
|
||||
for (const auto& service : services) {
|
||||
auto service_name = service->serviceName();
|
||||
auto reg_type = service->regType();
|
||||
auto ip = service->ipAddress();
|
||||
auto port = service->port();
|
||||
|
||||
if (wanted_service_name.empty()) {
|
||||
cb(service_name.c_str(), reg_type.c_str(), ip.c_str(), port);
|
||||
} else if (service_name == wanted_service_name) {
|
||||
cb(service_name.c_str(), reg_type.c_str(), ip.c_str(), port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
bool ResolvedService::connectByServiceName(const ServiceRegistry& services,
|
||||
const std::string& service_name) {
|
||||
initAdbServiceRegistries();
|
||||
for (const auto& service : services) {
|
||||
if (service_name == service->serviceName()) {
|
||||
D("Got service_name match [%s]", service->serviceName().c_str());
|
||||
return service->ConnectSecureWifiDevice();
|
||||
}
|
||||
}
|
||||
D("No registered serviceNames matched [%s]", service_name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// static
|
||||
void ResolvedService::removeDNSService(const char* regType, const char* serviceName) {
|
||||
D("%s: regType=[%s] serviceName=[%s]", __func__, regType, serviceName);
|
||||
int index = adb_DNSServiceIndexByName(regType);
|
||||
ServiceRegistry* services;
|
||||
switch (index) {
|
||||
case kADBTransportServiceRefIndex:
|
||||
services = sAdbTransportServices;
|
||||
break;
|
||||
case kADBSecurePairingServiceRefIndex:
|
||||
services = sAdbSecurePairingServices;
|
||||
break;
|
||||
case kADBSecureConnectServiceRefIndex:
|
||||
services = sAdbSecureConnectServices;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (services->empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string sName(serviceName);
|
||||
services->erase(std::remove_if(services->begin(), services->end(),
|
||||
[&sName](std::unique_ptr<ResolvedService>& service) {
|
||||
return (sName == service->serviceName());
|
||||
}),
|
||||
services->end());
|
||||
}
|
||||
|
||||
void adb_secure_foreach_pairing_service(const char* service_name,
|
||||
adb_secure_foreach_service_callback cb) {
|
||||
ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices, service_name, cb);
|
||||
}
|
||||
|
||||
void adb_secure_foreach_connect_service(const char* service_name,
|
||||
adb_secure_foreach_service_callback cb) {
|
||||
ResolvedService::forEachService(*ResolvedService::sAdbSecureConnectServices, service_name, cb);
|
||||
}
|
||||
|
||||
bool adb_secure_connect_by_service_name(const char* service_name) {
|
||||
return ResolvedService::connectByServiceName(*ResolvedService::sAdbSecureConnectServices,
|
||||
service_name);
|
||||
}
|
||||
|
||||
static void DNSSD_API register_service_ip(DNSServiceRef sdRef, DNSServiceFlags flags,
|
||||
uint32_t /*interfaceIndex*/,
|
||||
DNSServiceErrorType errorCode, const char* hostname,
|
||||
const sockaddr* address, uint32_t ttl, void* context) {
|
||||
D("%s: sdRef=%p flags=0x%08x errorCode=%u ttl=%u", __func__, sdRef, flags, errorCode, ttl);
|
||||
std::unique_ptr<ResolvedService> data(
|
||||
reinterpret_cast<ResolvedService*>(context));
|
||||
// Only resolve the address once. If the address or port changes, we'll just get another
|
||||
// registration.
|
||||
data->DestroyServiceRef();
|
||||
|
||||
if (errorCode != kDNSServiceErr_NoError) {
|
||||
D("Got error while looking up ipaddr [%u]", errorCode);
|
||||
return;
|
||||
}
|
||||
|
||||
if (flags & kDNSServiceFlagsAdd) {
|
||||
D("Resolved IP address for [%s]. Adding to service registry.", hostname);
|
||||
auto* ptr = data.release();
|
||||
if (!ptr->AddToServiceRegistry(address)) {
|
||||
data.reset(ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void DNSSD_API register_resolved_mdns_service(DNSServiceRef sdRef,
|
||||
DNSServiceFlags flags,
|
||||
uint32_t interfaceIndex,
|
||||
DNSServiceErrorType errorCode,
|
||||
const char* fullname,
|
||||
const char* hosttarget,
|
||||
uint16_t port,
|
||||
uint16_t txtLen,
|
||||
const unsigned char* txtRecord,
|
||||
void* context);
|
||||
|
||||
class DiscoveredService : public AsyncServiceRef {
|
||||
public:
|
||||
DiscoveredService(uint32_t interfaceIndex, const char* serviceName, const char* regtype,
|
||||
const char* domain)
|
||||
: serviceName_(serviceName), regType_(regtype) {
|
||||
DNSServiceErrorType ret =
|
||||
DNSServiceResolve(&sdRef_, 0, interfaceIndex, serviceName, regtype,
|
||||
domain, register_resolved_mdns_service,
|
||||
reinterpret_cast<void*>(this));
|
||||
|
||||
D("DNSServiceResolve for "
|
||||
"interfaceIndex %u "
|
||||
"serviceName %s "
|
||||
"regtype %s "
|
||||
"domain %s "
|
||||
": %d",
|
||||
interfaceIndex, serviceName, regtype, domain, ret);
|
||||
|
||||
if (ret == kDNSServiceErr_NoError) {
|
||||
Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
const char* ServiceName() {
|
||||
return serviceName_.c_str();
|
||||
}
|
||||
|
||||
const char* RegType() { return regType_.c_str(); }
|
||||
|
||||
private:
|
||||
std::string serviceName_;
|
||||
std::string regType_;
|
||||
};
|
||||
|
||||
// Returns the version the device wanted to advertise,
|
||||
// or -1 if parsing fails.
|
||||
static int parse_version_from_txt_record(uint16_t txtLen, const unsigned char* txtRecord) {
|
||||
if (!txtLen) return -1;
|
||||
if (!txtRecord) return -1;
|
||||
|
||||
// https://tools.ietf.org/html/rfc6763
|
||||
// """
|
||||
// 6.1. General Format Rules for DNS TXT Records
|
||||
//
|
||||
// A DNS TXT record can be up to 65535 (0xFFFF) bytes long. The total
|
||||
// length is indicated by the length given in the resource record header
|
||||
// in the DNS message. There is no way to tell directly from the data
|
||||
// alone how long it is (e.g., there is no length count at the start, or
|
||||
// terminating NULL byte at the end).
|
||||
// """
|
||||
|
||||
// Let's trust the TXT record's length byte
|
||||
// Worst case, it wastes 255 bytes
|
||||
std::vector<char> recordAsString(txtLen + 1, '\0');
|
||||
char* str = recordAsString.data();
|
||||
|
||||
memcpy(str, txtRecord + 1 /* skip the length byte */, txtLen);
|
||||
|
||||
// Check if it's the version key
|
||||
static const char* versionKey = "v=";
|
||||
size_t versionKeyLen = strlen(versionKey);
|
||||
|
||||
if (strncmp(versionKey, str, versionKeyLen)) return -1;
|
||||
|
||||
auto valueStart = str + versionKeyLen;
|
||||
|
||||
long parsedNumber = strtol(valueStart, 0, 10);
|
||||
|
||||
// No valid conversion. Also, 0
|
||||
// is not a valid version.
|
||||
if (!parsedNumber) return -1;
|
||||
|
||||
// Outside bounds of long.
|
||||
if (parsedNumber == LONG_MIN || parsedNumber == LONG_MAX) return -1;
|
||||
|
||||
// Possibly valid version
|
||||
return static_cast<int>(parsedNumber);
|
||||
}
|
||||
|
||||
static void DNSSD_API register_resolved_mdns_service(
|
||||
DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
|
||||
DNSServiceErrorType errorCode, const char* fullname, const char* hosttarget, uint16_t port,
|
||||
uint16_t txtLen, const unsigned char* txtRecord, void* context) {
|
||||
D("Resolved a service.");
|
||||
std::unique_ptr<DiscoveredService> discovered(
|
||||
reinterpret_cast<DiscoveredService*>(context));
|
||||
|
||||
if (errorCode != kDNSServiceErr_NoError) {
|
||||
D("Got error %d resolving service.", errorCode);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Reject certain combinations of invalid or mismatched client and
|
||||
// service versions here before creating anything.
|
||||
// At the moment, there is nothing to reject, so accept everything
|
||||
// as an optimistic default.
|
||||
auto serviceVersion = parse_version_from_txt_record(txtLen, txtRecord);
|
||||
|
||||
auto resolved = new ResolvedService(discovered->ServiceName(), discovered->RegType(),
|
||||
interfaceIndex, hosttarget, ntohs(port), serviceVersion);
|
||||
|
||||
if (! resolved->Initialized()) {
|
||||
D("Unable to init resolved service");
|
||||
delete resolved;
|
||||
}
|
||||
|
||||
if (flags) { /* Only ever equals MoreComing or 0 */
|
||||
D("releasing discovered service");
|
||||
discovered.release();
|
||||
}
|
||||
}
|
||||
|
||||
static void DNSSD_API on_service_browsed(DNSServiceRef sdRef, DNSServiceFlags flags,
|
||||
uint32_t interfaceIndex, DNSServiceErrorType errorCode,
|
||||
const char* serviceName, const char* regtype,
|
||||
const char* domain, void* /*context*/) {
|
||||
if (errorCode != kDNSServiceErr_NoError) {
|
||||
D("Got error %d during mDNS browse.", errorCode);
|
||||
DNSServiceRefDeallocate(sdRef);
|
||||
int serviceIndex = adb_DNSServiceIndexByName(regtype);
|
||||
if (serviceIndex != -1) {
|
||||
fdevent_destroy(service_ref_fdes[serviceIndex]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (flags & kDNSServiceFlagsAdd) {
|
||||
D("%s: Discover found new serviceName=[%s] regtype=[%s] domain=[%s]", __func__, serviceName,
|
||||
regtype, domain);
|
||||
auto discovered = new DiscoveredService(interfaceIndex, serviceName, regtype, domain);
|
||||
if (!discovered->Initialized()) {
|
||||
delete discovered;
|
||||
}
|
||||
} else {
|
||||
D("%s: Discover lost serviceName=[%s] regtype=[%s] domain=[%s]", __func__, serviceName,
|
||||
regtype, domain);
|
||||
ResolvedService::removeDNSService(regtype, serviceName);
|
||||
}
|
||||
}
|
||||
|
||||
void init_mdns_transport_discovery_thread(void) {
|
||||
config_auto_connect_services();
|
||||
std::string res;
|
||||
std::for_each(g_autoconn_whitelist.begin(), g_autoconn_whitelist.end(), [&](const int& i) {
|
||||
res += kADBDNSServices[i];
|
||||
res += ",";
|
||||
});
|
||||
D("mdns auto-connect whitelist: [%s]", res.data());
|
||||
|
||||
int errorCodes[kNumADBDNSServices];
|
||||
for (int i = 0; i < kNumADBDNSServices; ++i) {
|
||||
errorCodes[i] = DNSServiceBrowse(&service_refs[i], 0, 0, kADBDNSServices[i], nullptr,
|
||||
on_service_browsed, nullptr);
|
||||
|
||||
if (errorCodes[i] != kDNSServiceErr_NoError) {
|
||||
D("Got %d browsing for mDNS service %s.", errorCodes[i], kADBDNSServices[i]);
|
||||
}
|
||||
|
||||
if (errorCodes[i] == kDNSServiceErr_NoError) {
|
||||
fdevent_run_on_main_thread([i]() {
|
||||
service_ref_fdes[i] = fdevent_create(adb_DNSServiceRefSockFD(service_refs[i]),
|
||||
pump_service_ref, &service_refs[i]);
|
||||
fdevent_set(service_ref_fdes[i], FDE_READ);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init_mdns_transport_discovery(void) {
|
||||
ResolvedService::initAdbServiceRegistries();
|
||||
std::thread(init_mdns_transport_discovery_thread).detach();
|
||||
}
|
||||
|
||||
std::string mdns_check() {
|
||||
uint32_t daemon_version;
|
||||
uint32_t sz = sizeof(daemon_version);
|
||||
|
||||
auto dnserr = DNSServiceGetProperty(kDNSServiceProperty_DaemonVersion, &daemon_version, &sz);
|
||||
std::string result = "ERROR: mdns daemon unavailable";
|
||||
if (dnserr != kDNSServiceErr_NoError) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = android::base::StringPrintf("mdns daemon version [%u]", daemon_version);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string mdns_list_discovered_services() {
|
||||
std::string result;
|
||||
auto cb = [&](const char* service_name, const char* reg_type, const char* ip_addr,
|
||||
uint16_t port) {
|
||||
result += android::base::StringPrintf("%s\t%s\t%s:%u\n", service_name, reg_type, ip_addr,
|
||||
port);
|
||||
};
|
||||
|
||||
ResolvedService::forEachService(*ResolvedService::sAdbTransportServices, "", cb);
|
||||
ResolvedService::forEachService(*ResolvedService::sAdbSecureConnectServices, "", cb);
|
||||
ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices, "", cb);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<MdnsInfo> mdns_get_connect_service_info(std::string_view name) {
|
||||
CHECK(!name.empty());
|
||||
|
||||
// only adb server creates these registries
|
||||
if (!ResolvedService::sAdbTransportServices && !ResolvedService::sAdbSecureConnectServices) {
|
||||
return std::nullopt;
|
||||
}
|
||||
CHECK(ResolvedService::sAdbTransportServices);
|
||||
CHECK(ResolvedService::sAdbSecureConnectServices);
|
||||
|
||||
auto mdns_instance = mdns::mdns_parse_instance_name(name);
|
||||
if (!mdns_instance.has_value()) {
|
||||
D("Failed to parse mDNS name [%s]", name.data());
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<MdnsInfo> info;
|
||||
auto cb = [&](const char* service_name, const char* reg_type, const char* ip_addr,
|
||||
uint16_t port) { info.emplace(service_name, reg_type, ip_addr, port); };
|
||||
|
||||
std::string reg_type;
|
||||
if (!mdns_instance->service_name.empty()) {
|
||||
reg_type = android::base::StringPrintf("%s.%s", mdns_instance->service_name.data(),
|
||||
mdns_instance->transport_type.data());
|
||||
int index = adb_DNSServiceIndexByName(reg_type);
|
||||
switch (index) {
|
||||
case kADBTransportServiceRefIndex:
|
||||
ResolvedService::forEachService(*ResolvedService::sAdbTransportServices,
|
||||
mdns_instance->instance_name, cb);
|
||||
break;
|
||||
case kADBSecureConnectServiceRefIndex:
|
||||
ResolvedService::forEachService(*ResolvedService::sAdbSecureConnectServices,
|
||||
mdns_instance->instance_name, cb);
|
||||
break;
|
||||
default:
|
||||
D("Unknown reg_type [%s]", reg_type.data());
|
||||
return std::nullopt;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
for (const auto& service :
|
||||
{ResolvedService::sAdbTransportServices, ResolvedService::sAdbSecureConnectServices}) {
|
||||
ResolvedService::forEachService(*service, name, cb);
|
||||
if (info.has_value()) {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<MdnsInfo> mdns_get_pairing_service_info(std::string_view name) {
|
||||
CHECK(!name.empty());
|
||||
|
||||
auto mdns_instance = mdns::mdns_parse_instance_name(name);
|
||||
if (!mdns_instance.has_value()) {
|
||||
D("Failed to parse mDNS pairing name [%s]", name.data());
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<MdnsInfo> info;
|
||||
auto cb = [&](const char* service_name, const char* reg_type, const char* ip_addr,
|
||||
uint16_t port) { info.emplace(service_name, reg_type, ip_addr, port); };
|
||||
|
||||
// Verify it's a pairing service if user explicitly inputs it.
|
||||
if (!mdns_instance->service_name.empty()) {
|
||||
auto reg_type = android::base::StringPrintf("%s.%s", mdns_instance->service_name.data(),
|
||||
mdns_instance->transport_type.data());
|
||||
int index = adb_DNSServiceIndexByName(reg_type);
|
||||
switch (index) {
|
||||
case kADBSecurePairingServiceRefIndex:
|
||||
break;
|
||||
default:
|
||||
D("Not an adb pairing reg_type [%s]", reg_type.data());
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices, name, cb);
|
||||
return info;
|
||||
}
|
|
@ -1,212 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
|
||||
#define TRACE_TAG TRANSPORT
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#include "client/usb.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "sysdeps.h"
|
||||
#include "transport.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "adb.h"
|
||||
|
||||
#if ADB_HOST
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#define CHECK_PACKET_OVERFLOW 0
|
||||
#else
|
||||
#define CHECK_PACKET_OVERFLOW 1
|
||||
#endif
|
||||
|
||||
// Call usb_read using a buffer having a multiple of usb_get_max_packet_size() bytes
|
||||
// to avoid overflow. See http://libusb.sourceforge.net/api-1.0/packetoverflow.html.
|
||||
static int UsbReadMessage(usb_handle* h, amessage* msg) {
|
||||
D("UsbReadMessage");
|
||||
|
||||
#if CHECK_PACKET_OVERFLOW
|
||||
size_t usb_packet_size = usb_get_max_packet_size(h);
|
||||
CHECK_GE(usb_packet_size, sizeof(*msg));
|
||||
CHECK_LT(usb_packet_size, 4096ULL);
|
||||
|
||||
char buffer[4096];
|
||||
int n = usb_read(h, buffer, usb_packet_size);
|
||||
if (n != sizeof(*msg)) {
|
||||
D("usb_read returned unexpected length %d (expected %zu)", n, sizeof(*msg));
|
||||
return -1;
|
||||
}
|
||||
memcpy(msg, buffer, sizeof(*msg));
|
||||
return n;
|
||||
#else
|
||||
return usb_read(h, msg, sizeof(*msg));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Call usb_read using a buffer having a multiple of usb_get_max_packet_size() bytes
|
||||
// to avoid overflow. See http://libusb.sourceforge.net/api-1.0/packetoverflow.html.
|
||||
static int UsbReadPayload(usb_handle* h, apacket* p) {
|
||||
D("UsbReadPayload(%d)", p->msg.data_length);
|
||||
|
||||
if (p->msg.data_length > MAX_PAYLOAD) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if CHECK_PACKET_OVERFLOW
|
||||
size_t usb_packet_size = usb_get_max_packet_size(h);
|
||||
|
||||
// Round the data length up to the nearest packet size boundary.
|
||||
// The device won't send a zero packet for packet size aligned payloads,
|
||||
// so don't read any more packets than needed.
|
||||
size_t len = p->msg.data_length;
|
||||
size_t rem_size = len % usb_packet_size;
|
||||
if (rem_size) {
|
||||
len += usb_packet_size - rem_size;
|
||||
}
|
||||
|
||||
p->payload.resize(len);
|
||||
int rc = usb_read(h, &p->payload[0], p->payload.size());
|
||||
if (rc != static_cast<int>(p->msg.data_length)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
p->payload.resize(rc);
|
||||
return rc;
|
||||
#else
|
||||
p->payload.resize(p->msg.data_length);
|
||||
return usb_read(h, &p->payload[0], p->payload.size());
|
||||
#endif
|
||||
}
|
||||
|
||||
static int remote_read(apacket* p, usb_handle* usb) {
|
||||
int n = UsbReadMessage(usb, &p->msg);
|
||||
if (n < 0) {
|
||||
D("remote usb: read terminated (message)");
|
||||
return -1;
|
||||
}
|
||||
if (static_cast<size_t>(n) != sizeof(p->msg)) {
|
||||
D("remote usb: read received unexpected header length %d", n);
|
||||
return -1;
|
||||
}
|
||||
if (p->msg.data_length) {
|
||||
n = UsbReadPayload(usb, p);
|
||||
if (n < 0) {
|
||||
D("remote usb: terminated (data)");
|
||||
return -1;
|
||||
}
|
||||
if (static_cast<uint32_t>(n) != p->msg.data_length) {
|
||||
D("remote usb: read payload failed (need %u bytes, give %d bytes), skip it",
|
||||
p->msg.data_length, n);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// On Android devices, we rely on the kernel to provide buffered read.
|
||||
// So we can recover automatically from EOVERFLOW.
|
||||
static int remote_read(apacket* p, usb_handle* usb) {
|
||||
if (usb_read(usb, &p->msg, sizeof(amessage)) != sizeof(amessage)) {
|
||||
PLOG(ERROR) << "remote usb: read terminated (message)";
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (p->msg.data_length) {
|
||||
if (p->msg.data_length > MAX_PAYLOAD) {
|
||||
PLOG(ERROR) << "remote usb: read overflow (data length = " << p->msg.data_length << ")";
|
||||
return -1;
|
||||
}
|
||||
|
||||
p->payload.resize(p->msg.data_length);
|
||||
if (usb_read(usb, &p->payload[0], p->payload.size()) !=
|
||||
static_cast<int>(p->payload.size())) {
|
||||
PLOG(ERROR) << "remote usb: terminated (data)";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
UsbConnection::~UsbConnection() {
|
||||
usb_close(handle_);
|
||||
}
|
||||
|
||||
bool UsbConnection::Read(apacket* packet) {
|
||||
int rc = remote_read(packet, handle_);
|
||||
return rc == 0;
|
||||
}
|
||||
|
||||
bool UsbConnection::Write(apacket* packet) {
|
||||
int size = packet->msg.data_length;
|
||||
|
||||
if (usb_write(handle_, &packet->msg, sizeof(packet->msg)) != sizeof(packet->msg)) {
|
||||
PLOG(ERROR) << "remote usb: 1 - write terminated";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (packet->msg.data_length != 0 && usb_write(handle_, packet->payload.data(), size) != size) {
|
||||
PLOG(ERROR) << "remote usb: 2 - write terminated";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UsbConnection::DoTlsHandshake(RSA* key, std::string* auth_key) {
|
||||
// TODO: support TLS for usb connections
|
||||
LOG(FATAL) << "Not supported yet.";
|
||||
return false;
|
||||
}
|
||||
|
||||
void UsbConnection::Reset() {
|
||||
usb_reset(handle_);
|
||||
usb_kick(handle_);
|
||||
}
|
||||
|
||||
void UsbConnection::Close() {
|
||||
usb_kick(handle_);
|
||||
}
|
||||
|
||||
void init_usb_transport(atransport* t, usb_handle* h) {
|
||||
D("transport: usb");
|
||||
auto connection = std::make_unique<UsbConnection>(h);
|
||||
t->SetConnection(std::make_unique<BlockingConnectionAdapter>(std::move(connection)));
|
||||
t->type = kTransportUsb;
|
||||
t->SetUsbHandle(h);
|
||||
}
|
||||
|
||||
int is_adb_interface(int usb_class, int usb_subclass, int usb_protocol) {
|
||||
return (usb_class == ADB_CLASS && usb_subclass == ADB_SUBCLASS && usb_protocol == ADB_PROTOCOL);
|
||||
}
|
||||
|
||||
bool should_use_libusb() {
|
||||
#if !ADB_HOST
|
||||
return false;
|
||||
#else
|
||||
static bool enable = getenv("ADB_LIBUSB") && strcmp(getenv("ADB_LIBUSB"), "1") == 0;
|
||||
return enable;
|
||||
#endif
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "transport.h"
|
||||
|
||||
// USB host/client interface.
|
||||
|
||||
#define ADB_USB_INTERFACE(handle_ref_type) \
|
||||
void usb_init(); \
|
||||
void usb_cleanup(); \
|
||||
int usb_write(handle_ref_type h, const void* data, int len); \
|
||||
int usb_read(handle_ref_type h, void* data, int len); \
|
||||
int usb_close(handle_ref_type h); \
|
||||
void usb_reset(handle_ref_type h); \
|
||||
void usb_kick(handle_ref_type h); \
|
||||
size_t usb_get_max_packet_size(handle_ref_type)
|
||||
|
||||
// Linux and Darwin clients have native and libusb implementations.
|
||||
|
||||
namespace libusb {
|
||||
struct usb_handle;
|
||||
ADB_USB_INTERFACE(libusb::usb_handle*);
|
||||
} // namespace libusb
|
||||
|
||||
namespace native {
|
||||
struct usb_handle;
|
||||
ADB_USB_INTERFACE(native::usb_handle*);
|
||||
} // namespace native
|
||||
|
||||
// Empty base that both implementations' opaque handles inherit from.
|
||||
struct usb_handle {};
|
||||
|
||||
ADB_USB_INTERFACE(::usb_handle*);
|
||||
|
||||
// USB device detection.
|
||||
int is_adb_interface(int usb_class, int usb_subclass, int usb_protocol);
|
||||
|
||||
bool should_use_libusb();
|
||||
|
||||
struct UsbConnection : public BlockingConnection {
|
||||
explicit UsbConnection(usb_handle* handle) : handle_(handle) {}
|
||||
~UsbConnection();
|
||||
|
||||
bool Read(apacket* packet) override final;
|
||||
bool Write(apacket* packet) override final;
|
||||
bool DoTlsHandshake(RSA* key, std::string* auth_key) override final;
|
||||
|
||||
void Close() override final;
|
||||
virtual void Reset() override final;
|
||||
|
||||
usb_handle* handle_;
|
||||
};
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <android-base/logging.h>
|
||||
|
||||
#include "client/usb.h"
|
||||
|
||||
void usb_init() {
|
||||
if (should_use_libusb()) {
|
||||
LOG(DEBUG) << "using libusb backend";
|
||||
libusb::usb_init();
|
||||
} else {
|
||||
LOG(DEBUG) << "using native backend";
|
||||
native::usb_init();
|
||||
}
|
||||
}
|
||||
|
||||
void usb_cleanup() {
|
||||
if (should_use_libusb()) {
|
||||
libusb::usb_cleanup();
|
||||
} else {
|
||||
native::usb_cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
int usb_write(usb_handle* h, const void* data, int len) {
|
||||
return should_use_libusb()
|
||||
? libusb::usb_write(reinterpret_cast<libusb::usb_handle*>(h), data, len)
|
||||
: native::usb_write(reinterpret_cast<native::usb_handle*>(h), data, len);
|
||||
}
|
||||
|
||||
int usb_read(usb_handle* h, void* data, int len) {
|
||||
return should_use_libusb()
|
||||
? libusb::usb_read(reinterpret_cast<libusb::usb_handle*>(h), data, len)
|
||||
: native::usb_read(reinterpret_cast<native::usb_handle*>(h), data, len);
|
||||
}
|
||||
|
||||
int usb_close(usb_handle* h) {
|
||||
return should_use_libusb() ? libusb::usb_close(reinterpret_cast<libusb::usb_handle*>(h))
|
||||
: native::usb_close(reinterpret_cast<native::usb_handle*>(h));
|
||||
}
|
||||
|
||||
void usb_reset(usb_handle* h) {
|
||||
should_use_libusb() ? libusb::usb_reset(reinterpret_cast<libusb::usb_handle*>(h))
|
||||
: native::usb_reset(reinterpret_cast<native::usb_handle*>(h));
|
||||
}
|
||||
|
||||
void usb_kick(usb_handle* h) {
|
||||
should_use_libusb() ? libusb::usb_kick(reinterpret_cast<libusb::usb_handle*>(h))
|
||||
: native::usb_kick(reinterpret_cast<native::usb_handle*>(h));
|
||||
}
|
||||
|
||||
size_t usb_get_max_packet_size(usb_handle* h) {
|
||||
return should_use_libusb()
|
||||
? libusb::usb_get_max_packet_size(reinterpret_cast<libusb::usb_handle*>(h))
|
||||
: native::usb_get_max_packet_size(reinterpret_cast<native::usb_handle*>(h));
|
||||
}
|
|
@ -1,638 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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 "sysdeps.h"
|
||||
|
||||
#include "client/usb.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <libusb/libusb.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/strings.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "adb_utils.h"
|
||||
#include "transport.h"
|
||||
|
||||
using android::base::StringPrintf;
|
||||
|
||||
// RAII wrappers for libusb.
|
||||
struct ConfigDescriptorDeleter {
|
||||
void operator()(libusb_config_descriptor* desc) {
|
||||
libusb_free_config_descriptor(desc);
|
||||
}
|
||||
};
|
||||
|
||||
using unique_config_descriptor = std::unique_ptr<libusb_config_descriptor, ConfigDescriptorDeleter>;
|
||||
|
||||
struct DeviceHandleDeleter {
|
||||
void operator()(libusb_device_handle* h) {
|
||||
libusb_close(h);
|
||||
}
|
||||
};
|
||||
|
||||
using unique_device_handle = std::unique_ptr<libusb_device_handle, DeviceHandleDeleter>;
|
||||
|
||||
struct transfer_info {
|
||||
transfer_info(const char* name, uint16_t zero_mask, bool is_bulk_out)
|
||||
: name(name),
|
||||
transfer(libusb_alloc_transfer(0)),
|
||||
is_bulk_out(is_bulk_out),
|
||||
zero_mask(zero_mask) {}
|
||||
|
||||
~transfer_info() {
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
const char* name;
|
||||
libusb_transfer* transfer;
|
||||
bool is_bulk_out;
|
||||
bool transfer_complete;
|
||||
std::condition_variable cv;
|
||||
std::mutex mutex;
|
||||
uint16_t zero_mask;
|
||||
|
||||
void Notify() {
|
||||
LOG(DEBUG) << "notifying " << name << " transfer complete";
|
||||
transfer_complete = true;
|
||||
cv.notify_one();
|
||||
}
|
||||
};
|
||||
|
||||
namespace libusb {
|
||||
struct usb_handle : public ::usb_handle {
|
||||
usb_handle(const std::string& device_address, const std::string& serial,
|
||||
unique_device_handle&& device_handle, uint8_t interface, uint8_t bulk_in,
|
||||
uint8_t bulk_out, size_t zero_mask, size_t max_packet_size)
|
||||
: device_address(device_address),
|
||||
serial(serial),
|
||||
closing(false),
|
||||
device_handle(device_handle.release()),
|
||||
read("read", zero_mask, false),
|
||||
write("write", zero_mask, true),
|
||||
interface(interface),
|
||||
bulk_in(bulk_in),
|
||||
bulk_out(bulk_out),
|
||||
max_packet_size(max_packet_size) {}
|
||||
|
||||
~usb_handle() {
|
||||
Close();
|
||||
}
|
||||
|
||||
void Close() {
|
||||
std::unique_lock<std::mutex> lock(device_handle_mutex);
|
||||
// Cancelling transfers will trigger more Closes, so make sure this only happens once.
|
||||
if (closing) {
|
||||
return;
|
||||
}
|
||||
closing = true;
|
||||
|
||||
// Make sure that no new transfers come in.
|
||||
libusb_device_handle* handle = device_handle;
|
||||
if (!handle) {
|
||||
return;
|
||||
}
|
||||
|
||||
device_handle = nullptr;
|
||||
|
||||
// Cancel already dispatched transfers.
|
||||
libusb_cancel_transfer(read.transfer);
|
||||
libusb_cancel_transfer(write.transfer);
|
||||
|
||||
libusb_release_interface(handle, interface);
|
||||
libusb_close(handle);
|
||||
}
|
||||
|
||||
std::string device_address;
|
||||
std::string serial;
|
||||
|
||||
std::atomic<bool> closing;
|
||||
std::mutex device_handle_mutex;
|
||||
libusb_device_handle* device_handle;
|
||||
|
||||
transfer_info read;
|
||||
transfer_info write;
|
||||
|
||||
uint8_t interface;
|
||||
uint8_t bulk_in;
|
||||
uint8_t bulk_out;
|
||||
|
||||
size_t max_packet_size;
|
||||
};
|
||||
|
||||
static auto& usb_handles = *new std::unordered_map<std::string, std::unique_ptr<usb_handle>>();
|
||||
static auto& usb_handles_mutex = *new std::mutex();
|
||||
|
||||
static libusb_hotplug_callback_handle hotplug_handle;
|
||||
|
||||
static std::string get_device_address(libusb_device* device) {
|
||||
return StringPrintf("usb:%d:%d", libusb_get_bus_number(device),
|
||||
libusb_get_device_address(device));
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
static std::string get_device_serial_path(libusb_device* device) {
|
||||
uint8_t ports[7];
|
||||
int port_count = libusb_get_port_numbers(device, ports, 7);
|
||||
if (port_count < 0) return "";
|
||||
|
||||
std::string path =
|
||||
StringPrintf("/sys/bus/usb/devices/%d-%d", libusb_get_bus_number(device), ports[0]);
|
||||
for (int port = 1; port < port_count; ++port) {
|
||||
path += StringPrintf(".%d", ports[port]);
|
||||
}
|
||||
path += "/serial";
|
||||
return path;
|
||||
}
|
||||
|
||||
static std::string get_device_dev_path(libusb_device* device) {
|
||||
uint8_t ports[7];
|
||||
int port_count = libusb_get_port_numbers(device, ports, 7);
|
||||
if (port_count < 0) return "";
|
||||
return StringPrintf("/dev/bus/usb/%03u/%03u", libusb_get_bus_number(device), ports[0]);
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool endpoint_is_output(uint8_t endpoint) {
|
||||
return (endpoint & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT;
|
||||
}
|
||||
|
||||
static bool should_perform_zero_transfer(uint8_t endpoint, size_t write_length, uint16_t zero_mask) {
|
||||
return endpoint_is_output(endpoint) && write_length != 0 && zero_mask != 0 &&
|
||||
(write_length & zero_mask) == 0;
|
||||
}
|
||||
|
||||
static void process_device(libusb_device* device) {
|
||||
std::string device_address = get_device_address(device);
|
||||
std::string device_serial;
|
||||
|
||||
// Figure out if we want to open the device.
|
||||
libusb_device_descriptor device_desc;
|
||||
int rc = libusb_get_device_descriptor(device, &device_desc);
|
||||
if (rc != 0) {
|
||||
LOG(WARNING) << "failed to get device descriptor for device at " << device_address << ": "
|
||||
<< libusb_error_name(rc);
|
||||
return;
|
||||
}
|
||||
|
||||
if (device_desc.bDeviceClass != LIBUSB_CLASS_PER_INTERFACE) {
|
||||
// Assume that all Android devices have the device class set to per interface.
|
||||
// TODO: Is this assumption valid?
|
||||
LOG(VERBOSE) << "skipping device with incorrect class at " << device_address;
|
||||
return;
|
||||
}
|
||||
|
||||
libusb_config_descriptor* config_raw;
|
||||
rc = libusb_get_active_config_descriptor(device, &config_raw);
|
||||
if (rc != 0) {
|
||||
LOG(WARNING) << "failed to get active config descriptor for device at " << device_address
|
||||
<< ": " << libusb_error_name(rc);
|
||||
return;
|
||||
}
|
||||
const unique_config_descriptor config(config_raw);
|
||||
|
||||
// Use size_t for interface_num so <iostream>s don't mangle it.
|
||||
size_t interface_num;
|
||||
uint16_t zero_mask = 0;
|
||||
uint8_t bulk_in = 0, bulk_out = 0;
|
||||
size_t packet_size = 0;
|
||||
bool found_adb = false;
|
||||
|
||||
for (interface_num = 0; interface_num < config->bNumInterfaces; ++interface_num) {
|
||||
const libusb_interface& interface = config->interface[interface_num];
|
||||
if (interface.num_altsetting != 1) {
|
||||
// Assume that interfaces with alternate settings aren't adb interfaces.
|
||||
// TODO: Is this assumption valid?
|
||||
LOG(VERBOSE) << "skipping interface with incorrect num_altsetting at " << device_address
|
||||
<< " (interface " << interface_num << ")";
|
||||
continue;
|
||||
}
|
||||
|
||||
const libusb_interface_descriptor& interface_desc = interface.altsetting[0];
|
||||
if (!is_adb_interface(interface_desc.bInterfaceClass, interface_desc.bInterfaceSubClass,
|
||||
interface_desc.bInterfaceProtocol)) {
|
||||
LOG(VERBOSE) << "skipping non-adb interface at " << device_address << " (interface "
|
||||
<< interface_num << ")";
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG(VERBOSE) << "found potential adb interface at " << device_address << " (interface "
|
||||
<< interface_num << ")";
|
||||
|
||||
bool found_in = false;
|
||||
bool found_out = false;
|
||||
for (size_t endpoint_num = 0; endpoint_num < interface_desc.bNumEndpoints; ++endpoint_num) {
|
||||
const auto& endpoint_desc = interface_desc.endpoint[endpoint_num];
|
||||
const uint8_t endpoint_addr = endpoint_desc.bEndpointAddress;
|
||||
const uint8_t endpoint_attr = endpoint_desc.bmAttributes;
|
||||
|
||||
const uint8_t transfer_type = endpoint_attr & LIBUSB_TRANSFER_TYPE_MASK;
|
||||
|
||||
if (transfer_type != LIBUSB_TRANSFER_TYPE_BULK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (endpoint_is_output(endpoint_addr) && !found_out) {
|
||||
found_out = true;
|
||||
bulk_out = endpoint_addr;
|
||||
zero_mask = endpoint_desc.wMaxPacketSize - 1;
|
||||
} else if (!endpoint_is_output(endpoint_addr) && !found_in) {
|
||||
found_in = true;
|
||||
bulk_in = endpoint_addr;
|
||||
}
|
||||
|
||||
size_t endpoint_packet_size = endpoint_desc.wMaxPacketSize;
|
||||
CHECK(endpoint_packet_size != 0);
|
||||
if (packet_size == 0) {
|
||||
packet_size = endpoint_packet_size;
|
||||
} else {
|
||||
CHECK(packet_size == endpoint_packet_size);
|
||||
}
|
||||
}
|
||||
|
||||
if (found_in && found_out) {
|
||||
found_adb = true;
|
||||
break;
|
||||
} else {
|
||||
LOG(VERBOSE) << "rejecting potential adb interface at " << device_address
|
||||
<< "(interface " << interface_num << "): missing bulk endpoints "
|
||||
<< "(found_in = " << found_in << ", found_out = " << found_out << ")";
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_adb) {
|
||||
LOG(VERBOSE) << "skipping device with no adb interfaces at " << device_address;
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(usb_handles_mutex);
|
||||
if (usb_handles.find(device_address) != usb_handles.end()) {
|
||||
LOG(VERBOSE) << "device at " << device_address
|
||||
<< " has already been registered, skipping";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool writable = true;
|
||||
libusb_device_handle* handle_raw = nullptr;
|
||||
rc = libusb_open(device, &handle_raw);
|
||||
unique_device_handle handle(handle_raw);
|
||||
if (rc == 0) {
|
||||
LOG(DEBUG) << "successfully opened adb device at " << device_address << ", "
|
||||
<< StringPrintf("bulk_in = %#x, bulk_out = %#x", bulk_in, bulk_out);
|
||||
|
||||
device_serial.resize(255);
|
||||
rc = libusb_get_string_descriptor_ascii(handle_raw, device_desc.iSerialNumber,
|
||||
reinterpret_cast<unsigned char*>(&device_serial[0]),
|
||||
device_serial.length());
|
||||
if (rc == 0) {
|
||||
LOG(WARNING) << "received empty serial from device at " << device_address;
|
||||
return;
|
||||
} else if (rc < 0) {
|
||||
LOG(WARNING) << "failed to get serial from device at " << device_address
|
||||
<< libusb_error_name(rc);
|
||||
return;
|
||||
}
|
||||
device_serial.resize(rc);
|
||||
|
||||
// WARNING: this isn't released via RAII.
|
||||
rc = libusb_claim_interface(handle.get(), interface_num);
|
||||
if (rc != 0) {
|
||||
LOG(WARNING) << "failed to claim adb interface for device '" << device_serial << "'"
|
||||
<< libusb_error_name(rc);
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint8_t endpoint : {bulk_in, bulk_out}) {
|
||||
rc = libusb_clear_halt(handle.get(), endpoint);
|
||||
if (rc != 0) {
|
||||
LOG(WARNING) << "failed to clear halt on device '" << device_serial
|
||||
<< "' endpoint 0x" << std::hex << endpoint << ": "
|
||||
<< libusb_error_name(rc);
|
||||
libusb_release_interface(handle.get(), interface_num);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG(WARNING) << "failed to open usb device at " << device_address << ": "
|
||||
<< libusb_error_name(rc);
|
||||
writable = false;
|
||||
|
||||
#if defined(__linux__)
|
||||
// libusb doesn't think we should be messing around with devices we don't have
|
||||
// write access to, but Linux at least lets us get the serial number anyway.
|
||||
if (!android::base::ReadFileToString(get_device_serial_path(device), &device_serial)) {
|
||||
// We don't actually want to treat an unknown serial as an error because
|
||||
// devices aren't able to communicate a serial number in early bringup.
|
||||
// http://b/20883914
|
||||
device_serial = "unknown";
|
||||
}
|
||||
device_serial = android::base::Trim(device_serial);
|
||||
#else
|
||||
// On Mac OS and Windows, we're screwed. But I don't think this situation actually
|
||||
// happens on those OSes.
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::unique_ptr<usb_handle> result(new usb_handle(device_address, device_serial,
|
||||
std::move(handle), interface_num, bulk_in,
|
||||
bulk_out, zero_mask, packet_size));
|
||||
usb_handle* usb_handle_raw = result.get();
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(usb_handles_mutex);
|
||||
usb_handles[device_address] = std::move(result);
|
||||
|
||||
register_usb_transport(usb_handle_raw, device_serial.c_str(), device_address.c_str(),
|
||||
writable);
|
||||
}
|
||||
LOG(INFO) << "registered new usb device '" << device_serial << "'";
|
||||
}
|
||||
|
||||
static std::atomic<int> connecting_devices(0);
|
||||
|
||||
static void device_connected(libusb_device* device) {
|
||||
#if defined(__linux__)
|
||||
// Android's host linux libusb uses netlink instead of udev for device hotplug notification,
|
||||
// which means we can get hotplug notifications before udev has updated ownership/perms on the
|
||||
// device. Since we're not going to be able to link against the system's libudev any time soon,
|
||||
// hack around this by inserting a sleep.
|
||||
auto thread = std::thread([device]() {
|
||||
std::string device_path = get_device_dev_path(device);
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
process_device(device);
|
||||
if (--connecting_devices == 0) {
|
||||
adb_notify_device_scan_complete();
|
||||
}
|
||||
});
|
||||
thread.detach();
|
||||
#else
|
||||
process_device(device);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void device_disconnected(libusb_device* device) {
|
||||
std::string device_address = get_device_address(device);
|
||||
|
||||
LOG(INFO) << "device disconnected: " << device_address;
|
||||
std::unique_lock<std::mutex> lock(usb_handles_mutex);
|
||||
auto it = usb_handles.find(device_address);
|
||||
if (it != usb_handles.end()) {
|
||||
if (!it->second->device_handle) {
|
||||
// If the handle is null, we were never able to open the device.
|
||||
|
||||
// Temporarily release the usb handles mutex to avoid deadlock.
|
||||
std::unique_ptr<usb_handle> handle = std::move(it->second);
|
||||
usb_handles.erase(it);
|
||||
lock.unlock();
|
||||
unregister_usb_transport(handle.get());
|
||||
lock.lock();
|
||||
} else {
|
||||
// Closure of the transport will erase the usb_handle.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static auto& hotplug_queue = *new BlockingQueue<std::pair<libusb_hotplug_event, libusb_device*>>();
|
||||
static void hotplug_thread() {
|
||||
adb_thread_setname("libusb hotplug");
|
||||
while (true) {
|
||||
hotplug_queue.PopAll([](std::pair<libusb_hotplug_event, libusb_device*> pair) {
|
||||
libusb_hotplug_event event = pair.first;
|
||||
libusb_device* device = pair.second;
|
||||
if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) {
|
||||
device_connected(device);
|
||||
} else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) {
|
||||
device_disconnected(device);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static LIBUSB_CALL int hotplug_callback(libusb_context*, libusb_device* device,
|
||||
libusb_hotplug_event event, void*) {
|
||||
// We're called with the libusb lock taken. Call these on a separate thread outside of this
|
||||
// function so that the usb_handle mutex is always taken before the libusb mutex.
|
||||
static std::once_flag once;
|
||||
std::call_once(once, []() { std::thread(hotplug_thread).detach(); });
|
||||
|
||||
if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) {
|
||||
++connecting_devices;
|
||||
}
|
||||
hotplug_queue.Push({event, device});
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_init() {
|
||||
LOG(DEBUG) << "initializing libusb...";
|
||||
int rc = libusb_init(nullptr);
|
||||
if (rc != 0) {
|
||||
LOG(FATAL) << "failed to initialize libusb: " << libusb_error_name(rc);
|
||||
}
|
||||
|
||||
// Register the hotplug callback.
|
||||
rc = libusb_hotplug_register_callback(
|
||||
nullptr, static_cast<libusb_hotplug_event>(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED |
|
||||
LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT),
|
||||
LIBUSB_HOTPLUG_ENUMERATE, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY,
|
||||
LIBUSB_CLASS_PER_INTERFACE, hotplug_callback, nullptr, &hotplug_handle);
|
||||
|
||||
if (rc != LIBUSB_SUCCESS) {
|
||||
LOG(FATAL) << "failed to register libusb hotplug callback";
|
||||
}
|
||||
|
||||
// Spawn a thread for libusb_handle_events.
|
||||
std::thread([]() {
|
||||
adb_thread_setname("libusb");
|
||||
while (true) {
|
||||
libusb_handle_events(nullptr);
|
||||
}
|
||||
}).detach();
|
||||
}
|
||||
|
||||
void usb_cleanup() {
|
||||
libusb_hotplug_deregister_callback(nullptr, hotplug_handle);
|
||||
}
|
||||
|
||||
static LIBUSB_CALL void transfer_callback(libusb_transfer* transfer) {
|
||||
transfer_info* info = static_cast<transfer_info*>(transfer->user_data);
|
||||
|
||||
LOG(DEBUG) << info->name << " transfer callback entered";
|
||||
|
||||
// Make sure that the original submitter has made it to the condition_variable wait.
|
||||
std::unique_lock<std::mutex> lock(info->mutex);
|
||||
|
||||
LOG(DEBUG) << info->name << " callback successfully acquired lock";
|
||||
|
||||
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
|
||||
LOG(WARNING) << info->name << " transfer failed: " << libusb_error_name(transfer->status);
|
||||
info->Notify();
|
||||
return;
|
||||
}
|
||||
|
||||
// usb_read() can return when receiving some data.
|
||||
if (info->is_bulk_out && transfer->actual_length != transfer->length) {
|
||||
LOG(DEBUG) << info->name << " transfer incomplete, resubmitting";
|
||||
transfer->length -= transfer->actual_length;
|
||||
transfer->buffer += transfer->actual_length;
|
||||
int rc = libusb_submit_transfer(transfer);
|
||||
if (rc != 0) {
|
||||
LOG(WARNING) << "failed to submit " << info->name
|
||||
<< " transfer: " << libusb_error_name(rc);
|
||||
transfer->status = LIBUSB_TRANSFER_ERROR;
|
||||
info->Notify();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (should_perform_zero_transfer(transfer->endpoint, transfer->length, info->zero_mask)) {
|
||||
LOG(DEBUG) << "submitting zero-length write";
|
||||
transfer->length = 0;
|
||||
int rc = libusb_submit_transfer(transfer);
|
||||
if (rc != 0) {
|
||||
LOG(WARNING) << "failed to submit zero-length write: " << libusb_error_name(rc);
|
||||
transfer->status = LIBUSB_TRANSFER_ERROR;
|
||||
info->Notify();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(VERBOSE) << info->name << "transfer fully complete";
|
||||
info->Notify();
|
||||
}
|
||||
|
||||
// Dispatch a libusb transfer, unlock |device_lock|, and then wait for the result.
|
||||
static int perform_usb_transfer(usb_handle* h, transfer_info* info,
|
||||
std::unique_lock<std::mutex> device_lock) {
|
||||
libusb_transfer* transfer = info->transfer;
|
||||
|
||||
transfer->user_data = info;
|
||||
transfer->callback = transfer_callback;
|
||||
|
||||
LOG(DEBUG) << "locking " << info->name << " transfer_info mutex";
|
||||
std::unique_lock<std::mutex> lock(info->mutex);
|
||||
info->transfer_complete = false;
|
||||
LOG(DEBUG) << "submitting " << info->name << " transfer";
|
||||
int rc = libusb_submit_transfer(transfer);
|
||||
if (rc != 0) {
|
||||
LOG(WARNING) << "failed to submit " << info->name << " transfer: " << libusb_error_name(rc);
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG(DEBUG) << info->name << " transfer successfully submitted";
|
||||
device_lock.unlock();
|
||||
info->cv.wait(lock, [info]() { return info->transfer_complete; });
|
||||
if (transfer->status != 0) {
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_write(usb_handle* h, const void* d, int len) {
|
||||
LOG(DEBUG) << "usb_write of length " << len;
|
||||
|
||||
std::unique_lock<std::mutex> lock(h->device_handle_mutex);
|
||||
if (!h->device_handle) {
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
transfer_info* info = &h->write;
|
||||
info->transfer->dev_handle = h->device_handle;
|
||||
info->transfer->flags = 0;
|
||||
info->transfer->endpoint = h->bulk_out;
|
||||
info->transfer->type = LIBUSB_TRANSFER_TYPE_BULK;
|
||||
info->transfer->length = len;
|
||||
info->transfer->buffer = reinterpret_cast<unsigned char*>(const_cast<void*>(d));
|
||||
info->transfer->num_iso_packets = 0;
|
||||
|
||||
int rc = perform_usb_transfer(h, info, std::move(lock));
|
||||
LOG(DEBUG) << "usb_write(" << len << ") = " << rc;
|
||||
return info->transfer->actual_length;
|
||||
}
|
||||
|
||||
int usb_read(usb_handle* h, void* d, int len) {
|
||||
LOG(DEBUG) << "usb_read of length " << len;
|
||||
|
||||
std::unique_lock<std::mutex> lock(h->device_handle_mutex);
|
||||
if (!h->device_handle) {
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
transfer_info* info = &h->read;
|
||||
info->transfer->dev_handle = h->device_handle;
|
||||
info->transfer->flags = 0;
|
||||
info->transfer->endpoint = h->bulk_in;
|
||||
info->transfer->type = LIBUSB_TRANSFER_TYPE_BULK;
|
||||
info->transfer->length = len;
|
||||
info->transfer->buffer = reinterpret_cast<unsigned char*>(d);
|
||||
info->transfer->num_iso_packets = 0;
|
||||
|
||||
int rc = perform_usb_transfer(h, info, std::move(lock));
|
||||
LOG(DEBUG) << "usb_read(" << len << ") = " << rc << ", actual_length "
|
||||
<< info->transfer->actual_length;
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
return info->transfer->actual_length;
|
||||
}
|
||||
|
||||
int usb_close(usb_handle* h) {
|
||||
std::unique_lock<std::mutex> lock(usb_handles_mutex);
|
||||
auto it = usb_handles.find(h->device_address);
|
||||
if (it == usb_handles.end()) {
|
||||
LOG(FATAL) << "attempted to close unregistered usb_handle for '" << h->serial << "'";
|
||||
}
|
||||
usb_handles.erase(h->device_address);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_reset(usb_handle* h) {
|
||||
libusb_reset_device(h->device_handle);
|
||||
usb_kick(h);
|
||||
}
|
||||
|
||||
void usb_kick(usb_handle* h) {
|
||||
h->Close();
|
||||
}
|
||||
|
||||
size_t usb_get_max_packet_size(usb_handle* h) {
|
||||
CHECK(h->max_packet_size != 0);
|
||||
return h->max_packet_size;
|
||||
}
|
||||
|
||||
} // namespace libusb
|
|
@ -1,632 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
|
||||
#define TRACE_TAG USB
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#include "client/usb.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usbdevice_fs.h>
|
||||
#include <linux/version.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/strings.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "transport.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
using namespace std::literals;
|
||||
|
||||
/* usb scan debugging is waaaay too verbose */
|
||||
#define DBGX(x...)
|
||||
|
||||
namespace native {
|
||||
struct usb_handle : public ::usb_handle {
|
||||
~usb_handle() {
|
||||
if (fd != -1) unix_close(fd);
|
||||
}
|
||||
|
||||
std::string path;
|
||||
int fd = -1;
|
||||
unsigned char ep_in;
|
||||
unsigned char ep_out;
|
||||
|
||||
size_t max_packet_size;
|
||||
unsigned zero_mask;
|
||||
unsigned writeable = 1;
|
||||
|
||||
usbdevfs_urb urb_in;
|
||||
usbdevfs_urb urb_out;
|
||||
|
||||
bool urb_in_busy = false;
|
||||
bool urb_out_busy = false;
|
||||
bool dead = false;
|
||||
|
||||
std::condition_variable cv;
|
||||
std::mutex mutex;
|
||||
|
||||
// for garbage collecting disconnected devices
|
||||
bool mark;
|
||||
|
||||
// ID of thread currently in REAPURB
|
||||
pthread_t reaper_thread = 0;
|
||||
};
|
||||
|
||||
static auto& g_usb_handles_mutex = *new std::mutex();
|
||||
static auto& g_usb_handles = *new std::list<usb_handle*>();
|
||||
|
||||
static int is_known_device(std::string_view dev_name) {
|
||||
std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
|
||||
for (usb_handle* usb : g_usb_handles) {
|
||||
if (usb->path == dev_name) {
|
||||
// set mark flag to indicate this device is still alive
|
||||
usb->mark = true;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kick_disconnected_devices() {
|
||||
std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
|
||||
// kick any devices in the device list that were not found in the device scan
|
||||
for (usb_handle* usb : g_usb_handles) {
|
||||
if (!usb->mark) {
|
||||
usb_kick(usb);
|
||||
} else {
|
||||
usb->mark = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool contains_non_digit(const char* name) {
|
||||
while (*name) {
|
||||
if (!isdigit(*name++)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void find_usb_device(const std::string& base,
|
||||
void (*register_device_callback)(const char*, const char*,
|
||||
unsigned char, unsigned char, int, int,
|
||||
unsigned, size_t)) {
|
||||
std::unique_ptr<DIR, int(*)(DIR*)> bus_dir(opendir(base.c_str()), closedir);
|
||||
if (!bus_dir) return;
|
||||
|
||||
dirent* de;
|
||||
while ((de = readdir(bus_dir.get())) != nullptr) {
|
||||
if (contains_non_digit(de->d_name)) continue;
|
||||
|
||||
std::string bus_name = base + "/" + de->d_name;
|
||||
|
||||
std::unique_ptr<DIR, int(*)(DIR*)> dev_dir(opendir(bus_name.c_str()), closedir);
|
||||
if (!dev_dir) continue;
|
||||
|
||||
while ((de = readdir(dev_dir.get()))) {
|
||||
unsigned char devdesc[4096];
|
||||
unsigned char* bufptr = devdesc;
|
||||
unsigned char* bufend;
|
||||
struct usb_device_descriptor* device;
|
||||
struct usb_config_descriptor* config;
|
||||
struct usb_interface_descriptor* interface;
|
||||
struct usb_endpoint_descriptor *ep1, *ep2;
|
||||
unsigned zero_mask = 0;
|
||||
size_t max_packet_size = 0;
|
||||
unsigned vid, pid;
|
||||
|
||||
if (contains_non_digit(de->d_name)) continue;
|
||||
|
||||
std::string dev_name = bus_name + "/" + de->d_name;
|
||||
if (is_known_device(dev_name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int fd = unix_open(dev_name, O_RDONLY | O_CLOEXEC);
|
||||
if (fd == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t desclength = unix_read(fd, devdesc, sizeof(devdesc));
|
||||
bufend = bufptr + desclength;
|
||||
|
||||
// should have device and configuration descriptors, and atleast two endpoints
|
||||
if (desclength < USB_DT_DEVICE_SIZE + USB_DT_CONFIG_SIZE) {
|
||||
D("desclength %zu is too small", desclength);
|
||||
unix_close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
device = (struct usb_device_descriptor*)bufptr;
|
||||
bufptr += USB_DT_DEVICE_SIZE;
|
||||
|
||||
if((device->bLength != USB_DT_DEVICE_SIZE) || (device->bDescriptorType != USB_DT_DEVICE)) {
|
||||
unix_close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
vid = device->idVendor;
|
||||
pid = device->idProduct;
|
||||
DBGX("[ %s is V:%04x P:%04x ]\n", dev_name.c_str(), vid, pid);
|
||||
|
||||
// should have config descriptor next
|
||||
config = (struct usb_config_descriptor *)bufptr;
|
||||
bufptr += USB_DT_CONFIG_SIZE;
|
||||
if (config->bLength != USB_DT_CONFIG_SIZE || config->bDescriptorType != USB_DT_CONFIG) {
|
||||
D("usb_config_descriptor not found");
|
||||
unix_close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
// loop through all the descriptors and look for the ADB interface
|
||||
while (bufptr < bufend) {
|
||||
unsigned char length = bufptr[0];
|
||||
unsigned char type = bufptr[1];
|
||||
|
||||
if (type == USB_DT_INTERFACE) {
|
||||
interface = (struct usb_interface_descriptor *)bufptr;
|
||||
bufptr += length;
|
||||
|
||||
if (length != USB_DT_INTERFACE_SIZE) {
|
||||
D("interface descriptor has wrong size");
|
||||
break;
|
||||
}
|
||||
|
||||
DBGX("bInterfaceClass: %d, bInterfaceSubClass: %d,"
|
||||
"bInterfaceProtocol: %d, bNumEndpoints: %d\n",
|
||||
interface->bInterfaceClass, interface->bInterfaceSubClass,
|
||||
interface->bInterfaceProtocol, interface->bNumEndpoints);
|
||||
|
||||
if (interface->bNumEndpoints == 2 &&
|
||||
is_adb_interface(interface->bInterfaceClass, interface->bInterfaceSubClass,
|
||||
interface->bInterfaceProtocol)) {
|
||||
struct stat st;
|
||||
char pathbuf[128];
|
||||
char link[256];
|
||||
char *devpath = nullptr;
|
||||
|
||||
DBGX("looking for bulk endpoints\n");
|
||||
// looks like ADB...
|
||||
ep1 = (struct usb_endpoint_descriptor *)bufptr;
|
||||
bufptr += USB_DT_ENDPOINT_SIZE;
|
||||
// For USB 3.0 SuperSpeed devices, skip potential
|
||||
// USB 3.0 SuperSpeed Endpoint Companion descriptor
|
||||
if (bufptr+2 <= devdesc + desclength &&
|
||||
bufptr[0] == USB_DT_SS_EP_COMP_SIZE &&
|
||||
bufptr[1] == USB_DT_SS_ENDPOINT_COMP) {
|
||||
bufptr += USB_DT_SS_EP_COMP_SIZE;
|
||||
}
|
||||
ep2 = (struct usb_endpoint_descriptor *)bufptr;
|
||||
bufptr += USB_DT_ENDPOINT_SIZE;
|
||||
if (bufptr+2 <= devdesc + desclength &&
|
||||
bufptr[0] == USB_DT_SS_EP_COMP_SIZE &&
|
||||
bufptr[1] == USB_DT_SS_ENDPOINT_COMP) {
|
||||
bufptr += USB_DT_SS_EP_COMP_SIZE;
|
||||
}
|
||||
|
||||
if (bufptr > devdesc + desclength ||
|
||||
ep1->bLength != USB_DT_ENDPOINT_SIZE ||
|
||||
ep1->bDescriptorType != USB_DT_ENDPOINT ||
|
||||
ep2->bLength != USB_DT_ENDPOINT_SIZE ||
|
||||
ep2->bDescriptorType != USB_DT_ENDPOINT) {
|
||||
D("endpoints not found");
|
||||
break;
|
||||
}
|
||||
|
||||
// both endpoints should be bulk
|
||||
if (ep1->bmAttributes != USB_ENDPOINT_XFER_BULK ||
|
||||
ep2->bmAttributes != USB_ENDPOINT_XFER_BULK) {
|
||||
D("bulk endpoints not found");
|
||||
continue;
|
||||
}
|
||||
/* aproto 01 needs 0 termination */
|
||||
if (interface->bInterfaceProtocol == ADB_PROTOCOL) {
|
||||
max_packet_size = ep1->wMaxPacketSize;
|
||||
zero_mask = ep1->wMaxPacketSize - 1;
|
||||
}
|
||||
|
||||
// we have a match. now we just need to figure out which is in and which is out.
|
||||
unsigned char local_ep_in, local_ep_out;
|
||||
if (ep1->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
|
||||
local_ep_in = ep1->bEndpointAddress;
|
||||
local_ep_out = ep2->bEndpointAddress;
|
||||
} else {
|
||||
local_ep_in = ep2->bEndpointAddress;
|
||||
local_ep_out = ep1->bEndpointAddress;
|
||||
}
|
||||
|
||||
// Determine the device path
|
||||
if (!fstat(fd, &st) && S_ISCHR(st.st_mode)) {
|
||||
snprintf(pathbuf, sizeof(pathbuf), "/sys/dev/char/%d:%d",
|
||||
major(st.st_rdev), minor(st.st_rdev));
|
||||
ssize_t link_len = readlink(pathbuf, link, sizeof(link) - 1);
|
||||
if (link_len > 0) {
|
||||
link[link_len] = '\0';
|
||||
const char* slash = strrchr(link, '/');
|
||||
if (slash) {
|
||||
snprintf(pathbuf, sizeof(pathbuf),
|
||||
"usb:%s", slash + 1);
|
||||
devpath = pathbuf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
register_device_callback(dev_name.c_str(), devpath, local_ep_in,
|
||||
local_ep_out, interface->bInterfaceNumber,
|
||||
device->iSerialNumber, zero_mask, max_packet_size);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
bufptr += length;
|
||||
}
|
||||
} // end of while
|
||||
|
||||
unix_close(fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int usb_bulk_write(usb_handle* h, const void* data, int len) {
|
||||
std::unique_lock<std::mutex> lock(h->mutex);
|
||||
D("++ usb_bulk_write ++");
|
||||
|
||||
usbdevfs_urb* urb = &h->urb_out;
|
||||
memset(urb, 0, sizeof(*urb));
|
||||
urb->type = USBDEVFS_URB_TYPE_BULK;
|
||||
urb->endpoint = h->ep_out;
|
||||
urb->status = -1;
|
||||
urb->buffer = const_cast<void*>(data);
|
||||
urb->buffer_length = len;
|
||||
|
||||
if (h->dead) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (TEMP_FAILURE_RETRY(ioctl(h->fd, USBDEVFS_SUBMITURB, urb)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
h->urb_out_busy = true;
|
||||
while (true) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
if (h->cv.wait_until(lock, now + 5s) == std::cv_status::timeout || h->dead) {
|
||||
// TODO: call USBDEVFS_DISCARDURB?
|
||||
errno = ETIMEDOUT;
|
||||
return -1;
|
||||
}
|
||||
if (!h->urb_out_busy) {
|
||||
if (urb->status != 0) {
|
||||
errno = -urb->status;
|
||||
return -1;
|
||||
}
|
||||
return urb->actual_length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int usb_bulk_read(usb_handle* h, void* data, int len) {
|
||||
std::unique_lock<std::mutex> lock(h->mutex);
|
||||
D("++ usb_bulk_read ++");
|
||||
|
||||
usbdevfs_urb* urb = &h->urb_in;
|
||||
memset(urb, 0, sizeof(*urb));
|
||||
urb->type = USBDEVFS_URB_TYPE_BULK;
|
||||
urb->endpoint = h->ep_in;
|
||||
urb->status = -1;
|
||||
urb->buffer = data;
|
||||
urb->buffer_length = len;
|
||||
|
||||
if (h->dead) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (TEMP_FAILURE_RETRY(ioctl(h->fd, USBDEVFS_SUBMITURB, urb)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
h->urb_in_busy = true;
|
||||
while (true) {
|
||||
D("[ reap urb - wait ]");
|
||||
h->reaper_thread = pthread_self();
|
||||
int fd = h->fd;
|
||||
lock.unlock();
|
||||
|
||||
// This ioctl must not have TEMP_FAILURE_RETRY because we send SIGALRM to break out.
|
||||
usbdevfs_urb* out = nullptr;
|
||||
int res = ioctl(fd, USBDEVFS_REAPURB, &out);
|
||||
int saved_errno = errno;
|
||||
|
||||
lock.lock();
|
||||
h->reaper_thread = 0;
|
||||
if (h->dead) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (res < 0) {
|
||||
if (saved_errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
D("[ reap urb - error ]");
|
||||
errno = saved_errno;
|
||||
return -1;
|
||||
}
|
||||
D("[ urb @%p status = %d, actual = %d ]", out, out->status, out->actual_length);
|
||||
|
||||
if (out == &h->urb_in) {
|
||||
D("[ reap urb - IN complete ]");
|
||||
h->urb_in_busy = false;
|
||||
if (urb->status != 0) {
|
||||
errno = -urb->status;
|
||||
return -1;
|
||||
}
|
||||
return urb->actual_length;
|
||||
}
|
||||
if (out == &h->urb_out) {
|
||||
D("[ reap urb - OUT compelete ]");
|
||||
h->urb_out_busy = false;
|
||||
h->cv.notify_all();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int usb_write_split(usb_handle* h, unsigned char* data, int len) {
|
||||
for (int i = 0; i < len; i += 16384) {
|
||||
int chunk_size = (i + 16384 > len) ? len - i : 16384;
|
||||
int n = usb_bulk_write(h, data + i, chunk_size);
|
||||
if (n != chunk_size) {
|
||||
D("ERROR: n = %d, errno = %d (%s)", n, errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int usb_write(usb_handle* h, const void* _data, int len) {
|
||||
D("++ usb_write ++");
|
||||
|
||||
unsigned char* data = (unsigned char*)_data;
|
||||
|
||||
// The kernel will attempt to allocate a contiguous buffer for each write we submit.
|
||||
// This might fail due to heap fragmentation, so attempt a contiguous write once, and if that
|
||||
// fails, retry after having split the data into 16kB chunks to avoid allocation failure.
|
||||
int n = usb_bulk_write(h, data, len);
|
||||
if (n == -1 && errno == ENOMEM) {
|
||||
n = usb_write_split(h, data, len);
|
||||
}
|
||||
|
||||
if (n == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (h->zero_mask && !(len & h->zero_mask)) {
|
||||
// If we need 0-markers and our transfer is an even multiple of the packet size,
|
||||
// then send a zero marker.
|
||||
return usb_bulk_write(h, _data, 0) == 0 ? len : -1;
|
||||
}
|
||||
|
||||
D("-- usb_write --");
|
||||
return len;
|
||||
}
|
||||
|
||||
int usb_read(usb_handle *h, void *_data, int len)
|
||||
{
|
||||
unsigned char *data = (unsigned char*) _data;
|
||||
int n;
|
||||
|
||||
D("++ usb_read ++");
|
||||
int orig_len = len;
|
||||
while (len == orig_len) {
|
||||
int xfer = len;
|
||||
|
||||
D("[ usb read %d fd = %d], path=%s", xfer, h->fd, h->path.c_str());
|
||||
n = usb_bulk_read(h, data, xfer);
|
||||
D("[ usb read %d ] = %d, path=%s", xfer, n, h->path.c_str());
|
||||
if (n <= 0) {
|
||||
if((errno == ETIMEDOUT) && (h->fd != -1)) {
|
||||
D("[ timeout ]");
|
||||
continue;
|
||||
}
|
||||
D("ERROR: n = %d, errno = %d (%s)",
|
||||
n, errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
len -= n;
|
||||
data += n;
|
||||
}
|
||||
|
||||
D("-- usb_read --");
|
||||
return orig_len - len;
|
||||
}
|
||||
|
||||
void usb_reset(usb_handle* h) {
|
||||
ioctl(h->fd, USBDEVFS_RESET);
|
||||
usb_kick(h);
|
||||
}
|
||||
|
||||
void usb_kick(usb_handle* h) {
|
||||
std::lock_guard<std::mutex> lock(h->mutex);
|
||||
D("[ kicking %p (fd = %d) ]", h, h->fd);
|
||||
if (!h->dead) {
|
||||
h->dead = true;
|
||||
|
||||
if (h->writeable) {
|
||||
/* HACK ALERT!
|
||||
** Sometimes we get stuck in ioctl(USBDEVFS_REAPURB).
|
||||
** This is a workaround for that problem.
|
||||
*/
|
||||
if (h->reaper_thread) {
|
||||
pthread_kill(h->reaper_thread, SIGALRM);
|
||||
}
|
||||
|
||||
/* cancel any pending transactions
|
||||
** these will quietly fail if the txns are not active,
|
||||
** but this ensures that a reader blocked on REAPURB
|
||||
** will get unblocked
|
||||
*/
|
||||
ioctl(h->fd, USBDEVFS_DISCARDURB, &h->urb_in);
|
||||
ioctl(h->fd, USBDEVFS_DISCARDURB, &h->urb_out);
|
||||
h->urb_in.status = -ENODEV;
|
||||
h->urb_out.status = -ENODEV;
|
||||
h->urb_in_busy = false;
|
||||
h->urb_out_busy = false;
|
||||
h->cv.notify_all();
|
||||
} else {
|
||||
unregister_usb_transport(h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int usb_close(usb_handle* h) {
|
||||
std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
|
||||
g_usb_handles.remove(h);
|
||||
|
||||
D("-- usb close %p (fd = %d) --", h, h->fd);
|
||||
|
||||
delete h;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t usb_get_max_packet_size(usb_handle* h) {
|
||||
return h->max_packet_size;
|
||||
}
|
||||
|
||||
static void register_device(const char* dev_name, const char* dev_path, unsigned char ep_in,
|
||||
unsigned char ep_out, int interface, int serial_index,
|
||||
unsigned zero_mask, size_t max_packet_size) {
|
||||
// Since Linux will not reassign the device ID (and dev_name) as long as the
|
||||
// device is open, we can add to the list here once we open it and remove
|
||||
// from the list when we're finally closed and everything will work out
|
||||
// fine.
|
||||
//
|
||||
// If we have a usb_handle on the list of handles with a matching name, we
|
||||
// have no further work to do.
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
|
||||
for (usb_handle* usb: g_usb_handles) {
|
||||
if (usb->path == dev_name) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
D("[ usb located new device %s (%d/%d/%d) ]", dev_name, ep_in, ep_out, interface);
|
||||
std::unique_ptr<usb_handle> usb(new usb_handle);
|
||||
usb->path = dev_name;
|
||||
usb->ep_in = ep_in;
|
||||
usb->ep_out = ep_out;
|
||||
usb->zero_mask = zero_mask;
|
||||
usb->max_packet_size = max_packet_size;
|
||||
|
||||
// Initialize mark so we don't get garbage collected after the device scan.
|
||||
usb->mark = true;
|
||||
|
||||
usb->fd = unix_open(usb->path, O_RDWR | O_CLOEXEC);
|
||||
if (usb->fd == -1) {
|
||||
// Opening RW failed, so see if we have RO access.
|
||||
usb->fd = unix_open(usb->path, O_RDONLY | O_CLOEXEC);
|
||||
if (usb->fd == -1) {
|
||||
D("[ usb open %s failed: %s]", usb->path.c_str(), strerror(errno));
|
||||
return;
|
||||
}
|
||||
usb->writeable = 0;
|
||||
}
|
||||
|
||||
D("[ usb opened %s%s, fd=%d]",
|
||||
usb->path.c_str(), (usb->writeable ? "" : " (read-only)"), usb->fd);
|
||||
|
||||
if (usb->writeable) {
|
||||
if (ioctl(usb->fd, USBDEVFS_CLAIMINTERFACE, &interface) != 0) {
|
||||
D("[ usb ioctl(%d, USBDEVFS_CLAIMINTERFACE) failed: %s]", usb->fd, strerror(errno));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Read the device's serial number.
|
||||
std::string serial_path = android::base::StringPrintf(
|
||||
"/sys/bus/usb/devices/%s/serial", dev_path + 4);
|
||||
std::string serial;
|
||||
if (!android::base::ReadFileToString(serial_path, &serial)) {
|
||||
D("[ usb read %s failed: %s ]", serial_path.c_str(), strerror(errno));
|
||||
// We don't actually want to treat an unknown serial as an error because
|
||||
// devices aren't able to communicate a serial number in early bringup.
|
||||
// http://b/20883914
|
||||
serial = "";
|
||||
}
|
||||
serial = android::base::Trim(serial);
|
||||
|
||||
// Add to the end of the active handles.
|
||||
usb_handle* done_usb = usb.release();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
|
||||
g_usb_handles.push_back(done_usb);
|
||||
}
|
||||
register_usb_transport(done_usb, serial.c_str(), dev_path, done_usb->writeable);
|
||||
}
|
||||
|
||||
static void device_poll_thread() {
|
||||
adb_thread_setname("device poll");
|
||||
D("Created device thread");
|
||||
while (true) {
|
||||
// TODO: Use inotify.
|
||||
find_usb_device("/dev/bus/usb", register_device);
|
||||
adb_notify_device_scan_complete();
|
||||
kick_disconnected_devices();
|
||||
std::this_thread::sleep_for(1s);
|
||||
}
|
||||
}
|
||||
|
||||
void usb_init() {
|
||||
struct sigaction actions;
|
||||
memset(&actions, 0, sizeof(actions));
|
||||
sigemptyset(&actions.sa_mask);
|
||||
actions.sa_flags = 0;
|
||||
actions.sa_handler = [](int) {};
|
||||
sigaction(SIGALRM, &actions, nullptr);
|
||||
|
||||
std::thread(device_poll_thread).detach();
|
||||
}
|
||||
|
||||
void usb_cleanup() {}
|
||||
|
||||
} // namespace native
|
|
@ -1,592 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
|
||||
#define TRACE_TAG USB
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#include "client/usb.h"
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include <IOKit/IOCFPlugIn.h>
|
||||
#include <IOKit/usb/IOUSBLib.h>
|
||||
#include <IOKit/IOMessage.h>
|
||||
#include <mach/mach_port.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/thread_annotations.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "transport.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace native {
|
||||
struct usb_handle
|
||||
{
|
||||
UInt8 bulkIn;
|
||||
UInt8 bulkOut;
|
||||
IOUSBInterfaceInterface550** interface;
|
||||
unsigned int zero_mask;
|
||||
size_t max_packet_size;
|
||||
|
||||
// For garbage collecting disconnected devices.
|
||||
bool mark;
|
||||
std::string devpath;
|
||||
std::atomic<bool> dead;
|
||||
|
||||
usb_handle()
|
||||
: bulkIn(0),
|
||||
bulkOut(0),
|
||||
interface(nullptr),
|
||||
zero_mask(0),
|
||||
max_packet_size(0),
|
||||
mark(false),
|
||||
dead(false) {}
|
||||
};
|
||||
|
||||
static std::atomic<bool> usb_inited_flag;
|
||||
|
||||
static auto& g_usb_handles_mutex = *new std::mutex();
|
||||
static auto& g_usb_handles = *new std::vector<std::unique_ptr<usb_handle>>();
|
||||
|
||||
static bool IsKnownDevice(const std::string& devpath) {
|
||||
std::lock_guard<std::mutex> lock_guard(g_usb_handles_mutex);
|
||||
for (auto& usb : g_usb_handles) {
|
||||
if (usb->devpath == devpath) {
|
||||
// Set mark flag to indicate this device is still alive.
|
||||
usb->mark = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void usb_kick_locked(usb_handle* handle);
|
||||
|
||||
static void KickDisconnectedDevices() {
|
||||
std::lock_guard<std::mutex> lock_guard(g_usb_handles_mutex);
|
||||
for (auto& usb : g_usb_handles) {
|
||||
if (!usb->mark) {
|
||||
usb_kick_locked(usb.get());
|
||||
} else {
|
||||
usb->mark = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void AddDevice(std::unique_ptr<usb_handle> handle) {
|
||||
handle->mark = true;
|
||||
std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
|
||||
g_usb_handles.push_back(std::move(handle));
|
||||
}
|
||||
|
||||
static void AndroidInterfaceAdded(io_iterator_t iterator);
|
||||
static std::unique_ptr<usb_handle> CheckInterface(IOUSBInterfaceInterface550** iface, UInt16 vendor,
|
||||
UInt16 product);
|
||||
|
||||
static bool FindUSBDevices() {
|
||||
// Create the matching dictionary to find the Android device's adb interface.
|
||||
CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBInterfaceClassName);
|
||||
if (!matchingDict) {
|
||||
LOG(ERROR) << "couldn't create USB matching dictionary";
|
||||
return false;
|
||||
}
|
||||
// Create an iterator for all I/O Registry objects that match the dictionary.
|
||||
io_iterator_t iter = 0;
|
||||
kern_return_t kr = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &iter);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
LOG(ERROR) << "failed to get matching services";
|
||||
return false;
|
||||
}
|
||||
// Iterate over all matching objects.
|
||||
AndroidInterfaceAdded(iter);
|
||||
IOObjectRelease(iter);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
AndroidInterfaceAdded(io_iterator_t iterator)
|
||||
{
|
||||
kern_return_t kr;
|
||||
io_service_t usbDevice;
|
||||
io_service_t usbInterface;
|
||||
IOCFPlugInInterface **plugInInterface = NULL;
|
||||
IOUSBInterfaceInterface500 **iface = NULL;
|
||||
IOUSBDeviceInterface500 **dev = NULL;
|
||||
HRESULT result;
|
||||
SInt32 score;
|
||||
uint32_t locationId;
|
||||
UInt8 if_class, subclass, protocol;
|
||||
UInt16 vendor;
|
||||
UInt16 product;
|
||||
UInt8 serialIndex;
|
||||
char serial[256];
|
||||
std::string devpath;
|
||||
|
||||
while ((usbInterface = IOIteratorNext(iterator))) {
|
||||
//* Create an intermediate interface plugin
|
||||
kr = IOCreatePlugInInterfaceForService(usbInterface,
|
||||
kIOUSBInterfaceUserClientTypeID,
|
||||
kIOCFPlugInInterfaceID,
|
||||
&plugInInterface, &score);
|
||||
IOObjectRelease(usbInterface);
|
||||
if ((kIOReturnSuccess != kr) || (!plugInInterface)) {
|
||||
LOG(ERROR) << "Unable to create an interface plug-in (" << std::hex << kr << ")";
|
||||
continue;
|
||||
}
|
||||
|
||||
//* This gets us the interface object
|
||||
result = (*plugInInterface)->QueryInterface(
|
||||
plugInInterface,
|
||||
CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID500), (LPVOID*)&iface);
|
||||
//* We only needed the plugin to get the interface, so discard it
|
||||
(*plugInInterface)->Release(plugInInterface);
|
||||
if (result || !iface) {
|
||||
LOG(ERROR) << "Couldn't query the interface (" << std::hex << result << ")";
|
||||
continue;
|
||||
}
|
||||
|
||||
kr = (*iface)->GetInterfaceClass(iface, &if_class);
|
||||
kr = (*iface)->GetInterfaceSubClass(iface, &subclass);
|
||||
kr = (*iface)->GetInterfaceProtocol(iface, &protocol);
|
||||
if (!is_adb_interface(if_class, subclass, protocol)) {
|
||||
// Ignore non-ADB devices.
|
||||
LOG(DEBUG) << "Ignoring interface with incorrect class/subclass/protocol - " << if_class
|
||||
<< ", " << subclass << ", " << protocol;
|
||||
(*iface)->Release(iface);
|
||||
continue;
|
||||
}
|
||||
|
||||
//* this gets us an ioservice, with which we will find the actual
|
||||
//* device; after getting a plugin, and querying the interface, of
|
||||
//* course.
|
||||
//* Gotta love OS X
|
||||
kr = (*iface)->GetDevice(iface, &usbDevice);
|
||||
if (kIOReturnSuccess != kr || !usbDevice) {
|
||||
LOG(ERROR) << "Couldn't grab device from interface (" << std::hex << kr << ")";
|
||||
(*iface)->Release(iface);
|
||||
continue;
|
||||
}
|
||||
|
||||
plugInInterface = NULL;
|
||||
score = 0;
|
||||
//* create an intermediate device plugin
|
||||
kr = IOCreatePlugInInterfaceForService(usbDevice,
|
||||
kIOUSBDeviceUserClientTypeID,
|
||||
kIOCFPlugInInterfaceID,
|
||||
&plugInInterface, &score);
|
||||
//* only needed this to find the plugin
|
||||
(void)IOObjectRelease(usbDevice);
|
||||
if ((kIOReturnSuccess != kr) || (!plugInInterface)) {
|
||||
LOG(ERROR) << "Unable to create a device plug-in (" << std::hex << kr << ")";
|
||||
(*iface)->Release(iface);
|
||||
continue;
|
||||
}
|
||||
|
||||
result = (*plugInInterface)->QueryInterface(plugInInterface,
|
||||
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID500), (LPVOID*)&dev);
|
||||
//* only needed this to query the plugin
|
||||
(*plugInInterface)->Release(plugInInterface);
|
||||
if (result || !dev) {
|
||||
LOG(ERROR) << "Couldn't create a device interface (" << std::hex << result << ")";
|
||||
(*iface)->Release(iface);
|
||||
continue;
|
||||
}
|
||||
|
||||
//* Now after all that, we actually have a ref to the device and
|
||||
//* the interface that matched our criteria
|
||||
kr = (*dev)->GetDeviceVendor(dev, &vendor);
|
||||
kr = (*dev)->GetDeviceProduct(dev, &product);
|
||||
kr = (*dev)->GetLocationID(dev, &locationId);
|
||||
if (kr == KERN_SUCCESS) {
|
||||
devpath = android::base::StringPrintf("usb:%" PRIu32 "X", locationId);
|
||||
if (IsKnownDevice(devpath)) {
|
||||
(*dev)->Release(dev);
|
||||
(*iface)->Release(iface);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex);
|
||||
|
||||
if (serialIndex > 0) {
|
||||
IOUSBDevRequest req;
|
||||
UInt16 buffer[256];
|
||||
UInt16 languages[128];
|
||||
|
||||
memset(languages, 0, sizeof(languages));
|
||||
|
||||
req.bmRequestType =
|
||||
USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
|
||||
req.bRequest = kUSBRqGetDescriptor;
|
||||
req.wValue = (kUSBStringDesc << 8) | 0;
|
||||
req.wIndex = 0;
|
||||
req.pData = languages;
|
||||
req.wLength = sizeof(languages);
|
||||
kr = (*dev)->DeviceRequest(dev, &req);
|
||||
|
||||
if (kr == kIOReturnSuccess && req.wLenDone > 0) {
|
||||
|
||||
int langCount = (req.wLenDone - 2) / 2, lang;
|
||||
|
||||
for (lang = 1; lang <= langCount; lang++) {
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
memset(&req, 0, sizeof(req));
|
||||
|
||||
req.bmRequestType =
|
||||
USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
|
||||
req.bRequest = kUSBRqGetDescriptor;
|
||||
req.wValue = (kUSBStringDesc << 8) | serialIndex;
|
||||
req.wIndex = languages[lang];
|
||||
req.pData = buffer;
|
||||
req.wLength = sizeof(buffer);
|
||||
kr = (*dev)->DeviceRequest(dev, &req);
|
||||
|
||||
if (kr == kIOReturnSuccess && req.wLenDone > 0) {
|
||||
int i, count;
|
||||
|
||||
// skip first word, and copy the rest to the serial string,
|
||||
// changing shorts to bytes.
|
||||
count = (req.wLenDone - 1) / 2;
|
||||
for (i = 0; i < count; i++)
|
||||
serial[i] = buffer[i + 1];
|
||||
serial[i] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(*dev)->Release(dev);
|
||||
|
||||
VLOG(USB) << android::base::StringPrintf("Found vid=%04x pid=%04x serial=%s\n",
|
||||
vendor, product, serial);
|
||||
if (devpath.empty()) {
|
||||
devpath = serial;
|
||||
}
|
||||
if (IsKnownDevice(devpath)) {
|
||||
(*iface)->USBInterfaceClose(iface);
|
||||
(*iface)->Release(iface);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::unique_ptr<usb_handle> handle =
|
||||
CheckInterface((IOUSBInterfaceInterface550**)iface, vendor, product);
|
||||
if (handle == nullptr) {
|
||||
LOG(ERROR) << "Could not find device interface";
|
||||
(*iface)->Release(iface);
|
||||
continue;
|
||||
}
|
||||
handle->devpath = devpath;
|
||||
usb_handle* handle_p = handle.get();
|
||||
VLOG(USB) << "Add usb device " << serial;
|
||||
LOG(INFO) << "reported max packet size for " << serial << " is " << handle->max_packet_size;
|
||||
AddDevice(std::move(handle));
|
||||
register_usb_transport(reinterpret_cast<::usb_handle*>(handle_p), serial, devpath.c_str(),
|
||||
1);
|
||||
}
|
||||
}
|
||||
|
||||
// Used to clear both the endpoints before starting.
|
||||
// When adb quits, we might clear the host endpoint but not the device.
|
||||
// So we make sure both sides are clear before starting up.
|
||||
static bool ClearPipeStallBothEnds(IOUSBInterfaceInterface550** interface, UInt8 bulkEp) {
|
||||
IOReturn rc = (*interface)->ClearPipeStallBothEnds(interface, bulkEp);
|
||||
if (rc != kIOReturnSuccess) {
|
||||
LOG(ERROR) << "Could not clear pipe stall both ends: " << std::hex << rc;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//* TODO: simplify this further since we only register to get ADB interface
|
||||
//* subclass+protocol events
|
||||
static std::unique_ptr<usb_handle> CheckInterface(IOUSBInterfaceInterface550** interface,
|
||||
UInt16 vendor, UInt16 product) {
|
||||
std::unique_ptr<usb_handle> handle;
|
||||
IOReturn kr;
|
||||
UInt8 interfaceNumEndpoints, interfaceClass, interfaceSubClass, interfaceProtocol;
|
||||
UInt8 endpoint;
|
||||
|
||||
//* Now open the interface. This will cause the pipes associated with
|
||||
//* the endpoints in the interface descriptor to be instantiated
|
||||
kr = (*interface)->USBInterfaceOpen(interface);
|
||||
if (kr != kIOReturnSuccess) {
|
||||
LOG(ERROR) << "Could not open interface: " << std::hex << kr;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//* Get the number of endpoints associated with this interface
|
||||
kr = (*interface)->GetNumEndpoints(interface, &interfaceNumEndpoints);
|
||||
if (kr != kIOReturnSuccess) {
|
||||
LOG(ERROR) << "Unable to get number of endpoints: " << std::hex << kr;
|
||||
goto err_get_num_ep;
|
||||
}
|
||||
|
||||
//* Get interface class, subclass and protocol
|
||||
if ((*interface)->GetInterfaceClass(interface, &interfaceClass) != kIOReturnSuccess ||
|
||||
(*interface)->GetInterfaceSubClass(interface, &interfaceSubClass) != kIOReturnSuccess ||
|
||||
(*interface)->GetInterfaceProtocol(interface, &interfaceProtocol) != kIOReturnSuccess) {
|
||||
LOG(ERROR) << "Unable to get interface class, subclass and protocol";
|
||||
goto err_get_interface_class;
|
||||
}
|
||||
|
||||
//* check to make sure interface class, subclass and protocol match ADB
|
||||
//* avoid opening mass storage endpoints
|
||||
if (!is_adb_interface(interfaceClass, interfaceSubClass, interfaceProtocol)) {
|
||||
goto err_bad_adb_interface;
|
||||
}
|
||||
|
||||
handle.reset(new usb_handle);
|
||||
if (handle == nullptr) {
|
||||
goto err_bad_adb_interface;
|
||||
}
|
||||
|
||||
//* Iterate over the endpoints for this interface and find the first
|
||||
//* bulk in/out pipes available. These will be our read/write pipes.
|
||||
for (endpoint = 1; endpoint <= interfaceNumEndpoints; endpoint++) {
|
||||
UInt8 transferType;
|
||||
UInt16 maxPacketSize;
|
||||
UInt8 interval;
|
||||
UInt8 number;
|
||||
UInt8 direction;
|
||||
UInt8 maxBurst;
|
||||
UInt8 mult;
|
||||
UInt16 bytesPerInterval;
|
||||
|
||||
kr = (*interface)
|
||||
->GetPipePropertiesV2(interface, endpoint, &direction, &number, &transferType,
|
||||
&maxPacketSize, &interval, &maxBurst, &mult,
|
||||
&bytesPerInterval);
|
||||
if (kr != kIOReturnSuccess) {
|
||||
LOG(ERROR) << "FindDeviceInterface - could not get pipe properties: "
|
||||
<< std::hex << kr;
|
||||
goto err_get_pipe_props;
|
||||
}
|
||||
|
||||
if (kUSBBulk != transferType) continue;
|
||||
|
||||
if (kUSBIn == direction) {
|
||||
handle->bulkIn = endpoint;
|
||||
if (!ClearPipeStallBothEnds(interface, handle->bulkIn)) goto err_get_pipe_props;
|
||||
}
|
||||
|
||||
if (kUSBOut == direction) {
|
||||
handle->bulkOut = endpoint;
|
||||
if (!ClearPipeStallBothEnds(interface, handle->bulkOut)) goto err_get_pipe_props;
|
||||
}
|
||||
|
||||
if (maxBurst != 0)
|
||||
// bMaxBurst is the number of additional packets in the burst.
|
||||
maxPacketSize /= (maxBurst + 1);
|
||||
|
||||
// mult is only relevant for isochronous endpoints.
|
||||
CHECK_EQ(0, mult);
|
||||
|
||||
handle->zero_mask = maxPacketSize - 1;
|
||||
handle->max_packet_size = maxPacketSize;
|
||||
}
|
||||
|
||||
handle->interface = interface;
|
||||
return handle;
|
||||
|
||||
err_get_pipe_props:
|
||||
err_bad_adb_interface:
|
||||
err_get_interface_class:
|
||||
err_get_num_ep:
|
||||
(*interface)->USBInterfaceClose(interface);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::mutex& operate_device_lock = *new std::mutex();
|
||||
|
||||
static void RunLoopThread() {
|
||||
adb_thread_setname("RunLoop");
|
||||
|
||||
VLOG(USB) << "RunLoopThread started";
|
||||
while (true) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock_guard(operate_device_lock);
|
||||
FindUSBDevices();
|
||||
KickDisconnectedDevices();
|
||||
}
|
||||
// Signal the parent that we are running
|
||||
usb_inited_flag = true;
|
||||
std::this_thread::sleep_for(1s);
|
||||
}
|
||||
VLOG(USB) << "RunLoopThread done";
|
||||
}
|
||||
|
||||
void usb_cleanup() NO_THREAD_SAFETY_ANALYSIS {
|
||||
VLOG(USB) << "usb_cleanup";
|
||||
// Wait until usb operations in RunLoopThread finish, and prevent further operations.
|
||||
operate_device_lock.lock();
|
||||
close_usb_devices();
|
||||
}
|
||||
|
||||
void usb_init() {
|
||||
static bool initialized = false;
|
||||
if (!initialized) {
|
||||
usb_inited_flag = false;
|
||||
|
||||
std::thread(RunLoopThread).detach();
|
||||
|
||||
// Wait for initialization to finish
|
||||
while (!usb_inited_flag) {
|
||||
std::this_thread::sleep_for(100ms);
|
||||
}
|
||||
|
||||
adb_notify_device_scan_complete();
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
int usb_write(usb_handle *handle, const void *buf, int len)
|
||||
{
|
||||
IOReturn result;
|
||||
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
if (!handle || handle->dead)
|
||||
return -1;
|
||||
|
||||
if (NULL == handle->interface) {
|
||||
LOG(ERROR) << "usb_write interface was null";
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (0 == handle->bulkOut) {
|
||||
LOG(ERROR) << "bulkOut endpoint not assigned";
|
||||
return -1;
|
||||
}
|
||||
|
||||
result =
|
||||
(*handle->interface)->WritePipe(handle->interface, handle->bulkOut, (void *)buf, len);
|
||||
|
||||
if ((result == 0) && (handle->zero_mask)) {
|
||||
/* we need 0-markers and our transfer */
|
||||
if(!(len & handle->zero_mask)) {
|
||||
result =
|
||||
(*handle->interface)->WritePipe(
|
||||
handle->interface, handle->bulkOut, (void *)buf, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!result)
|
||||
return len;
|
||||
|
||||
LOG(ERROR) << "usb_write failed with status: " << std::hex << result;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int usb_read(usb_handle *handle, void *buf, int len)
|
||||
{
|
||||
IOReturn result;
|
||||
UInt32 numBytes = len;
|
||||
|
||||
if (!len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!handle || handle->dead) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (NULL == handle->interface) {
|
||||
LOG(ERROR) << "usb_read interface was null";
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (0 == handle->bulkIn) {
|
||||
LOG(ERROR) << "bulkIn endpoint not assigned";
|
||||
return -1;
|
||||
}
|
||||
|
||||
result = (*handle->interface)->ReadPipe(handle->interface, handle->bulkIn, buf, &numBytes);
|
||||
|
||||
if (kIOUSBPipeStalled == result) {
|
||||
LOG(ERROR) << "Pipe stalled, clearing stall.\n";
|
||||
(*handle->interface)->ClearPipeStall(handle->interface, handle->bulkIn);
|
||||
result = (*handle->interface)->ReadPipe(handle->interface, handle->bulkIn, buf, &numBytes);
|
||||
}
|
||||
|
||||
if (kIOReturnSuccess == result)
|
||||
return numBytes;
|
||||
else {
|
||||
LOG(ERROR) << "usb_read failed with status: " << std::hex << result;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int usb_close(usb_handle *handle)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
|
||||
for (auto it = g_usb_handles.begin(); it != g_usb_handles.end(); ++it) {
|
||||
if ((*it).get() == handle) {
|
||||
g_usb_handles.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_reset(usb_handle* handle) {
|
||||
// Unimplemented on OS X.
|
||||
usb_kick(handle);
|
||||
}
|
||||
|
||||
static void usb_kick_locked(usb_handle *handle)
|
||||
{
|
||||
LOG(INFO) << "Kicking handle";
|
||||
/* release the interface */
|
||||
if (!handle)
|
||||
return;
|
||||
|
||||
if (!handle->dead)
|
||||
{
|
||||
handle->dead = true;
|
||||
(*handle->interface)->USBInterfaceClose(handle->interface);
|
||||
(*handle->interface)->Release(handle->interface);
|
||||
}
|
||||
}
|
||||
|
||||
void usb_kick(usb_handle *handle) {
|
||||
// Use the lock to avoid multiple thread kicking the device at the same time.
|
||||
std::lock_guard<std::mutex> lock_guard(g_usb_handles_mutex);
|
||||
usb_kick_locked(handle);
|
||||
}
|
||||
|
||||
size_t usb_get_max_packet_size(usb_handle* handle) {
|
||||
return handle->max_packet_size;
|
||||
}
|
||||
|
||||
} // namespace native
|
|
@ -1,621 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
|
||||
#define TRACE_TAG USB
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#include "client/usb.h"
|
||||
|
||||
// clang-format off
|
||||
#include <winsock2.h> // winsock.h *must* be included before windows.h.
|
||||
#include <windows.h>
|
||||
// clang-format on
|
||||
#include <usb100.h>
|
||||
#include <winerror.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include <adb_api.h>
|
||||
|
||||
#include <android-base/errors.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "sysdeps/chrono.h"
|
||||
#include "transport.h"
|
||||
|
||||
namespace native {
|
||||
|
||||
/** Structure usb_handle describes our connection to the usb device via
|
||||
AdbWinApi.dll. This structure is returned from usb_open() routine and
|
||||
is expected in each subsequent call that is accessing the device.
|
||||
|
||||
Most members are protected by usb_lock, except for adb_{read,write}_pipe which
|
||||
rely on AdbWinApi.dll's handle validation and AdbCloseHandle(endpoint)'s
|
||||
ability to break a thread out of pipe IO.
|
||||
*/
|
||||
struct usb_handle : public ::usb_handle {
|
||||
/// Handle to USB interface
|
||||
ADBAPIHANDLE adb_interface;
|
||||
|
||||
/// Handle to USB read pipe (endpoint)
|
||||
ADBAPIHANDLE adb_read_pipe;
|
||||
|
||||
/// Handle to USB write pipe (endpoint)
|
||||
ADBAPIHANDLE adb_write_pipe;
|
||||
|
||||
/// Interface name
|
||||
wchar_t* interface_name;
|
||||
|
||||
/// Maximum packet size.
|
||||
unsigned max_packet_size;
|
||||
|
||||
/// Mask for determining when to use zero length packets
|
||||
unsigned zero_mask;
|
||||
};
|
||||
|
||||
/// Class ID assigned to the device by androidusb.sys
|
||||
static const GUID usb_class_id = ANDROID_USB_CLASS_ID;
|
||||
|
||||
/// List of opened usb handles
|
||||
static std::vector<usb_handle*>& handle_list = *new std::vector<usb_handle*>();
|
||||
|
||||
/// Locker for the list of opened usb handles
|
||||
static std::mutex& usb_lock = *new std::mutex();
|
||||
|
||||
/// Checks if there is opened usb handle in handle_list for this device.
|
||||
int known_device(const wchar_t* dev_name);
|
||||
|
||||
/// Checks if there is opened usb handle in handle_list for this device.
|
||||
/// usb_lock mutex must be held before calling this routine.
|
||||
int known_device_locked(const wchar_t* dev_name);
|
||||
|
||||
/// Registers opened usb handle (adds it to handle_list).
|
||||
int register_new_device(usb_handle* handle);
|
||||
|
||||
/// Checks if interface (device) matches certain criteria
|
||||
int recognized_device(usb_handle* handle);
|
||||
|
||||
/// Enumerates present and available interfaces (devices), opens new ones and
|
||||
/// registers usb transport for them.
|
||||
void find_devices();
|
||||
|
||||
/// Kicks all USB devices
|
||||
static void kick_devices();
|
||||
|
||||
/// Entry point for thread that polls (every second) for new usb interfaces.
|
||||
/// This routine calls find_devices in infinite loop.
|
||||
static void device_poll_thread();
|
||||
|
||||
/// Initializes this module
|
||||
void usb_init();
|
||||
|
||||
/// Opens usb interface (device) by interface (device) name.
|
||||
usb_handle* do_usb_open(const wchar_t* interface_name);
|
||||
|
||||
/// Writes data to the opened usb handle
|
||||
int usb_write(usb_handle* handle, const void* data, int len);
|
||||
|
||||
/// Reads data using the opened usb handle
|
||||
int usb_read(usb_handle* handle, void* data, int len);
|
||||
|
||||
/// Cleans up opened usb handle
|
||||
void usb_cleanup_handle(usb_handle* handle);
|
||||
|
||||
/// Cleans up (but don't close) opened usb handle
|
||||
void usb_kick(usb_handle* handle);
|
||||
|
||||
/// Closes opened usb handle
|
||||
int usb_close(usb_handle* handle);
|
||||
|
||||
int known_device_locked(const wchar_t* dev_name) {
|
||||
if (nullptr != dev_name) {
|
||||
// Iterate through the list looking for the name match.
|
||||
for (usb_handle* usb : handle_list) {
|
||||
// In Windows names are not case sensetive!
|
||||
if ((nullptr != usb->interface_name) && (0 == wcsicmp(usb->interface_name, dev_name))) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int known_device(const wchar_t* dev_name) {
|
||||
int ret = 0;
|
||||
|
||||
if (nullptr != dev_name) {
|
||||
std::lock_guard<std::mutex> lock(usb_lock);
|
||||
ret = known_device_locked(dev_name);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int register_new_device(usb_handle* handle) {
|
||||
if (nullptr == handle) return 0;
|
||||
|
||||
std::lock_guard<std::mutex> lock(usb_lock);
|
||||
|
||||
// Check if device is already in the list
|
||||
if (known_device_locked(handle->interface_name)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Not in the list. Add this handle to the list.
|
||||
handle_list.push_back(handle);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void device_poll_thread() {
|
||||
adb_thread_setname("Device Poll");
|
||||
D("Created device thread");
|
||||
|
||||
while (true) {
|
||||
find_devices();
|
||||
adb_notify_device_scan_complete();
|
||||
std::this_thread::sleep_for(1s);
|
||||
}
|
||||
}
|
||||
|
||||
static LRESULT CALLBACK _power_window_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
||||
switch (uMsg) {
|
||||
case WM_POWERBROADCAST:
|
||||
switch (wParam) {
|
||||
case PBT_APMRESUMEAUTOMATIC:
|
||||
// Resuming from sleep or hibernation, so kick all existing USB devices
|
||||
// and then allow the device_poll_thread to redetect USB devices from
|
||||
// scratch. If we don't do this, existing USB devices will never respond
|
||||
// to us because they'll be waiting for the connect/auth handshake.
|
||||
D("Received (WM_POWERBROADCAST, PBT_APMRESUMEAUTOMATIC) notification, "
|
||||
"so kicking all USB devices\n");
|
||||
kick_devices();
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
static void _power_notification_thread() {
|
||||
// This uses a thread with its own window message pump to get power
|
||||
// notifications. If adb runs from a non-interactive service account, this
|
||||
// might not work (not sure). If that happens to not work, we could use
|
||||
// heavyweight WMI APIs to get power notifications. But for the common case
|
||||
// of a developer's interactive session, a window message pump is more
|
||||
// appropriate.
|
||||
D("Created power notification thread");
|
||||
adb_thread_setname("Power Notifier");
|
||||
|
||||
// Window class names are process specific.
|
||||
static const WCHAR kPowerNotificationWindowClassName[] = L"PowerNotificationWindow";
|
||||
|
||||
// Get the HINSTANCE corresponding to the module that _power_window_proc
|
||||
// is in (the main module).
|
||||
const HINSTANCE instance = GetModuleHandleW(nullptr);
|
||||
if (!instance) {
|
||||
// This is such a common API call that this should never fail.
|
||||
LOG(FATAL) << "GetModuleHandleW failed: "
|
||||
<< android::base::SystemErrorCodeToString(GetLastError());
|
||||
}
|
||||
|
||||
WNDCLASSEXW wndclass;
|
||||
memset(&wndclass, 0, sizeof(wndclass));
|
||||
wndclass.cbSize = sizeof(wndclass);
|
||||
wndclass.lpfnWndProc = _power_window_proc;
|
||||
wndclass.hInstance = instance;
|
||||
wndclass.lpszClassName = kPowerNotificationWindowClassName;
|
||||
if (!RegisterClassExW(&wndclass)) {
|
||||
LOG(FATAL) << "RegisterClassExW failed: "
|
||||
<< android::base::SystemErrorCodeToString(GetLastError());
|
||||
}
|
||||
|
||||
if (!CreateWindowExW(WS_EX_NOACTIVATE, kPowerNotificationWindowClassName,
|
||||
L"ADB Power Notification Window", WS_POPUP, 0, 0, 0, 0, nullptr, nullptr,
|
||||
instance, nullptr)) {
|
||||
LOG(FATAL) << "CreateWindowExW failed: "
|
||||
<< android::base::SystemErrorCodeToString(GetLastError());
|
||||
}
|
||||
|
||||
MSG msg;
|
||||
while (GetMessageW(&msg, nullptr, 0, 0)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
}
|
||||
|
||||
// GetMessageW() will return false if a quit message is posted. We don't
|
||||
// do that, but it might be possible for that to occur when logging off or
|
||||
// shutting down. Not a big deal since the whole process will be going away
|
||||
// soon anyway.
|
||||
D("Power notification thread exiting");
|
||||
}
|
||||
|
||||
void usb_init() {
|
||||
std::thread(device_poll_thread).detach();
|
||||
std::thread(_power_notification_thread).detach();
|
||||
}
|
||||
|
||||
void usb_cleanup() {}
|
||||
|
||||
usb_handle* do_usb_open(const wchar_t* interface_name) {
|
||||
unsigned long name_len = 0;
|
||||
|
||||
// Allocate our handle
|
||||
usb_handle* ret = (usb_handle*)calloc(1, sizeof(usb_handle));
|
||||
if (nullptr == ret) {
|
||||
D("Could not allocate %u bytes for usb_handle: %s", sizeof(usb_handle), strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Create interface.
|
||||
ret->adb_interface = AdbCreateInterfaceByName(interface_name);
|
||||
if (nullptr == ret->adb_interface) {
|
||||
D("AdbCreateInterfaceByName failed: %s",
|
||||
android::base::SystemErrorCodeToString(GetLastError()).c_str());
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Open read pipe (endpoint)
|
||||
ret->adb_read_pipe = AdbOpenDefaultBulkReadEndpoint(
|
||||
ret->adb_interface, AdbOpenAccessTypeReadWrite, AdbOpenSharingModeReadWrite);
|
||||
if (nullptr == ret->adb_read_pipe) {
|
||||
D("AdbOpenDefaultBulkReadEndpoint failed: %s",
|
||||
android::base::SystemErrorCodeToString(GetLastError()).c_str());
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Open write pipe (endpoint)
|
||||
ret->adb_write_pipe = AdbOpenDefaultBulkWriteEndpoint(
|
||||
ret->adb_interface, AdbOpenAccessTypeReadWrite, AdbOpenSharingModeReadWrite);
|
||||
if (nullptr == ret->adb_write_pipe) {
|
||||
D("AdbOpenDefaultBulkWriteEndpoint failed: %s",
|
||||
android::base::SystemErrorCodeToString(GetLastError()).c_str());
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Save interface name
|
||||
// First get expected name length
|
||||
AdbGetInterfaceName(ret->adb_interface, nullptr, &name_len, false);
|
||||
if (0 == name_len) {
|
||||
D("AdbGetInterfaceName returned name length of zero: %s",
|
||||
android::base::SystemErrorCodeToString(GetLastError()).c_str());
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret->interface_name = (wchar_t*)malloc(name_len * sizeof(ret->interface_name[0]));
|
||||
if (nullptr == ret->interface_name) {
|
||||
D("Could not allocate %lu characters for interface_name: %s", name_len, strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Now save the name
|
||||
if (!AdbGetInterfaceName(ret->adb_interface, ret->interface_name, &name_len, false)) {
|
||||
D("AdbGetInterfaceName failed: %s",
|
||||
android::base::SystemErrorCodeToString(GetLastError()).c_str());
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// We're done at this point
|
||||
return ret;
|
||||
|
||||
fail:
|
||||
if (nullptr != ret) {
|
||||
usb_cleanup_handle(ret);
|
||||
free(ret);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int usb_write(usb_handle* handle, const void* data, int len) {
|
||||
unsigned long time_out = 5000;
|
||||
unsigned long written = 0;
|
||||
int err = 0;
|
||||
|
||||
D("usb_write %d", len);
|
||||
if (nullptr == handle) {
|
||||
D("usb_write was passed NULL handle");
|
||||
err = EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Perform write
|
||||
if (!AdbWriteEndpointSync(handle->adb_write_pipe, (void*)data, (unsigned long)len, &written,
|
||||
time_out)) {
|
||||
D("AdbWriteEndpointSync failed: %s",
|
||||
android::base::SystemErrorCodeToString(GetLastError()).c_str());
|
||||
err = EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Make sure that we've written what we were asked to write
|
||||
D("usb_write got: %ld, expected: %d", written, len);
|
||||
if (written != (unsigned long)len) {
|
||||
// If this occurs, this code should be changed to repeatedly call
|
||||
// AdbWriteEndpointSync() until all bytes are written.
|
||||
D("AdbWriteEndpointSync was supposed to write %d, but only wrote %ld", len, written);
|
||||
err = EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (handle->zero_mask && (len & handle->zero_mask) == 0) {
|
||||
// Send a zero length packet
|
||||
unsigned long dummy;
|
||||
if (!AdbWriteEndpointSync(handle->adb_write_pipe, (void*)data, 0, &dummy, time_out)) {
|
||||
D("AdbWriteEndpointSync of zero length packet failed: %s",
|
||||
android::base::SystemErrorCodeToString(GetLastError()).c_str());
|
||||
err = EIO;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
return written;
|
||||
|
||||
fail:
|
||||
// Any failure should cause us to kick the device instead of leaving it a
|
||||
// zombie state with potential to hang.
|
||||
if (nullptr != handle) {
|
||||
D("Kicking device due to error in usb_write");
|
||||
usb_kick(handle);
|
||||
}
|
||||
|
||||
D("usb_write failed");
|
||||
errno = err;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int usb_read(usb_handle* handle, void* data, int len) {
|
||||
unsigned long time_out = 0;
|
||||
unsigned long read = 0;
|
||||
int err = 0;
|
||||
int orig_len = len;
|
||||
|
||||
D("usb_read %d", len);
|
||||
if (nullptr == handle) {
|
||||
D("usb_read was passed NULL handle");
|
||||
err = EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
while (len == orig_len) {
|
||||
if (!AdbReadEndpointSync(handle->adb_read_pipe, data, len, &read, time_out)) {
|
||||
D("AdbReadEndpointSync failed: %s",
|
||||
android::base::SystemErrorCodeToString(GetLastError()).c_str());
|
||||
err = EIO;
|
||||
goto fail;
|
||||
}
|
||||
D("usb_read got: %ld, expected: %d", read, len);
|
||||
|
||||
data = (char*)data + read;
|
||||
len -= read;
|
||||
}
|
||||
|
||||
return orig_len - len;
|
||||
|
||||
fail:
|
||||
// Any failure should cause us to kick the device instead of leaving it a
|
||||
// zombie state with potential to hang.
|
||||
if (nullptr != handle) {
|
||||
D("Kicking device due to error in usb_read");
|
||||
usb_kick(handle);
|
||||
}
|
||||
|
||||
D("usb_read failed");
|
||||
errno = err;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Wrapper around AdbCloseHandle() that logs diagnostics.
|
||||
static void _adb_close_handle(ADBAPIHANDLE adb_handle) {
|
||||
if (!AdbCloseHandle(adb_handle)) {
|
||||
D("AdbCloseHandle(%p) failed: %s", adb_handle,
|
||||
android::base::SystemErrorCodeToString(GetLastError()).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void usb_cleanup_handle(usb_handle* handle) {
|
||||
D("usb_cleanup_handle");
|
||||
if (nullptr != handle) {
|
||||
if (nullptr != handle->interface_name) free(handle->interface_name);
|
||||
// AdbCloseHandle(pipe) will break any threads out of pending IO calls and
|
||||
// wait until the pipe no longer uses the interface. Then we can
|
||||
// AdbCloseHandle() the interface.
|
||||
if (nullptr != handle->adb_write_pipe) _adb_close_handle(handle->adb_write_pipe);
|
||||
if (nullptr != handle->adb_read_pipe) _adb_close_handle(handle->adb_read_pipe);
|
||||
if (nullptr != handle->adb_interface) _adb_close_handle(handle->adb_interface);
|
||||
|
||||
handle->interface_name = nullptr;
|
||||
handle->adb_write_pipe = nullptr;
|
||||
handle->adb_read_pipe = nullptr;
|
||||
handle->adb_interface = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void usb_reset(usb_handle* handle) {
|
||||
// Unimplemented on Windows.
|
||||
usb_kick(handle);
|
||||
}
|
||||
|
||||
static void usb_kick_locked(usb_handle* handle) {
|
||||
// The reason the lock must be acquired before calling this function is in
|
||||
// case multiple threads are trying to kick the same device at the same time.
|
||||
usb_cleanup_handle(handle);
|
||||
}
|
||||
|
||||
void usb_kick(usb_handle* handle) {
|
||||
D("usb_kick");
|
||||
if (nullptr != handle) {
|
||||
std::lock_guard<std::mutex> lock(usb_lock);
|
||||
usb_kick_locked(handle);
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int usb_close(usb_handle* handle) {
|
||||
D("usb_close");
|
||||
|
||||
if (nullptr != handle) {
|
||||
// Remove handle from the list
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(usb_lock);
|
||||
handle_list.erase(std::remove(handle_list.begin(), handle_list.end(), handle),
|
||||
handle_list.end());
|
||||
}
|
||||
|
||||
// Cleanup handle
|
||||
usb_cleanup_handle(handle);
|
||||
free(handle);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t usb_get_max_packet_size(usb_handle* handle) {
|
||||
return handle->max_packet_size;
|
||||
}
|
||||
|
||||
int recognized_device(usb_handle* handle) {
|
||||
if (nullptr == handle) return 0;
|
||||
|
||||
// Check vendor and product id first
|
||||
USB_DEVICE_DESCRIPTOR device_desc;
|
||||
|
||||
if (!AdbGetUsbDeviceDescriptor(handle->adb_interface, &device_desc)) {
|
||||
D("AdbGetUsbDeviceDescriptor failed: %s",
|
||||
android::base::SystemErrorCodeToString(GetLastError()).c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Then check interface properties
|
||||
USB_INTERFACE_DESCRIPTOR interf_desc;
|
||||
|
||||
if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface, &interf_desc)) {
|
||||
D("AdbGetUsbInterfaceDescriptor failed: %s",
|
||||
android::base::SystemErrorCodeToString(GetLastError()).c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Must have two endpoints
|
||||
if (2 != interf_desc.bNumEndpoints) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!is_adb_interface(interf_desc.bInterfaceClass, interf_desc.bInterfaceSubClass,
|
||||
interf_desc.bInterfaceProtocol)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
AdbEndpointInformation endpoint_info;
|
||||
// assuming zero is a valid bulk endpoint ID
|
||||
if (AdbGetEndpointInformation(handle->adb_interface, 0, &endpoint_info)) {
|
||||
handle->max_packet_size = endpoint_info.max_packet_size;
|
||||
handle->zero_mask = endpoint_info.max_packet_size - 1;
|
||||
D("device zero_mask: 0x%x", handle->zero_mask);
|
||||
} else {
|
||||
D("AdbGetEndpointInformation failed: %s",
|
||||
android::base::SystemErrorCodeToString(GetLastError()).c_str());
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void find_devices() {
|
||||
usb_handle* handle = nullptr;
|
||||
char entry_buffer[2048];
|
||||
AdbInterfaceInfo* next_interface = (AdbInterfaceInfo*)(&entry_buffer[0]);
|
||||
unsigned long entry_buffer_size = sizeof(entry_buffer);
|
||||
|
||||
// Enumerate all present and active interfaces.
|
||||
ADBAPIHANDLE enum_handle = AdbEnumInterfaces(usb_class_id, true, true, true);
|
||||
|
||||
if (nullptr == enum_handle) {
|
||||
D("AdbEnumInterfaces failed: %s",
|
||||
android::base::SystemErrorCodeToString(GetLastError()).c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
while (AdbNextInterface(enum_handle, next_interface, &entry_buffer_size)) {
|
||||
// Lets see if we already have this device in the list
|
||||
if (!known_device(next_interface->device_name)) {
|
||||
// This seems to be a new device. Open it!
|
||||
handle = do_usb_open(next_interface->device_name);
|
||||
if (nullptr != handle) {
|
||||
// Lets see if this interface (device) belongs to us
|
||||
if (recognized_device(handle)) {
|
||||
D("adding a new device %ls", next_interface->device_name);
|
||||
|
||||
// We don't request a wchar_t string from AdbGetSerialNumber() because of a bug
|
||||
// in adb_winusb_interface.cpp:CopyMemory(buffer, ser_num->bString,
|
||||
// bytes_written) where the last parameter should be (str_len *
|
||||
// sizeof(wchar_t)). The bug reads 2 bytes past the end of a stack buffer in the
|
||||
// best case, and in the unlikely case of a long serial number, it will read 2
|
||||
// bytes past the end of a heap allocation. This doesn't affect the resulting
|
||||
// string, but we should avoid the bad reads in the first place.
|
||||
char serial_number[512];
|
||||
unsigned long serial_number_len = sizeof(serial_number);
|
||||
if (AdbGetSerialNumber(handle->adb_interface, serial_number, &serial_number_len,
|
||||
true)) {
|
||||
// Lets make sure that we don't duplicate this device
|
||||
if (register_new_device(handle)) {
|
||||
register_usb_transport(handle, serial_number, nullptr, 1);
|
||||
} else {
|
||||
D("register_new_device failed for %ls", next_interface->device_name);
|
||||
usb_cleanup_handle(handle);
|
||||
free(handle);
|
||||
}
|
||||
} else {
|
||||
D("cannot get serial number: %s",
|
||||
android::base::SystemErrorCodeToString(GetLastError()).c_str());
|
||||
usb_cleanup_handle(handle);
|
||||
free(handle);
|
||||
}
|
||||
} else {
|
||||
usb_cleanup_handle(handle);
|
||||
free(handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
entry_buffer_size = sizeof(entry_buffer);
|
||||
}
|
||||
|
||||
if (GetLastError() != ERROR_NO_MORE_ITEMS) {
|
||||
// Only ERROR_NO_MORE_ITEMS is expected at the end of enumeration.
|
||||
D("AdbNextInterface failed: %s",
|
||||
android::base::SystemErrorCodeToString(GetLastError()).c_str());
|
||||
}
|
||||
|
||||
_adb_close_handle(enum_handle);
|
||||
}
|
||||
|
||||
static void kick_devices() {
|
||||
// Need to acquire lock to safely walk the list which might be modified
|
||||
// by another thread.
|
||||
std::lock_guard<std::mutex> lock(usb_lock);
|
||||
for (usb_handle* usb : handle_list) {
|
||||
usb_kick_locked(usb);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace native
|
|
@ -1,486 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
|
||||
#include <brotli/decode.h>
|
||||
#include <brotli/encode.h>
|
||||
#include <lz4frame.h>
|
||||
#include <zstd.h>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
enum class DecodeResult {
|
||||
Error,
|
||||
Done,
|
||||
NeedInput,
|
||||
MoreOutput,
|
||||
};
|
||||
|
||||
enum class EncodeResult {
|
||||
Error,
|
||||
Done,
|
||||
NeedInput,
|
||||
MoreOutput,
|
||||
};
|
||||
|
||||
struct Decoder {
|
||||
void Append(Block&& block) { input_buffer_.append(std::move(block)); }
|
||||
bool Finish() {
|
||||
bool old = std::exchange(finished_, true);
|
||||
if (old) {
|
||||
LOG(FATAL) << "Decoder::Finish called while already finished?";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual DecodeResult Decode(std::span<char>* output) = 0;
|
||||
|
||||
protected:
|
||||
Decoder(std::span<char> output_buffer) : output_buffer_(output_buffer) {}
|
||||
~Decoder() = default;
|
||||
|
||||
bool finished_ = false;
|
||||
IOVector input_buffer_;
|
||||
std::span<char> output_buffer_;
|
||||
};
|
||||
|
||||
struct Encoder {
|
||||
void Append(Block input) { input_buffer_.append(std::move(input)); }
|
||||
bool Finish() {
|
||||
bool old = std::exchange(finished_, true);
|
||||
if (old) {
|
||||
LOG(FATAL) << "Decoder::Finish called while already finished?";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual EncodeResult Encode(Block* output) = 0;
|
||||
|
||||
protected:
|
||||
explicit Encoder(size_t output_block_size) : output_block_size_(output_block_size) {}
|
||||
~Encoder() = default;
|
||||
|
||||
const size_t output_block_size_;
|
||||
bool finished_ = false;
|
||||
IOVector input_buffer_;
|
||||
};
|
||||
|
||||
struct NullDecoder final : public Decoder {
|
||||
explicit NullDecoder(std::span<char> output_buffer) : Decoder(output_buffer) {}
|
||||
|
||||
DecodeResult Decode(std::span<char>* output) final {
|
||||
size_t available_out = output_buffer_.size();
|
||||
void* p = output_buffer_.data();
|
||||
while (available_out > 0 && !input_buffer_.empty()) {
|
||||
size_t len = std::min(available_out, input_buffer_.front_size());
|
||||
p = mempcpy(p, input_buffer_.front_data(), len);
|
||||
available_out -= len;
|
||||
input_buffer_.drop_front(len);
|
||||
}
|
||||
*output = std::span(output_buffer_.data(), static_cast<char*>(p));
|
||||
if (input_buffer_.empty()) {
|
||||
return finished_ ? DecodeResult::Done : DecodeResult::NeedInput;
|
||||
}
|
||||
return DecodeResult::MoreOutput;
|
||||
}
|
||||
};
|
||||
|
||||
struct NullEncoder final : public Encoder {
|
||||
explicit NullEncoder(size_t output_block_size) : Encoder(output_block_size) {}
|
||||
|
||||
EncodeResult Encode(Block* output) final {
|
||||
output->clear();
|
||||
output->resize(output_block_size_);
|
||||
|
||||
size_t available_out = output->size();
|
||||
void* p = output->data();
|
||||
|
||||
while (available_out > 0 && !input_buffer_.empty()) {
|
||||
size_t len = std::min(available_out, input_buffer_.front_size());
|
||||
p = mempcpy(p, input_buffer_.front_data(), len);
|
||||
available_out -= len;
|
||||
input_buffer_.drop_front(len);
|
||||
}
|
||||
|
||||
output->resize(output->size() - available_out);
|
||||
|
||||
if (input_buffer_.empty()) {
|
||||
return finished_ ? EncodeResult::Done : EncodeResult::NeedInput;
|
||||
}
|
||||
return EncodeResult::MoreOutput;
|
||||
}
|
||||
};
|
||||
|
||||
struct BrotliDecoder final : public Decoder {
|
||||
explicit BrotliDecoder(std::span<char> output_buffer)
|
||||
: Decoder(output_buffer),
|
||||
decoder_(BrotliDecoderCreateInstance(nullptr, nullptr, nullptr),
|
||||
BrotliDecoderDestroyInstance) {}
|
||||
|
||||
DecodeResult Decode(std::span<char>* output) final {
|
||||
size_t available_in = input_buffer_.front_size();
|
||||
const uint8_t* next_in = reinterpret_cast<const uint8_t*>(input_buffer_.front_data());
|
||||
|
||||
size_t available_out = output_buffer_.size();
|
||||
uint8_t* next_out = reinterpret_cast<uint8_t*>(output_buffer_.data());
|
||||
|
||||
BrotliDecoderResult r = BrotliDecoderDecompressStream(
|
||||
decoder_.get(), &available_in, &next_in, &available_out, &next_out, nullptr);
|
||||
|
||||
size_t bytes_consumed = input_buffer_.front_size() - available_in;
|
||||
input_buffer_.drop_front(bytes_consumed);
|
||||
|
||||
size_t bytes_emitted = output_buffer_.size() - available_out;
|
||||
*output = std::span<char>(output_buffer_.data(), bytes_emitted);
|
||||
|
||||
switch (r) {
|
||||
case BROTLI_DECODER_RESULT_SUCCESS:
|
||||
// We need to wait for ID_DONE from the other end.
|
||||
return finished_ ? DecodeResult::Done : DecodeResult::NeedInput;
|
||||
case BROTLI_DECODER_RESULT_ERROR:
|
||||
return DecodeResult::Error;
|
||||
case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT:
|
||||
// Brotli guarantees as one of its invariants that if it returns NEEDS_MORE_INPUT,
|
||||
// it will consume the entire input buffer passed in, so we don't have to worry
|
||||
// about bytes left over in the front block with more input remaining.
|
||||
return DecodeResult::NeedInput;
|
||||
case BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT:
|
||||
return DecodeResult::MoreOutput;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<BrotliDecoderState, void (*)(BrotliDecoderState*)> decoder_;
|
||||
};
|
||||
|
||||
struct BrotliEncoder final : public Encoder {
|
||||
explicit BrotliEncoder(size_t output_block_size)
|
||||
: Encoder(output_block_size),
|
||||
output_block_(output_block_size_),
|
||||
output_bytes_left_(output_block_size_),
|
||||
encoder_(BrotliEncoderCreateInstance(nullptr, nullptr, nullptr),
|
||||
BrotliEncoderDestroyInstance) {
|
||||
BrotliEncoderSetParameter(encoder_.get(), BROTLI_PARAM_QUALITY, 1);
|
||||
}
|
||||
|
||||
EncodeResult Encode(Block* output) final {
|
||||
output->clear();
|
||||
|
||||
while (true) {
|
||||
size_t available_in = input_buffer_.front_size();
|
||||
const uint8_t* next_in = reinterpret_cast<const uint8_t*>(input_buffer_.front_data());
|
||||
|
||||
size_t available_out = output_bytes_left_;
|
||||
uint8_t* next_out = reinterpret_cast<uint8_t*>(
|
||||
output_block_.data() + (output_block_size_ - output_bytes_left_));
|
||||
|
||||
BrotliEncoderOperation op = BROTLI_OPERATION_PROCESS;
|
||||
if (finished_) {
|
||||
op = BROTLI_OPERATION_FINISH;
|
||||
}
|
||||
|
||||
if (!BrotliEncoderCompressStream(encoder_.get(), op, &available_in, &next_in,
|
||||
&available_out, &next_out, nullptr)) {
|
||||
return EncodeResult::Error;
|
||||
}
|
||||
|
||||
size_t bytes_consumed = input_buffer_.front_size() - available_in;
|
||||
input_buffer_.drop_front(bytes_consumed);
|
||||
|
||||
output_bytes_left_ = available_out;
|
||||
|
||||
if (BrotliEncoderIsFinished(encoder_.get())) {
|
||||
output_block_.resize(output_block_size_ - output_bytes_left_);
|
||||
*output = std::move(output_block_);
|
||||
return EncodeResult::Done;
|
||||
} else if (output_bytes_left_ == 0) {
|
||||
*output = std::move(output_block_);
|
||||
output_block_.resize(output_block_size_);
|
||||
output_bytes_left_ = output_block_size_;
|
||||
return EncodeResult::MoreOutput;
|
||||
} else if (input_buffer_.empty()) {
|
||||
return EncodeResult::NeedInput;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Block output_block_;
|
||||
size_t output_bytes_left_;
|
||||
std::unique_ptr<BrotliEncoderState, void (*)(BrotliEncoderState*)> encoder_;
|
||||
};
|
||||
|
||||
struct LZ4Decoder final : public Decoder {
|
||||
explicit LZ4Decoder(std::span<char> output_buffer)
|
||||
: Decoder(output_buffer), decoder_(nullptr, nullptr) {
|
||||
LZ4F_dctx* dctx;
|
||||
if (LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION) != 0) {
|
||||
LOG(FATAL) << "failed to initialize LZ4 decompression context";
|
||||
}
|
||||
decoder_ = std::unique_ptr<LZ4F_dctx, decltype(&LZ4F_freeDecompressionContext)>(
|
||||
dctx, LZ4F_freeDecompressionContext);
|
||||
}
|
||||
|
||||
DecodeResult Decode(std::span<char>* output) final {
|
||||
size_t available_in = input_buffer_.front_size();
|
||||
const char* next_in = input_buffer_.front_data();
|
||||
|
||||
size_t available_out = output_buffer_.size();
|
||||
char* next_out = output_buffer_.data();
|
||||
|
||||
size_t rc = LZ4F_decompress(decoder_.get(), next_out, &available_out, next_in,
|
||||
&available_in, nullptr);
|
||||
if (LZ4F_isError(rc)) {
|
||||
LOG(ERROR) << "LZ4F_decompress failed: " << LZ4F_getErrorName(rc);
|
||||
return DecodeResult::Error;
|
||||
}
|
||||
|
||||
input_buffer_.drop_front(available_in);
|
||||
|
||||
if (rc == 0) {
|
||||
if (!input_buffer_.empty()) {
|
||||
LOG(ERROR) << "LZ4 stream hit end before reading all data";
|
||||
return DecodeResult::Error;
|
||||
}
|
||||
lz4_done_ = true;
|
||||
}
|
||||
|
||||
*output = std::span<char>(output_buffer_.data(), available_out);
|
||||
|
||||
if (finished_) {
|
||||
return input_buffer_.empty() && lz4_done_ ? DecodeResult::Done
|
||||
: DecodeResult::MoreOutput;
|
||||
}
|
||||
|
||||
return DecodeResult::NeedInput;
|
||||
}
|
||||
|
||||
private:
|
||||
bool lz4_done_ = false;
|
||||
std::unique_ptr<LZ4F_dctx, LZ4F_errorCode_t (*)(LZ4F_dctx*)> decoder_;
|
||||
};
|
||||
|
||||
struct LZ4Encoder final : public Encoder {
|
||||
explicit LZ4Encoder(size_t output_block_size)
|
||||
: Encoder(output_block_size), encoder_(nullptr, nullptr) {
|
||||
LZ4F_cctx* cctx;
|
||||
if (LZ4F_createCompressionContext(&cctx, LZ4F_VERSION) != 0) {
|
||||
LOG(FATAL) << "failed to initialize LZ4 compression context";
|
||||
}
|
||||
encoder_ = std::unique_ptr<LZ4F_cctx, decltype(&LZ4F_freeCompressionContext)>(
|
||||
cctx, LZ4F_freeCompressionContext);
|
||||
Block header(LZ4F_HEADER_SIZE_MAX);
|
||||
size_t rc = LZ4F_compressBegin(encoder_.get(), header.data(), header.size(), nullptr);
|
||||
if (LZ4F_isError(rc)) {
|
||||
LOG(FATAL) << "LZ4F_compressBegin failed: %s", LZ4F_getErrorName(rc);
|
||||
}
|
||||
header.resize(rc);
|
||||
output_buffer_.append(std::move(header));
|
||||
}
|
||||
|
||||
// As an optimization, only emit a block if we have an entire output block ready, or we're done.
|
||||
bool OutputReady() const {
|
||||
return output_buffer_.size() >= output_block_size_ || lz4_finalized_;
|
||||
}
|
||||
|
||||
// TODO: Switch the output type to IOVector to remove a copy?
|
||||
EncodeResult Encode(Block* output) final {
|
||||
size_t available_in = input_buffer_.front_size();
|
||||
const char* next_in = input_buffer_.front_data();
|
||||
|
||||
// LZ4 makes no guarantees about being able to recover from trying to compress with an
|
||||
// insufficiently large output buffer. LZ4F_compressBound tells us how much buffer we
|
||||
// need to compress a given number of bytes, but the smallest value seems to be bigger
|
||||
// than SYNC_DATA_MAX, so we need to buffer ourselves.
|
||||
|
||||
// Input size chosen to be a local maximum for LZ4F_compressBound (i.e. the block size).
|
||||
constexpr size_t max_input_size = 65536;
|
||||
const size_t encode_block_size = LZ4F_compressBound(max_input_size, nullptr);
|
||||
|
||||
if (available_in != 0) {
|
||||
if (lz4_finalized_) {
|
||||
LOG(ERROR) << "LZ4Encoder received data after Finish?";
|
||||
return EncodeResult::Error;
|
||||
}
|
||||
|
||||
available_in = std::min(available_in, max_input_size);
|
||||
|
||||
Block encode_block(encode_block_size);
|
||||
size_t available_out = encode_block.capacity();
|
||||
char* next_out = encode_block.data();
|
||||
|
||||
size_t rc = LZ4F_compressUpdate(encoder_.get(), next_out, available_out, next_in,
|
||||
available_in, nullptr);
|
||||
if (LZ4F_isError(rc)) {
|
||||
LOG(ERROR) << "LZ4F_compressUpdate failed: " << LZ4F_getErrorName(rc);
|
||||
return EncodeResult::Error;
|
||||
}
|
||||
|
||||
input_buffer_.drop_front(available_in);
|
||||
|
||||
available_out -= rc;
|
||||
next_out += rc;
|
||||
|
||||
encode_block.resize(encode_block_size - available_out);
|
||||
output_buffer_.append(std::move(encode_block));
|
||||
}
|
||||
|
||||
if (finished_ && !lz4_finalized_) {
|
||||
lz4_finalized_ = true;
|
||||
|
||||
Block final_block(encode_block_size + 4);
|
||||
size_t rc = LZ4F_compressEnd(encoder_.get(), final_block.data(), final_block.size(),
|
||||
nullptr);
|
||||
if (LZ4F_isError(rc)) {
|
||||
LOG(ERROR) << "LZ4F_compressEnd failed: " << LZ4F_getErrorName(rc);
|
||||
return EncodeResult::Error;
|
||||
}
|
||||
|
||||
final_block.resize(rc);
|
||||
output_buffer_.append(std::move(final_block));
|
||||
}
|
||||
|
||||
if (OutputReady()) {
|
||||
size_t len = std::min(output_block_size_, output_buffer_.size());
|
||||
*output = output_buffer_.take_front(len).coalesce();
|
||||
} else {
|
||||
output->clear();
|
||||
}
|
||||
|
||||
if (lz4_finalized_ && output_buffer_.empty()) {
|
||||
return EncodeResult::Done;
|
||||
} else if (OutputReady()) {
|
||||
return EncodeResult::MoreOutput;
|
||||
}
|
||||
return EncodeResult::NeedInput;
|
||||
}
|
||||
|
||||
private:
|
||||
bool lz4_finalized_ = false;
|
||||
std::unique_ptr<LZ4F_cctx, LZ4F_errorCode_t (*)(LZ4F_cctx*)> encoder_;
|
||||
IOVector output_buffer_;
|
||||
};
|
||||
|
||||
struct ZstdDecoder final : public Decoder {
|
||||
explicit ZstdDecoder(std::span<char> output_buffer)
|
||||
: Decoder(output_buffer), decoder_(ZSTD_createDStream(), ZSTD_freeDStream) {
|
||||
if (!decoder_) {
|
||||
LOG(FATAL) << "failed to initialize Zstd decompression context";
|
||||
}
|
||||
}
|
||||
|
||||
DecodeResult Decode(std::span<char>* output) final {
|
||||
ZSTD_inBuffer in;
|
||||
in.src = input_buffer_.front_data();
|
||||
in.size = input_buffer_.front_size();
|
||||
in.pos = 0;
|
||||
|
||||
ZSTD_outBuffer out;
|
||||
out.dst = output_buffer_.data();
|
||||
// The standard specifies size() as returning size_t, but our current version of
|
||||
// libc++ returns a signed value instead.
|
||||
out.size = static_cast<size_t>(output_buffer_.size());
|
||||
out.pos = 0;
|
||||
|
||||
size_t rc = ZSTD_decompressStream(decoder_.get(), &out, &in);
|
||||
if (ZSTD_isError(rc)) {
|
||||
LOG(ERROR) << "ZSTD_decompressStream failed: " << ZSTD_getErrorName(rc);
|
||||
return DecodeResult::Error;
|
||||
}
|
||||
|
||||
input_buffer_.drop_front(in.pos);
|
||||
if (rc == 0) {
|
||||
if (!input_buffer_.empty()) {
|
||||
LOG(ERROR) << "Zstd stream hit end before reading all data";
|
||||
return DecodeResult::Error;
|
||||
}
|
||||
zstd_done_ = true;
|
||||
}
|
||||
|
||||
*output = std::span<char>(output_buffer_.data(), out.pos);
|
||||
|
||||
if (finished_) {
|
||||
return input_buffer_.empty() && zstd_done_ ? DecodeResult::Done
|
||||
: DecodeResult::MoreOutput;
|
||||
}
|
||||
return DecodeResult::NeedInput;
|
||||
}
|
||||
|
||||
private:
|
||||
bool zstd_done_ = false;
|
||||
std::unique_ptr<ZSTD_DStream, size_t (*)(ZSTD_DStream*)> decoder_;
|
||||
};
|
||||
|
||||
struct ZstdEncoder final : public Encoder {
|
||||
explicit ZstdEncoder(size_t output_block_size)
|
||||
: Encoder(output_block_size), encoder_(ZSTD_createCStream(), ZSTD_freeCStream) {
|
||||
if (!encoder_) {
|
||||
LOG(FATAL) << "failed to initialize Zstd compression context";
|
||||
}
|
||||
ZSTD_CCtx_setParameter(encoder_.get(), ZSTD_c_compressionLevel, 1);
|
||||
}
|
||||
|
||||
EncodeResult Encode(Block* output) final {
|
||||
ZSTD_inBuffer in;
|
||||
in.src = input_buffer_.front_data();
|
||||
in.size = input_buffer_.front_size();
|
||||
in.pos = 0;
|
||||
|
||||
output->resize(output_block_size_);
|
||||
|
||||
ZSTD_outBuffer out;
|
||||
out.dst = output->data();
|
||||
out.size = static_cast<size_t>(output->size());
|
||||
out.pos = 0;
|
||||
|
||||
ZSTD_EndDirective end_directive = finished_ ? ZSTD_e_end : ZSTD_e_continue;
|
||||
size_t rc = ZSTD_compressStream2(encoder_.get(), &out, &in, end_directive);
|
||||
if (ZSTD_isError(rc)) {
|
||||
LOG(ERROR) << "ZSTD_compressStream2 failed: " << ZSTD_getErrorName(rc);
|
||||
return EncodeResult::Error;
|
||||
}
|
||||
|
||||
input_buffer_.drop_front(in.pos);
|
||||
output->resize(out.pos);
|
||||
|
||||
if (rc == 0) {
|
||||
// Zstd finished flushing its data.
|
||||
if (finished_) {
|
||||
if (!input_buffer_.empty()) {
|
||||
LOG(ERROR) << "ZSTD_compressStream2 finished early";
|
||||
return EncodeResult::Error;
|
||||
}
|
||||
return EncodeResult::Done;
|
||||
} else {
|
||||
return input_buffer_.empty() ? EncodeResult::NeedInput : EncodeResult::MoreOutput;
|
||||
}
|
||||
} else {
|
||||
return EncodeResult::MoreOutput;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<ZSTD_CStream, size_t (*)(ZSTD_CStream*)> encoder_;
|
||||
};
|
|
@ -1,2 +0,0 @@
|
|||
/adbd.profdata
|
||||
/report
|
|
@ -1,119 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
OUTPUT_DIR=$(dirname "$0")
|
||||
. "$OUTPUT_DIR"/include.sh
|
||||
|
||||
TRACEDIR=`mktemp -d`
|
||||
|
||||
### Make sure we can connect to the device.
|
||||
|
||||
# Get the device's wlan0 address.
|
||||
IP_ADDR=$(adb shell ip route get 0.0.0.0 oif wlan0 | sed -En -e 's/.*src (\S+)\s.*/\1/p')
|
||||
REMOTE_PORT=5555
|
||||
REMOTE=$IP_ADDR:$REMOTE_PORT
|
||||
LOCAL_SERIAL=$(adb shell getprop ro.serialno)
|
||||
|
||||
# Check that we can connect to it.
|
||||
adb disconnect
|
||||
|
||||
TRANSPORT_ID=$(adb transport-id)
|
||||
adb tcpip $REMOTE_PORT
|
||||
adb -t $TRANSPORT_ID wait-for-disconnect
|
||||
|
||||
adb connect $REMOTE
|
||||
|
||||
REMOTE_FETCHED_SERIAL=$(adb -s $REMOTE shell getprop ro.serialno)
|
||||
|
||||
if [[ "$LOCAL_SERIAL" != "$REMOTE_FETCHED_SERIAL" ]]; then
|
||||
echo "Mismatch: local serial = $LOCAL_SERIAL, remote serial = $REMOTE_FETCHED_SERIAL"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Back to USB, and make sure adbd is root.
|
||||
adb -s $REMOTE usb
|
||||
adb disconnect $REMOTE
|
||||
|
||||
adb wait-for-device root
|
||||
adb root
|
||||
adb wait-for-device
|
||||
|
||||
TRANSPORT_ID=$(adb transport-id)
|
||||
adb usb
|
||||
adb -t $TRANSPORT_ID wait-for-disconnect
|
||||
|
||||
adb wait-for-device
|
||||
|
||||
### Run the adb unit tests and fetch traces from them.
|
||||
mkdir "$TRACEDIR"/test_traces
|
||||
adb shell rm -rf /data/local/tmp/adb_coverage
|
||||
adb shell mkdir /data/local/tmp/adb_coverage
|
||||
|
||||
for TEST in $ADB_TESTS; do
|
||||
adb shell LLVM_PROFILE_FILE=/data/local/tmp/adb_coverage/$TEST.profraw /data/nativetest64/$TEST/$TEST
|
||||
adb pull /data/local/tmp/adb_coverage/$TEST.profraw "$TRACEDIR"/test_traces/
|
||||
done
|
||||
|
||||
adb pull /data/local/tmp/adb_coverage "$TRACEDIR"/test_traces
|
||||
|
||||
# Clear logcat and increase the buffer to something ridiculous so we can fetch the pids of adbd later.
|
||||
adb shell logcat -c -G128M
|
||||
|
||||
# Turn on extremely verbose logging so as to not count debug logging against us.
|
||||
adb shell setprop persist.adb.trace_mask 1
|
||||
|
||||
### Run test_device.py over USB.
|
||||
TRANSPORT_ID=$(adb transport-id)
|
||||
adb shell killall adbd
|
||||
adb -t $TRANSPORT_ID wait-for-disconnect
|
||||
|
||||
adb wait-for-device shell rm -rf "/data/misc/trace/*" /data/local/tmp/adb_coverage/
|
||||
"$OUTPUT_DIR"/../test_device.py
|
||||
|
||||
# Do a usb reset to exercise the disconnect code.
|
||||
adb_usbreset
|
||||
adb wait-for-device
|
||||
|
||||
# Dump traces from the currently running adbd.
|
||||
adb shell killall -37 adbd
|
||||
|
||||
echo Waiting for adbd to finish dumping traces
|
||||
sleep 5
|
||||
|
||||
# Restart adbd in tcp mode.
|
||||
TRANSPORT_ID=$(adb transport-id)
|
||||
adb tcpip $REMOTE_PORT
|
||||
adb -t $TRANSPORT_ID wait-for-disconnect
|
||||
|
||||
adb connect $REMOTE
|
||||
adb -s $REMOTE wait-for-device
|
||||
|
||||
# Instead of running test_device.py again, which takes forever, do some I/O back and forth instead.
|
||||
dd if=/dev/zero bs=1024 count=10240 | adb -s $REMOTE raw sink:10485760
|
||||
adb -s $REMOTE raw source:10485760 | dd of=/dev/null bs=1024 count=10240
|
||||
|
||||
# Dump traces again.
|
||||
adb disconnect $REMOTE
|
||||
adb shell killall -37 adbd
|
||||
|
||||
echo Waiting for adbd to finish dumping traces
|
||||
sleep 5
|
||||
|
||||
adb pull /data/misc/trace "$TRACEDIR"/
|
||||
echo Pulled traces to $TRACEDIR
|
||||
|
||||
# Identify which of the trace files are actually adbd, in case something else exited simultaneously.
|
||||
ADBD_PIDS=$(adb shell "logcat -d -s adbd --format=process | grep 'adbd started' | cut -c 3-7 | tr -d ' ' | sort | uniq")
|
||||
mkdir "$TRACEDIR"/adbd_traces
|
||||
|
||||
adb shell 'setprop persist.adb.trace_mask 0; killall adbd'
|
||||
|
||||
IFS=$'\n'
|
||||
for PID in $ADBD_PIDS; do
|
||||
cp "$TRACEDIR"/trace/clang-$PID-*.profraw "$TRACEDIR"/adbd_traces 2>/dev/null || true
|
||||
done
|
||||
unset IFS
|
||||
|
||||
### Merge the traces.
|
||||
llvm-profdata merge --output="$OUTPUT_DIR"/adbd.profdata "$TRACEDIR"/adbd_traces/* "$TRACEDIR"/test_traces/*
|
|
@ -1,5 +0,0 @@
|
|||
ADB_TESTS="adbd_test adb_crypto_test adb_pairing_auth_test adb_pairing_connection_test adb_tls_connection_test"
|
||||
ADB_TEST_BINARIES=""
|
||||
for TEST in $ADB_TESTS; do
|
||||
ADB_TEST_BINARIES="--object=$ANDROID_PRODUCT_OUT/data/nativetest64/$TEST/$TEST $ADB_TEST_BINARIES"
|
||||
done
|
|
@ -1,22 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
OUTPUT_DIR=$(realpath $(dirname "$0"))
|
||||
. "$OUTPUT_DIR"/include.sh
|
||||
|
||||
rm -rf "$OUTPUT_DIR"/report
|
||||
|
||||
cd $ANDROID_BUILD_TOP
|
||||
llvm-cov show --instr-profile="$OUTPUT_DIR"/adbd.profdata \
|
||||
$ANDROID_PRODUCT_OUT/apex/com.android.adbd/bin/adbd \
|
||||
/proc/self/cwd/system/core/adb \
|
||||
$ADB_TEST_BINARIES \
|
||||
--show-region-summary=false \
|
||||
--format=html -o "$OUTPUT_DIR"/report
|
||||
|
||||
llvm-cov report --instr-profile="$OUTPUT_DIR"/adbd.profdata \
|
||||
$ANDROID_PRODUCT_OUT/apex/com.android.adbd/bin/adbd \
|
||||
/proc/self/cwd/system/core/adb \
|
||||
$ADB_TEST_BINARIES \
|
||||
--show-region-summary=false
|
|
@ -1,22 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
OUTPUT_DIR=$(realpath $(dirname "$0"))
|
||||
. "$OUTPUT_DIR"/include.sh
|
||||
|
||||
BASE_PATH=/proc/self/cwd/system/core/adb
|
||||
PATHS=""
|
||||
if [[ $# == 0 ]]; then
|
||||
PATHS=$BASE_PATH
|
||||
else
|
||||
for arg in "$@"; do
|
||||
PATHS="$PATHS $BASE_PATH/$arg"
|
||||
done
|
||||
fi
|
||||
|
||||
cd $ANDROID_BUILD_TOP
|
||||
llvm-cov show --instr-profile="$OUTPUT_DIR"/adbd.profdata \
|
||||
$ANDROID_PRODUCT_OUT/apex/com.android.adbd/bin/adbd \
|
||||
$PATHS \
|
||||
$ADB_TEST_BINARIES
|
|
@ -1,83 +0,0 @@
|
|||
// Copyright (C) 2019 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.
|
||||
|
||||
cc_defaults {
|
||||
name: "libadb_crypto_defaults",
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Wthread-safety",
|
||||
"-Werror",
|
||||
],
|
||||
|
||||
compile_multilib: "both",
|
||||
|
||||
srcs: [
|
||||
"key.cpp",
|
||||
"rsa_2048_key.cpp",
|
||||
"x509_generator.cpp",
|
||||
],
|
||||
|
||||
target: {
|
||||
windows: {
|
||||
compile_multilib: "first",
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
|
||||
export_include_dirs: ["include"],
|
||||
|
||||
visibility: [
|
||||
"//bootable/recovery/minadbd:__subpackages__",
|
||||
"//packages/modules/adb:__subpackages__",
|
||||
"//system/core/adb:__subpackages__",
|
||||
],
|
||||
|
||||
host_supported: true,
|
||||
recovery_available: true,
|
||||
|
||||
shared_libs: [
|
||||
"libadb_protos",
|
||||
"libadb_sysdeps",
|
||||
"libbase",
|
||||
"liblog",
|
||||
"libcrypto",
|
||||
"libcrypto_utils",
|
||||
],
|
||||
}
|
||||
|
||||
cc_library {
|
||||
name: "libadb_crypto",
|
||||
defaults: ["libadb_crypto_defaults"],
|
||||
|
||||
apex_available: [
|
||||
"com.android.adbd",
|
||||
"test_com.android.adbd",
|
||||
],
|
||||
}
|
||||
|
||||
// For running atest (b/147158681)
|
||||
cc_library_static {
|
||||
name: "libadb_crypto_static",
|
||||
defaults: ["libadb_crypto_defaults"],
|
||||
|
||||
apex_available: [
|
||||
"//apex_available:platform",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libadb_protos_static",
|
||||
"libadb_sysdeps",
|
||||
],
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include "key_type.pb.h"
|
||||
|
||||
namespace adb {
|
||||
namespace crypto {
|
||||
|
||||
// Class that represents a public/private key pair.
|
||||
class Key {
|
||||
public:
|
||||
explicit Key(bssl::UniquePtr<EVP_PKEY>&& pkey, adb::proto::KeyType type)
|
||||
: pkey_(std::move(pkey)), key_type_(type) {}
|
||||
Key(Key&&) = default;
|
||||
Key& operator=(Key&&) = default;
|
||||
|
||||
EVP_PKEY* GetEvpPkey() const { return pkey_.get(); }
|
||||
adb::proto::KeyType GetKeyType() const { return key_type_; }
|
||||
static std::string ToPEMString(EVP_PKEY* pkey);
|
||||
|
||||
private:
|
||||
bssl::UniquePtr<EVP_PKEY> pkey_;
|
||||
adb::proto::KeyType key_type_;
|
||||
}; // Key
|
||||
|
||||
} // namespace crypto
|
||||
} // namespace adb
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "adb/crypto/key.h"
|
||||
|
||||
namespace adb {
|
||||
namespace crypto {
|
||||
|
||||
// Create a new RSA2048 key pair.
|
||||
std::optional<Key> CreateRSA2048Key();
|
||||
|
||||
// Generates the public key from the RSA private key.
|
||||
bool CalculatePublicKey(std::string* out, RSA* private_key);
|
||||
|
||||
} // namespace crypto
|
||||
} // namespace adb
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openssl/x509v3.h>
|
||||
|
||||
namespace adb {
|
||||
namespace crypto {
|
||||
|
||||
// Generate a X.509 certificate based on the key |pkey|.
|
||||
bssl::UniquePtr<X509> GenerateX509Certificate(EVP_PKEY* pkey);
|
||||
|
||||
// Convert X509* to PEM string format
|
||||
std::string X509ToPEMString(X509* x509);
|
||||
|
||||
} // namespace crypto
|
||||
} // namespace adb
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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 "adb/crypto/key.h"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/rsa.h>
|
||||
|
||||
namespace adb {
|
||||
namespace crypto {
|
||||
|
||||
// static
|
||||
std::string Key::ToPEMString(EVP_PKEY* pkey) {
|
||||
bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
|
||||
int rc = PEM_write_bio_PKCS8PrivateKey(bio.get(), pkey, nullptr, nullptr, 0, nullptr, nullptr);
|
||||
if (rc != 1) {
|
||||
LOG(ERROR) << "PEM_write_bio_PKCS8PrivateKey failed";
|
||||
return "";
|
||||
}
|
||||
|
||||
BUF_MEM* mem = nullptr;
|
||||
BIO_get_mem_ptr(bio.get(), &mem);
|
||||
if (!mem || !mem->data || !mem->length) {
|
||||
LOG(ERROR) << "BIO_get_mem_ptr failed";
|
||||
return "";
|
||||
}
|
||||
|
||||
return std::string(mem->data, mem->length);
|
||||
}
|
||||
|
||||
} // namespace crypto
|
||||
} // namespace adb
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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 "adb/crypto/rsa_2048_key.h"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <crypto_utils/android_pubkey.h>
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <sysdeps/env.h>
|
||||
|
||||
namespace adb {
|
||||
namespace crypto {
|
||||
|
||||
bool CalculatePublicKey(std::string* out, RSA* private_key) {
|
||||
uint8_t binary_key_data[ANDROID_PUBKEY_ENCODED_SIZE];
|
||||
if (!android_pubkey_encode(private_key, binary_key_data, sizeof(binary_key_data))) {
|
||||
LOG(ERROR) << "Failed to convert to public key";
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t expected_length;
|
||||
if (!EVP_EncodedLength(&expected_length, sizeof(binary_key_data))) {
|
||||
LOG(ERROR) << "Public key too large to base64 encode";
|
||||
return false;
|
||||
}
|
||||
|
||||
out->resize(expected_length);
|
||||
size_t actual_length = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(out->data()), binary_key_data,
|
||||
sizeof(binary_key_data));
|
||||
out->resize(actual_length);
|
||||
out->append(" ");
|
||||
out->append(sysdeps::GetLoginNameUTF8());
|
||||
out->append("@");
|
||||
out->append(sysdeps::GetHostNameUTF8());
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<Key> CreateRSA2048Key() {
|
||||
bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
|
||||
bssl::UniquePtr<BIGNUM> exponent(BN_new());
|
||||
bssl::UniquePtr<RSA> rsa(RSA_new());
|
||||
if (!pkey || !exponent || !rsa) {
|
||||
LOG(ERROR) << "Failed to allocate key";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
BN_set_word(exponent.get(), RSA_F4);
|
||||
RSA_generate_key_ex(rsa.get(), 2048, exponent.get(), nullptr);
|
||||
EVP_PKEY_set1_RSA(pkey.get(), rsa.get());
|
||||
|
||||
return std::optional<Key>{Key(std::move(pkey), adb::proto::KeyType::RSA_2048)};
|
||||
}
|
||||
|
||||
} // namespace crypto
|
||||
} // namespace adb
|
|
@ -1,42 +0,0 @@
|
|||
//
|
||||
// Copyright (C) 2019 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.
|
||||
//
|
||||
|
||||
cc_test {
|
||||
name: "adb_crypto_test",
|
||||
srcs: [
|
||||
"rsa_2048_key_test.cpp",
|
||||
"x509_generator_test.cpp",
|
||||
],
|
||||
|
||||
compile_multilib: "first",
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libcrypto",
|
||||
"libcrypto_utils",
|
||||
"libprotobuf-cpp-lite",
|
||||
],
|
||||
|
||||
// Let's statically link them so we don't have to install it onto the
|
||||
// system image for testing.
|
||||
static_libs: [
|
||||
"libadb_crypto_static",
|
||||
"libadb_protos_static",
|
||||
"libadb_sysdeps",
|
||||
],
|
||||
|
||||
test_suites: ["device-tests"],
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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 <gtest/gtest.h>
|
||||
|
||||
#include <resolv.h>
|
||||
|
||||
#include <adb/crypto/rsa_2048_key.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <crypto_utils/android_pubkey.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
namespace adb {
|
||||
namespace crypto {
|
||||
|
||||
TEST(RSA2048Key, Smoke) {
|
||||
auto rsa_2048 = CreateRSA2048Key();
|
||||
EXPECT_NE(rsa_2048, std::nullopt);
|
||||
EXPECT_EQ(rsa_2048->GetKeyType(), adb::proto::KeyType::RSA_2048);
|
||||
ASSERT_NE(rsa_2048->GetEvpPkey(), nullptr);
|
||||
|
||||
// The public key string format is expected to be: "<pub_key> <host_name>"
|
||||
std::string pub_key_plus_name;
|
||||
auto* rsa = EVP_PKEY_get0_RSA(rsa_2048->GetEvpPkey());
|
||||
ASSERT_TRUE(CalculatePublicKey(&pub_key_plus_name, rsa));
|
||||
std::vector<std::string> split = android::base::Split(std::string(pub_key_plus_name), " \t");
|
||||
EXPECT_EQ(split.size(), 2);
|
||||
|
||||
LOG(INFO) << "pub_key=[" << pub_key_plus_name << "]";
|
||||
|
||||
// Try to sign something and decode it.
|
||||
const char token[SHA_DIGEST_LENGTH] = "abcdefghij123456789";
|
||||
std::vector<uint8_t> sig(RSA_size(rsa));
|
||||
unsigned sig_len;
|
||||
EXPECT_EQ(RSA_sign(NID_sha1, reinterpret_cast<const uint8_t*>(token), sizeof(token), sig.data(),
|
||||
&sig_len, rsa),
|
||||
1);
|
||||
sig.resize(sig_len);
|
||||
|
||||
{
|
||||
uint8_t keybuf[ANDROID_PUBKEY_ENCODED_SIZE + 1];
|
||||
const std::string& pubkey = split[0];
|
||||
ASSERT_EQ(b64_pton(pubkey.c_str(), keybuf, sizeof(keybuf)), ANDROID_PUBKEY_ENCODED_SIZE);
|
||||
RSA* key = nullptr;
|
||||
ASSERT_TRUE(android_pubkey_decode(keybuf, ANDROID_PUBKEY_ENCODED_SIZE, &key));
|
||||
EXPECT_EQ(RSA_verify(NID_sha1, reinterpret_cast<const uint8_t*>(token), sizeof(token),
|
||||
sig.data(), sig.size(), key),
|
||||
1);
|
||||
RSA_free(key);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace crypto
|
||||
} // namespace adb
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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 <gtest/gtest.h>
|
||||
|
||||
#include <resolv.h>
|
||||
|
||||
#include <adb/crypto/rsa_2048_key.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <crypto_utils/android_pubkey.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
namespace adb {
|
||||
namespace crypto {
|
||||
|
||||
TEST(RSA2048Key, Smoke) {
|
||||
auto rsa_2048 = CreateRSA2048Key();
|
||||
EXPECT_NE(rsa_2048, std::nullopt);
|
||||
EXPECT_EQ(rsa_2048->GetKeyType(), adb::proto::KeyType::RSA_2048);
|
||||
ASSERT_NE(rsa_2048->GetEvpPkey(), nullptr);
|
||||
|
||||
// The public key string format is expected to be: "<pub_key> <host_name>"
|
||||
std::string pub_key_plus_name;
|
||||
auto* rsa = EVP_PKEY_get0_RSA(rsa_2048->GetEvpPkey());
|
||||
ASSERT_TRUE(CalculatePublicKey(&pub_key_plus_name, rsa));
|
||||
std::vector<std::string> split = android::base::Split(std::string(pub_key_plus_name), " \t");
|
||||
EXPECT_EQ(split.size(), 2);
|
||||
|
||||
LOG(INFO) << "pub_key=[" << pub_key_plus_name << "]";
|
||||
|
||||
std::string pemString = Key::ToPEMString(rsa_2048->GetEvpPkey());
|
||||
ASSERT_FALSE(pemString.empty());
|
||||
|
||||
// Try to sign something and decode it.
|
||||
const char token[SHA_DIGEST_LENGTH] = "abcdefghij123456789";
|
||||
std::vector<uint8_t> sig(RSA_size(rsa));
|
||||
unsigned sig_len;
|
||||
EXPECT_EQ(RSA_sign(NID_sha1, reinterpret_cast<const uint8_t*>(token), sizeof(token), sig.data(),
|
||||
&sig_len, rsa),
|
||||
1);
|
||||
sig.resize(sig_len);
|
||||
|
||||
{
|
||||
uint8_t keybuf[ANDROID_PUBKEY_ENCODED_SIZE + 1];
|
||||
const std::string& pubkey = split[0];
|
||||
ASSERT_EQ(b64_pton(pubkey.c_str(), keybuf, sizeof(keybuf)), ANDROID_PUBKEY_ENCODED_SIZE);
|
||||
RSA* key = nullptr;
|
||||
ASSERT_TRUE(android_pubkey_decode(keybuf, ANDROID_PUBKEY_ENCODED_SIZE, &key));
|
||||
EXPECT_EQ(RSA_verify(NID_sha1, reinterpret_cast<const uint8_t*>(token), sizeof(token),
|
||||
sig.data(), sig.size(), key),
|
||||
1);
|
||||
RSA_free(key);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace crypto
|
||||
} // namespace adb
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue