mirror of
https://github.com/multirom-nexus6p/multirom_adbd.git
synced 2025-11-04 05:35:34 +08:00
Init from Omni 6.0 repo
Taken from system/core/adb on the omnirom/android-6.0 branch (2b143b247a69deba1577dd1b2ca92138be2b48dc)
This commit is contained in:
11
.clang-format
Normal file
11
.clang-format
Normal file
@@ -0,0 +1,11 @@
|
||||
BasedOnStyle: Google
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: false
|
||||
|
||||
CommentPragmas: NOLINT:.*
|
||||
DerivePointerAlignment: false
|
||||
IndentWidth: 4
|
||||
PointerAlignment: Left
|
||||
TabWidth: 4
|
||||
UseTab: Never
|
||||
PenaltyExcessCharacter: 32
|
||||
260
Android.mk
Normal file
260
Android.mk
Normal file
@@ -0,0 +1,260 @@
|
||||
# Copyright 2005 The Android Open Source Project
|
||||
#
|
||||
# Android.mk for adb
|
||||
#
|
||||
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
ifeq ($(HOST_OS),windows)
|
||||
adb_host_clang := false # libc++ for mingw not ready yet.
|
||||
else
|
||||
adb_host_clang := true
|
||||
endif
|
||||
|
||||
adb_version := $(shell git -C $(LOCAL_PATH) rev-parse --short=12 HEAD 2>/dev/null)-android
|
||||
|
||||
ADB_COMMON_CFLAGS := \
|
||||
-Wall -Werror \
|
||||
-Wno-unused-parameter \
|
||||
-DADB_REVISION='"$(adb_version)"' \
|
||||
|
||||
# libadb
|
||||
# =========================================================
|
||||
|
||||
# Much of adb is duplicated in bootable/recovery/minadb and fastboot. Changes
|
||||
# made to adb rarely get ported to the other two, so the trees have diverged a
|
||||
# bit. We'd like to stop this because it is a maintenance nightmare, but the
|
||||
# divergence makes this difficult to do all at once. For now, we will start
|
||||
# small by moving common files into a static library. Hopefully some day we can
|
||||
# get enough of adb in here that we no longer need minadb. https://b/17626262
|
||||
LIBADB_SRC_FILES := \
|
||||
adb.cpp \
|
||||
adb_auth.cpp \
|
||||
adb_io.cpp \
|
||||
adb_listeners.cpp \
|
||||
adb_utils.cpp \
|
||||
sockets.cpp \
|
||||
transport.cpp \
|
||||
transport_local.cpp \
|
||||
transport_usb.cpp \
|
||||
|
||||
LIBADB_TEST_SRCS := \
|
||||
adb_io_test.cpp \
|
||||
adb_utils_test.cpp \
|
||||
transport_test.cpp \
|
||||
|
||||
LIBADB_CFLAGS := \
|
||||
$(ADB_COMMON_CFLAGS) \
|
||||
-Wno-missing-field-initializers \
|
||||
-fvisibility=hidden \
|
||||
|
||||
LIBADB_darwin_SRC_FILES := \
|
||||
fdevent.cpp \
|
||||
get_my_path_darwin.cpp \
|
||||
usb_osx.cpp \
|
||||
|
||||
LIBADB_linux_SRC_FILES := \
|
||||
fdevent.cpp \
|
||||
get_my_path_linux.cpp \
|
||||
usb_linux.cpp \
|
||||
|
||||
LIBADB_windows_SRC_FILES := \
|
||||
get_my_path_windows.cpp \
|
||||
sysdeps_win32.cpp \
|
||||
usb_windows.cpp \
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_CLANG := true
|
||||
LOCAL_MODULE := libadbd
|
||||
LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=0
|
||||
LOCAL_SRC_FILES := \
|
||||
$(LIBADB_SRC_FILES) \
|
||||
adb_auth_client.cpp \
|
||||
fdevent.cpp \
|
||||
jdwp_service.cpp \
|
||||
qemu_tracing.cpp \
|
||||
usb_linux_client.cpp \
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := libbase
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_CLANG := $(adb_host_clang)
|
||||
LOCAL_MODULE := libadb
|
||||
LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=1
|
||||
LOCAL_SRC_FILES := \
|
||||
$(LIBADB_SRC_FILES) \
|
||||
$(LIBADB_$(HOST_OS)_SRC_FILES) \
|
||||
adb_auth_host.cpp \
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := libbase
|
||||
|
||||
# Even though we're building a static library (and thus there's no link step for
|
||||
# this to take effect), this adds the SSL includes to our path.
|
||||
LOCAL_STATIC_LIBRARIES := libcrypto_static
|
||||
|
||||
ifeq ($(HOST_OS),windows)
|
||||
LOCAL_C_INCLUDES += development/host/windows/usb/api/
|
||||
endif
|
||||
|
||||
include $(BUILD_HOST_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_CLANG := true
|
||||
LOCAL_MODULE := adbd_test
|
||||
LOCAL_CFLAGS := -DADB_HOST=0 $(LIBADB_CFLAGS)
|
||||
LOCAL_SRC_FILES := $(LIBADB_TEST_SRCS)
|
||||
LOCAL_STATIC_LIBRARIES := libadbd
|
||||
LOCAL_SHARED_LIBRARIES := liblog libbase libcutils
|
||||
include $(BUILD_NATIVE_TEST)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_CLANG := $(adb_host_clang)
|
||||
LOCAL_MODULE := adb_test
|
||||
LOCAL_CFLAGS := -DADB_HOST=1 $(LIBADB_CFLAGS)
|
||||
LOCAL_SRC_FILES := $(LIBADB_TEST_SRCS) services.cpp
|
||||
LOCAL_SHARED_LIBRARIES := liblog libbase
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libadb \
|
||||
libcrypto_static \
|
||||
libcutils \
|
||||
|
||||
ifeq ($(HOST_OS),linux)
|
||||
LOCAL_LDLIBS += -lrt -ldl -lpthread
|
||||
endif
|
||||
|
||||
ifeq ($(HOST_OS),darwin)
|
||||
LOCAL_LDLIBS += -framework CoreFoundation -framework IOKit
|
||||
endif
|
||||
|
||||
include $(BUILD_HOST_NATIVE_TEST)
|
||||
|
||||
# adb device tracker (used by ddms) test tool
|
||||
# =========================================================
|
||||
|
||||
ifeq ($(HOST_OS),linux)
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_CLANG := $(adb_host_clang)
|
||||
LOCAL_MODULE := adb_device_tracker_test
|
||||
LOCAL_CFLAGS := -DADB_HOST=1 $(LIBADB_CFLAGS)
|
||||
LOCAL_SRC_FILES := test_track_devices.cpp
|
||||
LOCAL_SHARED_LIBRARIES := liblog libbase
|
||||
LOCAL_STATIC_LIBRARIES := libadb libcrypto_static libcutils
|
||||
LOCAL_LDLIBS += -lrt -ldl -lpthread
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
||||
endif
|
||||
|
||||
# adb host tool
|
||||
# =========================================================
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
ifeq ($(HOST_OS),linux)
|
||||
LOCAL_LDLIBS += -lrt -ldl -lpthread
|
||||
LOCAL_CFLAGS += -DWORKAROUND_BUG6558362
|
||||
endif
|
||||
|
||||
ifeq ($(HOST_OS),darwin)
|
||||
LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
|
||||
LOCAL_CFLAGS += -Wno-sizeof-pointer-memaccess -Wno-unused-parameter
|
||||
endif
|
||||
|
||||
ifeq ($(HOST_OS),windows)
|
||||
LOCAL_LDLIBS += -lws2_32 -lgdi32
|
||||
EXTRA_STATIC_LIBS := AdbWinApi
|
||||
endif
|
||||
|
||||
LOCAL_CLANG := $(adb_host_clang)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
adb_main.cpp \
|
||||
console.cpp \
|
||||
commandline.cpp \
|
||||
adb_client.cpp \
|
||||
services.cpp \
|
||||
file_sync_client.cpp \
|
||||
|
||||
LOCAL_CFLAGS += \
|
||||
$(ADB_COMMON_CFLAGS) \
|
||||
-D_GNU_SOURCE \
|
||||
-DADB_HOST=1 \
|
||||
|
||||
LOCAL_MODULE := adb
|
||||
LOCAL_MODULE_TAGS := debug
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libadb \
|
||||
libbase \
|
||||
libcrypto_static \
|
||||
libcutils \
|
||||
liblog \
|
||||
$(EXTRA_STATIC_LIBS) \
|
||||
|
||||
# libc++ not available on windows yet
|
||||
ifneq ($(HOST_OS),windows)
|
||||
LOCAL_CXX_STL := libc++_static
|
||||
endif
|
||||
|
||||
# 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
|
||||
LOCAL_SHARED_LIBRARIES :=
|
||||
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
||||
|
||||
$(call dist-for-goals,dist_files sdk,$(LOCAL_BUILT_MODULE))
|
||||
|
||||
ifeq ($(HOST_OS),windows)
|
||||
$(LOCAL_INSTALLED_MODULE): \
|
||||
$(HOST_OUT_EXECUTABLES)/AdbWinApi.dll \
|
||||
$(HOST_OUT_EXECUTABLES)/AdbWinUsbApi.dll
|
||||
endif
|
||||
|
||||
|
||||
# adbd device daemon
|
||||
# =========================================================
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_CLANG := true
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
adb_main.cpp \
|
||||
services.cpp \
|
||||
file_sync_service.cpp \
|
||||
framebuffer_service.cpp \
|
||||
remount_service.cpp \
|
||||
set_verity_enable_state_service.cpp \
|
||||
|
||||
LOCAL_CFLAGS := \
|
||||
$(ADB_COMMON_CFLAGS) \
|
||||
-DADB_HOST=0 \
|
||||
-D_GNU_SOURCE \
|
||||
-Wno-deprecated-declarations \
|
||||
|
||||
LOCAL_CFLAGS += -DALLOW_ADBD_NO_AUTH=$(if $(filter userdebug eng,$(TARGET_BUILD_VARIANT)),1,0)
|
||||
|
||||
ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
|
||||
LOCAL_CFLAGS += -DALLOW_ADBD_DISABLE_VERITY=1
|
||||
LOCAL_CFLAGS += -DALLOW_ADBD_ROOT=1
|
||||
endif
|
||||
|
||||
LOCAL_MODULE := adbd
|
||||
|
||||
LOCAL_FORCE_STATIC_EXECUTABLE := true
|
||||
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
|
||||
LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
|
||||
LOCAL_C_INCLUDES += system/extras/ext4_utils
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libadbd \
|
||||
libbase \
|
||||
libfs_mgr \
|
||||
liblog \
|
||||
libcutils \
|
||||
libc \
|
||||
libmincrypt \
|
||||
libselinux \
|
||||
libext4_utils_static \
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
2
CPPLINT.cfg
Normal file
2
CPPLINT.cfg
Normal file
@@ -0,0 +1,2 @@
|
||||
set noparent
|
||||
filter=-build/header_guard,-build/include,-readability/function
|
||||
0
MODULE_LICENSE_APACHE2
Normal file
0
MODULE_LICENSE_APACHE2
Normal file
191
NOTICE
Normal file
191
NOTICE
Normal file
@@ -0,0 +1,191 @@
|
||||
|
||||
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
|
||||
|
||||
139
OVERVIEW.TXT
Normal file
139
OVERVIEW.TXT
Normal file
@@ -0,0 +1,139 @@
|
||||
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 what is called a 'service' 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
|
||||
if 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. It doesn't
|
||||
need to know.
|
||||
|
||||
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
|
||||
state. They are a few couple 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.
|
||||
|
||||
3. As a special exception, for 'host:version', a 4-byte
|
||||
hex string corresponding to the server's internal version number
|
||||
|
||||
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.
|
||||
259
SERVICES.TXT
Normal file
259
SERVICES.TXT
Normal file
@@ -0,0 +1,259 @@
|
||||
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.
|
||||
|
||||
As a special exception, the server will respond with a 4-byte
|
||||
hex string corresponding to its internal version number, without
|
||||
any OKAY or FAIL.
|
||||
|
||||
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 synchronisation 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'.
|
||||
84
SYNC.TXT
Normal file
84
SYNC.TXT
Normal file
@@ -0,0 +1,84 @@
|
||||
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
|
||||
differ 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 called sync request and sync
|
||||
responses. The first four bytes is an id and specifies sync request is
|
||||
represented by four utf-8 characters. The last four bytes is 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
|
||||
SEND - Send a file to device
|
||||
RECV - Retreive a file from device
|
||||
|
||||
Not yet documented:
|
||||
STAT - Stat a file
|
||||
ULNK - Unlink (remove) a file. (Not currently supported)
|
||||
|
||||
For all of the sync request above the must be followed by length number of
|
||||
bytes containing an 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 beeing "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 an 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 chucks 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
|
||||
transfered. Each chunk must not be larger than 64k.
|
||||
|
||||
When the file is tranfered 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 chuck 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 chuck size. After follows chunk size number of bytes. This is repeated
|
||||
until the file is transfered. Each chuck will not be larger than 64k.
|
||||
|
||||
When the file is transfered a sync resopnse "DONE" is retrieved where the
|
||||
length can be ignored.
|
||||
|
||||
931
adb.cpp
Normal file
931
adb.cpp
Normal file
@@ -0,0 +1,931 @@
|
||||
/*
|
||||
* 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 TRACE_ADB
|
||||
|
||||
#include "sysdeps.h"
|
||||
#include "adb.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <base/stringprintf.h>
|
||||
#include <base/strings.h>
|
||||
|
||||
#include "adb_auth.h"
|
||||
#include "adb_io.h"
|
||||
#include "adb_listeners.h"
|
||||
#include "transport.h"
|
||||
|
||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
|
||||
|
||||
#if !ADB_HOST
|
||||
#include <cutils/properties.h>
|
||||
#include <sys/capability.h>
|
||||
#include <sys/mount.h>
|
||||
#endif
|
||||
|
||||
ADB_MUTEX_DEFINE( D_lock );
|
||||
|
||||
int HOST = 0;
|
||||
|
||||
#if !ADB_HOST
|
||||
const char *adb_device_banner = "device";
|
||||
#endif
|
||||
|
||||
void fatal(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
fprintf(stderr, "error: ");
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, "\n");
|
||||
va_end(ap);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
void fatal_errno(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
fprintf(stderr, "error: %s: ", strerror(errno));
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, "\n");
|
||||
va_end(ap);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
#if !ADB_HOST
|
||||
void start_device_log(void) {
|
||||
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);
|
||||
|
||||
std::string path = android::base::StringPrintf("/data/adb/adb-%s-%d", timestamp, getpid());
|
||||
int fd = unix_open(path.c_str(), 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());
|
||||
adb_close(fd);
|
||||
}
|
||||
#endif
|
||||
|
||||
int adb_trace_mask;
|
||||
|
||||
std::string get_trace_setting_from_env() {
|
||||
const char* setting = getenv("ADB_TRACE");
|
||||
if (setting == nullptr) {
|
||||
setting = "";
|
||||
}
|
||||
|
||||
return std::string(setting);
|
||||
}
|
||||
|
||||
#if !ADB_HOST
|
||||
std::string get_trace_setting_from_prop() {
|
||||
char buf[PROPERTY_VALUE_MAX];
|
||||
property_get("persist.adb.trace_mask", buf, "");
|
||||
return std::string(buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string get_trace_setting() {
|
||||
#if ADB_HOST
|
||||
return get_trace_setting_from_env();
|
||||
#else
|
||||
return get_trace_setting_from_prop();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Split the comma/space/colum/semi-column 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.
|
||||
void adb_trace_init() {
|
||||
const std::string trace_setting = get_trace_setting();
|
||||
|
||||
static const struct {
|
||||
const char* tag;
|
||||
int flag;
|
||||
} tags[] = {
|
||||
{ "1", 0 },
|
||||
{ "all", 0 },
|
||||
{ "adb", TRACE_ADB },
|
||||
{ "sockets", TRACE_SOCKETS },
|
||||
{ "packets", TRACE_PACKETS },
|
||||
{ "rwx", TRACE_RWX },
|
||||
{ "usb", TRACE_USB },
|
||||
{ "sync", TRACE_SYNC },
|
||||
{ "sysdeps", TRACE_SYSDEPS },
|
||||
{ "transport", TRACE_TRANSPORT },
|
||||
{ "jdwp", TRACE_JDWP },
|
||||
{ "services", TRACE_SERVICES },
|
||||
{ "auth", TRACE_AUTH },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
if (trace_setting.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use a comma/colon/semi-colon/space separated list
|
||||
const char* p = trace_setting.c_str();
|
||||
while (*p) {
|
||||
int len, tagn;
|
||||
|
||||
const char* q = strpbrk(p, " ,:;");
|
||||
if (q == NULL) {
|
||||
q = p + strlen(p);
|
||||
}
|
||||
len = q - p;
|
||||
|
||||
for (tagn = 0; tags[tagn].tag != NULL; tagn++) {
|
||||
int taglen = strlen(tags[tagn].tag);
|
||||
|
||||
if (len == taglen && !memcmp(tags[tagn].tag, p, len)) {
|
||||
int flag = tags[tagn].flag;
|
||||
if (flag == 0) {
|
||||
adb_trace_mask = ~0;
|
||||
return;
|
||||
}
|
||||
adb_trace_mask |= (1 << flag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
p = q;
|
||||
if (*p)
|
||||
p++;
|
||||
}
|
||||
|
||||
#if !ADB_HOST
|
||||
start_device_log();
|
||||
#endif
|
||||
}
|
||||
|
||||
apacket* get_apacket(void)
|
||||
{
|
||||
apacket* p = reinterpret_cast<apacket*>(malloc(sizeof(apacket)));
|
||||
if (p == nullptr) {
|
||||
fatal("failed to allocate an apacket");
|
||||
}
|
||||
|
||||
memset(p, 0, sizeof(apacket) - MAX_PAYLOAD);
|
||||
return p;
|
||||
}
|
||||
|
||||
void put_apacket(apacket *p)
|
||||
{
|
||||
free(p);
|
||||
}
|
||||
|
||||
void handle_online(atransport *t)
|
||||
{
|
||||
D("adb: online\n");
|
||||
t->online = 1;
|
||||
}
|
||||
|
||||
void handle_offline(atransport *t)
|
||||
{
|
||||
D("adb: offline\n");
|
||||
//Close the associated usb
|
||||
t->online = 0;
|
||||
run_transport_disconnects(t);
|
||||
}
|
||||
|
||||
#if DEBUG_PACKETS
|
||||
#define DUMPMAX 32
|
||||
void print_packet(const char *label, apacket *p)
|
||||
{
|
||||
char *tag;
|
||||
char *x;
|
||||
unsigned count;
|
||||
|
||||
switch(p->msg.command){
|
||||
case A_SYNC: tag = "SYNC"; break;
|
||||
case A_CNXN: tag = "CNXN" ; break;
|
||||
case A_OPEN: tag = "OPEN"; break;
|
||||
case A_OKAY: tag = "OKAY"; break;
|
||||
case A_CLSE: tag = "CLSE"; break;
|
||||
case A_WRTE: tag = "WRTE"; break;
|
||||
case A_AUTH: tag = "AUTH"; break;
|
||||
default: tag = "????"; break;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s: %s %08x %08x %04x \"",
|
||||
label, tag, p->msg.arg0, p->msg.arg1, p->msg.data_length);
|
||||
count = p->msg.data_length;
|
||||
x = (char*) p->data;
|
||||
if(count > DUMPMAX) {
|
||||
count = DUMPMAX;
|
||||
tag = "\n";
|
||||
} else {
|
||||
tag = "\"\n";
|
||||
}
|
||||
while(count-- > 0){
|
||||
if((*x >= ' ') && (*x < 127)) {
|
||||
fputc(*x, stderr);
|
||||
} else {
|
||||
fputc('.', stderr);
|
||||
}
|
||||
x++;
|
||||
}
|
||||
fputs(tag, stderr);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void send_ready(unsigned local, unsigned remote, atransport *t)
|
||||
{
|
||||
D("Calling send_ready \n");
|
||||
apacket *p = get_apacket();
|
||||
p->msg.command = A_OKAY;
|
||||
p->msg.arg0 = local;
|
||||
p->msg.arg1 = remote;
|
||||
send_packet(p, t);
|
||||
}
|
||||
|
||||
static void send_close(unsigned local, unsigned remote, atransport *t)
|
||||
{
|
||||
D("Calling send_close \n");
|
||||
apacket *p = get_apacket();
|
||||
p->msg.command = A_CLSE;
|
||||
p->msg.arg0 = local;
|
||||
p->msg.arg1 = remote;
|
||||
send_packet(p, t);
|
||||
}
|
||||
|
||||
static size_t fill_connect_data(char *buf, size_t bufsize)
|
||||
{
|
||||
#if ADB_HOST
|
||||
return snprintf(buf, bufsize, "host::") + 1;
|
||||
#else
|
||||
static const char *cnxn_props[] = {
|
||||
"ro.product.name",
|
||||
"ro.product.model",
|
||||
"ro.product.device",
|
||||
};
|
||||
static const int num_cnxn_props = ARRAY_SIZE(cnxn_props);
|
||||
int i;
|
||||
size_t remaining = bufsize;
|
||||
size_t len;
|
||||
|
||||
len = snprintf(buf, remaining, "%s::", adb_device_banner);
|
||||
remaining -= len;
|
||||
buf += len;
|
||||
for (i = 0; i < num_cnxn_props; i++) {
|
||||
char value[PROPERTY_VALUE_MAX];
|
||||
property_get(cnxn_props[i], value, "");
|
||||
len = snprintf(buf, remaining, "%s=%s;", cnxn_props[i], value);
|
||||
remaining -= len;
|
||||
buf += len;
|
||||
}
|
||||
|
||||
return bufsize - remaining + 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void send_connect(atransport *t)
|
||||
{
|
||||
D("Calling send_connect \n");
|
||||
apacket *cp = get_apacket();
|
||||
cp->msg.command = A_CNXN;
|
||||
cp->msg.arg0 = A_VERSION;
|
||||
cp->msg.arg1 = MAX_PAYLOAD;
|
||||
cp->msg.data_length = fill_connect_data((char *)cp->data,
|
||||
sizeof(cp->data));
|
||||
send_packet(cp, t);
|
||||
}
|
||||
|
||||
// qual_overwrite is used to overwrite a qualifier string. dst is a
|
||||
// pointer to a char pointer. It is assumed that if *dst is non-NULL, it
|
||||
// was malloc'ed and needs to freed. *dst will be set to a dup of src.
|
||||
// TODO: switch to std::string for these atransport fields instead.
|
||||
static void qual_overwrite(char** dst, const std::string& src) {
|
||||
free(*dst);
|
||||
*dst = strdup(src.c_str());
|
||||
}
|
||||
|
||||
void parse_banner(const char* banner, atransport* t) {
|
||||
D("parse_banner: %s\n", banner);
|
||||
|
||||
// The format is something like:
|
||||
// "device::ro.product.name=x;ro.product.model=y;ro.product.device=z;".
|
||||
std::vector<std::string> pieces = android::base::Split(banner, ":");
|
||||
|
||||
if (pieces.size() > 2) {
|
||||
const std::string& props = pieces[2];
|
||||
for (auto& prop : android::base::Split(props, ";")) {
|
||||
// The list of properties was traditionally ;-terminated rather than ;-separated.
|
||||
if (prop.empty()) continue;
|
||||
|
||||
std::vector<std::string> key_value = android::base::Split(prop, "=");
|
||||
if (key_value.size() != 2) continue;
|
||||
|
||||
const std::string& key = key_value[0];
|
||||
const std::string& value = key_value[1];
|
||||
if (key == "ro.product.name") {
|
||||
qual_overwrite(&t->product, value);
|
||||
} else if (key == "ro.product.model") {
|
||||
qual_overwrite(&t->model, value);
|
||||
} else if (key == "ro.product.device") {
|
||||
qual_overwrite(&t->device, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& type = pieces[0];
|
||||
if (type == "bootloader") {
|
||||
D("setting connection_state to CS_BOOTLOADER\n");
|
||||
t->connection_state = CS_BOOTLOADER;
|
||||
update_transports();
|
||||
} else if (type == "device") {
|
||||
D("setting connection_state to CS_DEVICE\n");
|
||||
t->connection_state = CS_DEVICE;
|
||||
update_transports();
|
||||
} else if (type == "recovery") {
|
||||
D("setting connection_state to CS_RECOVERY\n");
|
||||
t->connection_state = CS_RECOVERY;
|
||||
update_transports();
|
||||
} else if (type == "sideload") {
|
||||
D("setting connection_state to CS_SIDELOAD\n");
|
||||
t->connection_state = CS_SIDELOAD;
|
||||
update_transports();
|
||||
} else {
|
||||
D("setting connection_state to CS_HOST\n");
|
||||
t->connection_state = CS_HOST;
|
||||
}
|
||||
}
|
||||
|
||||
void handle_packet(apacket *p, atransport *t)
|
||||
{
|
||||
asocket *s;
|
||||
|
||||
D("handle_packet() %c%c%c%c\n", ((char*) (&(p->msg.command)))[0],
|
||||
((char*) (&(p->msg.command)))[1],
|
||||
((char*) (&(p->msg.command)))[2],
|
||||
((char*) (&(p->msg.command)))[3]);
|
||||
print_packet("recv", p);
|
||||
|
||||
switch(p->msg.command){
|
||||
case A_SYNC:
|
||||
if(p->msg.arg0){
|
||||
send_packet(p, t);
|
||||
if(HOST) send_connect(t);
|
||||
} else {
|
||||
t->connection_state = CS_OFFLINE;
|
||||
handle_offline(t);
|
||||
send_packet(p, t);
|
||||
}
|
||||
return;
|
||||
|
||||
case A_CNXN: /* CONNECT(version, maxdata, "system-id-string") */
|
||||
/* XXX verify version, etc */
|
||||
if(t->connection_state != CS_OFFLINE) {
|
||||
t->connection_state = CS_OFFLINE;
|
||||
handle_offline(t);
|
||||
}
|
||||
|
||||
parse_banner(reinterpret_cast<const char*>(p->data), t);
|
||||
|
||||
if (HOST || !auth_required) {
|
||||
handle_online(t);
|
||||
if (!HOST) send_connect(t);
|
||||
} else {
|
||||
send_auth_request(t);
|
||||
}
|
||||
break;
|
||||
|
||||
case A_AUTH:
|
||||
if (p->msg.arg0 == ADB_AUTH_TOKEN) {
|
||||
t->connection_state = CS_UNAUTHORIZED;
|
||||
t->key = adb_auth_nextkey(t->key);
|
||||
if (t->key) {
|
||||
send_auth_response(p->data, p->msg.data_length, t);
|
||||
} else {
|
||||
/* No more private keys to try, send the public key */
|
||||
send_auth_publickey(t);
|
||||
}
|
||||
} else if (p->msg.arg0 == ADB_AUTH_SIGNATURE) {
|
||||
if (adb_auth_verify(t->token, p->data, p->msg.data_length)) {
|
||||
adb_auth_verified(t);
|
||||
t->failed_auth_attempts = 0;
|
||||
} else {
|
||||
if (t->failed_auth_attempts++ > 10)
|
||||
adb_sleep_ms(1000);
|
||||
send_auth_request(t);
|
||||
}
|
||||
} else if (p->msg.arg0 == ADB_AUTH_RSAPUBLICKEY) {
|
||||
adb_auth_confirm_key(p->data, p->msg.data_length, t);
|
||||
}
|
||||
break;
|
||||
|
||||
case A_OPEN: /* OPEN(local-id, 0, "destination") */
|
||||
if (t->online && p->msg.arg0 != 0 && p->msg.arg1 == 0) {
|
||||
char *name = (char*) p->data;
|
||||
name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0;
|
||||
s = create_local_service_socket(name);
|
||||
if(s == 0) {
|
||||
send_close(0, p->msg.arg0, t);
|
||||
} else {
|
||||
s->peer = create_remote_socket(p->msg.arg0, t);
|
||||
s->peer->peer = s;
|
||||
send_ready(s->id, s->peer->id, t);
|
||||
s->ready(s);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case A_OKAY: /* READY(local-id, remote-id, "") */
|
||||
if (t->online && p->msg.arg0 != 0 && p->msg.arg1 != 0) {
|
||||
if((s = find_local_socket(p->msg.arg1, 0))) {
|
||||
if(s->peer == 0) {
|
||||
/* On first READY message, create the connection. */
|
||||
s->peer = create_remote_socket(p->msg.arg0, t);
|
||||
s->peer->peer = s;
|
||||
s->ready(s);
|
||||
} else if (s->peer->id == p->msg.arg0) {
|
||||
/* Other READY messages must use the same local-id */
|
||||
s->ready(s);
|
||||
} else {
|
||||
D("Invalid A_OKAY(%d,%d), expected A_OKAY(%d,%d) on transport %s\n",
|
||||
p->msg.arg0, p->msg.arg1, s->peer->id, p->msg.arg1, t->serial);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case A_CLSE: /* CLOSE(local-id, remote-id, "") or CLOSE(0, remote-id, "") */
|
||||
if (t->online && p->msg.arg1 != 0) {
|
||||
if((s = find_local_socket(p->msg.arg1, p->msg.arg0))) {
|
||||
/* According to protocol.txt, p->msg.arg0 might be 0 to indicate
|
||||
* a failed OPEN only. However, due to a bug in previous ADB
|
||||
* versions, CLOSE(0, remote-id, "") was also used for normal
|
||||
* CLOSE() operations.
|
||||
*
|
||||
* This is bad because it means a compromised adbd could
|
||||
* send packets to close connections between the host and
|
||||
* other devices. To avoid this, only allow this if the local
|
||||
* socket has a peer on the same transport.
|
||||
*/
|
||||
if (p->msg.arg0 == 0 && s->peer && s->peer->transport != t) {
|
||||
D("Invalid A_CLSE(0, %u) from transport %s, expected transport %s\n",
|
||||
p->msg.arg1, t->serial, s->peer->transport->serial);
|
||||
} else {
|
||||
s->close(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case A_WRTE: /* WRITE(local-id, remote-id, <data>) */
|
||||
if (t->online && p->msg.arg0 != 0 && p->msg.arg1 != 0) {
|
||||
if((s = find_local_socket(p->msg.arg1, p->msg.arg0))) {
|
||||
unsigned rid = p->msg.arg0;
|
||||
p->len = p->msg.data_length;
|
||||
|
||||
if(s->enqueue(s, p) == 0) {
|
||||
D("Enqueue the socket\n");
|
||||
send_ready(s->id, rid, t);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("handle_packet: what is %08x?!\n", p->msg.command);
|
||||
}
|
||||
|
||||
put_apacket(p);
|
||||
}
|
||||
|
||||
#if ADB_HOST
|
||||
|
||||
int launch_server(int server_port)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
/* we need to start the server in the background */
|
||||
/* we create a PIPE that will be used to wait for the server's "OK" */
|
||||
/* message since the pipe handles must be inheritable, we use a */
|
||||
/* security attribute */
|
||||
HANDLE pipe_read, pipe_write;
|
||||
HANDLE stdout_handle, stderr_handle;
|
||||
SECURITY_ATTRIBUTES sa;
|
||||
STARTUPINFO startup;
|
||||
PROCESS_INFORMATION pinfo;
|
||||
char program_path[ MAX_PATH ];
|
||||
int ret;
|
||||
|
||||
sa.nLength = sizeof(sa);
|
||||
sa.lpSecurityDescriptor = NULL;
|
||||
sa.bInheritHandle = TRUE;
|
||||
|
||||
/* create pipe, and ensure its read handle isn't inheritable */
|
||||
ret = CreatePipe( &pipe_read, &pipe_write, &sa, 0 );
|
||||
if (!ret) {
|
||||
fprintf(stderr, "CreatePipe() failure, error %ld\n", GetLastError() );
|
||||
return -1;
|
||||
}
|
||||
|
||||
SetHandleInformation( pipe_read, HANDLE_FLAG_INHERIT, 0 );
|
||||
|
||||
/* Some programs want to launch an adb command and collect its output by
|
||||
* calling CreateProcess with inheritable stdout/stderr handles, then
|
||||
* using read() to get its output. When this happens, the stdout/stderr
|
||||
* handles passed to the adb client process will also be inheritable.
|
||||
* When starting the adb server here, care must be taken to reset them
|
||||
* to non-inheritable.
|
||||
* Otherwise, something bad happens: even if the adb command completes,
|
||||
* the calling process is stuck while read()-ing from the stdout/stderr
|
||||
* descriptors, because they're connected to corresponding handles in the
|
||||
* adb server process (even if the latter never uses/writes to them).
|
||||
*/
|
||||
stdout_handle = GetStdHandle( STD_OUTPUT_HANDLE );
|
||||
stderr_handle = GetStdHandle( STD_ERROR_HANDLE );
|
||||
if (stdout_handle != INVALID_HANDLE_VALUE) {
|
||||
SetHandleInformation( stdout_handle, HANDLE_FLAG_INHERIT, 0 );
|
||||
}
|
||||
if (stderr_handle != INVALID_HANDLE_VALUE) {
|
||||
SetHandleInformation( stderr_handle, HANDLE_FLAG_INHERIT, 0 );
|
||||
}
|
||||
|
||||
ZeroMemory( &startup, sizeof(startup) );
|
||||
startup.cb = sizeof(startup);
|
||||
startup.hStdInput = GetStdHandle( STD_INPUT_HANDLE );
|
||||
startup.hStdOutput = pipe_write;
|
||||
startup.hStdError = GetStdHandle( STD_ERROR_HANDLE );
|
||||
startup.dwFlags = STARTF_USESTDHANDLES;
|
||||
|
||||
ZeroMemory( &pinfo, sizeof(pinfo) );
|
||||
|
||||
/* get path of current program */
|
||||
GetModuleFileName( NULL, program_path, sizeof(program_path) );
|
||||
char args[64];
|
||||
snprintf(args, sizeof(args), "adb -P %d fork-server server", server_port);
|
||||
ret = CreateProcess(
|
||||
program_path, /* program path */
|
||||
args,
|
||||
/* the fork-server argument will set the
|
||||
debug = 2 in the child */
|
||||
NULL, /* process handle is not inheritable */
|
||||
NULL, /* thread handle is not inheritable */
|
||||
TRUE, /* yes, inherit some handles */
|
||||
DETACHED_PROCESS, /* the new process doesn't have a console */
|
||||
NULL, /* use parent's environment block */
|
||||
NULL, /* use parent's starting directory */
|
||||
&startup, /* startup info, i.e. std handles */
|
||||
&pinfo );
|
||||
|
||||
CloseHandle( pipe_write );
|
||||
|
||||
if (!ret) {
|
||||
fprintf(stderr, "CreateProcess failure, error %ld\n", GetLastError() );
|
||||
CloseHandle( pipe_read );
|
||||
return -1;
|
||||
}
|
||||
|
||||
CloseHandle( pinfo.hProcess );
|
||||
CloseHandle( pinfo.hThread );
|
||||
|
||||
/* wait for the "OK\n" message */
|
||||
{
|
||||
char temp[3];
|
||||
DWORD count;
|
||||
|
||||
ret = ReadFile( pipe_read, temp, 3, &count, NULL );
|
||||
CloseHandle( pipe_read );
|
||||
if ( !ret ) {
|
||||
fprintf(stderr, "could not read ok from ADB Server, error = %ld\n", GetLastError() );
|
||||
return -1;
|
||||
}
|
||||
if (count != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') {
|
||||
fprintf(stderr, "ADB server didn't ACK\n" );
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#else /* !defined(_WIN32) */
|
||||
char path[PATH_MAX];
|
||||
int fd[2];
|
||||
|
||||
// set up a pipe so the child can tell us when it is ready.
|
||||
// fd[0] will be parent's end, and fd[1] will get mapped to stderr in the child.
|
||||
if (pipe(fd)) {
|
||||
fprintf(stderr, "pipe failed in launch_server, errno: %d\n", errno);
|
||||
return -1;
|
||||
}
|
||||
get_my_path(path, PATH_MAX);
|
||||
pid_t pid = fork();
|
||||
if(pid < 0) return -1;
|
||||
|
||||
if (pid == 0) {
|
||||
// child side of the fork
|
||||
|
||||
// redirect stderr to the pipe
|
||||
// we use stderr instead of stdout due to stdout's buffering behavior.
|
||||
adb_close(fd[0]);
|
||||
dup2(fd[1], STDERR_FILENO);
|
||||
adb_close(fd[1]);
|
||||
|
||||
char str_port[30];
|
||||
snprintf(str_port, sizeof(str_port), "%d", server_port);
|
||||
// child process
|
||||
int result = execl(path, "adb", "-P", str_port, "fork-server", "server", NULL);
|
||||
// this should not return
|
||||
fprintf(stderr, "OOPS! execl returned %d, errno: %d\n", result, errno);
|
||||
} else {
|
||||
// parent side of the fork
|
||||
|
||||
char temp[3];
|
||||
|
||||
temp[0] = 'A'; temp[1] = 'B'; temp[2] = 'C';
|
||||
// wait for the "OK\n" message
|
||||
adb_close(fd[1]);
|
||||
int ret = adb_read(fd[0], temp, 3);
|
||||
int saved_errno = errno;
|
||||
adb_close(fd[0]);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "could not read ok from ADB Server, errno = %d\n", saved_errno);
|
||||
return -1;
|
||||
}
|
||||
if (ret != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') {
|
||||
fprintf(stderr, "ADB server didn't ACK\n" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
setsid();
|
||||
}
|
||||
#endif /* !defined(_WIN32) */
|
||||
return 0;
|
||||
}
|
||||
#endif /* ADB_HOST */
|
||||
|
||||
// Try to handle a network forwarding request.
|
||||
// This returns 1 on success, 0 on failure, and -1 to indicate this is not
|
||||
// a forwarding-related request.
|
||||
int handle_forward_request(const char* service, transport_type ttype, char* serial, int reply_fd)
|
||||
{
|
||||
if (!strcmp(service, "list-forward")) {
|
||||
// Create the list of forward redirections.
|
||||
std::string listeners = format_listeners();
|
||||
#if ADB_HOST
|
||||
SendOkay(reply_fd);
|
||||
#endif
|
||||
SendProtocolString(reply_fd, listeners);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!strcmp(service, "killforward-all")) {
|
||||
remove_all_listeners();
|
||||
#if ADB_HOST
|
||||
/* On the host: 1st OKAY is connect, 2nd OKAY is status */
|
||||
SendOkay(reply_fd);
|
||||
#endif
|
||||
SendOkay(reply_fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!strncmp(service, "forward:",8) ||
|
||||
!strncmp(service, "killforward:",12)) {
|
||||
char *local, *remote;
|
||||
atransport *transport;
|
||||
|
||||
int createForward = strncmp(service, "kill", 4);
|
||||
int no_rebind = 0;
|
||||
|
||||
local = strchr(service, ':') + 1;
|
||||
|
||||
// Handle forward:norebind:<local>... here
|
||||
if (createForward && !strncmp(local, "norebind:", 9)) {
|
||||
no_rebind = 1;
|
||||
local = strchr(local, ':') + 1;
|
||||
}
|
||||
|
||||
remote = strchr(local,';');
|
||||
|
||||
if (createForward) {
|
||||
// Check forward: parameter format: '<local>;<remote>'
|
||||
if(remote == 0) {
|
||||
SendFail(reply_fd, "malformed forward spec");
|
||||
return 1;
|
||||
}
|
||||
|
||||
*remote++ = 0;
|
||||
if((local[0] == 0) || (remote[0] == 0) || (remote[0] == '*')) {
|
||||
SendFail(reply_fd, "malformed forward spec");
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
// Check killforward: parameter format: '<local>'
|
||||
if (local[0] == 0) {
|
||||
SendFail(reply_fd, "malformed forward spec");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
std::string error_msg;
|
||||
transport = acquire_one_transport(CS_ANY, ttype, serial, &error_msg);
|
||||
if (!transport) {
|
||||
SendFail(reply_fd, error_msg);
|
||||
return 1;
|
||||
}
|
||||
|
||||
install_status_t r;
|
||||
if (createForward) {
|
||||
r = install_listener(local, remote, transport, no_rebind);
|
||||
} else {
|
||||
r = remove_listener(local, transport);
|
||||
}
|
||||
if (r == INSTALL_STATUS_OK) {
|
||||
#if ADB_HOST
|
||||
/* On the host: 1st OKAY is connect, 2nd OKAY is status */
|
||||
SendOkay(reply_fd);
|
||||
#endif
|
||||
SendOkay(reply_fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string message;
|
||||
switch (r) {
|
||||
case INSTALL_STATUS_OK: message = " "; break;
|
||||
case INSTALL_STATUS_INTERNAL_ERROR: message = "internal error"; break;
|
||||
case INSTALL_STATUS_CANNOT_BIND:
|
||||
message = android::base::StringPrintf("cannot bind to socket: %s", strerror(errno));
|
||||
break;
|
||||
case INSTALL_STATUS_CANNOT_REBIND:
|
||||
message = android::base::StringPrintf("cannot rebind existing socket: %s", strerror(errno));
|
||||
break;
|
||||
case INSTALL_STATUS_LISTENER_NOT_FOUND: message = "listener not found"; break;
|
||||
}
|
||||
SendFail(reply_fd, message);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s)
|
||||
{
|
||||
if(!strcmp(service, "kill")) {
|
||||
fprintf(stderr,"adb server killed by remote request\n");
|
||||
fflush(stdout);
|
||||
SendOkay(reply_fd);
|
||||
usb_cleanup();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
#if ADB_HOST
|
||||
atransport *transport = NULL;
|
||||
// "transport:" is used for switching transport with a specified serial number
|
||||
// "transport-usb:" is used for switching transport to the only USB transport
|
||||
// "transport-local:" is used for switching transport to the only local transport
|
||||
// "transport-any:" is used for switching transport to the only transport
|
||||
if (!strncmp(service, "transport", strlen("transport"))) {
|
||||
transport_type type = kTransportAny;
|
||||
|
||||
if (!strncmp(service, "transport-usb", strlen("transport-usb"))) {
|
||||
type = kTransportUsb;
|
||||
} else if (!strncmp(service, "transport-local", strlen("transport-local"))) {
|
||||
type = kTransportLocal;
|
||||
} else if (!strncmp(service, "transport-any", strlen("transport-any"))) {
|
||||
type = kTransportAny;
|
||||
} else if (!strncmp(service, "transport:", strlen("transport:"))) {
|
||||
service += strlen("transport:");
|
||||
serial = service;
|
||||
}
|
||||
|
||||
std::string error_msg = "unknown failure";
|
||||
transport = acquire_one_transport(CS_ANY, type, serial, &error_msg);
|
||||
|
||||
if (transport) {
|
||||
s->transport = transport;
|
||||
SendOkay(reply_fd);
|
||||
} else {
|
||||
SendFail(reply_fd, error_msg);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// return a list of all connected devices
|
||||
if (!strncmp(service, "devices", 7)) {
|
||||
bool long_listing = (strcmp(service+7, "-l") == 0);
|
||||
if (long_listing || service[7] == 0) {
|
||||
D("Getting device list...\n");
|
||||
std::string device_list = list_transports(long_listing);
|
||||
D("Sending device list...\n");
|
||||
SendOkay(reply_fd);
|
||||
SendProtocolString(reply_fd, device_list);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// remove TCP transport
|
||||
if (!strncmp(service, "disconnect:", 11)) {
|
||||
char buffer[4096];
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
char* serial = service + 11;
|
||||
if (serial[0] == 0) {
|
||||
// disconnect from all TCP devices
|
||||
unregister_all_tcp_transports();
|
||||
} else {
|
||||
char hostbuf[100];
|
||||
// assume port 5555 if no port is specified
|
||||
if (!strchr(serial, ':')) {
|
||||
snprintf(hostbuf, sizeof(hostbuf) - 1, "%s:5555", serial);
|
||||
serial = hostbuf;
|
||||
}
|
||||
atransport *t = find_transport(serial);
|
||||
|
||||
if (t) {
|
||||
unregister_transport(t);
|
||||
} else {
|
||||
snprintf(buffer, sizeof(buffer), "No such device %s", serial);
|
||||
}
|
||||
}
|
||||
|
||||
SendOkay(reply_fd);
|
||||
SendProtocolString(reply_fd, buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// returns our value for ADB_SERVER_VERSION
|
||||
if (!strcmp(service, "version")) {
|
||||
SendOkay(reply_fd);
|
||||
SendProtocolString(reply_fd, android::base::StringPrintf("%04x", ADB_SERVER_VERSION));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!strncmp(service,"get-serialno",strlen("get-serialno"))) {
|
||||
const char *out = "unknown";
|
||||
transport = acquire_one_transport(CS_ANY, ttype, serial, NULL);
|
||||
if (transport && transport->serial) {
|
||||
out = transport->serial;
|
||||
}
|
||||
SendOkay(reply_fd);
|
||||
SendProtocolString(reply_fd, out);
|
||||
return 0;
|
||||
}
|
||||
if(!strncmp(service,"get-devpath",strlen("get-devpath"))) {
|
||||
const char *out = "unknown";
|
||||
transport = acquire_one_transport(CS_ANY, ttype, serial, NULL);
|
||||
if (transport && transport->devpath) {
|
||||
out = transport->devpath;
|
||||
}
|
||||
SendOkay(reply_fd);
|
||||
SendProtocolString(reply_fd, out);
|
||||
return 0;
|
||||
}
|
||||
// indicates a new emulator instance has started
|
||||
if (!strncmp(service,"emulator:",9)) {
|
||||
int port = atoi(service+9);
|
||||
local_connect(port);
|
||||
/* we don't even need to send a reply */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!strncmp(service,"get-state",strlen("get-state"))) {
|
||||
transport = acquire_one_transport(CS_ANY, ttype, serial, NULL);
|
||||
SendOkay(reply_fd);
|
||||
SendProtocolString(reply_fd, transport->connection_state_name());
|
||||
return 0;
|
||||
}
|
||||
#endif // ADB_HOST
|
||||
|
||||
int ret = handle_forward_request(service, ttype, serial, reply_fd);
|
||||
if (ret >= 0)
|
||||
return ret - 1;
|
||||
return -1;
|
||||
}
|
||||
381
adb.h
Normal file
381
adb.h
Normal file
@@ -0,0 +1,381 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __ADB_H
|
||||
#define __ADB_H
|
||||
|
||||
#include <limits.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "adb_trace.h"
|
||||
#include "fdevent.h"
|
||||
|
||||
#define MAX_PAYLOAD 4096
|
||||
|
||||
#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
|
||||
|
||||
// ADB protocol version.
|
||||
#define A_VERSION 0x01000000
|
||||
|
||||
// Used for help/version information.
|
||||
#define ADB_VERSION_MAJOR 1
|
||||
#define ADB_VERSION_MINOR 0
|
||||
|
||||
// Increment this when we want to force users to start a new adb server.
|
||||
#define ADB_SERVER_VERSION 32
|
||||
|
||||
struct atransport;
|
||||
struct usb_handle;
|
||||
|
||||
struct amessage {
|
||||
unsigned command; /* command identifier constant */
|
||||
unsigned arg0; /* first argument */
|
||||
unsigned arg1; /* second argument */
|
||||
unsigned data_length; /* length of payload (0 is allowed) */
|
||||
unsigned data_check; /* checksum of data payload */
|
||||
unsigned magic; /* command ^ 0xffffffff */
|
||||
};
|
||||
|
||||
struct apacket
|
||||
{
|
||||
apacket *next;
|
||||
|
||||
unsigned len;
|
||||
unsigned char *ptr;
|
||||
|
||||
amessage msg;
|
||||
unsigned char data[MAX_PAYLOAD];
|
||||
};
|
||||
|
||||
/* An asocket represents one half of a connection between a local and
|
||||
** remote entity. A local asocket is bound to a file descriptor. A
|
||||
** remote asocket is bound to the protocol engine.
|
||||
*/
|
||||
struct asocket {
|
||||
/* chain pointers for the local/remote list of
|
||||
** asockets that this asocket lives in
|
||||
*/
|
||||
asocket *next;
|
||||
asocket *prev;
|
||||
|
||||
/* the unique identifier for this asocket
|
||||
*/
|
||||
unsigned id;
|
||||
|
||||
/* flag: set when the socket's peer has closed
|
||||
** but packets are still queued for delivery
|
||||
*/
|
||||
int closing;
|
||||
|
||||
/* flag: quit adbd when both ends close the
|
||||
** local service socket
|
||||
*/
|
||||
int exit_on_close;
|
||||
|
||||
/* the asocket we are connected to
|
||||
*/
|
||||
|
||||
asocket *peer;
|
||||
|
||||
/* For local asockets, the fde is used to bind
|
||||
** us to our fd event system. For remote asockets
|
||||
** these fields are not used.
|
||||
*/
|
||||
fdevent fde;
|
||||
int fd;
|
||||
|
||||
/* queue of apackets waiting to be written
|
||||
*/
|
||||
apacket *pkt_first;
|
||||
apacket *pkt_last;
|
||||
|
||||
/* enqueue is called by our peer when it has data
|
||||
** for us. It should return 0 if we can accept more
|
||||
** data or 1 if not. If we return 1, we must call
|
||||
** peer->ready() when we once again are ready to
|
||||
** receive data.
|
||||
*/
|
||||
int (*enqueue)(asocket *s, apacket *pkt);
|
||||
|
||||
/* ready is called by the peer when it is ready for
|
||||
** us to send data via enqueue again
|
||||
*/
|
||||
void (*ready)(asocket *s);
|
||||
|
||||
/* shutdown is called by the peer before it goes away.
|
||||
** the socket should not do any further calls on its peer.
|
||||
** Always followed by a call to close. Optional, i.e. can be NULL.
|
||||
*/
|
||||
void (*shutdown)(asocket *s);
|
||||
|
||||
/* close is called by the peer when it has gone away.
|
||||
** we are not allowed to make any further calls on the
|
||||
** peer once our close method is called.
|
||||
*/
|
||||
void (*close)(asocket *s);
|
||||
|
||||
/* A socket is bound to atransport */
|
||||
atransport *transport;
|
||||
};
|
||||
|
||||
|
||||
/* 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;
|
||||
adisconnect* next;
|
||||
adisconnect* prev;
|
||||
};
|
||||
|
||||
|
||||
/* 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 transport_type {
|
||||
kTransportUsb,
|
||||
kTransportLocal,
|
||||
kTransportAny,
|
||||
kTransportHost,
|
||||
};
|
||||
|
||||
#define TOKEN_SIZE 20
|
||||
|
||||
struct atransport
|
||||
{
|
||||
atransport *next;
|
||||
atransport *prev;
|
||||
|
||||
int (*read_from_remote)(apacket *p, atransport *t);
|
||||
int (*write_to_remote)(apacket *p, atransport *t);
|
||||
void (*close)(atransport *t);
|
||||
void (*kick)(atransport *t);
|
||||
|
||||
int fd;
|
||||
int transport_socket;
|
||||
fdevent transport_fde;
|
||||
int ref_count;
|
||||
unsigned sync_token;
|
||||
int connection_state;
|
||||
int online;
|
||||
transport_type type;
|
||||
|
||||
/* usb handle or socket fd as needed */
|
||||
usb_handle *usb;
|
||||
int sfd;
|
||||
|
||||
/* used to identify transports for clients */
|
||||
char *serial;
|
||||
char *product;
|
||||
char *model;
|
||||
char *device;
|
||||
char *devpath;
|
||||
int adb_port; // Use for emulators (local transport)
|
||||
|
||||
/* a list of adisconnect callbacks called when the transport is kicked */
|
||||
int kicked;
|
||||
adisconnect disconnects;
|
||||
|
||||
void *key;
|
||||
unsigned char token[TOKEN_SIZE];
|
||||
fdevent auth_fde;
|
||||
unsigned failed_auth_attempts;
|
||||
|
||||
const char* connection_state_name() const;
|
||||
};
|
||||
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
struct alistener
|
||||
{
|
||||
alistener *next;
|
||||
alistener *prev;
|
||||
|
||||
fdevent fde;
|
||||
int fd;
|
||||
|
||||
char *local_name;
|
||||
char *connect_to;
|
||||
atransport *transport;
|
||||
adisconnect disconnect;
|
||||
};
|
||||
|
||||
|
||||
void print_packet(const char *label, apacket *p);
|
||||
|
||||
asocket *find_local_socket(unsigned local_id, unsigned remote_id);
|
||||
void install_local_socket(asocket *s);
|
||||
void remove_socket(asocket *s);
|
||||
void close_all_sockets(atransport *t);
|
||||
|
||||
asocket *create_local_socket(int fd);
|
||||
asocket *create_local_service_socket(const char *destination);
|
||||
|
||||
asocket *create_remote_socket(unsigned id, atransport *t);
|
||||
void connect_to_remote(asocket *s, const char *destination);
|
||||
void connect_to_smartsocket(asocket *s);
|
||||
|
||||
void fatal(const char *fmt, ...);
|
||||
void fatal_errno(const char *fmt, ...);
|
||||
|
||||
void handle_packet(apacket *p, atransport *t);
|
||||
|
||||
void get_my_path(char *s, size_t maxLen);
|
||||
int launch_server(int server_port);
|
||||
int adb_main(int is_daemon, int server_port);
|
||||
|
||||
/* initialize a transport object's func pointers and state */
|
||||
#if ADB_HOST
|
||||
int get_available_local_transport_index();
|
||||
#endif
|
||||
int init_socket_transport(atransport *t, int s, int port, int local);
|
||||
void init_usb_transport(atransport *t, usb_handle *usb, int state);
|
||||
|
||||
#if ADB_HOST
|
||||
atransport* find_emulator_transport_by_adb_port(int adb_port);
|
||||
#endif
|
||||
|
||||
int service_to_fd(const char *name);
|
||||
#if ADB_HOST
|
||||
asocket *host_service_to_socket(const char* name, const char *serial);
|
||||
#endif
|
||||
|
||||
#if !ADB_HOST
|
||||
int init_jdwp(void);
|
||||
asocket* create_jdwp_service_socket();
|
||||
asocket* create_jdwp_tracker_service_socket();
|
||||
int create_jdwp_connection_fd(int jdwp_pid);
|
||||
#endif
|
||||
|
||||
int handle_forward_request(const char* service, transport_type ttype, char* serial, int reply_fd);
|
||||
|
||||
#if !ADB_HOST
|
||||
void framebuffer_service(int fd, void *cookie);
|
||||
void set_verity_enabled_state_service(int fd, void* cookie);
|
||||
#endif
|
||||
|
||||
/* 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
|
||||
|
||||
#if ADB_HOST_ON_TARGET
|
||||
/* adb and adbd are coexisting on the target, so use 5038 for adb
|
||||
* to avoid conflicting with adbd's usage of 5037
|
||||
*/
|
||||
# define DEFAULT_ADB_PORT 5038
|
||||
#else
|
||||
# define DEFAULT_ADB_PORT 5037
|
||||
#endif
|
||||
|
||||
#define DEFAULT_ADB_LOCAL_TRANSPORT_PORT 5555
|
||||
|
||||
#define ADB_CLASS 0xff
|
||||
#define ADB_SUBCLASS 0x42
|
||||
#define ADB_PROTOCOL 0x1
|
||||
|
||||
|
||||
void local_init(int port);
|
||||
int local_connect(int port);
|
||||
int local_connect_arbitrary_ports(int console_port, int adb_port);
|
||||
|
||||
/* usb host/client interface */
|
||||
void usb_init();
|
||||
void usb_cleanup();
|
||||
int usb_write(usb_handle *h, const void *data, int len);
|
||||
int usb_read(usb_handle *h, void *data, int len);
|
||||
int usb_close(usb_handle *h);
|
||||
void usb_kick(usb_handle *h);
|
||||
|
||||
/* used for USB device detection */
|
||||
#if ADB_HOST
|
||||
int is_adb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_protocol);
|
||||
#endif
|
||||
|
||||
int adb_commandline(int argc, const char **argv);
|
||||
|
||||
int connection_state(atransport *t);
|
||||
|
||||
#define CS_ANY -1
|
||||
#define CS_OFFLINE 0
|
||||
#define CS_BOOTLOADER 1
|
||||
#define CS_DEVICE 2
|
||||
#define CS_HOST 3
|
||||
#define CS_RECOVERY 4
|
||||
#define CS_NOPERM 5 /* Insufficient permissions to communicate with the device */
|
||||
#define CS_SIDELOAD 6
|
||||
#define CS_UNAUTHORIZED 7
|
||||
|
||||
extern const char *adb_device_banner;
|
||||
extern int HOST;
|
||||
extern int SHELL_EXIT_NOTIFY_FD;
|
||||
|
||||
enum subproc_mode {
|
||||
SUBPROC_PTY = 0,
|
||||
SUBPROC_RAW = 1,
|
||||
} ;
|
||||
|
||||
#define CHUNK_SIZE (64*1024)
|
||||
|
||||
#if !ADB_HOST
|
||||
#define USB_ADB_PATH "/dev/android_adb"
|
||||
|
||||
#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
|
||||
|
||||
int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s);
|
||||
|
||||
void handle_online(atransport *t);
|
||||
void handle_offline(atransport *t);
|
||||
|
||||
void send_connect(atransport *t);
|
||||
|
||||
#endif
|
||||
95
adb_auth.cpp
Normal file
95
adb_auth.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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 TRACE_ADB
|
||||
|
||||
#include "sysdeps.h"
|
||||
#include "adb_auth.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "transport.h"
|
||||
|
||||
bool auth_required = true;
|
||||
|
||||
void send_auth_request(atransport *t)
|
||||
{
|
||||
D("Calling send_auth_request\n");
|
||||
apacket *p;
|
||||
int ret;
|
||||
|
||||
ret = adb_auth_generate_token(t->token, sizeof(t->token));
|
||||
if (ret != sizeof(t->token)) {
|
||||
D("Error generating token ret=%d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
p = get_apacket();
|
||||
memcpy(p->data, t->token, ret);
|
||||
p->msg.command = A_AUTH;
|
||||
p->msg.arg0 = ADB_AUTH_TOKEN;
|
||||
p->msg.data_length = ret;
|
||||
send_packet(p, t);
|
||||
}
|
||||
|
||||
void send_auth_response(uint8_t *token, size_t token_size, atransport *t)
|
||||
{
|
||||
D("Calling send_auth_response\n");
|
||||
apacket *p = get_apacket();
|
||||
int ret;
|
||||
|
||||
ret = adb_auth_sign(t->key, token, token_size, p->data);
|
||||
if (!ret) {
|
||||
D("Error signing the token\n");
|
||||
put_apacket(p);
|
||||
return;
|
||||
}
|
||||
|
||||
p->msg.command = A_AUTH;
|
||||
p->msg.arg0 = ADB_AUTH_SIGNATURE;
|
||||
p->msg.data_length = ret;
|
||||
send_packet(p, t);
|
||||
}
|
||||
|
||||
void send_auth_publickey(atransport *t)
|
||||
{
|
||||
D("Calling send_auth_publickey\n");
|
||||
apacket *p = get_apacket();
|
||||
int ret;
|
||||
|
||||
ret = adb_auth_get_userkey(p->data, sizeof(p->data));
|
||||
if (!ret) {
|
||||
D("Failed to get user public key\n");
|
||||
put_apacket(p);
|
||||
return;
|
||||
}
|
||||
|
||||
p->msg.command = A_AUTH;
|
||||
p->msg.arg0 = ADB_AUTH_RSAPUBLICKEY;
|
||||
p->msg.data_length = ret;
|
||||
send_packet(p, t);
|
||||
}
|
||||
|
||||
void adb_auth_verified(atransport *t)
|
||||
{
|
||||
handle_online(t);
|
||||
send_connect(t);
|
||||
}
|
||||
67
adb_auth.h
Normal file
67
adb_auth.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
extern bool auth_required;
|
||||
|
||||
int adb_auth_keygen(const char* filename);
|
||||
void adb_auth_verified(atransport *t);
|
||||
|
||||
void send_auth_request(atransport *t);
|
||||
void send_auth_response(uint8_t *token, size_t token_size, atransport *t);
|
||||
void send_auth_publickey(atransport *t);
|
||||
|
||||
/* 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(void);
|
||||
int adb_auth_sign(void *key, const unsigned char* token, size_t token_size,
|
||||
unsigned char* sig);
|
||||
void *adb_auth_nextkey(void *current);
|
||||
int adb_auth_get_userkey(unsigned char *data, size_t len);
|
||||
|
||||
static inline int adb_auth_generate_token(void *token, size_t token_size) { return 0; }
|
||||
static inline int adb_auth_verify(void *token, void *sig, int siglen) { return 0; }
|
||||
static inline void adb_auth_confirm_key(unsigned char *data, size_t len, atransport *t) { }
|
||||
|
||||
#else // !ADB_HOST
|
||||
|
||||
static inline int adb_auth_sign(void* key, const unsigned char* token,
|
||||
size_t token_size, unsigned char* sig) {
|
||||
return 0;
|
||||
}
|
||||
static inline void *adb_auth_nextkey(void *current) { return NULL; }
|
||||
static inline int adb_auth_get_userkey(unsigned char *data, size_t len) { return 0; }
|
||||
|
||||
void adbd_auth_init(void);
|
||||
void adbd_cloexec_auth_socket();
|
||||
int adb_auth_generate_token(void *token, size_t token_size);
|
||||
int adb_auth_verify(uint8_t* token, uint8_t* sig, int siglen);
|
||||
void adb_auth_confirm_key(unsigned char *data, size_t len, atransport *t);
|
||||
|
||||
#endif // ADB_HOST
|
||||
|
||||
#endif // __ADB_AUTH_H
|
||||
275
adb_auth_client.cpp
Normal file
275
adb_auth_client.cpp
Normal file
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* 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 TRACE_AUTH
|
||||
|
||||
#include "sysdeps.h"
|
||||
#include "adb_auth.h"
|
||||
|
||||
#include <resolv.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cutils/list.h"
|
||||
#include "cutils/sockets.h"
|
||||
#include "mincrypt/rsa.h"
|
||||
#include "mincrypt/sha.h"
|
||||
|
||||
#include "adb.h"
|
||||
#include "fdevent.h"
|
||||
#include "transport.h"
|
||||
|
||||
struct adb_public_key {
|
||||
struct listnode node;
|
||||
RSAPublicKey key;
|
||||
};
|
||||
|
||||
static const char *key_paths[] = {
|
||||
"/adb_keys",
|
||||
"/data/misc/adb/adb_keys",
|
||||
NULL
|
||||
};
|
||||
|
||||
static fdevent listener_fde;
|
||||
static int framework_fd = -1;
|
||||
|
||||
static void usb_disconnected(void* unused, atransport* t);
|
||||
static struct adisconnect usb_disconnect = { usb_disconnected, 0, 0, 0 };
|
||||
static atransport* usb_transport;
|
||||
static bool needs_retry = false;
|
||||
|
||||
static void read_keys(const char *file, struct listnode *list)
|
||||
{
|
||||
FILE *f;
|
||||
char buf[MAX_PAYLOAD];
|
||||
char *sep;
|
||||
int ret;
|
||||
|
||||
f = fopen(file, "re");
|
||||
if (!f) {
|
||||
D("Can't open '%s'\n", file);
|
||||
return;
|
||||
}
|
||||
|
||||
while (fgets(buf, sizeof(buf), f)) {
|
||||
/* Allocate 4 extra bytes to decode the base64 data in-place */
|
||||
auto key = reinterpret_cast<adb_public_key*>(
|
||||
calloc(1, sizeof(adb_public_key) + 4));
|
||||
if (key == nullptr) {
|
||||
D("Can't malloc key\n");
|
||||
break;
|
||||
}
|
||||
|
||||
sep = strpbrk(buf, " \t");
|
||||
if (sep)
|
||||
*sep = '\0';
|
||||
|
||||
ret = __b64_pton(buf, (u_char *)&key->key, sizeof(key->key) + 4);
|
||||
if (ret != sizeof(key->key)) {
|
||||
D("%s: Invalid base64 data ret=%d\n", file, ret);
|
||||
free(key);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key->key.len != RSANUMWORDS) {
|
||||
D("%s: Invalid key len %d\n", file, key->key.len);
|
||||
free(key);
|
||||
continue;
|
||||
}
|
||||
|
||||
list_add_tail(list, &key->node);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
static void free_keys(struct listnode *list)
|
||||
{
|
||||
struct listnode *item;
|
||||
|
||||
while (!list_empty(list)) {
|
||||
item = list_head(list);
|
||||
list_remove(item);
|
||||
free(node_to_item(item, struct adb_public_key, node));
|
||||
}
|
||||
}
|
||||
|
||||
static void load_keys(struct listnode *list)
|
||||
{
|
||||
const char* path;
|
||||
const char** paths = key_paths;
|
||||
struct stat buf;
|
||||
|
||||
list_init(list);
|
||||
|
||||
while ((path = *paths++)) {
|
||||
if (!stat(path, &buf)) {
|
||||
D("Loading keys from '%s'\n", path);
|
||||
read_keys(path, list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int adb_auth_generate_token(void *token, size_t token_size)
|
||||
{
|
||||
FILE *f;
|
||||
int ret;
|
||||
|
||||
f = fopen("/dev/urandom", "re");
|
||||
if (!f)
|
||||
return 0;
|
||||
|
||||
ret = fread(token, token_size, 1, f);
|
||||
|
||||
fclose(f);
|
||||
return ret * token_size;
|
||||
}
|
||||
|
||||
int adb_auth_verify(uint8_t* token, uint8_t* sig, int siglen)
|
||||
{
|
||||
struct listnode *item;
|
||||
struct listnode key_list;
|
||||
int ret = 0;
|
||||
|
||||
if (siglen != RSANUMBYTES)
|
||||
return 0;
|
||||
|
||||
load_keys(&key_list);
|
||||
|
||||
list_for_each(item, &key_list) {
|
||||
adb_public_key* key = node_to_item(item, struct adb_public_key, node);
|
||||
ret = RSA_verify(&key->key, sig, siglen, token, SHA_DIGEST_SIZE);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
free_keys(&key_list);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void usb_disconnected(void* unused, atransport* t)
|
||||
{
|
||||
D("USB disconnect\n");
|
||||
remove_transport_disconnect(usb_transport, &usb_disconnect);
|
||||
usb_transport = NULL;
|
||||
needs_retry = false;
|
||||
}
|
||||
|
||||
static void adb_auth_event(int fd, unsigned events, void *data)
|
||||
{
|
||||
char response[2];
|
||||
int ret;
|
||||
|
||||
if (events & FDE_READ) {
|
||||
ret = unix_read(fd, response, sizeof(response));
|
||||
if (ret <= 0) {
|
||||
D("Framework disconnect\n");
|
||||
if (usb_transport)
|
||||
fdevent_remove(&usb_transport->auth_fde);
|
||||
framework_fd = -1;
|
||||
}
|
||||
else if (ret == 2 && response[0] == 'O' && response[1] == 'K') {
|
||||
if (usb_transport)
|
||||
adb_auth_verified(usb_transport);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void adb_auth_confirm_key(unsigned char *key, size_t len, atransport *t)
|
||||
{
|
||||
char msg[MAX_PAYLOAD];
|
||||
int ret;
|
||||
|
||||
if (!usb_transport) {
|
||||
usb_transport = t;
|
||||
add_transport_disconnect(t, &usb_disconnect);
|
||||
}
|
||||
|
||||
if (framework_fd < 0) {
|
||||
D("Client not connected\n");
|
||||
needs_retry = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (key[len - 1] != '\0') {
|
||||
D("Key must be a null-terminated string\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ret = snprintf(msg, sizeof(msg), "PK%s", key);
|
||||
if (ret >= (signed)sizeof(msg)) {
|
||||
D("Key too long. ret=%d", ret);
|
||||
return;
|
||||
}
|
||||
D("Sending '%s'\n", msg);
|
||||
|
||||
ret = unix_write(framework_fd, msg, ret);
|
||||
if (ret < 0) {
|
||||
D("Failed to write PK, errno=%d\n", errno);
|
||||
return;
|
||||
}
|
||||
|
||||
fdevent_install(&t->auth_fde, framework_fd, adb_auth_event, t);
|
||||
fdevent_add(&t->auth_fde, FDE_READ);
|
||||
}
|
||||
|
||||
static void adb_auth_listener(int fd, unsigned events, void *data)
|
||||
{
|
||||
struct sockaddr addr;
|
||||
socklen_t alen;
|
||||
int s;
|
||||
|
||||
alen = sizeof(addr);
|
||||
|
||||
s = adb_socket_accept(fd, &addr, &alen);
|
||||
if (s < 0) {
|
||||
D("Failed to accept: errno=%d\n", errno);
|
||||
return;
|
||||
}
|
||||
|
||||
framework_fd = s;
|
||||
|
||||
if (needs_retry) {
|
||||
needs_retry = false;
|
||||
send_auth_request(usb_transport);
|
||||
}
|
||||
}
|
||||
|
||||
void adbd_cloexec_auth_socket() {
|
||||
int fd = android_get_control_socket("adbd");
|
||||
if (fd == -1) {
|
||||
D("Failed to get adbd socket\n");
|
||||
return;
|
||||
}
|
||||
fcntl(fd, F_SETFD, FD_CLOEXEC);
|
||||
}
|
||||
|
||||
void adbd_auth_init(void) {
|
||||
int fd = android_get_control_socket("adbd");
|
||||
if (fd == -1) {
|
||||
D("Failed to get adbd socket\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (listen(fd, 4) == -1) {
|
||||
D("Failed to listen on '%d'\n", fd);
|
||||
return;
|
||||
}
|
||||
|
||||
fdevent_install(&listener_fde, fd, adb_auth_listener, NULL);
|
||||
fdevent_add(&listener_fde, FDE_READ);
|
||||
}
|
||||
464
adb_auth_host.cpp
Normal file
464
adb_auth_host.cpp
Normal file
@@ -0,0 +1,464 @@
|
||||
/*
|
||||
* 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 TRACE_AUTH
|
||||
|
||||
#include "sysdeps.h"
|
||||
#include "adb_auth.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
# ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# endif
|
||||
# include "windows.h"
|
||||
# include "shlobj.h"
|
||||
#else
|
||||
# include <sys/types.h>
|
||||
# include <sys/stat.h>
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "adb.h"
|
||||
|
||||
/* HACK: we need the RSAPublicKey struct
|
||||
* but RSA_verify conflits with openssl */
|
||||
#define RSA_verify RSA_verify_mincrypt
|
||||
#include "mincrypt/rsa.h"
|
||||
#undef RSA_verify
|
||||
|
||||
#include <base/strings.h>
|
||||
#include <cutils/list.h>
|
||||
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/objects.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#if defined(OPENSSL_IS_BORINGSSL)
|
||||
#include <openssl/base64.h>
|
||||
#endif
|
||||
|
||||
#define ANDROID_PATH ".android"
|
||||
#define ADB_KEY_FILE "adbkey"
|
||||
|
||||
struct adb_private_key {
|
||||
struct listnode node;
|
||||
RSA *rsa;
|
||||
};
|
||||
|
||||
static struct listnode key_list;
|
||||
|
||||
|
||||
/* Convert OpenSSL RSA private key to android pre-computed RSAPublicKey format */
|
||||
static int RSA_to_RSAPublicKey(RSA *rsa, RSAPublicKey *pkey)
|
||||
{
|
||||
int ret = 1;
|
||||
unsigned int i;
|
||||
|
||||
BN_CTX* ctx = BN_CTX_new();
|
||||
BIGNUM* r32 = BN_new();
|
||||
BIGNUM* rr = BN_new();
|
||||
BIGNUM* r = BN_new();
|
||||
BIGNUM* rem = BN_new();
|
||||
BIGNUM* n = BN_new();
|
||||
BIGNUM* n0inv = BN_new();
|
||||
|
||||
if (RSA_size(rsa) != RSANUMBYTES) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
BN_set_bit(r32, 32);
|
||||
BN_copy(n, rsa->n);
|
||||
BN_set_bit(r, RSANUMWORDS * 32);
|
||||
BN_mod_sqr(rr, r, n, ctx);
|
||||
BN_div(NULL, rem, n, r32, ctx);
|
||||
BN_mod_inverse(n0inv, rem, r32, ctx);
|
||||
|
||||
pkey->len = RSANUMWORDS;
|
||||
pkey->n0inv = 0 - BN_get_word(n0inv);
|
||||
for (i = 0; i < RSANUMWORDS; i++) {
|
||||
BN_div(rr, rem, rr, r32, ctx);
|
||||
pkey->rr[i] = BN_get_word(rem);
|
||||
BN_div(n, rem, n, r32, ctx);
|
||||
pkey->n[i] = BN_get_word(rem);
|
||||
}
|
||||
pkey->exponent = BN_get_word(rsa->e);
|
||||
|
||||
out:
|
||||
BN_free(n0inv);
|
||||
BN_free(n);
|
||||
BN_free(rem);
|
||||
BN_free(r);
|
||||
BN_free(rr);
|
||||
BN_free(r32);
|
||||
BN_CTX_free(ctx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void get_user_info(char *buf, size_t len)
|
||||
{
|
||||
char hostname[1024], username[1024];
|
||||
int ret = -1;
|
||||
|
||||
if (getenv("HOSTNAME") != NULL) {
|
||||
strncpy(hostname, getenv("HOSTNAME"), sizeof(hostname));
|
||||
hostname[sizeof(hostname)-1] = '\0';
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
if (ret < 0)
|
||||
ret = gethostname(hostname, sizeof(hostname));
|
||||
#endif
|
||||
if (ret < 0)
|
||||
strcpy(hostname, "unknown");
|
||||
|
||||
ret = -1;
|
||||
|
||||
if (getenv("LOGNAME") != NULL) {
|
||||
strncpy(username, getenv("LOGNAME"), sizeof(username));
|
||||
username[sizeof(username)-1] = '\0';
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
#if !defined _WIN32 && !defined ADB_HOST_ON_TARGET
|
||||
if (ret < 0)
|
||||
ret = getlogin_r(username, sizeof(username));
|
||||
#endif
|
||||
if (ret < 0)
|
||||
strcpy(username, "unknown");
|
||||
|
||||
ret = snprintf(buf, len, " %s@%s", username, hostname);
|
||||
if (ret >= (signed)len)
|
||||
buf[len - 1] = '\0';
|
||||
}
|
||||
|
||||
static int write_public_keyfile(RSA *private_key, const char *private_key_path)
|
||||
{
|
||||
RSAPublicKey pkey;
|
||||
FILE *outfile = NULL;
|
||||
char path[PATH_MAX], info[MAX_PAYLOAD];
|
||||
uint8_t* encoded = nullptr;
|
||||
size_t encoded_length;
|
||||
int ret = 0;
|
||||
|
||||
if (snprintf(path, sizeof(path), "%s.pub", private_key_path) >=
|
||||
(int)sizeof(path)) {
|
||||
D("Path too long while writing public key\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!RSA_to_RSAPublicKey(private_key, &pkey)) {
|
||||
D("Failed to convert to publickey\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
outfile = fopen(path, "w");
|
||||
if (!outfile) {
|
||||
D("Failed to open '%s'\n", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
D("Writing public key to '%s'\n", path);
|
||||
|
||||
#if defined(OPENSSL_IS_BORINGSSL)
|
||||
if (!EVP_EncodedLength(&encoded_length, sizeof(pkey))) {
|
||||
D("Public key too large to base64 encode");
|
||||
goto out;
|
||||
}
|
||||
#else
|
||||
/* While we switch from OpenSSL to BoringSSL we have to implement
|
||||
* |EVP_EncodedLength| here. */
|
||||
encoded_length = 1 + ((sizeof(pkey) + 2) / 3 * 4);
|
||||
#endif
|
||||
|
||||
encoded = new uint8_t[encoded_length];
|
||||
if (encoded == nullptr) {
|
||||
D("Allocation failure");
|
||||
goto out;
|
||||
}
|
||||
|
||||
encoded_length = EVP_EncodeBlock(encoded, (uint8_t*) &pkey, sizeof(pkey));
|
||||
get_user_info(info, sizeof(info));
|
||||
|
||||
if (fwrite(encoded, encoded_length, 1, outfile) != 1 ||
|
||||
fwrite(info, strlen(info), 1, outfile) != 1) {
|
||||
D("Write error while writing public key");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 1;
|
||||
|
||||
out:
|
||||
if (outfile != NULL) {
|
||||
fclose(outfile);
|
||||
}
|
||||
delete[] encoded;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int generate_key(const char *file)
|
||||
{
|
||||
EVP_PKEY* pkey = EVP_PKEY_new();
|
||||
BIGNUM* exponent = BN_new();
|
||||
RSA* rsa = RSA_new();
|
||||
mode_t old_mask;
|
||||
FILE *f = NULL;
|
||||
int ret = 0;
|
||||
|
||||
D("generate_key '%s'\n", file);
|
||||
|
||||
if (!pkey || !exponent || !rsa) {
|
||||
D("Failed to allocate key\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
BN_set_word(exponent, RSA_F4);
|
||||
RSA_generate_key_ex(rsa, 2048, exponent, NULL);
|
||||
EVP_PKEY_set1_RSA(pkey, rsa);
|
||||
|
||||
old_mask = umask(077);
|
||||
|
||||
f = fopen(file, "w");
|
||||
if (!f) {
|
||||
D("Failed to open '%s'\n", file);
|
||||
umask(old_mask);
|
||||
goto out;
|
||||
}
|
||||
|
||||
umask(old_mask);
|
||||
|
||||
if (!PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL)) {
|
||||
D("Failed to write key\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!write_public_keyfile(rsa, file)) {
|
||||
D("Failed to write public key\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 1;
|
||||
|
||||
out:
|
||||
if (f)
|
||||
fclose(f);
|
||||
EVP_PKEY_free(pkey);
|
||||
RSA_free(rsa);
|
||||
BN_free(exponent);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int read_key(const char *file, struct listnode *list)
|
||||
{
|
||||
D("read_key '%s'\n", file);
|
||||
|
||||
FILE* fp = fopen(file, "r");
|
||||
if (!fp) {
|
||||
D("Failed to open '%s': %s\n", file, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
adb_private_key* key = new adb_private_key;
|
||||
key->rsa = RSA_new();
|
||||
|
||||
if (!PEM_read_RSAPrivateKey(fp, &key->rsa, NULL, NULL)) {
|
||||
D("Failed to read key\n");
|
||||
fclose(fp);
|
||||
RSA_free(key->rsa);
|
||||
delete key;
|
||||
return 0;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
list_add_tail(list, &key->node);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int get_user_keyfilepath(char *filename, size_t len)
|
||||
{
|
||||
const char *format, *home;
|
||||
char android_dir[PATH_MAX];
|
||||
struct stat buf;
|
||||
#ifdef _WIN32
|
||||
char path[PATH_MAX];
|
||||
home = getenv("ANDROID_SDK_HOME");
|
||||
if (!home) {
|
||||
SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, 0, path);
|
||||
home = path;
|
||||
}
|
||||
format = "%s\\%s";
|
||||
#else
|
||||
home = getenv("HOME");
|
||||
if (!home)
|
||||
return -1;
|
||||
format = "%s/%s";
|
||||
#endif
|
||||
|
||||
D("home '%s'\n", home);
|
||||
|
||||
if (snprintf(android_dir, sizeof(android_dir), format, home,
|
||||
ANDROID_PATH) >= (int)sizeof(android_dir))
|
||||
return -1;
|
||||
|
||||
if (stat(android_dir, &buf)) {
|
||||
if (adb_mkdir(android_dir, 0750) < 0) {
|
||||
D("Cannot mkdir '%s'", android_dir);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return snprintf(filename, len, format, android_dir, ADB_KEY_FILE);
|
||||
}
|
||||
|
||||
static int get_user_key(struct listnode *list)
|
||||
{
|
||||
struct stat buf;
|
||||
char path[PATH_MAX];
|
||||
int ret;
|
||||
|
||||
ret = get_user_keyfilepath(path, sizeof(path));
|
||||
if (ret < 0 || ret >= (signed)sizeof(path)) {
|
||||
D("Error getting user key filename");
|
||||
return 0;
|
||||
}
|
||||
|
||||
D("user key '%s'\n", path);
|
||||
|
||||
if (stat(path, &buf) == -1) {
|
||||
if (!generate_key(path)) {
|
||||
D("Failed to generate new key\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return read_key(path, list);
|
||||
}
|
||||
|
||||
static void get_vendor_keys(struct listnode* key_list) {
|
||||
const char* adb_keys_path = getenv("ADB_VENDOR_KEYS");
|
||||
if (adb_keys_path == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& path : android::base::Split(adb_keys_path, ENV_PATH_SEPARATOR_STR)) {
|
||||
if (!read_key(path.c_str(), key_list)) {
|
||||
D("Failed to read '%s'\n", path.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int adb_auth_sign(void *node, const unsigned char* token, size_t token_size,
|
||||
unsigned char* sig)
|
||||
{
|
||||
unsigned int len;
|
||||
struct adb_private_key *key = node_to_item(node, struct adb_private_key, node);
|
||||
|
||||
if (token_size != TOKEN_SIZE) {
|
||||
D("Unexpected token size %zd\n", token_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!RSA_sign(NID_sha1, token, token_size, sig, &len, key->rsa)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
D("adb_auth_sign len=%d\n", len);
|
||||
return (int)len;
|
||||
}
|
||||
|
||||
void *adb_auth_nextkey(void *current)
|
||||
{
|
||||
struct listnode *item;
|
||||
|
||||
if (list_empty(&key_list))
|
||||
return NULL;
|
||||
|
||||
if (!current)
|
||||
return list_head(&key_list);
|
||||
|
||||
list_for_each(item, &key_list) {
|
||||
if (item == current) {
|
||||
/* current is the last item, we tried all the keys */
|
||||
if (item->next == &key_list)
|
||||
return NULL;
|
||||
return item->next;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int adb_auth_get_userkey(unsigned char *data, size_t len)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
int ret = get_user_keyfilepath(path, sizeof(path) - 4);
|
||||
if (ret < 0 || ret >= (signed)(sizeof(path) - 4)) {
|
||||
D("Error getting user key filename");
|
||||
return 0;
|
||||
}
|
||||
strcat(path, ".pub");
|
||||
|
||||
// TODO(danalbert): ReadFileToString
|
||||
unsigned size;
|
||||
char* file_data = reinterpret_cast<char*>(load_file(path, &size));
|
||||
if (file_data == nullptr) {
|
||||
D("Can't load '%s'\n", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (len < (size_t)(size + 1)) {
|
||||
D("%s: Content too large ret=%d\n", path, size);
|
||||
free(file_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(data, file_data, size);
|
||||
free(file_data);
|
||||
file_data = nullptr;
|
||||
data[size] = '\0';
|
||||
|
||||
return size + 1;
|
||||
}
|
||||
|
||||
int adb_auth_keygen(const char* filename) {
|
||||
adb_trace_mask |= (1 << TRACE_AUTH);
|
||||
return (generate_key(filename) == 0);
|
||||
}
|
||||
|
||||
void adb_auth_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
D("adb_auth_init\n");
|
||||
|
||||
list_init(&key_list);
|
||||
|
||||
ret = get_user_key(&key_list);
|
||||
if (!ret) {
|
||||
D("Failed to get user key\n");
|
||||
return;
|
||||
}
|
||||
|
||||
get_vendor_keys(&key_list);
|
||||
}
|
||||
318
adb_client.cpp
Normal file
318
adb_client.cpp
Normal file
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
* 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 TRACE_ADB
|
||||
|
||||
#include "sysdeps.h"
|
||||
#include "adb_client.h"
|
||||
|
||||
#include <errno.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 <string>
|
||||
#include <vector>
|
||||
|
||||
#include <base/stringprintf.h>
|
||||
#include <base/strings.h>
|
||||
|
||||
#include "adb_io.h"
|
||||
|
||||
static transport_type __adb_transport = kTransportAny;
|
||||
static const char* __adb_serial = NULL;
|
||||
|
||||
static int __adb_server_port = DEFAULT_ADB_PORT;
|
||||
static const char* __adb_server_name = NULL;
|
||||
|
||||
static std::string perror_str(const char* msg) {
|
||||
return android::base::StringPrintf("%s: %s", msg, strerror(errno));
|
||||
}
|
||||
|
||||
static bool ReadProtocolString(int 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, 0, 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;
|
||||
}
|
||||
|
||||
void adb_set_transport(transport_type type, const char* serial)
|
||||
{
|
||||
__adb_transport = type;
|
||||
__adb_serial = serial;
|
||||
}
|
||||
|
||||
void adb_set_tcp_specifics(int server_port)
|
||||
{
|
||||
__adb_server_port = server_port;
|
||||
}
|
||||
|
||||
void adb_set_tcp_name(const char* hostname)
|
||||
{
|
||||
__adb_server_name = hostname;
|
||||
}
|
||||
|
||||
int adb_get_emulator_console_port() {
|
||||
if (__adb_serial) {
|
||||
// The user specified a serial number; is it an emulator?
|
||||
int port;
|
||||
return (sscanf(__adb_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)) {
|
||||
printf("no emulator connected: %s\n", error.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
int port;
|
||||
size_t emulator_count = 0;
|
||||
for (auto& device : android::base::Split(devices, "\n")) {
|
||||
if (sscanf(device.c_str(), "emulator-%d", &port) == 1) {
|
||||
if (++emulator_count > 1) {
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (emulator_count == 0) return -1;
|
||||
return port;
|
||||
}
|
||||
|
||||
static int switch_socket_transport(int fd, std::string* error) {
|
||||
std::string service;
|
||||
if (__adb_serial) {
|
||||
service += "host:transport:";
|
||||
service += __adb_serial;
|
||||
} else {
|
||||
const char* transport_type = "???";
|
||||
switch (__adb_transport) {
|
||||
case kTransportUsb:
|
||||
transport_type = "transport-usb";
|
||||
break;
|
||||
case kTransportLocal:
|
||||
transport_type = "transport-local";
|
||||
break;
|
||||
case kTransportAny:
|
||||
transport_type = "transport-any";
|
||||
break;
|
||||
case kTransportHost:
|
||||
// no switch necessary
|
||||
return 0;
|
||||
}
|
||||
service += "host:";
|
||||
service += transport_type;
|
||||
}
|
||||
|
||||
if (!SendProtocolString(fd, service)) {
|
||||
*error = perror_str("write failure during connection");
|
||||
adb_close(fd);
|
||||
return -1;
|
||||
}
|
||||
D("Switch transport in progress\n");
|
||||
|
||||
if (!adb_status(fd, error)) {
|
||||
adb_close(fd);
|
||||
D("Switch transport failed: %s\n", error->c_str());
|
||||
return -1;
|
||||
}
|
||||
D("Switch transport success\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool adb_status(int 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;
|
||||
}
|
||||
|
||||
int _adb_connect(const std::string& service, std::string* error) {
|
||||
D("_adb_connect: %s\n", service.c_str());
|
||||
if (service.empty() || service.size() > 1024) {
|
||||
*error = android::base::StringPrintf("bad service name length (%d)",
|
||||
static_cast<int>(service.size()));
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fd;
|
||||
if (__adb_server_name) {
|
||||
fd = socket_network_client(__adb_server_name, __adb_server_port, SOCK_STREAM);
|
||||
} else {
|
||||
fd = socket_loopback_client(__adb_server_port, SOCK_STREAM);
|
||||
}
|
||||
if (fd < 0) {
|
||||
*error = perror_str("cannot connect to daemon");
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (memcmp(&service[0],"host",4) != 0 && switch_socket_transport(fd, error)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!SendProtocolString(fd, service)) {
|
||||
*error = perror_str("write failure during connection");
|
||||
adb_close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!adb_status(fd, error)) {
|
||||
adb_close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
D("_adb_connect: return fd %d\n", fd);
|
||||
return fd;
|
||||
}
|
||||
|
||||
int adb_connect(const std::string& service, std::string* error) {
|
||||
// first query the adb server's version
|
||||
int fd = _adb_connect("host:version", error);
|
||||
|
||||
D("adb_connect: service %s\n", service.c_str());
|
||||
if (fd == -2 && __adb_server_name) {
|
||||
fprintf(stderr,"** Cannot start server on remote host\n");
|
||||
return fd;
|
||||
} else if (fd == -2) {
|
||||
fprintf(stdout,"* daemon not running. starting it now on port %d *\n",
|
||||
__adb_server_port);
|
||||
start_server:
|
||||
if (launch_server(__adb_server_port)) {
|
||||
fprintf(stderr,"* failed to start daemon *\n");
|
||||
return -1;
|
||||
} else {
|
||||
fprintf(stdout,"* daemon started successfully *\n");
|
||||
}
|
||||
/* give the server some time to start properly and detect devices */
|
||||
adb_sleep_ms(3000);
|
||||
// fall through to _adb_connect
|
||||
} else {
|
||||
// if server was running, check its version to make sure it is not out of date
|
||||
int version = ADB_SERVER_VERSION - 1;
|
||||
|
||||
// if we have a file descriptor, then parse version result
|
||||
if (fd >= 0) {
|
||||
std::string version_string;
|
||||
if (!ReadProtocolString(fd, &version_string, error)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
adb_close(fd);
|
||||
|
||||
if (sscanf(&version_string[0], "%04x", &version) != 1) {
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
// if fd is -1, then check for "unknown host service",
|
||||
// which would indicate a version of adb that does not support the version command
|
||||
if (*error == "unknown host service") {
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
|
||||
if (version != ADB_SERVER_VERSION) {
|
||||
printf("adb server is out of date. killing...\n");
|
||||
fd = _adb_connect("host:kill", error);
|
||||
adb_close(fd);
|
||||
|
||||
/* XXX can we better detect its death? */
|
||||
adb_sleep_ms(2000);
|
||||
goto start_server;
|
||||
}
|
||||
}
|
||||
|
||||
// if the command is start-server, we are done.
|
||||
if (service == "host:start-server") {
|
||||
return 0;
|
||||
}
|
||||
|
||||
fd = _adb_connect(service, error);
|
||||
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\n", fd);
|
||||
|
||||
return fd;
|
||||
error:
|
||||
adb_close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int adb_command(const std::string& service, std::string* error) {
|
||||
int fd = adb_connect(service, error);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "error: %s\n", error->c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!adb_status(fd, error)) {
|
||||
adb_close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool adb_query(const std::string& service, std::string* result, std::string* error) {
|
||||
D("adb_query: %s\n", service.c_str());
|
||||
int fd = adb_connect(service, error);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr,"error: %s\n", error->c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
result->clear();
|
||||
if (!ReadProtocolString(fd, result, error)) {
|
||||
adb_close(fd);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
53
adb_client.h
Normal file
53
adb_client.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#ifndef _ADB_CLIENT_H_
|
||||
#define _ADB_CLIENT_H_
|
||||
|
||||
#include "adb.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
/* 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(const std::string& service, std::string* error);
|
||||
int _adb_connect(const std::string& service, std::string* error);
|
||||
|
||||
/* connect to adb, connect to the named service, return 0 if
|
||||
** the connection succeeded AND the service returned OKAY
|
||||
*/
|
||||
int adb_command(const std::string& service, std::string* error);
|
||||
|
||||
// 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* result, std::string* error);
|
||||
|
||||
/* Set the preferred transport to connect to.
|
||||
*/
|
||||
void adb_set_transport(transport_type type, const char* serial);
|
||||
|
||||
/* Set TCP specifics of the transport to use
|
||||
*/
|
||||
void adb_set_tcp_specifics(int server_port);
|
||||
|
||||
/* Set TCP Hostname of the transport to use
|
||||
*/
|
||||
void adb_set_tcp_name(const char* hostname);
|
||||
|
||||
/* Return the console port of the currently connected emulator (if any)
|
||||
* of -1 if there is no emulator, and -2 if there is more than one.
|
||||
* assumes adb_set_transport() was alled previously...
|
||||
*/
|
||||
int adb_get_emulator_console_port(void);
|
||||
|
||||
/* send commands to the current emulator instance. will fail if there
|
||||
* is zero, or more than 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** argv);
|
||||
|
||||
// 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(int fd, std::string* error);
|
||||
|
||||
#endif
|
||||
123
adb_io.cpp
Normal file
123
adb_io.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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 TRACE_RWX
|
||||
|
||||
#include "adb_io.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <base/stringprintf.h>
|
||||
|
||||
#include "adb_trace.h"
|
||||
#include "adb_utils.h"
|
||||
#include "sysdeps.h"
|
||||
|
||||
bool SendProtocolString(int fd, const std::string& s) {
|
||||
int length = s.size();
|
||||
if (length > 0xffff) {
|
||||
length = 0xffff;
|
||||
}
|
||||
|
||||
return WriteFdFmt(fd, "%04x", length) && WriteFdExactly(fd, s);
|
||||
}
|
||||
|
||||
bool SendOkay(int fd) {
|
||||
return WriteFdExactly(fd, "OKAY", 4);
|
||||
}
|
||||
|
||||
bool SendFail(int fd, const std::string& reason) {
|
||||
return WriteFdExactly(fd, "FAIL", 4) && SendProtocolString(fd, reason);
|
||||
}
|
||||
|
||||
bool ReadFdExactly(int fd, void* buf, size_t len) {
|
||||
char* p = reinterpret_cast<char*>(buf);
|
||||
|
||||
size_t len0 = len;
|
||||
|
||||
D("readx: fd=%d wanted=%zu\n", fd, 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\n", fd, errno, strerror(errno));
|
||||
return false;
|
||||
} else {
|
||||
D("readx: fd=%d disconnected\n", fd);
|
||||
errno = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
D("readx: fd=%d wanted=%zu got=%zu\n", fd, len0, len0 - len);
|
||||
if (ADB_TRACING) {
|
||||
dump_hex(reinterpret_cast<const unsigned char*>(buf), len0);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteFdExactly(int fd, const void* buf, size_t len) {
|
||||
const char* p = reinterpret_cast<const char*>(buf);
|
||||
int r;
|
||||
|
||||
D("writex: fd=%d len=%d: ", fd, (int)len);
|
||||
if (ADB_TRACING) {
|
||||
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\n", fd, errno, strerror(errno));
|
||||
if (errno == EAGAIN) {
|
||||
adb_sleep_ms(1); // just yield some cpu time
|
||||
continue;
|
||||
} else if (errno == EPIPE) {
|
||||
D("writex: fd=%d disconnected\n", fd);
|
||||
errno = 0;
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
len -= r;
|
||||
p += r;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteFdExactly(int fd, const char* str) {
|
||||
return WriteFdExactly(fd, str, strlen(str));
|
||||
}
|
||||
|
||||
bool WriteFdExactly(int fd, const std::string& str) {
|
||||
return WriteFdExactly(fd, str.c_str(), str.size());
|
||||
}
|
||||
|
||||
bool WriteFdFmt(int 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);
|
||||
}
|
||||
59
adb_io.h
Normal file
59
adb_io.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
// Sends the protocol "OKAY" message.
|
||||
bool SendOkay(int fd);
|
||||
|
||||
// Sends the protocol "FAIL" message, with the given failure reason.
|
||||
bool SendFail(int fd, const std::string& reason);
|
||||
|
||||
// Writes a protocol-format string; a four hex digit length followed by the string data.
|
||||
bool SendProtocolString(int fd, const std::string& s);
|
||||
|
||||
/*
|
||||
* 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(int fd, void *buf, size_t len);
|
||||
|
||||
/*
|
||||
* 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(int fd, const void* buf, size_t len);
|
||||
|
||||
// Same as above, but for strings.
|
||||
bool WriteFdExactly(int fd, const char* s);
|
||||
bool WriteFdExactly(int fd, const std::string& s);
|
||||
|
||||
// Same as above, but formats the string to send.
|
||||
bool WriteFdFmt(int fd, const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3)));
|
||||
|
||||
#endif /* ADB_IO_H */
|
||||
167
adb_io_test.cpp
Normal file
167
adb_io_test.cpp
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* 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 "base/file.h"
|
||||
|
||||
class TemporaryFile {
|
||||
public:
|
||||
TemporaryFile() {
|
||||
init("/data/local/tmp");
|
||||
if (fd == -1) {
|
||||
init("/tmp");
|
||||
}
|
||||
}
|
||||
|
||||
~TemporaryFile() {
|
||||
close(fd);
|
||||
unlink(filename);
|
||||
}
|
||||
|
||||
int fd;
|
||||
char filename[1024];
|
||||
|
||||
private:
|
||||
void init(const char* tmp_dir) {
|
||||
snprintf(filename, sizeof(filename), "%s/TemporaryFile-XXXXXX", tmp_dir);
|
||||
fd = mkstemp(filename);
|
||||
}
|
||||
};
|
||||
|
||||
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, SEEK_SET, 0));
|
||||
|
||||
// Test reading the whole file.
|
||||
char buf[sizeof(expected)] = {};
|
||||
ASSERT_TRUE(ReadFdExactly(tf.fd, buf, sizeof(buf) - 1)) << strerror(errno);
|
||||
EXPECT_STREQ(expected, buf);
|
||||
}
|
||||
|
||||
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, SEEK_SET, 0));
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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, SEEK_SET, 0));
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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, SEEK_SET, 0));
|
||||
|
||||
std::string s;
|
||||
ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s));
|
||||
EXPECT_STREQ(expected, s.c_str());
|
||||
}
|
||||
|
||||
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, SEEK_SET, 0));
|
||||
|
||||
std::string expected(buf);
|
||||
expected.pop_back();
|
||||
|
||||
std::string s;
|
||||
ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s));
|
||||
EXPECT_EQ(expected, s);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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, SEEK_SET, 0));
|
||||
|
||||
std::string s;
|
||||
ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s));
|
||||
EXPECT_STREQ(str, s.c_str());
|
||||
}
|
||||
|
||||
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, SEEK_SET, 0));
|
||||
|
||||
std::string s;
|
||||
ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s));
|
||||
EXPECT_STREQ("Foobar123", s.c_str());
|
||||
}
|
||||
274
adb_listeners.cpp
Normal file
274
adb_listeners.cpp
Normal file
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* 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 <base/stringprintf.h>
|
||||
|
||||
#include "sysdeps.h"
|
||||
#include "transport.h"
|
||||
|
||||
int gListenAll = 0; /* Not static because it is used in commandline.c. */
|
||||
|
||||
alistener listener_list = {
|
||||
.next = &listener_list,
|
||||
.prev = &listener_list,
|
||||
};
|
||||
|
||||
void ss_listener_event_func(int _fd, unsigned ev, void *_l)
|
||||
{
|
||||
asocket *s;
|
||||
|
||||
if(ev & FDE_READ) {
|
||||
struct sockaddr addr;
|
||||
socklen_t alen;
|
||||
int fd;
|
||||
|
||||
alen = sizeof(addr);
|
||||
fd = adb_socket_accept(_fd, &addr, &alen);
|
||||
if(fd < 0) return;
|
||||
|
||||
adb_socket_setbufsize(fd, CHUNK_SIZE);
|
||||
|
||||
s = create_local_socket(fd);
|
||||
if(s) {
|
||||
connect_to_smartsocket(s);
|
||||
return;
|
||||
}
|
||||
|
||||
adb_close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
void listener_event_func(int _fd, unsigned ev, void* _l)
|
||||
{
|
||||
alistener* listener = reinterpret_cast<alistener*>(_l);
|
||||
asocket *s;
|
||||
|
||||
if (ev & FDE_READ) {
|
||||
struct sockaddr addr;
|
||||
socklen_t alen;
|
||||
int fd;
|
||||
|
||||
alen = sizeof(addr);
|
||||
fd = adb_socket_accept(_fd, &addr, &alen);
|
||||
if (fd < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
s = create_local_socket(fd);
|
||||
if (s) {
|
||||
s->transport = listener->transport;
|
||||
connect_to_remote(s, listener->connect_to);
|
||||
return;
|
||||
}
|
||||
|
||||
adb_close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
static void free_listener(alistener* l)
|
||||
{
|
||||
if (l->next) {
|
||||
l->next->prev = l->prev;
|
||||
l->prev->next = l->next;
|
||||
l->next = l->prev = l;
|
||||
}
|
||||
|
||||
// closes the corresponding fd
|
||||
fdevent_remove(&l->fde);
|
||||
|
||||
if (l->local_name)
|
||||
free((char*)l->local_name);
|
||||
|
||||
if (l->connect_to)
|
||||
free((char*)l->connect_to);
|
||||
|
||||
if (l->transport) {
|
||||
remove_transport_disconnect(l->transport, &l->disconnect);
|
||||
}
|
||||
free(l);
|
||||
}
|
||||
|
||||
void listener_disconnect(void* listener, atransport* t)
|
||||
{
|
||||
free_listener(reinterpret_cast<alistener*>(listener));
|
||||
}
|
||||
|
||||
int local_name_to_fd(const char *name)
|
||||
{
|
||||
int port;
|
||||
|
||||
if(!strncmp("tcp:", name, 4)){
|
||||
int ret;
|
||||
port = atoi(name + 4);
|
||||
|
||||
if (gListenAll > 0) {
|
||||
ret = socket_inaddr_any_server(port, SOCK_STREAM);
|
||||
} else {
|
||||
ret = socket_loopback_server(port, SOCK_STREAM);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#ifndef HAVE_WIN32_IPC /* no Unix-domain sockets on Win32 */
|
||||
// It's non-sensical to support the "reserved" space on the adb host side
|
||||
if(!strncmp(name, "local:", 6)) {
|
||||
return socket_local_server(name + 6,
|
||||
ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
|
||||
} else if(!strncmp(name, "localabstract:", 14)) {
|
||||
return socket_local_server(name + 14,
|
||||
ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
|
||||
} else if(!strncmp(name, "localfilesystem:", 16)) {
|
||||
return socket_local_server(name + 16,
|
||||
ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM);
|
||||
}
|
||||
|
||||
#endif
|
||||
printf("unknown local portname '%s'\n", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Write the list of current listeners (network redirections) into a string.
|
||||
std::string format_listeners() {
|
||||
std::string result;
|
||||
for (alistener* l = listener_list.next; l != &listener_list; l = l->next) {
|
||||
// Ignore special listeners like those for *smartsocket*
|
||||
if (l->connect_to[0] == '*') {
|
||||
continue;
|
||||
}
|
||||
// <device-serial> " " <local-name> " " <remote-name> "\n"
|
||||
android::base::StringAppendF(&result, "%s %s %s\n",
|
||||
l->transport->serial, l->local_name, l->connect_to);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
install_status_t remove_listener(const char *local_name, atransport* transport)
|
||||
{
|
||||
alistener *l;
|
||||
|
||||
for (l = listener_list.next; l != &listener_list; l = l->next) {
|
||||
if (!strcmp(local_name, l->local_name)) {
|
||||
listener_disconnect(l, l->transport);
|
||||
return INSTALL_STATUS_OK;
|
||||
}
|
||||
}
|
||||
return INSTALL_STATUS_LISTENER_NOT_FOUND;
|
||||
}
|
||||
|
||||
void remove_all_listeners(void)
|
||||
{
|
||||
alistener *l, *l_next;
|
||||
for (l = listener_list.next; l != &listener_list; l = l_next) {
|
||||
l_next = l->next;
|
||||
// Never remove smart sockets.
|
||||
if (l->connect_to[0] == '*')
|
||||
continue;
|
||||
listener_disconnect(l, l->transport);
|
||||
}
|
||||
}
|
||||
|
||||
install_status_t install_listener(const std::string& local_name,
|
||||
const char *connect_to,
|
||||
atransport* transport,
|
||||
int no_rebind)
|
||||
{
|
||||
for (alistener* l = listener_list.next; l != &listener_list; l = l->next) {
|
||||
if (local_name == l->local_name) {
|
||||
char* cto;
|
||||
|
||||
/* can't repurpose a smartsocket */
|
||||
if(l->connect_to[0] == '*') {
|
||||
return INSTALL_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
/* can't repurpose a listener if 'no_rebind' is true */
|
||||
if (no_rebind) {
|
||||
return INSTALL_STATUS_CANNOT_REBIND;
|
||||
}
|
||||
|
||||
cto = strdup(connect_to);
|
||||
if(cto == 0) {
|
||||
return INSTALL_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
free((void*) l->connect_to);
|
||||
l->connect_to = cto;
|
||||
if (l->transport != transport) {
|
||||
remove_transport_disconnect(l->transport, &l->disconnect);
|
||||
l->transport = transport;
|
||||
add_transport_disconnect(l->transport, &l->disconnect);
|
||||
}
|
||||
return INSTALL_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
alistener* listener = reinterpret_cast<alistener*>(
|
||||
calloc(1, sizeof(alistener)));
|
||||
if (listener == nullptr) {
|
||||
goto nomem;
|
||||
}
|
||||
|
||||
listener->local_name = strdup(local_name.c_str());
|
||||
if (listener->local_name == nullptr) {
|
||||
goto nomem;
|
||||
}
|
||||
|
||||
listener->connect_to = strdup(connect_to);
|
||||
if (listener->connect_to == nullptr) {
|
||||
goto nomem;
|
||||
}
|
||||
|
||||
listener->fd = local_name_to_fd(listener->local_name);
|
||||
if (listener->fd < 0) {
|
||||
printf("cannot bind '%s': %s\n", listener->local_name, strerror(errno));
|
||||
free(listener->local_name);
|
||||
free(listener->connect_to);
|
||||
free(listener);
|
||||
return INSTALL_STATUS_CANNOT_BIND;
|
||||
}
|
||||
|
||||
close_on_exec(listener->fd);
|
||||
if (!strcmp(listener->connect_to, "*smartsocket*")) {
|
||||
fdevent_install(&listener->fde, listener->fd, ss_listener_event_func,
|
||||
listener);
|
||||
} else {
|
||||
fdevent_install(&listener->fde, listener->fd, listener_event_func,
|
||||
listener);
|
||||
}
|
||||
fdevent_set(&listener->fde, FDE_READ);
|
||||
|
||||
listener->next = &listener_list;
|
||||
listener->prev = listener_list.prev;
|
||||
listener->next->prev = listener;
|
||||
listener->prev->next = listener;
|
||||
listener->transport = transport;
|
||||
|
||||
if (transport) {
|
||||
listener->disconnect.opaque = listener;
|
||||
listener->disconnect.func = listener_disconnect;
|
||||
add_transport_disconnect(transport, &listener->disconnect);
|
||||
}
|
||||
return INSTALL_STATUS_OK;
|
||||
|
||||
nomem:
|
||||
fatal("cannot allocate listener");
|
||||
return INSTALL_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
49
adb_listeners.h
Normal file
49
adb_listeners.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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_LISTENERS_H
|
||||
#define __ADB_LISTENERS_H
|
||||
|
||||
#include "adb.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
// error/status codes for install_listener.
|
||||
enum install_status_t {
|
||||
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,
|
||||
};
|
||||
|
||||
extern alistener listener_list;
|
||||
|
||||
void listener_disconnect(void* _l, atransport* t);
|
||||
void listener_event_func(int _fd, unsigned ev, void *_l);
|
||||
void ss_listener_event_func(int _fd, unsigned ev, void *_l);
|
||||
|
||||
install_status_t install_listener(const std::string& local_name,
|
||||
const char* connect_to,
|
||||
atransport* transport,
|
||||
int no_rebind);
|
||||
|
||||
std::string format_listeners();
|
||||
|
||||
install_status_t remove_listener(const char* local_name, atransport* transport);
|
||||
void remove_all_listeners(void);
|
||||
|
||||
#endif /* __ADB_LISTENERS_H */
|
||||
411
adb_main.cpp
Normal file
411
adb_main.cpp
Normal file
@@ -0,0 +1,411 @@
|
||||
/*
|
||||
* 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 TRACE_ADB
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "adb_auth.h"
|
||||
#include "adb_listeners.h"
|
||||
#include "transport.h"
|
||||
|
||||
#include <base/stringprintf.h>
|
||||
|
||||
#if !ADB_HOST
|
||||
#include <getopt.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#include "cutils/properties.h"
|
||||
#include "private/android_filesystem_config.h"
|
||||
#include "selinux/selinux.h"
|
||||
|
||||
#include "qemu_tracing.h"
|
||||
#endif
|
||||
|
||||
static void adb_cleanup(void)
|
||||
{
|
||||
usb_cleanup();
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
static BOOL WINAPI ctrlc_handler(DWORD type)
|
||||
{
|
||||
exit(STATUS_CONTROL_C_EXIT);
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ADB_HOST
|
||||
#ifdef WORKAROUND_BUG6558362
|
||||
#include <sched.h>
|
||||
#define AFFINITY_ENVVAR "ADB_CPU_AFFINITY_BUG6558362"
|
||||
void adb_set_affinity(void)
|
||||
{
|
||||
cpu_set_t cpu_set;
|
||||
const char* cpunum_str = getenv(AFFINITY_ENVVAR);
|
||||
char* strtol_res;
|
||||
int cpu_num;
|
||||
|
||||
if (!cpunum_str || !*cpunum_str)
|
||||
return;
|
||||
cpu_num = strtol(cpunum_str, &strtol_res, 0);
|
||||
if (*strtol_res != '\0')
|
||||
fatal("bad number (%s) in env var %s. Expecting 0..n.\n", cpunum_str, AFFINITY_ENVVAR);
|
||||
|
||||
sched_getaffinity(0, sizeof(cpu_set), &cpu_set);
|
||||
D("orig cpu_set[0]=0x%08lx\n", cpu_set.__bits[0]);
|
||||
CPU_ZERO(&cpu_set);
|
||||
CPU_SET(cpu_num, &cpu_set);
|
||||
sched_setaffinity(0, sizeof(cpu_set), &cpu_set);
|
||||
sched_getaffinity(0, sizeof(cpu_set), &cpu_set);
|
||||
D("new cpu_set[0]=0x%08lx\n", cpu_set.__bits[0]);
|
||||
}
|
||||
#endif
|
||||
#else /* ADB_HOST */
|
||||
static const char *root_seclabel = NULL;
|
||||
|
||||
static void drop_capabilities_bounding_set_if_needed() {
|
||||
#ifdef ALLOW_ADBD_ROOT
|
||||
char value[PROPERTY_VALUE_MAX];
|
||||
property_get("ro.debuggable", value, "");
|
||||
if (strcmp(value, "1") == 0) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
int i;
|
||||
for (i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) {
|
||||
if (i == CAP_SETUID || i == CAP_SETGID) {
|
||||
// CAP_SETUID CAP_SETGID needed by /system/bin/run-as
|
||||
continue;
|
||||
}
|
||||
int err = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
|
||||
|
||||
// Some kernels don't have file capabilities compiled in, and
|
||||
// prctl(PR_CAPBSET_DROP) returns EINVAL. Don't automatically
|
||||
// die when we see such misconfigured kernels.
|
||||
if ((err < 0) && (errno != EINVAL)) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool should_drop_privileges() {
|
||||
#if defined(ALLOW_ADBD_ROOT)
|
||||
char value[PROPERTY_VALUE_MAX];
|
||||
|
||||
// The emulator is never secure, so don't drop privileges there.
|
||||
// TODO: this seems like a bug --- shouldn't the emulator behave like a device?
|
||||
property_get("ro.kernel.qemu", value, "");
|
||||
if (strcmp(value, "1") == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The properties that affect `adb root` and `adb unroot` are ro.secure and
|
||||
// ro.debuggable. In this context the names don't make the expected behavior
|
||||
// particularly obvious.
|
||||
//
|
||||
// ro.debuggable:
|
||||
// Allowed to become root, but not necessarily the default. Set to 1 on
|
||||
// eng and userdebug builds.
|
||||
//
|
||||
// ro.secure:
|
||||
// Drop privileges by default. Set to 1 on userdebug and user builds.
|
||||
property_get("ro.secure", value, "1");
|
||||
bool ro_secure = (strcmp(value, "1") == 0);
|
||||
|
||||
property_get("ro.debuggable", value, "");
|
||||
bool ro_debuggable = (strcmp(value, "1") == 0);
|
||||
|
||||
// Drop privileges if ro.secure is set...
|
||||
bool drop = ro_secure;
|
||||
|
||||
property_get("service.adb.root", value, "");
|
||||
bool adb_root = (strcmp(value, "1") == 0);
|
||||
bool adb_unroot = (strcmp(value, "0") == 0);
|
||||
|
||||
// ...except "adb root" lets you keep privileges in a debuggable build.
|
||||
if (ro_debuggable && adb_root) {
|
||||
drop = false;
|
||||
}
|
||||
|
||||
// ...and "adb unroot" lets you explicitly drop privileges.
|
||||
if (adb_unroot) {
|
||||
drop = true;
|
||||
}
|
||||
|
||||
return drop;
|
||||
#else
|
||||
return true; // "adb root" not allowed, always drop privileges.
|
||||
#endif /* ALLOW_ADBD_ROOT */
|
||||
}
|
||||
#endif /* ADB_HOST */
|
||||
|
||||
void start_logging(void)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
char temp[ MAX_PATH ];
|
||||
FILE* fnul;
|
||||
FILE* flog;
|
||||
|
||||
GetTempPath( sizeof(temp) - 8, temp );
|
||||
strcat( temp, "adb.log" );
|
||||
|
||||
/* Win32 specific redirections */
|
||||
fnul = fopen( "NUL", "rt" );
|
||||
if (fnul != NULL)
|
||||
stdin[0] = fnul[0];
|
||||
|
||||
flog = fopen( temp, "at" );
|
||||
if (flog == NULL)
|
||||
flog = fnul;
|
||||
|
||||
setvbuf( flog, NULL, _IONBF, 0 );
|
||||
|
||||
stdout[0] = flog[0];
|
||||
stderr[0] = flog[0];
|
||||
fprintf(stderr,"--- adb starting (pid %d) ---\n", getpid());
|
||||
#else
|
||||
int fd;
|
||||
|
||||
fd = unix_open("/dev/null", O_RDONLY);
|
||||
dup2(fd, 0);
|
||||
adb_close(fd);
|
||||
|
||||
fd = unix_open("/tmp/adb.log", O_WRONLY | O_CREAT | O_APPEND, 0640);
|
||||
if(fd < 0) {
|
||||
fd = unix_open("/dev/null", O_WRONLY);
|
||||
}
|
||||
dup2(fd, 1);
|
||||
dup2(fd, 2);
|
||||
adb_close(fd);
|
||||
fprintf(stderr,"--- adb starting (pid %d) ---\n", getpid());
|
||||
#endif
|
||||
}
|
||||
|
||||
int adb_main(int is_daemon, int server_port)
|
||||
{
|
||||
#if !ADB_HOST
|
||||
int port;
|
||||
char value[PROPERTY_VALUE_MAX];
|
||||
|
||||
umask(000);
|
||||
#endif
|
||||
|
||||
atexit(adb_cleanup);
|
||||
#if defined(_WIN32)
|
||||
SetConsoleCtrlHandler( ctrlc_handler, TRUE );
|
||||
#else
|
||||
// No SIGCHLD. Let the service subproc handle its children.
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
init_transport_registration();
|
||||
|
||||
#if ADB_HOST
|
||||
HOST = 1;
|
||||
|
||||
#ifdef WORKAROUND_BUG6558362
|
||||
if(is_daemon) adb_set_affinity();
|
||||
#endif
|
||||
usb_init();
|
||||
local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
|
||||
adb_auth_init();
|
||||
|
||||
std::string local_name = android::base::StringPrintf("tcp:%d", server_port);
|
||||
if (install_listener(local_name, "*smartsocket*", NULL, 0)) {
|
||||
exit(1);
|
||||
}
|
||||
#else
|
||||
// We need to call this even if auth isn't enabled because the file
|
||||
// descriptor will always be open.
|
||||
adbd_cloexec_auth_socket();
|
||||
|
||||
if (ALLOW_ADBD_NO_AUTH && property_get_bool("ro.adb.secure", 0) == 0) {
|
||||
auth_required = false;
|
||||
}
|
||||
|
||||
adbd_auth_init();
|
||||
|
||||
// Our external storage path may be different than apps, since
|
||||
// we aren't able to bind mount after dropping root.
|
||||
const char* adb_external_storage = getenv("ADB_EXTERNAL_STORAGE");
|
||||
if (NULL != adb_external_storage) {
|
||||
setenv("EXTERNAL_STORAGE", adb_external_storage, 1);
|
||||
} else {
|
||||
D("Warning: ADB_EXTERNAL_STORAGE is not set. Leaving EXTERNAL_STORAGE"
|
||||
" unchanged.\n");
|
||||
}
|
||||
|
||||
/* add extra groups:
|
||||
** AID_ADB to access the USB driver
|
||||
** AID_LOG to read system logs (adb logcat)
|
||||
** AID_INPUT to diagnose input issues (getevent)
|
||||
** AID_INET to diagnose network issues (ping)
|
||||
** AID_NET_BT and AID_NET_BT_ADMIN to diagnose bluetooth (hcidump)
|
||||
** AID_SDCARD_R to allow reading from the SD card
|
||||
** AID_SDCARD_RW to allow writing to the SD card
|
||||
** AID_NET_BW_STATS to read out qtaguid statistics
|
||||
*/
|
||||
gid_t groups[] = { AID_ADB, AID_LOG, AID_INPUT, AID_INET, AID_NET_BT,
|
||||
AID_NET_BT_ADMIN, AID_SDCARD_R, AID_SDCARD_RW,
|
||||
AID_NET_BW_STATS };
|
||||
if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* don't listen on a port (default 5037) if running in secure mode */
|
||||
/* don't run as root if we are running in secure mode */
|
||||
if (should_drop_privileges()) {
|
||||
drop_capabilities_bounding_set_if_needed();
|
||||
|
||||
/* then switch user and group to "shell" */
|
||||
if (setgid(AID_SHELL) != 0) {
|
||||
exit(1);
|
||||
}
|
||||
if (setuid(AID_SHELL) != 0) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
D("Local port disabled\n");
|
||||
} else {
|
||||
if ((root_seclabel != NULL) && (is_selinux_enabled() > 0)) {
|
||||
// b/12587913: fix setcon to allow const pointers
|
||||
if (setcon((char *)root_seclabel) < 0) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
std::string local_name = android::base::StringPrintf("tcp:%d", server_port);
|
||||
if (install_listener(local_name, "*smartsocket*", NULL, 0)) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int usb = 0;
|
||||
if (access(USB_ADB_PATH, F_OK) == 0 || access(USB_FFS_ADB_EP0, F_OK) == 0) {
|
||||
// listen on USB
|
||||
usb_init();
|
||||
usb = 1;
|
||||
}
|
||||
|
||||
// If one of these properties is set, also listen on that port
|
||||
// If one of the properties isn't set and we couldn't listen on usb,
|
||||
// listen on the default port.
|
||||
property_get("service.adb.tcp.port", value, "");
|
||||
if (!value[0]) {
|
||||
property_get("persist.adb.tcp.port", value, "");
|
||||
}
|
||||
if (sscanf(value, "%d", &port) == 1 && port > 0) {
|
||||
printf("using port=%d\n", port);
|
||||
// listen on TCP port specified by service.adb.tcp.port property
|
||||
local_init(port);
|
||||
} else if (!usb) {
|
||||
// listen on default port
|
||||
local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
|
||||
}
|
||||
|
||||
D("adb_main(): pre init_jdwp()\n");
|
||||
init_jdwp();
|
||||
D("adb_main(): post init_jdwp()\n");
|
||||
#endif
|
||||
|
||||
if (is_daemon)
|
||||
{
|
||||
// inform our parent that we are up and running.
|
||||
#if defined(_WIN32)
|
||||
DWORD count;
|
||||
WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ), "OK\n", 3, &count, NULL );
|
||||
#else
|
||||
fprintf(stderr, "OK\n");
|
||||
#endif
|
||||
start_logging();
|
||||
}
|
||||
D("Event loop starting\n");
|
||||
|
||||
fdevent_loop();
|
||||
|
||||
usb_cleanup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if !ADB_HOST
|
||||
void close_stdin() {
|
||||
int fd = unix_open("/dev/null", O_RDONLY);
|
||||
if (fd == -1) {
|
||||
perror("failed to open /dev/null, stdin will remain open");
|
||||
return;
|
||||
}
|
||||
dup2(fd, 0);
|
||||
adb_close(fd);
|
||||
}
|
||||
#endif
|
||||
|
||||
// TODO(danalbert): Split this file up into adb_main.cpp and adbd_main.cpp.
|
||||
int main(int argc, char **argv) {
|
||||
#if ADB_HOST
|
||||
// adb client/server
|
||||
adb_sysdeps_init();
|
||||
adb_trace_init();
|
||||
D("Handling commandline()\n");
|
||||
return adb_commandline(argc - 1, const_cast<const char**>(argv + 1));
|
||||
#else
|
||||
// adbd
|
||||
while (true) {
|
||||
static struct option opts[] = {
|
||||
{"root_seclabel", required_argument, nullptr, 's'},
|
||||
{"device_banner", required_argument, nullptr, 'b'},
|
||||
{"version", no_argument, nullptr, 'v'},
|
||||
};
|
||||
|
||||
int option_index = 0;
|
||||
int c = getopt_long(argc, argv, "", opts, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
switch (c) {
|
||||
case 's':
|
||||
root_seclabel = optarg;
|
||||
break;
|
||||
case 'b':
|
||||
adb_device_banner = optarg;
|
||||
break;
|
||||
case 'v':
|
||||
printf("Android Debug Bridge Daemon version %d.%d.%d %s\n",
|
||||
ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION,
|
||||
ADB_REVISION);
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
close_stdin();
|
||||
|
||||
adb_trace_init();
|
||||
|
||||
/* If adbd runs inside the emulator this will enable adb tracing via
|
||||
* adb-debug qemud service in the emulator. */
|
||||
adb_qemu_trace_init();
|
||||
|
||||
D("Handling main()\n");
|
||||
return adb_main(0, DEFAULT_ADB_PORT);
|
||||
#endif
|
||||
}
|
||||
116
adb_trace.h
Normal file
116
adb_trace.h
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#if !ADB_HOST
|
||||
#include <android/log.h>
|
||||
#else
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
/* 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.c
|
||||
*/
|
||||
enum AdbTrace {
|
||||
TRACE_ADB = 0, /* 0x001 */
|
||||
TRACE_SOCKETS,
|
||||
TRACE_PACKETS,
|
||||
TRACE_TRANSPORT,
|
||||
TRACE_RWX, /* 0x010 */
|
||||
TRACE_USB,
|
||||
TRACE_SYNC,
|
||||
TRACE_SYSDEPS,
|
||||
TRACE_JDWP, /* 0x100 */
|
||||
TRACE_SERVICES,
|
||||
TRACE_AUTH,
|
||||
TRACE_FDEVENT,
|
||||
} ;
|
||||
|
||||
#if !ADB_HOST
|
||||
/*
|
||||
* When running inside the emulator, guest's adbd can connect to 'adb-debug'
|
||||
* qemud service that can display adb trace messages (on condition that emulator
|
||||
* has been started with '-debug adb' option).
|
||||
*/
|
||||
|
||||
/* Delivers a trace message to the emulator via QEMU pipe. */
|
||||
void adb_qemu_trace(const char* fmt, ...);
|
||||
/* Macro to use to send ADB trace messages to the emulator. */
|
||||
#define DQ(...) adb_qemu_trace(__VA_ARGS__)
|
||||
#else
|
||||
#define DQ(...) ((void)0)
|
||||
#endif /* !ADB_HOST */
|
||||
|
||||
extern int adb_trace_mask;
|
||||
extern unsigned char adb_trace_output_count;
|
||||
void adb_trace_init(void);
|
||||
|
||||
# define ADB_TRACING ((adb_trace_mask & (1 << TRACE_TAG)) != 0)
|
||||
|
||||
/* you must define TRACE_TAG before using this macro */
|
||||
#if ADB_HOST
|
||||
# define D(...) \
|
||||
do { \
|
||||
if (ADB_TRACING) { \
|
||||
int save_errno = errno; \
|
||||
adb_mutex_lock(&D_lock); \
|
||||
fprintf(stderr, "%16s: %5d:%5lu | ", \
|
||||
__FUNCTION__, \
|
||||
getpid(), adb_thread_id()); \
|
||||
errno = save_errno; \
|
||||
fprintf(stderr, __VA_ARGS__ ); \
|
||||
fflush(stderr); \
|
||||
adb_mutex_unlock(&D_lock); \
|
||||
errno = save_errno; \
|
||||
} \
|
||||
} while (0)
|
||||
# define DR(...) \
|
||||
do { \
|
||||
if (ADB_TRACING) { \
|
||||
int save_errno = errno; \
|
||||
adb_mutex_lock(&D_lock); \
|
||||
errno = save_errno; \
|
||||
fprintf(stderr, __VA_ARGS__ ); \
|
||||
fflush(stderr); \
|
||||
adb_mutex_unlock(&D_lock); \
|
||||
errno = save_errno; \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
# define D(...) \
|
||||
do { \
|
||||
if (ADB_TRACING) { \
|
||||
__android_log_print( \
|
||||
ANDROID_LOG_INFO, \
|
||||
__FUNCTION__, \
|
||||
__VA_ARGS__ ); \
|
||||
} \
|
||||
} while (0)
|
||||
# define DR(...) \
|
||||
do { \
|
||||
if (ADB_TRACING) { \
|
||||
__android_log_print( \
|
||||
ANDROID_LOG_INFO, \
|
||||
__FUNCTION__, \
|
||||
__VA_ARGS__ ); \
|
||||
} \
|
||||
} while (0)
|
||||
#endif /* ADB_HOST */
|
||||
|
||||
#endif /* __ADB_TRACE_H */
|
||||
86
adb_utils.cpp
Normal file
86
adb_utils.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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 TRACE_ADB
|
||||
|
||||
#include "adb_utils.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <base/stringprintf.h>
|
||||
|
||||
#include "adb_trace.h"
|
||||
#include "sysdeps.h"
|
||||
|
||||
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 lstat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode);
|
||||
}
|
||||
|
||||
std::string escape_arg(const std::string& s) {
|
||||
std::string result = 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.
|
||||
for (size_t i = 0; i < s.size(); ++i) {
|
||||
if (s[i] == '\'') {
|
||||
result.insert(i, "'\\'");
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Prefix and suffix the whole string with '.
|
||||
result.insert(result.begin(), '\'');
|
||||
result.push_back('\'');
|
||||
return result;
|
||||
}
|
||||
|
||||
void dump_hex(const void* data, size_t byte_count) {
|
||||
byte_count = std::min(byte_count, size_t(16));
|
||||
|
||||
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 c = p[i];
|
||||
if (c < 32 || c > 127) {
|
||||
c = '.';
|
||||
}
|
||||
line.push_back(c);
|
||||
}
|
||||
|
||||
DR("%s\n", line.c_str());
|
||||
}
|
||||
29
adb_utils.h
Normal file
29
adb_utils.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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_UTILS_H_
|
||||
#define _ADB_UTILS_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
bool getcwd(std::string* cwd);
|
||||
bool directory_exists(const std::string& path);
|
||||
|
||||
std::string escape_arg(const std::string& s);
|
||||
|
||||
void dump_hex(const void* ptr, size_t byte_count);
|
||||
|
||||
#endif
|
||||
52
adb_utils_test.cpp
Normal file
52
adb_utils_test.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(adb_utils, directory_exists) {
|
||||
ASSERT_TRUE(directory_exists("/proc"));
|
||||
ASSERT_FALSE(directory_exists("/proc/self")); // Symbolic link.
|
||||
ASSERT_FALSE(directory_exists("/proc/does-not-exist"));
|
||||
}
|
||||
|
||||
TEST(adb_utils, escape_arg) {
|
||||
ASSERT_EQ(R"('')", escape_arg(""));
|
||||
|
||||
ASSERT_EQ(R"('abc')", escape_arg("abc"));
|
||||
|
||||
ASSERT_EQ(R"(' abc')", escape_arg(" abc"));
|
||||
ASSERT_EQ(R"(''\''abc')", escape_arg("'abc"));
|
||||
ASSERT_EQ(R"('"abc')", escape_arg("\"abc"));
|
||||
ASSERT_EQ(R"('\abc')", escape_arg("\\abc"));
|
||||
ASSERT_EQ(R"('(abc')", escape_arg("(abc"));
|
||||
ASSERT_EQ(R"(')abc')", escape_arg(")abc"));
|
||||
|
||||
ASSERT_EQ(R"('abc abc')", escape_arg("abc abc"));
|
||||
ASSERT_EQ(R"('abc'\''abc')", escape_arg("abc'abc"));
|
||||
ASSERT_EQ(R"('abc"abc')", escape_arg("abc\"abc"));
|
||||
ASSERT_EQ(R"('abc\abc')", escape_arg("abc\\abc"));
|
||||
ASSERT_EQ(R"('abc(abc')", escape_arg("abc(abc"));
|
||||
ASSERT_EQ(R"('abc)abc')", escape_arg("abc)abc"));
|
||||
|
||||
ASSERT_EQ(R"('abc ')", escape_arg("abc "));
|
||||
ASSERT_EQ(R"('abc'\''')", escape_arg("abc'"));
|
||||
ASSERT_EQ(R"('abc"')", escape_arg("abc\""));
|
||||
ASSERT_EQ(R"('abc\')", escape_arg("abc\\"));
|
||||
ASSERT_EQ(R"('abc(')", escape_arg("abc("));
|
||||
ASSERT_EQ(R"('abc)')", escape_arg("abc)"));
|
||||
}
|
||||
1728
commandline.cpp
Normal file
1728
commandline.cpp
Normal file
File diff suppressed because it is too large
Load Diff
45
console.cpp
Normal file
45
console.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#include "sysdeps.h"
|
||||
#include "adb.h"
|
||||
#include "adb_client.h"
|
||||
#include <stdio.h>
|
||||
|
||||
static int connect_to_console(void)
|
||||
{
|
||||
int fd, port;
|
||||
|
||||
port = adb_get_emulator_console_port();
|
||||
if (port < 0) {
|
||||
if (port == -2)
|
||||
fprintf(stderr, "error: more than one emulator detected. use -s option\n");
|
||||
else
|
||||
fprintf(stderr, "error: no emulator detected\n");
|
||||
return -1;
|
||||
}
|
||||
fd = socket_loopback_client( port, SOCK_STREAM );
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "error: could not connect to TCP port %d\n", port);
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
||||
int adb_send_emulator_command(int argc, const char** argv)
|
||||
{
|
||||
int fd, nn;
|
||||
|
||||
fd = connect_to_console();
|
||||
if (fd < 0)
|
||||
return 1;
|
||||
|
||||
#define QUIT "quit\n"
|
||||
|
||||
for (nn = 1; nn < argc; nn++) {
|
||||
adb_write( fd, argv[nn], strlen(argv[nn]) );
|
||||
adb_write( fd, (nn == argc-1) ? "\n" : " ", 1 );
|
||||
}
|
||||
adb_write( fd, QUIT, sizeof(QUIT)-1 );
|
||||
adb_close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
695
fdevent.cpp
Normal file
695
fdevent.cpp
Normal file
@@ -0,0 +1,695 @@
|
||||
/* http://frotznet.googlecode.com/svn/trunk/utils/fdevent.c
|
||||
**
|
||||
** 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.
|
||||
** 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 TRACE_FDEVENT
|
||||
|
||||
#include "sysdeps.h"
|
||||
#include "fdevent.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "adb_io.h"
|
||||
#include "adb_trace.h"
|
||||
|
||||
/* !!! Do not enable DEBUG for the adb that will run as the server:
|
||||
** both stdout and stderr are used to communicate between the client
|
||||
** and server. Any extra output will cause failures.
|
||||
*/
|
||||
#define DEBUG 0 /* non-0 will break adb server */
|
||||
|
||||
// This socket is used when a subproc shell service exists.
|
||||
// It wakes up the fdevent_loop() and cause the correct handling
|
||||
// of the shell's pseudo-tty master. I.e. force close it.
|
||||
int SHELL_EXIT_NOTIFY_FD = -1;
|
||||
|
||||
static void fatal(const char *fn, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
fprintf(stderr, "%s:", fn);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
abort();
|
||||
}
|
||||
|
||||
#define FATAL(x...) fatal(__FUNCTION__, x)
|
||||
|
||||
#if DEBUG
|
||||
static void dump_fde(fdevent *fde, const char *info)
|
||||
{
|
||||
adb_mutex_lock(&D_lock);
|
||||
fprintf(stderr,"FDE #%03d %c%c%c %s\n", fde->fd,
|
||||
fde->state & FDE_READ ? 'R' : ' ',
|
||||
fde->state & FDE_WRITE ? 'W' : ' ',
|
||||
fde->state & FDE_ERROR ? 'E' : ' ',
|
||||
info);
|
||||
adb_mutex_unlock(&D_lock);
|
||||
}
|
||||
#else
|
||||
#define dump_fde(fde, info) do { } while(0)
|
||||
#endif
|
||||
|
||||
#define FDE_EVENTMASK 0x00ff
|
||||
#define FDE_STATEMASK 0xff00
|
||||
|
||||
#define FDE_ACTIVE 0x0100
|
||||
#define FDE_PENDING 0x0200
|
||||
#define FDE_CREATED 0x0400
|
||||
|
||||
static void fdevent_plist_enqueue(fdevent *node);
|
||||
static void fdevent_plist_remove(fdevent *node);
|
||||
static fdevent *fdevent_plist_dequeue(void);
|
||||
static void fdevent_subproc_event_func(int fd, unsigned events, void *userdata);
|
||||
|
||||
static fdevent list_pending = {
|
||||
.next = &list_pending,
|
||||
.prev = &list_pending,
|
||||
.fd = -1,
|
||||
.force_eof = 0,
|
||||
.state = 0,
|
||||
.events = 0,
|
||||
.func = nullptr,
|
||||
.arg = nullptr,
|
||||
};
|
||||
|
||||
static fdevent **fd_table = 0;
|
||||
static int fd_table_max = 0;
|
||||
|
||||
#ifdef CRAPTASTIC
|
||||
//HAVE_EPOLL
|
||||
|
||||
#include <sys/epoll.h>
|
||||
|
||||
static int epoll_fd = -1;
|
||||
|
||||
static void fdevent_init()
|
||||
{
|
||||
/* XXX: what's a good size for the passed in hint? */
|
||||
epoll_fd = epoll_create(256);
|
||||
|
||||
if(epoll_fd < 0) {
|
||||
perror("epoll_create() failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* mark for close-on-exec */
|
||||
fcntl(epoll_fd, F_SETFD, FD_CLOEXEC);
|
||||
}
|
||||
|
||||
static void fdevent_connect(fdevent *fde)
|
||||
{
|
||||
struct epoll_event ev;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.events = 0;
|
||||
ev.data.ptr = fde;
|
||||
|
||||
#if 0
|
||||
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) {
|
||||
perror("epoll_ctl() failed\n");
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void fdevent_disconnect(fdevent *fde)
|
||||
{
|
||||
struct epoll_event ev;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.events = 0;
|
||||
ev.data.ptr = fde;
|
||||
|
||||
/* technically we only need to delete if we
|
||||
** were actively monitoring events, but let's
|
||||
** be aggressive and do it anyway, just in case
|
||||
** something's out of sync
|
||||
*/
|
||||
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fde->fd, &ev);
|
||||
}
|
||||
|
||||
static void fdevent_update(fdevent *fde, unsigned events)
|
||||
{
|
||||
struct epoll_event ev;
|
||||
int active;
|
||||
|
||||
active = (fde->state & FDE_EVENTMASK) != 0;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.events = 0;
|
||||
ev.data.ptr = fde;
|
||||
|
||||
if(events & FDE_READ) ev.events |= EPOLLIN;
|
||||
if(events & FDE_WRITE) ev.events |= EPOLLOUT;
|
||||
if(events & FDE_ERROR) ev.events |= (EPOLLERR | EPOLLHUP);
|
||||
|
||||
fde->state = (fde->state & FDE_STATEMASK) | events;
|
||||
|
||||
if(active) {
|
||||
/* we're already active. if we're changing to *no*
|
||||
** events being monitored, we need to delete, otherwise
|
||||
** we need to just modify
|
||||
*/
|
||||
if(ev.events) {
|
||||
if(epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fde->fd, &ev)) {
|
||||
perror("epoll_ctl() failed\n");
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
if(epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fde->fd, &ev)) {
|
||||
perror("epoll_ctl() failed\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* we're not active. if we're watching events, we need
|
||||
** to add, otherwise we can just do nothing
|
||||
*/
|
||||
if(ev.events) {
|
||||
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) {
|
||||
perror("epoll_ctl() failed\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void fdevent_process()
|
||||
{
|
||||
struct epoll_event events[256];
|
||||
fdevent *fde;
|
||||
int i, n;
|
||||
|
||||
n = epoll_wait(epoll_fd, events, 256, -1);
|
||||
|
||||
if(n < 0) {
|
||||
if(errno == EINTR) return;
|
||||
perror("epoll_wait");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for(i = 0; i < n; i++) {
|
||||
struct epoll_event *ev = events + i;
|
||||
fde = ev->data.ptr;
|
||||
|
||||
if(ev->events & EPOLLIN) {
|
||||
fde->events |= FDE_READ;
|
||||
}
|
||||
if(ev->events & EPOLLOUT) {
|
||||
fde->events |= FDE_WRITE;
|
||||
}
|
||||
if(ev->events & (EPOLLERR | EPOLLHUP)) {
|
||||
fde->events |= FDE_ERROR;
|
||||
}
|
||||
if(fde->events) {
|
||||
if(fde->state & FDE_PENDING) continue;
|
||||
fde->state |= FDE_PENDING;
|
||||
fdevent_plist_enqueue(fde);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else /* USE_SELECT */
|
||||
|
||||
#ifdef HAVE_WINSOCK
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <sys/select.h>
|
||||
#endif
|
||||
|
||||
static fd_set read_fds;
|
||||
static fd_set write_fds;
|
||||
static fd_set error_fds;
|
||||
|
||||
static int select_n = 0;
|
||||
|
||||
static void fdevent_init(void)
|
||||
{
|
||||
FD_ZERO(&read_fds);
|
||||
FD_ZERO(&write_fds);
|
||||
FD_ZERO(&error_fds);
|
||||
}
|
||||
|
||||
static void fdevent_connect(fdevent *fde)
|
||||
{
|
||||
if(fde->fd >= select_n) {
|
||||
select_n = fde->fd + 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void fdevent_disconnect(fdevent *fde)
|
||||
{
|
||||
int i, n;
|
||||
|
||||
FD_CLR(fde->fd, &read_fds);
|
||||
FD_CLR(fde->fd, &write_fds);
|
||||
FD_CLR(fde->fd, &error_fds);
|
||||
|
||||
for(n = 0, i = 0; i < select_n; i++) {
|
||||
if(fd_table[i] != 0) n = i;
|
||||
}
|
||||
select_n = n + 1;
|
||||
}
|
||||
|
||||
static void fdevent_update(fdevent *fde, unsigned events)
|
||||
{
|
||||
if(events & FDE_READ) {
|
||||
FD_SET(fde->fd, &read_fds);
|
||||
} else {
|
||||
FD_CLR(fde->fd, &read_fds);
|
||||
}
|
||||
if(events & FDE_WRITE) {
|
||||
FD_SET(fde->fd, &write_fds);
|
||||
} else {
|
||||
FD_CLR(fde->fd, &write_fds);
|
||||
}
|
||||
if(events & FDE_ERROR) {
|
||||
FD_SET(fde->fd, &error_fds);
|
||||
} else {
|
||||
FD_CLR(fde->fd, &error_fds);
|
||||
}
|
||||
|
||||
fde->state = (fde->state & FDE_STATEMASK) | events;
|
||||
}
|
||||
|
||||
/* Looks at fd_table[] for bad FDs and sets bit in fds.
|
||||
** Returns the number of bad FDs.
|
||||
*/
|
||||
static int fdevent_fd_check(fd_set *fds)
|
||||
{
|
||||
int i, n = 0;
|
||||
fdevent *fde;
|
||||
|
||||
for(i = 0; i < select_n; i++) {
|
||||
fde = fd_table[i];
|
||||
if(fde == 0) continue;
|
||||
if(fcntl(i, F_GETFL, NULL) < 0) {
|
||||
FD_SET(i, fds);
|
||||
n++;
|
||||
// fde->state |= FDE_DONT_CLOSE;
|
||||
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
#if !DEBUG
|
||||
static inline void dump_all_fds(const char* /* extra_msg */) {}
|
||||
#else
|
||||
static void dump_all_fds(const char *extra_msg)
|
||||
{
|
||||
int i;
|
||||
fdevent *fde;
|
||||
// per fd: 4 digits (but really: log10(FD_SETSIZE)), 1 staus, 1 blank
|
||||
char msg_buff[FD_SETSIZE*6 + 1], *pb=msg_buff;
|
||||
size_t max_chars = FD_SETSIZE * 6 + 1;
|
||||
int printed_out;
|
||||
#define SAFE_SPRINTF(...) \
|
||||
do { \
|
||||
printed_out = snprintf(pb, max_chars, __VA_ARGS__); \
|
||||
if (printed_out <= 0) { \
|
||||
D("... snprintf failed.\n"); \
|
||||
return; \
|
||||
} \
|
||||
if (max_chars < (unsigned int)printed_out) { \
|
||||
D("... snprintf out of space.\n"); \
|
||||
return; \
|
||||
} \
|
||||
pb += printed_out; \
|
||||
max_chars -= printed_out; \
|
||||
} while(0)
|
||||
|
||||
for(i = 0; i < select_n; i++) {
|
||||
fde = fd_table[i];
|
||||
SAFE_SPRINTF("%d", i);
|
||||
if(fde == 0) {
|
||||
SAFE_SPRINTF("? ");
|
||||
continue;
|
||||
}
|
||||
if(fcntl(i, F_GETFL, NULL) < 0) {
|
||||
SAFE_SPRINTF("b");
|
||||
}
|
||||
SAFE_SPRINTF(" ");
|
||||
}
|
||||
D("%s fd_table[]->fd = {%s}\n", extra_msg, msg_buff);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void fdevent_process()
|
||||
{
|
||||
int i, n;
|
||||
fdevent *fde;
|
||||
unsigned events;
|
||||
fd_set rfd, wfd, efd;
|
||||
|
||||
memcpy(&rfd, &read_fds, sizeof(fd_set));
|
||||
memcpy(&wfd, &write_fds, sizeof(fd_set));
|
||||
memcpy(&efd, &error_fds, sizeof(fd_set));
|
||||
|
||||
dump_all_fds("pre select()");
|
||||
|
||||
n = select(select_n, &rfd, &wfd, &efd, NULL);
|
||||
int saved_errno = errno;
|
||||
D("select() returned n=%d, errno=%d\n", n, n<0?saved_errno:0);
|
||||
|
||||
dump_all_fds("post select()");
|
||||
|
||||
if(n < 0) {
|
||||
switch(saved_errno) {
|
||||
case EINTR: return;
|
||||
case EBADF:
|
||||
// Can't trust the FD sets after an error.
|
||||
FD_ZERO(&wfd);
|
||||
FD_ZERO(&efd);
|
||||
FD_ZERO(&rfd);
|
||||
break;
|
||||
default:
|
||||
D("Unexpected select() error=%d\n", saved_errno);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(n <= 0) {
|
||||
// We fake a read, as the rest of the code assumes
|
||||
// that errors will be detected at that point.
|
||||
n = fdevent_fd_check(&rfd);
|
||||
}
|
||||
|
||||
for(i = 0; (i < select_n) && (n > 0); i++) {
|
||||
events = 0;
|
||||
if(FD_ISSET(i, &rfd)) { events |= FDE_READ; n--; }
|
||||
if(FD_ISSET(i, &wfd)) { events |= FDE_WRITE; n--; }
|
||||
if(FD_ISSET(i, &efd)) { events |= FDE_ERROR; n--; }
|
||||
|
||||
if(events) {
|
||||
fde = fd_table[i];
|
||||
if(fde == 0)
|
||||
FATAL("missing fde for fd %d\n", i);
|
||||
|
||||
fde->events |= events;
|
||||
|
||||
D("got events fde->fd=%d events=%04x, state=%04x\n",
|
||||
fde->fd, fde->events, fde->state);
|
||||
if(fde->state & FDE_PENDING) continue;
|
||||
fde->state |= FDE_PENDING;
|
||||
fdevent_plist_enqueue(fde);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void fdevent_register(fdevent *fde)
|
||||
{
|
||||
if(fde->fd < 0) {
|
||||
FATAL("bogus negative fd (%d)\n", fde->fd);
|
||||
}
|
||||
|
||||
if(fde->fd >= fd_table_max) {
|
||||
int oldmax = fd_table_max;
|
||||
if(fde->fd > 32000) {
|
||||
FATAL("bogus huuuuge fd (%d)\n", fde->fd);
|
||||
}
|
||||
if(fd_table_max == 0) {
|
||||
fdevent_init();
|
||||
fd_table_max = 256;
|
||||
}
|
||||
while(fd_table_max <= fde->fd) {
|
||||
fd_table_max *= 2;
|
||||
}
|
||||
fd_table = reinterpret_cast<fdevent**>(
|
||||
realloc(fd_table, sizeof(fdevent*) * fd_table_max));
|
||||
if(fd_table == 0) {
|
||||
FATAL("could not expand fd_table to %d entries\n", fd_table_max);
|
||||
}
|
||||
memset(fd_table + oldmax, 0, sizeof(int) * (fd_table_max - oldmax));
|
||||
}
|
||||
|
||||
fd_table[fde->fd] = fde;
|
||||
}
|
||||
|
||||
static void fdevent_unregister(fdevent *fde)
|
||||
{
|
||||
if((fde->fd < 0) || (fde->fd >= fd_table_max)) {
|
||||
FATAL("fd out of range (%d)\n", fde->fd);
|
||||
}
|
||||
|
||||
if(fd_table[fde->fd] != fde) {
|
||||
FATAL("fd_table out of sync [%d]\n", fde->fd);
|
||||
}
|
||||
|
||||
fd_table[fde->fd] = 0;
|
||||
|
||||
if(!(fde->state & FDE_DONT_CLOSE)) {
|
||||
dump_fde(fde, "close");
|
||||
adb_close(fde->fd);
|
||||
}
|
||||
}
|
||||
|
||||
static void fdevent_plist_enqueue(fdevent *node)
|
||||
{
|
||||
fdevent *list = &list_pending;
|
||||
|
||||
node->next = list;
|
||||
node->prev = list->prev;
|
||||
node->prev->next = node;
|
||||
list->prev = node;
|
||||
}
|
||||
|
||||
static void fdevent_plist_remove(fdevent *node)
|
||||
{
|
||||
node->prev->next = node->next;
|
||||
node->next->prev = node->prev;
|
||||
node->next = 0;
|
||||
node->prev = 0;
|
||||
}
|
||||
|
||||
static fdevent *fdevent_plist_dequeue(void)
|
||||
{
|
||||
fdevent *list = &list_pending;
|
||||
fdevent *node = list->next;
|
||||
|
||||
if(node == list) return 0;
|
||||
|
||||
list->next = node->next;
|
||||
list->next->prev = list;
|
||||
node->next = 0;
|
||||
node->prev = 0;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static void fdevent_call_fdfunc(fdevent* fde)
|
||||
{
|
||||
unsigned events = fde->events;
|
||||
fde->events = 0;
|
||||
if(!(fde->state & FDE_PENDING)) return;
|
||||
fde->state &= (~FDE_PENDING);
|
||||
dump_fde(fde, "callback");
|
||||
fde->func(fde->fd, events, fde->arg);
|
||||
}
|
||||
|
||||
static void fdevent_subproc_event_func(int fd, unsigned ev,
|
||||
void* /* userdata */)
|
||||
{
|
||||
|
||||
D("subproc handling on fd=%d ev=%04x\n", fd, ev);
|
||||
|
||||
// Hook oneself back into the fde's suitable for select() on read.
|
||||
if((fd < 0) || (fd >= fd_table_max)) {
|
||||
FATAL("fd %d out of range for fd_table \n", fd);
|
||||
}
|
||||
fdevent *fde = fd_table[fd];
|
||||
fdevent_add(fde, FDE_READ);
|
||||
|
||||
if(ev & FDE_READ){
|
||||
int subproc_fd;
|
||||
|
||||
if(!ReadFdExactly(fd, &subproc_fd, sizeof(subproc_fd))) {
|
||||
FATAL("Failed to read the subproc's fd from fd=%d\n", fd);
|
||||
}
|
||||
if((subproc_fd < 0) || (subproc_fd >= fd_table_max)) {
|
||||
D("subproc_fd %d out of range 0, fd_table_max=%d\n",
|
||||
subproc_fd, fd_table_max);
|
||||
return;
|
||||
}
|
||||
fdevent *subproc_fde = fd_table[subproc_fd];
|
||||
if(!subproc_fde) {
|
||||
D("subproc_fd %d cleared from fd_table\n", subproc_fd);
|
||||
return;
|
||||
}
|
||||
if(subproc_fde->fd != subproc_fd) {
|
||||
// Already reallocated?
|
||||
D("subproc_fd %d != fd_table[].fd %d\n", subproc_fd, subproc_fde->fd);
|
||||
return;
|
||||
}
|
||||
|
||||
subproc_fde->force_eof = 1;
|
||||
|
||||
int rcount = 0;
|
||||
ioctl(subproc_fd, FIONREAD, &rcount);
|
||||
D("subproc with fd=%d has rcount=%d err=%d\n",
|
||||
subproc_fd, rcount, errno);
|
||||
|
||||
if(rcount) {
|
||||
// If there is data left, it will show up in the select().
|
||||
// This works because there is no other thread reading that
|
||||
// data when in this fd_func().
|
||||
return;
|
||||
}
|
||||
|
||||
D("subproc_fde.state=%04x\n", subproc_fde->state);
|
||||
subproc_fde->events |= FDE_READ;
|
||||
if(subproc_fde->state & FDE_PENDING) {
|
||||
return;
|
||||
}
|
||||
subproc_fde->state |= FDE_PENDING;
|
||||
fdevent_call_fdfunc(subproc_fde);
|
||||
}
|
||||
}
|
||||
|
||||
fdevent *fdevent_create(int fd, fd_func func, void *arg)
|
||||
{
|
||||
fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
|
||||
if(fde == 0) return 0;
|
||||
fdevent_install(fde, fd, func, arg);
|
||||
fde->state |= FDE_CREATED;
|
||||
return fde;
|
||||
}
|
||||
|
||||
void fdevent_destroy(fdevent *fde)
|
||||
{
|
||||
if(fde == 0) return;
|
||||
if(!(fde->state & FDE_CREATED)) {
|
||||
FATAL("fde %p not created by fdevent_create()\n", fde);
|
||||
}
|
||||
fdevent_remove(fde);
|
||||
free(fde);
|
||||
}
|
||||
|
||||
void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg)
|
||||
{
|
||||
memset(fde, 0, sizeof(fdevent));
|
||||
fde->state = FDE_ACTIVE;
|
||||
fde->fd = fd;
|
||||
fde->force_eof = 0;
|
||||
fde->func = func;
|
||||
fde->arg = arg;
|
||||
|
||||
#ifndef HAVE_WINSOCK
|
||||
fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
#endif
|
||||
fdevent_register(fde);
|
||||
dump_fde(fde, "connect");
|
||||
fdevent_connect(fde);
|
||||
fde->state |= FDE_ACTIVE;
|
||||
}
|
||||
|
||||
void fdevent_remove(fdevent *fde)
|
||||
{
|
||||
if(fde->state & FDE_PENDING) {
|
||||
fdevent_plist_remove(fde);
|
||||
}
|
||||
|
||||
if(fde->state & FDE_ACTIVE) {
|
||||
fdevent_disconnect(fde);
|
||||
dump_fde(fde, "disconnect");
|
||||
fdevent_unregister(fde);
|
||||
}
|
||||
|
||||
fde->state = 0;
|
||||
fde->events = 0;
|
||||
}
|
||||
|
||||
|
||||
void fdevent_set(fdevent *fde, unsigned events)
|
||||
{
|
||||
events &= FDE_EVENTMASK;
|
||||
|
||||
if((fde->state & FDE_EVENTMASK) == events) return;
|
||||
|
||||
if(fde->state & FDE_ACTIVE) {
|
||||
fdevent_update(fde, events);
|
||||
dump_fde(fde, "update");
|
||||
}
|
||||
|
||||
fde->state = (fde->state & FDE_STATEMASK) | events;
|
||||
|
||||
if(fde->state & FDE_PENDING) {
|
||||
/* if we're pending, make sure
|
||||
** we don't signal an event that
|
||||
** is no longer wanted.
|
||||
*/
|
||||
fde->events &= (~events);
|
||||
if(fde->events == 0) {
|
||||
fdevent_plist_remove(fde);
|
||||
fde->state &= (~FDE_PENDING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fdevent_add(fdevent *fde, unsigned events)
|
||||
{
|
||||
fdevent_set(
|
||||
fde, (fde->state & FDE_EVENTMASK) | (events & FDE_EVENTMASK));
|
||||
}
|
||||
|
||||
void fdevent_del(fdevent *fde, unsigned events)
|
||||
{
|
||||
fdevent_set(
|
||||
fde, (fde->state & FDE_EVENTMASK) & (~(events & FDE_EVENTMASK)));
|
||||
}
|
||||
|
||||
void fdevent_subproc_setup()
|
||||
{
|
||||
int s[2];
|
||||
|
||||
if(adb_socketpair(s)) {
|
||||
FATAL("cannot create shell-exit socket-pair\n");
|
||||
}
|
||||
D("socketpair: (%d,%d)", s[0], s[1]);
|
||||
|
||||
SHELL_EXIT_NOTIFY_FD = s[0];
|
||||
fdevent *fde;
|
||||
fde = fdevent_create(s[1], fdevent_subproc_event_func, NULL);
|
||||
if(!fde)
|
||||
FATAL("cannot create fdevent for shell-exit handler\n");
|
||||
fdevent_add(fde, FDE_READ);
|
||||
}
|
||||
|
||||
void fdevent_loop()
|
||||
{
|
||||
fdevent *fde;
|
||||
fdevent_subproc_setup();
|
||||
|
||||
for(;;) {
|
||||
D("--- ---- waiting for events\n");
|
||||
|
||||
fdevent_process();
|
||||
|
||||
while((fde = fdevent_plist_dequeue())) {
|
||||
fdevent_call_fdfunc(fde);
|
||||
}
|
||||
}
|
||||
}
|
||||
81
fdevent.h
Normal file
81
fdevent.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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 __FDEVENT_H
|
||||
#define __FDEVENT_H
|
||||
|
||||
#include <stdint.h> /* for int64_t */
|
||||
|
||||
/* events that may be observed */
|
||||
#define FDE_READ 0x0001
|
||||
#define FDE_WRITE 0x0002
|
||||
#define FDE_ERROR 0x0004
|
||||
#define FDE_TIMEOUT 0x0008
|
||||
|
||||
/* features that may be set (via the events set/add/del interface) */
|
||||
#define FDE_DONT_CLOSE 0x0080
|
||||
|
||||
struct fdevent;
|
||||
|
||||
typedef void (*fd_func)(int fd, unsigned events, void *userdata);
|
||||
|
||||
/* Allocate and initialize a new fdevent object
|
||||
* Note: use FD_TIMER as 'fd' to create a fd-less object
|
||||
* (used to implement timers).
|
||||
*/
|
||||
fdevent *fdevent_create(int fd, fd_func func, void *arg);
|
||||
|
||||
/* Uninitialize and deallocate an fdevent object that was
|
||||
** created by fdevent_create()
|
||||
*/
|
||||
void fdevent_destroy(fdevent *fde);
|
||||
|
||||
/* Initialize an fdevent object that was externally allocated
|
||||
*/
|
||||
void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg);
|
||||
|
||||
/* Uninitialize an fdevent object that was initialized by
|
||||
** fdevent_install()
|
||||
*/
|
||||
void fdevent_remove(fdevent *item);
|
||||
|
||||
/* Change which events should cause notifications
|
||||
*/
|
||||
void fdevent_set(fdevent *fde, unsigned events);
|
||||
void fdevent_add(fdevent *fde, unsigned events);
|
||||
void fdevent_del(fdevent *fde, unsigned events);
|
||||
|
||||
void fdevent_set_timeout(fdevent *fde, int64_t timeout_ms);
|
||||
|
||||
/* loop forever, handling events.
|
||||
*/
|
||||
void fdevent_loop();
|
||||
|
||||
struct fdevent {
|
||||
fdevent *next;
|
||||
fdevent *prev;
|
||||
|
||||
int fd;
|
||||
int force_eof;
|
||||
|
||||
uint16_t state;
|
||||
uint16_t events;
|
||||
|
||||
fd_func func;
|
||||
void *arg;
|
||||
};
|
||||
|
||||
#endif
|
||||
1048
file_sync_client.cpp
Normal file
1048
file_sync_client.cpp
Normal file
File diff suppressed because it is too large
Load Diff
469
file_sync_service.cpp
Normal file
469
file_sync_service.cpp
Normal file
@@ -0,0 +1,469 @@
|
||||
/*
|
||||
* 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 TRACE_SYNC
|
||||
|
||||
#include "sysdeps.h"
|
||||
#include "file_sync_service.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <selinux/android.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <utime.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "adb_io.h"
|
||||
#include "private/android_filesystem_config.h"
|
||||
|
||||
static bool should_use_fs_config(const char* path) {
|
||||
// TODO: use fs_config to configure permissions on /data.
|
||||
return strncmp("/system/", path, strlen("/system/")) == 0 ||
|
||||
strncmp("/vendor/", path, strlen("/vendor/")) == 0 ||
|
||||
strncmp("/oem/", path, strlen("/oem/")) == 0;
|
||||
}
|
||||
|
||||
static int mkdirs(char *name)
|
||||
{
|
||||
int ret;
|
||||
char *x = name + 1;
|
||||
uid_t uid = -1;
|
||||
gid_t gid = -1;
|
||||
unsigned int mode = 0775;
|
||||
uint64_t cap = 0;
|
||||
|
||||
if(name[0] != '/') return -1;
|
||||
|
||||
for(;;) {
|
||||
x = adb_dirstart(x);
|
||||
if(x == 0) return 0;
|
||||
*x = 0;
|
||||
if (should_use_fs_config(name)) {
|
||||
fs_config(name, 1, NULL, &uid, &gid, &mode, &cap);
|
||||
}
|
||||
ret = adb_mkdir(name, mode);
|
||||
if((ret < 0) && (errno != EEXIST)) {
|
||||
D("mkdir(\"%s\") -> %s\n", name, strerror(errno));
|
||||
*x = '/';
|
||||
return ret;
|
||||
} else if(ret == 0) {
|
||||
ret = chown(name, uid, gid);
|
||||
if (ret < 0) {
|
||||
*x = '/';
|
||||
return ret;
|
||||
}
|
||||
selinux_android_restorecon(name, 0);
|
||||
}
|
||||
*x++ = '/';
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_stat(int s, const char *path)
|
||||
{
|
||||
syncmsg msg;
|
||||
struct stat st;
|
||||
|
||||
msg.stat.id = ID_STAT;
|
||||
|
||||
if(lstat(path, &st)) {
|
||||
msg.stat.mode = 0;
|
||||
msg.stat.size = 0;
|
||||
msg.stat.time = 0;
|
||||
} else {
|
||||
msg.stat.mode = htoll(st.st_mode);
|
||||
msg.stat.size = htoll(st.st_size);
|
||||
msg.stat.time = htoll(st.st_mtime);
|
||||
}
|
||||
|
||||
return WriteFdExactly(s, &msg.stat, sizeof(msg.stat)) ? 0 : -1;
|
||||
}
|
||||
|
||||
static int do_list(int s, const char *path)
|
||||
{
|
||||
DIR *d;
|
||||
struct dirent *de;
|
||||
struct stat st;
|
||||
syncmsg msg;
|
||||
int len;
|
||||
|
||||
char tmp[1024 + 256 + 1];
|
||||
char *fname;
|
||||
|
||||
len = strlen(path);
|
||||
memcpy(tmp, path, len);
|
||||
tmp[len] = '/';
|
||||
fname = tmp + len + 1;
|
||||
|
||||
msg.dent.id = ID_DENT;
|
||||
|
||||
d = opendir(path);
|
||||
if(d == 0) goto done;
|
||||
|
||||
while((de = readdir(d))) {
|
||||
int len = strlen(de->d_name);
|
||||
|
||||
/* not supposed to be possible, but
|
||||
if it does happen, let's not buffer overrun */
|
||||
if(len > 256) continue;
|
||||
|
||||
strcpy(fname, de->d_name);
|
||||
if(lstat(tmp, &st) == 0) {
|
||||
msg.dent.mode = htoll(st.st_mode);
|
||||
msg.dent.size = htoll(st.st_size);
|
||||
msg.dent.time = htoll(st.st_mtime);
|
||||
msg.dent.namelen = htoll(len);
|
||||
|
||||
if(!WriteFdExactly(s, &msg.dent, sizeof(msg.dent)) ||
|
||||
!WriteFdExactly(s, de->d_name, len)) {
|
||||
closedir(d);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir(d);
|
||||
|
||||
done:
|
||||
msg.dent.id = ID_DONE;
|
||||
msg.dent.mode = 0;
|
||||
msg.dent.size = 0;
|
||||
msg.dent.time = 0;
|
||||
msg.dent.namelen = 0;
|
||||
return WriteFdExactly(s, &msg.dent, sizeof(msg.dent)) ? 0 : -1;
|
||||
}
|
||||
|
||||
static int fail_message(int s, const char *reason)
|
||||
{
|
||||
syncmsg msg;
|
||||
int len = strlen(reason);
|
||||
|
||||
D("sync: failure: %s\n", reason);
|
||||
|
||||
msg.data.id = ID_FAIL;
|
||||
msg.data.size = htoll(len);
|
||||
if(!WriteFdExactly(s, &msg.data, sizeof(msg.data)) ||
|
||||
!WriteFdExactly(s, reason, len)) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int fail_errno(int s)
|
||||
{
|
||||
return fail_message(s, strerror(errno));
|
||||
}
|
||||
|
||||
static int handle_send_file(int s, char *path, uid_t uid,
|
||||
gid_t gid, mode_t mode, char *buffer, bool do_unlink)
|
||||
{
|
||||
syncmsg msg;
|
||||
unsigned int timestamp = 0;
|
||||
int fd;
|
||||
|
||||
fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
|
||||
if(fd < 0 && errno == ENOENT) {
|
||||
if(mkdirs(path) != 0) {
|
||||
if(fail_errno(s))
|
||||
return -1;
|
||||
fd = -1;
|
||||
} else {
|
||||
fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
|
||||
}
|
||||
}
|
||||
if(fd < 0 && errno == EEXIST) {
|
||||
fd = adb_open_mode(path, O_WRONLY | O_CLOEXEC, mode);
|
||||
}
|
||||
if(fd < 0) {
|
||||
if(fail_errno(s))
|
||||
return -1;
|
||||
fd = -1;
|
||||
} else {
|
||||
if(fchown(fd, uid, gid) != 0) {
|
||||
fail_errno(s);
|
||||
errno = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* fchown clears the setuid bit - restore it if present.
|
||||
* Ignore the result of calling fchmod. It's not supported
|
||||
* by all filesystems. b/12441485
|
||||
*/
|
||||
fchmod(fd, mode);
|
||||
}
|
||||
|
||||
for(;;) {
|
||||
unsigned int len;
|
||||
|
||||
if(!ReadFdExactly(s, &msg.data, sizeof(msg.data)))
|
||||
goto fail;
|
||||
|
||||
if(msg.data.id != ID_DATA) {
|
||||
if(msg.data.id == ID_DONE) {
|
||||
timestamp = ltohl(msg.data.size);
|
||||
break;
|
||||
}
|
||||
fail_message(s, "invalid data message");
|
||||
goto fail;
|
||||
}
|
||||
len = ltohl(msg.data.size);
|
||||
if(len > SYNC_DATA_MAX) {
|
||||
fail_message(s, "oversize data message");
|
||||
goto fail;
|
||||
}
|
||||
if(!ReadFdExactly(s, buffer, len))
|
||||
goto fail;
|
||||
|
||||
if(fd < 0)
|
||||
continue;
|
||||
if(!WriteFdExactly(fd, buffer, len)) {
|
||||
int saved_errno = errno;
|
||||
adb_close(fd);
|
||||
if (do_unlink) adb_unlink(path);
|
||||
fd = -1;
|
||||
errno = saved_errno;
|
||||
if(fail_errno(s)) return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if(fd >= 0) {
|
||||
struct utimbuf u;
|
||||
adb_close(fd);
|
||||
selinux_android_restorecon(path, 0);
|
||||
u.actime = timestamp;
|
||||
u.modtime = timestamp;
|
||||
utime(path, &u);
|
||||
|
||||
msg.status.id = ID_OKAY;
|
||||
msg.status.msglen = 0;
|
||||
if(!WriteFdExactly(s, &msg.status, sizeof(msg.status)))
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if(fd >= 0)
|
||||
adb_close(fd);
|
||||
if (do_unlink) adb_unlink(path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
extern int handle_send_link(int s, char *path, char *buffer) __attribute__((error("no symlinks on Windows")));
|
||||
#else
|
||||
static int handle_send_link(int s, char *path, char *buffer)
|
||||
{
|
||||
syncmsg msg;
|
||||
unsigned int len;
|
||||
int ret;
|
||||
|
||||
if(!ReadFdExactly(s, &msg.data, sizeof(msg.data)))
|
||||
return -1;
|
||||
|
||||
if(msg.data.id != ID_DATA) {
|
||||
fail_message(s, "invalid data message: expected ID_DATA");
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = ltohl(msg.data.size);
|
||||
if(len > SYNC_DATA_MAX) {
|
||||
fail_message(s, "oversize data message");
|
||||
return -1;
|
||||
}
|
||||
if(!ReadFdExactly(s, buffer, len))
|
||||
return -1;
|
||||
|
||||
ret = symlink(buffer, path);
|
||||
if(ret && errno == ENOENT) {
|
||||
if(mkdirs(path) != 0) {
|
||||
fail_errno(s);
|
||||
return -1;
|
||||
}
|
||||
ret = symlink(buffer, path);
|
||||
}
|
||||
if(ret) {
|
||||
fail_errno(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!ReadFdExactly(s, &msg.data, sizeof(msg.data)))
|
||||
return -1;
|
||||
|
||||
if(msg.data.id == ID_DONE) {
|
||||
msg.status.id = ID_OKAY;
|
||||
msg.status.msglen = 0;
|
||||
if(!WriteFdExactly(s, &msg.status, sizeof(msg.status)))
|
||||
return -1;
|
||||
} else {
|
||||
fail_message(s, "invalid data message: expected ID_DONE");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int do_send(int s, char *path, char *buffer)
|
||||
{
|
||||
unsigned int mode;
|
||||
bool is_link = false;
|
||||
bool do_unlink;
|
||||
|
||||
char* tmp = strrchr(path,',');
|
||||
if(tmp) {
|
||||
*tmp = 0;
|
||||
errno = 0;
|
||||
mode = strtoul(tmp + 1, NULL, 0);
|
||||
is_link = S_ISLNK((mode_t) mode);
|
||||
mode &= 0777;
|
||||
}
|
||||
if(!tmp || errno) {
|
||||
mode = 0644;
|
||||
is_link = 0;
|
||||
do_unlink = true;
|
||||
} else {
|
||||
struct stat st;
|
||||
/* Don't delete files before copying if they are not "regular" */
|
||||
do_unlink = lstat(path, &st) || S_ISREG(st.st_mode) || S_ISLNK(st.st_mode);
|
||||
if (do_unlink) {
|
||||
adb_unlink(path);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_link) {
|
||||
return handle_send_link(s, path, buffer);
|
||||
}
|
||||
|
||||
uid_t uid = -1;
|
||||
gid_t gid = -1;
|
||||
uint64_t cap = 0;
|
||||
|
||||
/* copy user permission bits to "group" and "other" permissions */
|
||||
mode |= ((mode >> 3) & 0070);
|
||||
mode |= ((mode >> 3) & 0007);
|
||||
|
||||
tmp = path;
|
||||
if(*tmp == '/') {
|
||||
tmp++;
|
||||
}
|
||||
if (should_use_fs_config(path)) {
|
||||
fs_config(tmp, 0, NULL, &uid, &gid, &mode, &cap);
|
||||
}
|
||||
return handle_send_file(s, path, uid, gid, mode, buffer, do_unlink);
|
||||
}
|
||||
|
||||
static int do_recv(int s, const char *path, char *buffer)
|
||||
{
|
||||
syncmsg msg;
|
||||
int fd, r;
|
||||
|
||||
fd = adb_open(path, O_RDONLY | O_CLOEXEC);
|
||||
if(fd < 0) {
|
||||
if(fail_errno(s)) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
msg.data.id = ID_DATA;
|
||||
for(;;) {
|
||||
r = adb_read(fd, buffer, SYNC_DATA_MAX);
|
||||
if(r <= 0) {
|
||||
if(r == 0) break;
|
||||
if(errno == EINTR) continue;
|
||||
r = fail_errno(s);
|
||||
adb_close(fd);
|
||||
return r;
|
||||
}
|
||||
msg.data.size = htoll(r);
|
||||
if(!WriteFdExactly(s, &msg.data, sizeof(msg.data)) ||
|
||||
!WriteFdExactly(s, buffer, r)) {
|
||||
adb_close(fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
adb_close(fd);
|
||||
|
||||
msg.data.id = ID_DONE;
|
||||
msg.data.size = 0;
|
||||
if(!WriteFdExactly(s, &msg.data, sizeof(msg.data))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void file_sync_service(int fd, void *cookie)
|
||||
{
|
||||
syncmsg msg;
|
||||
char name[1025];
|
||||
unsigned namelen;
|
||||
|
||||
char *buffer = reinterpret_cast<char*>(malloc(SYNC_DATA_MAX));
|
||||
if(buffer == 0) goto fail;
|
||||
|
||||
for(;;) {
|
||||
D("sync: waiting for command\n");
|
||||
|
||||
if(!ReadFdExactly(fd, &msg.req, sizeof(msg.req))) {
|
||||
fail_message(fd, "command read failure");
|
||||
break;
|
||||
}
|
||||
namelen = ltohl(msg.req.namelen);
|
||||
if(namelen > 1024) {
|
||||
fail_message(fd, "invalid namelen");
|
||||
break;
|
||||
}
|
||||
if(!ReadFdExactly(fd, name, namelen)) {
|
||||
fail_message(fd, "filename read failure");
|
||||
break;
|
||||
}
|
||||
name[namelen] = 0;
|
||||
|
||||
msg.req.namelen = 0;
|
||||
D("sync: '%s' '%s'\n", (char*) &msg.req, name);
|
||||
|
||||
switch(msg.req.id) {
|
||||
case ID_STAT:
|
||||
if(do_stat(fd, name)) goto fail;
|
||||
break;
|
||||
case ID_LIST:
|
||||
if(do_list(fd, name)) goto fail;
|
||||
break;
|
||||
case ID_SEND:
|
||||
if(do_send(fd, name, buffer)) goto fail;
|
||||
break;
|
||||
case ID_RECV:
|
||||
if(do_recv(fd, name, buffer)) goto fail;
|
||||
break;
|
||||
case ID_QUIT:
|
||||
goto fail;
|
||||
default:
|
||||
fail_message(fd, "unknown command");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
fail:
|
||||
if(buffer != 0) free(buffer);
|
||||
D("sync: done\n");
|
||||
adb_close(fd);
|
||||
}
|
||||
77
file_sync_service.h
Normal file
77
file_sync_service.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _FILE_SYNC_SERVICE_H_
|
||||
#define _FILE_SYNC_SERVICE_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#define htoll(x) (x)
|
||||
#define ltohl(x) (x)
|
||||
|
||||
#define MKID(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24))
|
||||
|
||||
#define ID_STAT MKID('S','T','A','T')
|
||||
#define ID_LIST MKID('L','I','S','T')
|
||||
#define ID_ULNK MKID('U','L','N','K')
|
||||
#define ID_SEND MKID('S','E','N','D')
|
||||
#define ID_RECV MKID('R','E','C','V')
|
||||
#define ID_DENT MKID('D','E','N','T')
|
||||
#define ID_DONE MKID('D','O','N','E')
|
||||
#define ID_DATA MKID('D','A','T','A')
|
||||
#define ID_OKAY MKID('O','K','A','Y')
|
||||
#define ID_FAIL MKID('F','A','I','L')
|
||||
#define ID_QUIT MKID('Q','U','I','T')
|
||||
|
||||
union syncmsg {
|
||||
unsigned id;
|
||||
struct {
|
||||
unsigned id;
|
||||
unsigned namelen;
|
||||
} req;
|
||||
struct {
|
||||
unsigned id;
|
||||
unsigned mode;
|
||||
unsigned size;
|
||||
unsigned time;
|
||||
} stat;
|
||||
struct {
|
||||
unsigned id;
|
||||
unsigned mode;
|
||||
unsigned size;
|
||||
unsigned time;
|
||||
unsigned namelen;
|
||||
} dent;
|
||||
struct {
|
||||
unsigned id;
|
||||
unsigned size;
|
||||
} data;
|
||||
struct {
|
||||
unsigned id;
|
||||
unsigned msglen;
|
||||
} status;
|
||||
} ;
|
||||
|
||||
|
||||
void file_sync_service(int fd, void *cookie);
|
||||
int do_sync_ls(const char *path);
|
||||
int do_sync_push(const char *lpath, const char *rpath, int show_progress);
|
||||
int do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only);
|
||||
int do_sync_pull(const char *rpath, const char *lpath, int show_progress, int pullTime);
|
||||
|
||||
#define SYNC_DATA_MAX (64*1024)
|
||||
|
||||
#endif
|
||||
185
framebuffer_service.cpp
Normal file
185
framebuffer_service.cpp
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/fb.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#include "adb.h"
|
||||
#include "adb_io.h"
|
||||
#include "fdevent.h"
|
||||
|
||||
/* TODO:
|
||||
** - sync with vsync to avoid tearing
|
||||
*/
|
||||
/* This version number defines the format of the fbinfo struct.
|
||||
It must match versioning in ddms where this data is consumed. */
|
||||
#define DDMS_RAWIMAGE_VERSION 1
|
||||
struct fbinfo {
|
||||
unsigned int version;
|
||||
unsigned int bpp;
|
||||
unsigned int size;
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
unsigned int red_offset;
|
||||
unsigned int red_length;
|
||||
unsigned int blue_offset;
|
||||
unsigned int blue_length;
|
||||
unsigned int green_offset;
|
||||
unsigned int green_length;
|
||||
unsigned int alpha_offset;
|
||||
unsigned int alpha_length;
|
||||
} __attribute__((packed));
|
||||
|
||||
void framebuffer_service(int fd, void *cookie)
|
||||
{
|
||||
struct fbinfo fbinfo;
|
||||
unsigned int i, bsize;
|
||||
char buf[640];
|
||||
int fd_screencap;
|
||||
int w, h, f;
|
||||
int fds[2];
|
||||
pid_t pid;
|
||||
|
||||
if (pipe2(fds, O_CLOEXEC) < 0) goto pipefail;
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0) goto done;
|
||||
|
||||
if (pid == 0) {
|
||||
dup2(fds[1], STDOUT_FILENO);
|
||||
adb_close(fds[0]);
|
||||
adb_close(fds[1]);
|
||||
const char* command = "screencap";
|
||||
const char *args[2] = {command, NULL};
|
||||
execvp(command, (char**)args);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
adb_close(fds[1]);
|
||||
fd_screencap = fds[0];
|
||||
|
||||
/* read w, h & format */
|
||||
if(!ReadFdExactly(fd_screencap, &w, 4)) goto done;
|
||||
if(!ReadFdExactly(fd_screencap, &h, 4)) goto done;
|
||||
if(!ReadFdExactly(fd_screencap, &f, 4)) goto done;
|
||||
|
||||
fbinfo.version = DDMS_RAWIMAGE_VERSION;
|
||||
/* see hardware/hardware.h */
|
||||
switch (f) {
|
||||
case 1: /* RGBA_8888 */
|
||||
fbinfo.bpp = 32;
|
||||
fbinfo.size = w * h * 4;
|
||||
fbinfo.width = w;
|
||||
fbinfo.height = h;
|
||||
fbinfo.red_offset = 0;
|
||||
fbinfo.red_length = 8;
|
||||
fbinfo.green_offset = 8;
|
||||
fbinfo.green_length = 8;
|
||||
fbinfo.blue_offset = 16;
|
||||
fbinfo.blue_length = 8;
|
||||
fbinfo.alpha_offset = 24;
|
||||
fbinfo.alpha_length = 8;
|
||||
break;
|
||||
case 2: /* RGBX_8888 */
|
||||
fbinfo.bpp = 32;
|
||||
fbinfo.size = w * h * 4;
|
||||
fbinfo.width = w;
|
||||
fbinfo.height = h;
|
||||
fbinfo.red_offset = 0;
|
||||
fbinfo.red_length = 8;
|
||||
fbinfo.green_offset = 8;
|
||||
fbinfo.green_length = 8;
|
||||
fbinfo.blue_offset = 16;
|
||||
fbinfo.blue_length = 8;
|
||||
fbinfo.alpha_offset = 24;
|
||||
fbinfo.alpha_length = 0;
|
||||
break;
|
||||
case 3: /* RGB_888 */
|
||||
fbinfo.bpp = 24;
|
||||
fbinfo.size = w * h * 3;
|
||||
fbinfo.width = w;
|
||||
fbinfo.height = h;
|
||||
fbinfo.red_offset = 0;
|
||||
fbinfo.red_length = 8;
|
||||
fbinfo.green_offset = 8;
|
||||
fbinfo.green_length = 8;
|
||||
fbinfo.blue_offset = 16;
|
||||
fbinfo.blue_length = 8;
|
||||
fbinfo.alpha_offset = 24;
|
||||
fbinfo.alpha_length = 0;
|
||||
break;
|
||||
case 4: /* RGB_565 */
|
||||
fbinfo.bpp = 16;
|
||||
fbinfo.size = w * h * 2;
|
||||
fbinfo.width = w;
|
||||
fbinfo.height = h;
|
||||
fbinfo.red_offset = 11;
|
||||
fbinfo.red_length = 5;
|
||||
fbinfo.green_offset = 5;
|
||||
fbinfo.green_length = 6;
|
||||
fbinfo.blue_offset = 0;
|
||||
fbinfo.blue_length = 5;
|
||||
fbinfo.alpha_offset = 0;
|
||||
fbinfo.alpha_length = 0;
|
||||
break;
|
||||
case 5: /* BGRA_8888 */
|
||||
fbinfo.bpp = 32;
|
||||
fbinfo.size = w * h * 4;
|
||||
fbinfo.width = w;
|
||||
fbinfo.height = h;
|
||||
fbinfo.red_offset = 16;
|
||||
fbinfo.red_length = 8;
|
||||
fbinfo.green_offset = 8;
|
||||
fbinfo.green_length = 8;
|
||||
fbinfo.blue_offset = 0;
|
||||
fbinfo.blue_length = 8;
|
||||
fbinfo.alpha_offset = 24;
|
||||
fbinfo.alpha_length = 8;
|
||||
break;
|
||||
default:
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* write header */
|
||||
if(!WriteFdExactly(fd, &fbinfo, sizeof(fbinfo))) goto done;
|
||||
|
||||
/* write data */
|
||||
for(i = 0; i < fbinfo.size; i += bsize) {
|
||||
bsize = sizeof(buf);
|
||||
if (i + bsize > fbinfo.size)
|
||||
bsize = fbinfo.size - i;
|
||||
if(!ReadFdExactly(fd_screencap, buf, bsize)) goto done;
|
||||
if(!WriteFdExactly(fd, buf, bsize)) goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
adb_close(fds[0]);
|
||||
|
||||
TEMP_FAILURE_RETRY(waitpid(pid, NULL, 0));
|
||||
pipefail:
|
||||
adb_close(fd);
|
||||
}
|
||||
32
get_my_path_darwin.cpp
Normal file
32
get_my_path_darwin.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#import <Carbon/Carbon.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "adb.h"
|
||||
|
||||
void get_my_path(char *s, size_t maxLen)
|
||||
{
|
||||
CFBundleRef mainBundle = CFBundleGetMainBundle();
|
||||
CFURLRef executableURL = CFBundleCopyExecutableURL(mainBundle);
|
||||
CFStringRef executablePathString = CFURLCopyFileSystemPath(executableURL, kCFURLPOSIXPathStyle);
|
||||
CFRelease(executableURL);
|
||||
|
||||
CFStringGetFileSystemRepresentation(executablePathString, s, maxLen);
|
||||
CFRelease(executablePathString);
|
||||
}
|
||||
|
||||
35
get_my_path_linux.cpp
Normal file
35
get_my_path_linux.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "adb.h"
|
||||
|
||||
void get_my_path(char *exe, size_t maxLen)
|
||||
{
|
||||
char proc[64];
|
||||
snprintf(proc, sizeof proc, "/proc/%d/exe", getpid());
|
||||
int err = readlink(proc, exe, maxLen - 1);
|
||||
if(err > 0) {
|
||||
exe[err] = '\0';
|
||||
} else {
|
||||
exe[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
36
get_my_path_windows.cpp
Normal file
36
get_my_path_windows.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "adb.h"
|
||||
|
||||
void get_my_path(char *exe, size_t maxLen)
|
||||
{
|
||||
char *r;
|
||||
|
||||
/* XXX: should be GetModuleFileNameA */
|
||||
if (GetModuleFileName(NULL, exe, maxLen) > 0) {
|
||||
r = strrchr(exe, '\\');
|
||||
if (r != NULL)
|
||||
*r = '\0';
|
||||
} else {
|
||||
exe[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
752
jdwp_service.cpp
Normal file
752
jdwp_service.cpp
Normal file
@@ -0,0 +1,752 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* implement the "debug-ports" and "track-debug-ports" device services */
|
||||
|
||||
#define TRACE_TAG TRACE_JDWP
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "adb.h"
|
||||
|
||||
/* here's how these things work.
|
||||
|
||||
when adbd starts, it creates a unix server socket
|
||||
named @vm-debug-control (@ is a shortcut for "first byte is zero"
|
||||
to use the private namespace instead of the file system)
|
||||
|
||||
when a new JDWP daemon thread starts in a new VM process, it creates
|
||||
a connection to @vm-debug-control to announce its availability.
|
||||
|
||||
|
||||
JDWP thread @vm-debug-control
|
||||
| |
|
||||
|-------------------------------> |
|
||||
| hello I'm in process <pid> |
|
||||
| |
|
||||
| |
|
||||
|
||||
the connection is kept alive. it will be closed automatically if
|
||||
the JDWP process terminates (this allows adbd to detect dead
|
||||
processes).
|
||||
|
||||
adbd thus maintains a list of "active" JDWP processes. it can send
|
||||
its content to clients through the "device:debug-ports" service,
|
||||
or even updates through the "device:track-debug-ports" service.
|
||||
|
||||
when a debugger wants to connect, it simply runs the command
|
||||
equivalent to "adb forward tcp:<hostport> jdwp:<pid>"
|
||||
|
||||
"jdwp:<pid>" is a new forward destination format used to target
|
||||
a given JDWP process on the device. when sutch a request arrives,
|
||||
adbd does the following:
|
||||
|
||||
- first, it calls socketpair() to create a pair of equivalent
|
||||
sockets.
|
||||
|
||||
- it attaches the first socket in the pair to a local socket
|
||||
which is itself attached to the transport's remote socket:
|
||||
|
||||
|
||||
- it sends the file descriptor of the second socket directly
|
||||
to the JDWP process with the help of sendmsg()
|
||||
|
||||
|
||||
JDWP thread @vm-debug-control
|
||||
| |
|
||||
| <----------------------|
|
||||
| OK, try this file descriptor |
|
||||
| |
|
||||
| |
|
||||
|
||||
then, the JDWP thread uses this new socket descriptor as its
|
||||
pass-through connection to the debugger (and receives the
|
||||
JDWP-Handshake message, answers to it, etc...)
|
||||
|
||||
this gives the following graphics:
|
||||
____________________________________
|
||||
| |
|
||||
| ADB Server (host) |
|
||||
| |
|
||||
Debugger <---> LocalSocket <----> RemoteSocket |
|
||||
| ^^ |
|
||||
|___________________________||_______|
|
||||
||
|
||||
Transport ||
|
||||
(TCP for emulator - USB for device) ||
|
||||
||
|
||||
___________________________||_______
|
||||
| || |
|
||||
| ADBD (device) || |
|
||||
| VV |
|
||||
JDWP <======> LocalSocket <----> RemoteSocket |
|
||||
| |
|
||||
|____________________________________|
|
||||
|
||||
due to the way adb works, this doesn't need a special socket
|
||||
type or fancy handling of socket termination if either the debugger
|
||||
or the JDWP process closes the connection.
|
||||
|
||||
THIS IS THE SIMPLEST IMPLEMENTATION I COULD FIND, IF YOU HAPPEN
|
||||
TO HAVE A BETTER IDEA, LET ME KNOW - Digit
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
/** JDWP PID List Support Code
|
||||
** for each JDWP process, we record its pid and its connected socket
|
||||
**/
|
||||
|
||||
#define MAX_OUT_FDS 4
|
||||
|
||||
#if !ADB_HOST
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
struct JdwpProcess {
|
||||
JdwpProcess* next;
|
||||
JdwpProcess* prev;
|
||||
int pid;
|
||||
int socket;
|
||||
fdevent* fde;
|
||||
|
||||
char in_buff[4]; /* input character to read PID */
|
||||
int in_len; /* number from JDWP process */
|
||||
|
||||
int out_fds[MAX_OUT_FDS]; /* output array of file descriptors */
|
||||
int out_count; /* to send to the JDWP process */
|
||||
};
|
||||
|
||||
static JdwpProcess _jdwp_list;
|
||||
|
||||
static int
|
||||
jdwp_process_list( char* buffer, int bufferlen )
|
||||
{
|
||||
char* end = buffer + bufferlen;
|
||||
char* p = buffer;
|
||||
JdwpProcess* proc = _jdwp_list.next;
|
||||
|
||||
for ( ; proc != &_jdwp_list; proc = proc->next ) {
|
||||
int len;
|
||||
|
||||
/* skip transient connections */
|
||||
if (proc->pid < 0)
|
||||
continue;
|
||||
|
||||
len = snprintf(p, end-p, "%d\n", proc->pid);
|
||||
if (p + len >= end)
|
||||
break;
|
||||
p += len;
|
||||
}
|
||||
p[0] = 0;
|
||||
return (p - buffer);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
jdwp_process_list_msg( char* buffer, int bufferlen )
|
||||
{
|
||||
char head[5];
|
||||
int len = jdwp_process_list( buffer+4, bufferlen-4 );
|
||||
snprintf(head, sizeof head, "%04x", len);
|
||||
memcpy(buffer, head, 4);
|
||||
return len + 4;
|
||||
}
|
||||
|
||||
|
||||
static void jdwp_process_list_updated(void);
|
||||
|
||||
static void
|
||||
jdwp_process_free( JdwpProcess* proc )
|
||||
{
|
||||
if (proc) {
|
||||
int n;
|
||||
|
||||
proc->prev->next = proc->next;
|
||||
proc->next->prev = proc->prev;
|
||||
|
||||
if (proc->socket >= 0) {
|
||||
adb_shutdown(proc->socket);
|
||||
adb_close(proc->socket);
|
||||
proc->socket = -1;
|
||||
}
|
||||
|
||||
if (proc->fde != NULL) {
|
||||
fdevent_destroy(proc->fde);
|
||||
proc->fde = NULL;
|
||||
}
|
||||
proc->pid = -1;
|
||||
|
||||
for (n = 0; n < proc->out_count; n++) {
|
||||
adb_close(proc->out_fds[n]);
|
||||
}
|
||||
proc->out_count = 0;
|
||||
|
||||
free(proc);
|
||||
|
||||
jdwp_process_list_updated();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void jdwp_process_event(int, unsigned, void*); /* forward */
|
||||
|
||||
|
||||
static JdwpProcess*
|
||||
jdwp_process_alloc( int socket )
|
||||
{
|
||||
JdwpProcess* proc = reinterpret_cast<JdwpProcess*>(
|
||||
calloc(1, sizeof(*proc)));
|
||||
|
||||
if (proc == NULL) {
|
||||
D("not enough memory to create new JDWP process\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
proc->socket = socket;
|
||||
proc->pid = -1;
|
||||
proc->next = proc;
|
||||
proc->prev = proc;
|
||||
|
||||
proc->fde = fdevent_create( socket, jdwp_process_event, proc );
|
||||
if (proc->fde == NULL) {
|
||||
D("could not create fdevent for new JDWP process\n" );
|
||||
free(proc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
proc->fde->state |= FDE_DONT_CLOSE;
|
||||
proc->in_len = 0;
|
||||
proc->out_count = 0;
|
||||
|
||||
/* append to list */
|
||||
proc->next = &_jdwp_list;
|
||||
proc->prev = proc->next->prev;
|
||||
|
||||
proc->prev->next = proc;
|
||||
proc->next->prev = proc;
|
||||
|
||||
/* start by waiting for the PID */
|
||||
fdevent_add(proc->fde, FDE_READ);
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
jdwp_process_event( int socket, unsigned events, void* _proc )
|
||||
{
|
||||
JdwpProcess* proc = reinterpret_cast<JdwpProcess*>(_proc);
|
||||
|
||||
if (events & FDE_READ) {
|
||||
if (proc->pid < 0) {
|
||||
/* read the PID as a 4-hexchar string */
|
||||
char* p = proc->in_buff + proc->in_len;
|
||||
int size = 4 - proc->in_len;
|
||||
char temp[5];
|
||||
while (size > 0) {
|
||||
int len = recv( socket, p, size, 0 );
|
||||
if (len < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
if (errno == EAGAIN)
|
||||
return;
|
||||
/* this can fail here if the JDWP process crashes very fast */
|
||||
D("weird unknown JDWP process failure: %s\n",
|
||||
strerror(errno));
|
||||
|
||||
goto CloseProcess;
|
||||
}
|
||||
if (len == 0) { /* end of stream ? */
|
||||
D("weird end-of-stream from unknown JDWP process\n");
|
||||
goto CloseProcess;
|
||||
}
|
||||
p += len;
|
||||
proc->in_len += len;
|
||||
size -= len;
|
||||
}
|
||||
/* we have read 4 characters, now decode the pid */
|
||||
memcpy(temp, proc->in_buff, 4);
|
||||
temp[4] = 0;
|
||||
|
||||
if (sscanf( temp, "%04x", &proc->pid ) != 1) {
|
||||
D("could not decode JDWP %p PID number: '%s'\n", proc, temp);
|
||||
goto CloseProcess;
|
||||
}
|
||||
|
||||
/* all is well, keep reading to detect connection closure */
|
||||
D("Adding pid %d to jdwp process list\n", proc->pid);
|
||||
jdwp_process_list_updated();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* the pid was read, if we get there it's probably because the connection
|
||||
* was closed (e.g. the JDWP process exited or crashed) */
|
||||
char buf[32];
|
||||
|
||||
for (;;) {
|
||||
int len = recv(socket, buf, sizeof(buf), 0);
|
||||
|
||||
if (len <= 0) {
|
||||
if (len < 0 && errno == EINTR)
|
||||
continue;
|
||||
if (len < 0 && errno == EAGAIN)
|
||||
return;
|
||||
else {
|
||||
D("terminating JDWP %d connection: %s\n", proc->pid,
|
||||
strerror(errno));
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
D( "ignoring unexpected JDWP %d control socket activity (%d bytes)\n",
|
||||
proc->pid, len );
|
||||
}
|
||||
}
|
||||
|
||||
CloseProcess:
|
||||
if (proc->pid >= 0)
|
||||
D( "remove pid %d to jdwp process list\n", proc->pid );
|
||||
jdwp_process_free(proc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (events & FDE_WRITE) {
|
||||
D("trying to write to JDWP pid controli (count=%d first=%d) %d\n",
|
||||
proc->pid, proc->out_count, proc->out_fds[0]);
|
||||
if (proc->out_count > 0) {
|
||||
int fd = proc->out_fds[0];
|
||||
int n, ret;
|
||||
struct cmsghdr* cmsg;
|
||||
struct msghdr msg;
|
||||
struct iovec iov;
|
||||
char dummy = '!';
|
||||
char buffer[sizeof(struct cmsghdr) + sizeof(int)];
|
||||
int flags;
|
||||
|
||||
iov.iov_base = &dummy;
|
||||
iov.iov_len = 1;
|
||||
msg.msg_name = NULL;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_flags = 0;
|
||||
msg.msg_control = buffer;
|
||||
msg.msg_controllen = sizeof(buffer);
|
||||
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg->cmsg_len = msg.msg_controllen;
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
((int*)CMSG_DATA(cmsg))[0] = fd;
|
||||
|
||||
flags = fcntl(proc->socket,F_GETFL,0);
|
||||
|
||||
if (flags == -1) {
|
||||
D("failed to get cntl flags for socket %d: %s\n",
|
||||
proc->pid, strerror(errno));
|
||||
goto CloseProcess;
|
||||
|
||||
}
|
||||
|
||||
if (fcntl(proc->socket, F_SETFL, flags & ~O_NONBLOCK) == -1) {
|
||||
D("failed to remove O_NONBLOCK flag for socket %d: %s\n",
|
||||
proc->pid, strerror(errno));
|
||||
goto CloseProcess;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
ret = sendmsg(proc->socket, &msg, 0);
|
||||
if (ret >= 0) {
|
||||
adb_close(fd);
|
||||
break;
|
||||
}
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
D("sending new file descriptor to JDWP %d failed: %s\n",
|
||||
proc->pid, strerror(errno));
|
||||
goto CloseProcess;
|
||||
}
|
||||
|
||||
D("sent file descriptor %d to JDWP process %d\n",
|
||||
fd, proc->pid);
|
||||
|
||||
for (n = 1; n < proc->out_count; n++)
|
||||
proc->out_fds[n-1] = proc->out_fds[n];
|
||||
|
||||
if (fcntl(proc->socket, F_SETFL, flags) == -1) {
|
||||
D("failed to set O_NONBLOCK flag for socket %d: %s\n",
|
||||
proc->pid, strerror(errno));
|
||||
goto CloseProcess;
|
||||
}
|
||||
|
||||
if (--proc->out_count == 0)
|
||||
fdevent_del( proc->fde, FDE_WRITE );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
create_jdwp_connection_fd(int pid)
|
||||
{
|
||||
JdwpProcess* proc = _jdwp_list.next;
|
||||
|
||||
D("looking for pid %d in JDWP process list\n", pid);
|
||||
for ( ; proc != &_jdwp_list; proc = proc->next ) {
|
||||
if (proc->pid == pid) {
|
||||
goto FoundIt;
|
||||
}
|
||||
}
|
||||
D("search failed !!\n");
|
||||
return -1;
|
||||
|
||||
FoundIt:
|
||||
{
|
||||
int fds[2];
|
||||
|
||||
if (proc->out_count >= MAX_OUT_FDS) {
|
||||
D("%s: too many pending JDWP connection for pid %d\n",
|
||||
__FUNCTION__, pid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (adb_socketpair(fds) < 0) {
|
||||
D("%s: socket pair creation failed: %s\n",
|
||||
__FUNCTION__, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
D("socketpair: (%d,%d)", fds[0], fds[1]);
|
||||
|
||||
proc->out_fds[ proc->out_count ] = fds[1];
|
||||
if (++proc->out_count == 1)
|
||||
fdevent_add( proc->fde, FDE_WRITE );
|
||||
|
||||
return fds[0];
|
||||
}
|
||||
}
|
||||
|
||||
/** VM DEBUG CONTROL SOCKET
|
||||
**
|
||||
** we do implement a custom asocket to receive the data
|
||||
**/
|
||||
|
||||
/* name of the debug control Unix socket */
|
||||
#define JDWP_CONTROL_NAME "\0jdwp-control"
|
||||
#define JDWP_CONTROL_NAME_LEN (sizeof(JDWP_CONTROL_NAME)-1)
|
||||
|
||||
struct JdwpControl {
|
||||
int listen_socket;
|
||||
fdevent* fde;
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
jdwp_control_event(int s, unsigned events, void* user);
|
||||
|
||||
|
||||
static int
|
||||
jdwp_control_init( JdwpControl* control,
|
||||
const char* sockname,
|
||||
int socknamelen )
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
socklen_t addrlen;
|
||||
int s;
|
||||
int maxpath = sizeof(addr.sun_path);
|
||||
int pathlen = socknamelen;
|
||||
|
||||
if (pathlen >= maxpath) {
|
||||
D( "vm debug control socket name too long (%d extra chars)\n",
|
||||
pathlen+1-maxpath );
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
memcpy(addr.sun_path, sockname, socknamelen);
|
||||
|
||||
s = socket( AF_UNIX, SOCK_STREAM, 0 );
|
||||
if (s < 0) {
|
||||
D( "could not create vm debug control socket. %d: %s\n",
|
||||
errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
addrlen = (pathlen + sizeof(addr.sun_family));
|
||||
|
||||
if (bind(s, (struct sockaddr*)&addr, addrlen) < 0) {
|
||||
D( "could not bind vm debug control socket: %d: %s\n",
|
||||
errno, strerror(errno) );
|
||||
adb_close(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( listen(s, 4) < 0 ) {
|
||||
D("listen failed in jdwp control socket: %d: %s\n",
|
||||
errno, strerror(errno));
|
||||
adb_close(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
control->listen_socket = s;
|
||||
|
||||
control->fde = fdevent_create(s, jdwp_control_event, control);
|
||||
if (control->fde == NULL) {
|
||||
D( "could not create fdevent for jdwp control socket\n" );
|
||||
adb_close(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* only wait for incoming connections */
|
||||
fdevent_add(control->fde, FDE_READ);
|
||||
close_on_exec(s);
|
||||
|
||||
D("jdwp control socket started (%d)\n", control->listen_socket);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
jdwp_control_event( int s, unsigned events, void* _control )
|
||||
{
|
||||
JdwpControl* control = (JdwpControl*) _control;
|
||||
|
||||
if (events & FDE_READ) {
|
||||
struct sockaddr addr;
|
||||
socklen_t addrlen = sizeof(addr);
|
||||
int s = -1;
|
||||
JdwpProcess* proc;
|
||||
|
||||
do {
|
||||
s = adb_socket_accept( control->listen_socket, &addr, &addrlen );
|
||||
if (s < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
if (errno == ECONNABORTED) {
|
||||
/* oops, the JDWP process died really quick */
|
||||
D("oops, the JDWP process died really quick\n");
|
||||
return;
|
||||
}
|
||||
/* the socket is probably closed ? */
|
||||
D( "weird accept() failed on jdwp control socket: %s\n",
|
||||
strerror(errno) );
|
||||
return;
|
||||
}
|
||||
}
|
||||
while (s < 0);
|
||||
|
||||
proc = jdwp_process_alloc( s );
|
||||
if (proc == NULL)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static JdwpControl _jdwp_control;
|
||||
|
||||
/** "jdwp" local service implementation
|
||||
** this simply returns the list of known JDWP process pids
|
||||
**/
|
||||
|
||||
struct JdwpSocket {
|
||||
asocket socket;
|
||||
int pass;
|
||||
};
|
||||
|
||||
static void
|
||||
jdwp_socket_close( asocket* s )
|
||||
{
|
||||
asocket* peer = s->peer;
|
||||
|
||||
remove_socket(s);
|
||||
|
||||
if (peer) {
|
||||
peer->peer = NULL;
|
||||
peer->close(peer);
|
||||
}
|
||||
free(s);
|
||||
}
|
||||
|
||||
static int
|
||||
jdwp_socket_enqueue( asocket* s, apacket* p )
|
||||
{
|
||||
/* you can't write to this asocket */
|
||||
put_apacket(p);
|
||||
s->peer->close(s->peer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
jdwp_socket_ready( asocket* s )
|
||||
{
|
||||
JdwpSocket* jdwp = (JdwpSocket*)s;
|
||||
asocket* peer = jdwp->socket.peer;
|
||||
|
||||
/* on the first call, send the list of pids,
|
||||
* on the second one, close the connection
|
||||
*/
|
||||
if (jdwp->pass == 0) {
|
||||
apacket* p = get_apacket();
|
||||
p->len = jdwp_process_list((char*)p->data, MAX_PAYLOAD);
|
||||
peer->enqueue(peer, p);
|
||||
jdwp->pass = 1;
|
||||
}
|
||||
else {
|
||||
peer->close(peer);
|
||||
}
|
||||
}
|
||||
|
||||
asocket*
|
||||
create_jdwp_service_socket( void )
|
||||
{
|
||||
JdwpSocket* s = reinterpret_cast<JdwpSocket*>(calloc(sizeof(*s), 1));
|
||||
|
||||
if (s == NULL)
|
||||
return NULL;
|
||||
|
||||
install_local_socket(&s->socket);
|
||||
|
||||
s->socket.ready = jdwp_socket_ready;
|
||||
s->socket.enqueue = jdwp_socket_enqueue;
|
||||
s->socket.close = jdwp_socket_close;
|
||||
s->pass = 0;
|
||||
|
||||
return &s->socket;
|
||||
}
|
||||
|
||||
/** "track-jdwp" local service implementation
|
||||
** this periodically sends the list of known JDWP process pids
|
||||
** to the client...
|
||||
**/
|
||||
|
||||
struct JdwpTracker {
|
||||
asocket socket;
|
||||
JdwpTracker* next;
|
||||
JdwpTracker* prev;
|
||||
int need_update;
|
||||
};
|
||||
|
||||
static JdwpTracker _jdwp_trackers_list;
|
||||
|
||||
|
||||
static void
|
||||
jdwp_process_list_updated(void)
|
||||
{
|
||||
char buffer[1024];
|
||||
int len;
|
||||
JdwpTracker* t = _jdwp_trackers_list.next;
|
||||
|
||||
len = jdwp_process_list_msg(buffer, sizeof(buffer));
|
||||
|
||||
for ( ; t != &_jdwp_trackers_list; t = t->next ) {
|
||||
apacket* p = get_apacket();
|
||||
asocket* peer = t->socket.peer;
|
||||
memcpy(p->data, buffer, len);
|
||||
p->len = len;
|
||||
peer->enqueue( peer, p );
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
jdwp_tracker_close( asocket* s )
|
||||
{
|
||||
JdwpTracker* tracker = (JdwpTracker*) s;
|
||||
asocket* peer = s->peer;
|
||||
|
||||
if (peer) {
|
||||
peer->peer = NULL;
|
||||
peer->close(peer);
|
||||
}
|
||||
|
||||
remove_socket(s);
|
||||
|
||||
tracker->prev->next = tracker->next;
|
||||
tracker->next->prev = tracker->prev;
|
||||
|
||||
free(s);
|
||||
}
|
||||
|
||||
static void
|
||||
jdwp_tracker_ready( asocket* s )
|
||||
{
|
||||
JdwpTracker* t = (JdwpTracker*) s;
|
||||
|
||||
if (t->need_update) {
|
||||
apacket* p = get_apacket();
|
||||
t->need_update = 0;
|
||||
p->len = jdwp_process_list_msg((char*)p->data, sizeof(p->data));
|
||||
s->peer->enqueue(s->peer, p);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
jdwp_tracker_enqueue( asocket* s, apacket* p )
|
||||
{
|
||||
/* you can't write to this socket */
|
||||
put_apacket(p);
|
||||
s->peer->close(s->peer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
asocket*
|
||||
create_jdwp_tracker_service_socket( void )
|
||||
{
|
||||
JdwpTracker* t = reinterpret_cast<JdwpTracker*>(calloc(sizeof(*t), 1));
|
||||
|
||||
if (t == NULL)
|
||||
return NULL;
|
||||
|
||||
t->next = &_jdwp_trackers_list;
|
||||
t->prev = t->next->prev;
|
||||
|
||||
t->next->prev = t;
|
||||
t->prev->next = t;
|
||||
|
||||
install_local_socket(&t->socket);
|
||||
|
||||
t->socket.ready = jdwp_tracker_ready;
|
||||
t->socket.enqueue = jdwp_tracker_enqueue;
|
||||
t->socket.close = jdwp_tracker_close;
|
||||
t->need_update = 1;
|
||||
|
||||
return &t->socket;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
init_jdwp(void)
|
||||
{
|
||||
_jdwp_list.next = &_jdwp_list;
|
||||
_jdwp_list.prev = &_jdwp_list;
|
||||
|
||||
_jdwp_trackers_list.next = &_jdwp_trackers_list;
|
||||
_jdwp_trackers_list.prev = &_jdwp_trackers_list;
|
||||
|
||||
return jdwp_control_init( &_jdwp_control,
|
||||
JDWP_CONTROL_NAME,
|
||||
JDWP_CONTROL_NAME_LEN );
|
||||
}
|
||||
|
||||
#endif /* !ADB_HOST */
|
||||
25
mutex_list.h
Normal file
25
mutex_list.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/* the list of mutexes used by adb */
|
||||
/* #ifndef __MUTEX_LIST_H
|
||||
* Do not use an include-guard. This file is included once to declare the locks
|
||||
* and once in win32 to actually do the runtime initialization.
|
||||
*/
|
||||
#ifndef ADB_MUTEX
|
||||
#error ADB_MUTEX not defined when including this file
|
||||
#endif
|
||||
ADB_MUTEX(socket_list_lock)
|
||||
ADB_MUTEX(transport_lock)
|
||||
#if ADB_HOST
|
||||
ADB_MUTEX(local_transports_lock)
|
||||
#endif
|
||||
ADB_MUTEX(usb_lock)
|
||||
|
||||
// Sadly logging to /data/adb/adb-... is not thread safe.
|
||||
// After modifying adb.h::D() to count invocations:
|
||||
// DEBUG(jpa):0:Handling main()
|
||||
// DEBUG(jpa):1:[ usb_init - starting thread ]
|
||||
// (Oopsies, no :2:, and matching message is also gone.)
|
||||
// DEBUG(jpa):3:[ usb_thread - opening device ]
|
||||
// DEBUG(jpa):4:jdwp control socket started (10)
|
||||
ADB_MUTEX(D_lock)
|
||||
|
||||
#undef ADB_MUTEX
|
||||
271
protocol.txt
Normal file
271
protocol.txt
Normal file
@@ -0,0 +1,271 @@
|
||||
|
||||
--- a replacement for aproto -------------------------------------------
|
||||
|
||||
When it comes down to it, aproto's primary purpose is to forward
|
||||
various streams between the host computer and client device (in either
|
||||
direction).
|
||||
|
||||
This replacement further simplifies the concept, reducing the protocol
|
||||
to an extremely straightforward model optimized to accomplish the
|
||||
forwarding of these streams and removing additional state or
|
||||
complexity.
|
||||
|
||||
The host side becomes a simple comms bridge with no "UI", which will
|
||||
be used by either commandline or interactive tools to communicate with
|
||||
a device or emulator that is connected to the bridge.
|
||||
|
||||
The protocol is designed to be straightforward and well-defined enough
|
||||
that if it needs to be reimplemented in another environment (Java
|
||||
perhaps), there should not problems ensuring perfect interoperability.
|
||||
|
||||
The protocol discards the layering aproto has and should allow the
|
||||
implementation to be much more robust.
|
||||
|
||||
|
||||
--- protocol overview and basics ---------------------------------------
|
||||
|
||||
The transport layer deals in "messages", which consist of a 24 byte
|
||||
header followed (optionally) by a payload. The header consists of 6
|
||||
32 bit words which are sent across the wire in little endian format.
|
||||
|
||||
struct message {
|
||||
unsigned command; /* command identifier constant */
|
||||
unsigned arg0; /* first argument */
|
||||
unsigned arg1; /* second argument */
|
||||
unsigned data_length; /* length of payload (0 is allowed) */
|
||||
unsigned data_crc32; /* crc32 of data payload */
|
||||
unsigned magic; /* command ^ 0xffffffff */
|
||||
};
|
||||
|
||||
Receipt of an invalid message header, corrupt message payload, or an
|
||||
unrecognized command MUST result in the closing of the remote
|
||||
connection. The protocol depends on shared state and any break in the
|
||||
message stream will result in state getting out of sync.
|
||||
|
||||
The following sections describe the six defined message types in
|
||||
detail. Their format is COMMAND(arg0, arg1, payload) where the payload
|
||||
is represented by a quoted string or an empty string if none should be
|
||||
sent.
|
||||
|
||||
The identifiers "local-id" and "remote-id" are always relative to the
|
||||
*sender* of the message, so for a receiver, the meanings are effectively
|
||||
reversed.
|
||||
|
||||
|
||||
|
||||
--- CONNECT(version, maxdata, "system-identity-string") ----------------
|
||||
|
||||
The CONNECT message establishes the presence of a remote system.
|
||||
The version is used to ensure protocol compatibility and maxdata
|
||||
declares the maximum message body size that the remote system
|
||||
is willing to accept.
|
||||
|
||||
Currently, version=0x01000000 and maxdata=4096
|
||||
|
||||
Both sides send a CONNECT message when the connection between them is
|
||||
established. Until a CONNECT message is received no other messages may
|
||||
be sent. Any messages received before a CONNECT message MUST be ignored.
|
||||
|
||||
If a CONNECT message is received with an unknown version or insufficiently
|
||||
large maxdata value, the connection with the other side must be closed.
|
||||
|
||||
The system identity string should be "<systemtype>:<serialno>:<banner>"
|
||||
where systemtype is "bootloader", "device", or "host", serialno is some
|
||||
kind of unique ID (or empty), and banner is a human-readable version
|
||||
or identifier string. The banner is used to transmit useful properties.
|
||||
|
||||
|
||||
--- AUTH(type, 0, "data") ----------------------------------------------
|
||||
|
||||
The AUTH message informs the recipient that authentication is required to
|
||||
connect to the sender. If type is TOKEN(1), data is a random token that
|
||||
the recipient can sign with a private key. The recipient replies with an
|
||||
AUTH packet where type is SIGNATURE(2) and data is the signature. If the
|
||||
signature verification succeeds, the sender replies with a CONNECT packet.
|
||||
|
||||
If the signature verification fails, the sender replies with a new AUTH
|
||||
packet and a new random token, so that the recipient can retry signing
|
||||
with a different private key.
|
||||
|
||||
Once the recipient has tried all its private keys, it can reply with an
|
||||
AUTH packet where type is RSAPUBLICKEY(3) and data is the public key. If
|
||||
possible, an on-screen confirmation may be displayed for the user to
|
||||
confirm they want to install the public key on the device.
|
||||
|
||||
|
||||
--- OPEN(local-id, 0, "destination") -----------------------------------
|
||||
|
||||
The OPEN message informs the recipient that the sender has a stream
|
||||
identified by local-id that it wishes to connect to the named
|
||||
destination in the message payload. The local-id may not be zero.
|
||||
|
||||
The OPEN message MUST result in either a READY message indicating that
|
||||
the connection has been established (and identifying the other end) or
|
||||
a CLOSE message, indicating failure. An OPEN message also implies
|
||||
a READY message sent at the same time.
|
||||
|
||||
Common destination naming conventions include:
|
||||
|
||||
* "tcp:<host>:<port>" - host may be omitted to indicate localhost
|
||||
* "udp:<host>:<port>" - host may be omitted to indicate localhost
|
||||
* "local-dgram:<identifier>"
|
||||
* "local-stream:<identifier>"
|
||||
* "shell" - local shell service
|
||||
* "upload" - service for pushing files across (like aproto's /sync)
|
||||
* "fs-bridge" - FUSE protocol filesystem bridge
|
||||
|
||||
|
||||
--- READY(local-id, remote-id, "") -------------------------------------
|
||||
|
||||
The READY message informs the recipient that the sender's stream
|
||||
identified by local-id is ready for write messages and that it is
|
||||
connected to the recipient's stream identified by remote-id.
|
||||
|
||||
Neither the local-id nor the remote-id may be zero.
|
||||
|
||||
A READY message containing a remote-id which does not map to an open
|
||||
stream on the recipient's side is ignored. The stream may have been
|
||||
closed while this message was in-flight.
|
||||
|
||||
The local-id is ignored on all but the first READY message (where it
|
||||
is used to establish the connection). Nonetheless, the local-id MUST
|
||||
not change on later READY messages sent to the same stream.
|
||||
|
||||
|
||||
|
||||
--- WRITE(0, remote-id, "data") ----------------------------------------
|
||||
|
||||
The WRITE message sends data to the recipient's stream identified by
|
||||
remote-id. The payload MUST be <= maxdata in length.
|
||||
|
||||
A WRITE message containing a remote-id which does not map to an open
|
||||
stream on the recipient's side is ignored. The stream may have been
|
||||
closed while this message was in-flight.
|
||||
|
||||
A WRITE message may not be sent until a READY message is received.
|
||||
Once a WRITE message is sent, an additional WRITE message may not be
|
||||
sent until another READY message has been received. Recipients of
|
||||
a WRITE message that is in violation of this requirement will CLOSE
|
||||
the connection.
|
||||
|
||||
|
||||
--- CLOSE(local-id, remote-id, "") -------------------------------------
|
||||
|
||||
The CLOSE message informs recipient that the connection between the
|
||||
sender's stream (local-id) and the recipient's stream (remote-id) is
|
||||
broken. The remote-id MUST not be zero, but the local-id MAY be zero
|
||||
if this CLOSE indicates a failed OPEN.
|
||||
|
||||
A CLOSE message containing a remote-id which does not map to an open
|
||||
stream on the recipient's side is ignored. The stream may have
|
||||
already been closed by the recipient while this message was in-flight.
|
||||
|
||||
The recipient should not respond to a CLOSE message in any way. The
|
||||
recipient should cancel pending WRITEs or CLOSEs, but this is not a
|
||||
requirement, since they will be ignored.
|
||||
|
||||
|
||||
--- SYNC(online, sequence, "") -----------------------------------------
|
||||
|
||||
The SYNC message is used by the io pump to make sure that stale
|
||||
outbound messages are discarded when the connection to the remote side
|
||||
is broken. It is only used internally to the bridge and never valid
|
||||
to send across the wire.
|
||||
|
||||
* when the connection to the remote side goes offline, the io pump
|
||||
sends a SYNC(0, 0) and starts discarding all messages
|
||||
* when the connection to the remote side is established, the io pump
|
||||
sends a SYNC(1, token) and continues to discard messages
|
||||
* when the io pump receives a matching SYNC(1, token), it once again
|
||||
starts accepting messages to forward to the remote side
|
||||
|
||||
|
||||
--- message command constants ------------------------------------------
|
||||
|
||||
#define A_SYNC 0x434e5953
|
||||
#define A_CNXN 0x4e584e43
|
||||
#define A_AUTH 0x48545541
|
||||
#define A_OPEN 0x4e45504f
|
||||
#define A_OKAY 0x59414b4f
|
||||
#define A_CLSE 0x45534c43
|
||||
#define A_WRTE 0x45545257
|
||||
|
||||
|
||||
|
||||
--- implementation details ---------------------------------------------
|
||||
|
||||
The core of the bridge program will use three threads. One thread
|
||||
will be a select/epoll loop to handle io between various inbound and
|
||||
outbound connections and the connection to the remote side.
|
||||
|
||||
The remote side connection will be implemented as two threads (one for
|
||||
reading, one for writing) and a datagram socketpair to provide the
|
||||
channel between the main select/epoll thread and the remote connection
|
||||
threadpair. The reason for this is that for usb connections, the
|
||||
kernel interface on linux and osx does not allow you to do meaningful
|
||||
nonblocking IO.
|
||||
|
||||
The endian swapping for the message headers will happen (as needed) in
|
||||
the remote connection threadpair and that the rest of the program will
|
||||
always treat message header values as native-endian.
|
||||
|
||||
The bridge program will be able to have a number of mini-servers
|
||||
compiled in. They will be published under known names (examples
|
||||
"shell", "fs-bridge", etc) and upon receiving an OPEN() to such a
|
||||
service, the bridge program will create a stream socketpair and spawn
|
||||
a thread or subprocess to handle the io.
|
||||
|
||||
|
||||
--- simplified / embedded implementation -------------------------------
|
||||
|
||||
For limited environments, like the bootloader, it is allowable to
|
||||
support a smaller, fixed number of channels using pre-assigned channel
|
||||
ID numbers such that only one stream may be connected to a bootloader
|
||||
endpoint at any given time. The protocol remains unchanged, but the
|
||||
"embedded" version of it is less dynamic.
|
||||
|
||||
The bootloader will support two streams. A "bootloader:debug" stream,
|
||||
which may be opened to get debug messages from the bootloader and a
|
||||
"bootloader:control", stream which will support the set of basic
|
||||
bootloader commands.
|
||||
|
||||
Example command stream dialogues:
|
||||
"flash_kernel,2515049,........\n" "okay\n"
|
||||
"flash_ramdisk,5038,........\n" "fail,flash write error\n"
|
||||
"bogus_command......" <CLOSE>
|
||||
|
||||
|
||||
--- future expansion ---------------------------------------------------
|
||||
|
||||
I plan on providing either a message or a special control stream so that
|
||||
the client device could ask the host computer to setup inbound socket
|
||||
translations on the fly on behalf of the client device.
|
||||
|
||||
|
||||
The initial design does handshaking to provide flow control, with a
|
||||
message flow that looks like:
|
||||
|
||||
>OPEN <READY >WRITE <READY >WRITE <READY >WRITE <CLOSE
|
||||
|
||||
The far side may choose to issue the READY message as soon as it receives
|
||||
a WRITE or it may defer the READY until the write to the local stream
|
||||
succeeds. A future version may want to do some level of windowing where
|
||||
multiple WRITEs may be sent without requiring individual READY acks.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
--- smartsockets -------------------------------------------------------
|
||||
|
||||
Port 5037 is used for smart sockets which allow a client on the host
|
||||
side to request access to a service in the host adb daemon or in the
|
||||
remote (device) daemon. The service is requested by ascii name,
|
||||
preceeded by a 4 digit hex length. Upon successful connection an
|
||||
"OKAY" response is sent, otherwise a "FAIL" message is returned. Once
|
||||
connected the client is talking to that (remote or local) service.
|
||||
|
||||
client: <hex4> <service-name>
|
||||
server: "OKAY"
|
||||
|
||||
client: <hex4> <service-name>
|
||||
server: "FAIL" <hex4> <reason>
|
||||
|
||||
69
qemu_tracing.cpp
Normal file
69
qemu_tracing.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Implements ADB tracing inside the emulator.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "sysdeps.h"
|
||||
#include "qemu_tracing.h"
|
||||
|
||||
/*
|
||||
* Redefine open and write for qemu_pipe.h that contains inlined references
|
||||
* to those routines. We will redifine them back after qemu_pipe.h inclusion.
|
||||
*/
|
||||
|
||||
#undef open
|
||||
#undef write
|
||||
#define open adb_open
|
||||
#define write adb_write
|
||||
#include <hardware/qemu_pipe.h>
|
||||
#undef open
|
||||
#undef write
|
||||
#define open ___xxx_open
|
||||
#define write ___xxx_write
|
||||
|
||||
/* A handle to adb-debug qemud service in the emulator. */
|
||||
int adb_debug_qemu = -1;
|
||||
|
||||
/* Initializes connection with the adb-debug qemud service in the emulator. */
|
||||
int adb_qemu_trace_init(void)
|
||||
{
|
||||
char con_name[32];
|
||||
|
||||
if (adb_debug_qemu >= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* adb debugging QEMUD service connection request. */
|
||||
snprintf(con_name, sizeof(con_name), "qemud:adb-debug");
|
||||
adb_debug_qemu = qemu_pipe_open(con_name);
|
||||
return (adb_debug_qemu >= 0) ? 0 : -1;
|
||||
}
|
||||
|
||||
void adb_qemu_trace(const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
char msg[1024];
|
||||
|
||||
if (adb_debug_qemu >= 0) {
|
||||
vsnprintf(msg, sizeof(msg), fmt, args);
|
||||
adb_write(adb_debug_qemu, msg, strlen(msg));
|
||||
}
|
||||
}
|
||||
28
qemu_tracing.h
Normal file
28
qemu_tracing.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Implements ADB tracing inside the emulator.
|
||||
*/
|
||||
|
||||
#ifndef __QEMU_TRACING_H
|
||||
#define __QEMU_TRACING_H
|
||||
|
||||
/* Initializes connection with the adb-debug qemud service in the emulator. */
|
||||
int adb_qemu_trace_init(void);
|
||||
void adb_qemu_trace(const char* fmt, ...);
|
||||
|
||||
#endif /* __QEMU_TRACING_H */
|
||||
122
remount_service.cpp
Normal file
122
remount_service.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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 TRACE_ADB
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <mntent.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mount.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "adb.h"
|
||||
#include "adb_io.h"
|
||||
#include "adb_utils.h"
|
||||
#include "cutils/properties.h"
|
||||
|
||||
// Returns the device used to mount a directory in /proc/mounts.
|
||||
static std::string find_mount(const char* dir) {
|
||||
std::unique_ptr<FILE, int(*)(FILE*)> fp(setmntent("/proc/mounts", "r"), endmntent);
|
||||
if (!fp) {
|
||||
return "";
|
||||
}
|
||||
|
||||
mntent* e;
|
||||
while ((e = getmntent(fp.get())) != nullptr) {
|
||||
if (strcmp(dir, e->mnt_dir) == 0) {
|
||||
return e->mnt_fsname;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
bool make_block_device_writable(const std::string& dev) {
|
||||
int fd = unix_open(dev.c_str(), O_RDONLY | O_CLOEXEC);
|
||||
if (fd == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int OFF = 0;
|
||||
bool result = (ioctl(fd, BLKROSET, &OFF) != -1);
|
||||
adb_close(fd);
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool remount_partition(int fd, const char* dir) {
|
||||
if (!directory_exists(dir)) {
|
||||
return true;
|
||||
}
|
||||
std::string dev = find_mount(dir);
|
||||
if (dev.empty()) {
|
||||
return true;
|
||||
}
|
||||
if (!make_block_device_writable(dev)) {
|
||||
WriteFdFmt(fd, "remount of %s failed; couldn't make block device %s writable: %s\n",
|
||||
dir, dev.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
if (mount(dev.c_str(), dir, "none", MS_REMOUNT, nullptr) == -1) {
|
||||
WriteFdFmt(fd, "remount of %s failed: %s\n", dir, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void remount_service(int fd, void* cookie) {
|
||||
if (getuid() != 0) {
|
||||
WriteFdExactly(fd, "Not running as root. Try \"adb root\" first.\n");
|
||||
adb_close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
char prop_buf[PROPERTY_VALUE_MAX];
|
||||
property_get("partition.system.verified", prop_buf, "");
|
||||
bool system_verified = (strlen(prop_buf) > 0);
|
||||
|
||||
property_get("partition.vendor.verified", prop_buf, "");
|
||||
bool vendor_verified = (strlen(prop_buf) > 0);
|
||||
|
||||
if (system_verified || vendor_verified) {
|
||||
// Allow remount but warn of likely bad effects
|
||||
bool both = system_verified && vendor_verified;
|
||||
WriteFdFmt(fd,
|
||||
"dm_verity is enabled on the %s%s%s partition%s.\n",
|
||||
system_verified ? "system" : "",
|
||||
both ? " and " : "",
|
||||
vendor_verified ? "vendor" : "",
|
||||
both ? "s" : "");
|
||||
WriteFdExactly(fd,
|
||||
"Use \"adb disable-verity\" to disable verity.\n"
|
||||
"If you do not, remount may succeed, however, you will still "
|
||||
"not be able to write to these volumes.\n");
|
||||
}
|
||||
|
||||
bool success = true;
|
||||
success &= remount_partition(fd, "/system");
|
||||
success &= remount_partition(fd, "/vendor");
|
||||
success &= remount_partition(fd, "/oem");
|
||||
|
||||
WriteFdExactly(fd, success ? "remount succeeded\n" : "remount failed\n");
|
||||
|
||||
adb_close(fd);
|
||||
}
|
||||
25
remount_service.h
Normal file
25
remount_service.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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 _REMOUNT_SERVICE_H_
|
||||
#define _REMOUNT_SERVICE_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
bool make_block_device_writable(const std::string&);
|
||||
void remount_service(int, void*);
|
||||
|
||||
#endif
|
||||
700
services.cpp
Normal file
700
services.cpp
Normal file
@@ -0,0 +1,700 @@
|
||||
/*
|
||||
* 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 TRACE_SERVICES
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <base/file.h>
|
||||
#include <base/stringprintf.h>
|
||||
#include <base/strings.h>
|
||||
|
||||
#if !ADB_HOST
|
||||
#include "cutils/android_reboot.h"
|
||||
#include "cutils/properties.h"
|
||||
#endif
|
||||
|
||||
#include "adb.h"
|
||||
#include "adb_io.h"
|
||||
#include "file_sync_service.h"
|
||||
#include "remount_service.h"
|
||||
#include "transport.h"
|
||||
|
||||
struct stinfo {
|
||||
void (*func)(int fd, void *cookie);
|
||||
int fd;
|
||||
void *cookie;
|
||||
};
|
||||
|
||||
|
||||
void *service_bootstrap_func(void *x)
|
||||
{
|
||||
stinfo* sti = reinterpret_cast<stinfo*>(x);
|
||||
sti->func(sti->fd, sti->cookie);
|
||||
free(sti);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if !ADB_HOST
|
||||
|
||||
void restart_root_service(int fd, void *cookie) {
|
||||
if (getuid() == 0) {
|
||||
WriteFdExactly(fd, "adbd is already running as root\n");
|
||||
adb_close(fd);
|
||||
} else {
|
||||
char value[PROPERTY_VALUE_MAX];
|
||||
property_get("ro.debuggable", value, "");
|
||||
if (strcmp(value, "1") != 0) {
|
||||
WriteFdExactly(fd, "adbd cannot run as root in production builds\n");
|
||||
adb_close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
property_set("service.adb.root", "1");
|
||||
WriteFdExactly(fd, "restarting adbd as root\n");
|
||||
adb_close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
void restart_unroot_service(int fd, void *cookie) {
|
||||
if (getuid() != 0) {
|
||||
WriteFdExactly(fd, "adbd not running as root\n");
|
||||
adb_close(fd);
|
||||
} else {
|
||||
property_set("service.adb.root", "0");
|
||||
WriteFdExactly(fd, "restarting adbd as non root\n");
|
||||
adb_close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
void restart_tcp_service(int fd, void *cookie) {
|
||||
int port = (int) (uintptr_t) cookie;
|
||||
if (port <= 0) {
|
||||
WriteFdFmt(fd, "invalid port %d\n", port);
|
||||
adb_close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
char value[PROPERTY_VALUE_MAX];
|
||||
snprintf(value, sizeof(value), "%d", port);
|
||||
property_set("service.adb.tcp.port", value);
|
||||
WriteFdFmt(fd, "restarting in TCP mode port: %d\n", port);
|
||||
adb_close(fd);
|
||||
}
|
||||
|
||||
void restart_usb_service(int fd, void *cookie) {
|
||||
property_set("service.adb.tcp.port", "0");
|
||||
WriteFdExactly(fd, "restarting in USB mode\n");
|
||||
adb_close(fd);
|
||||
}
|
||||
|
||||
static bool reboot_service_impl(int fd, const char* arg) {
|
||||
const char* reboot_arg = arg;
|
||||
bool auto_reboot = false;
|
||||
|
||||
if (strcmp(reboot_arg, "sideload-auto-reboot") == 0) {
|
||||
auto_reboot = true;
|
||||
reboot_arg = "sideload";
|
||||
}
|
||||
|
||||
// It reboots into sideload mode by setting "--sideload" or "--sideload_auto_reboot"
|
||||
// in the command file.
|
||||
if (strcmp(reboot_arg, "sideload") == 0) {
|
||||
if (getuid() != 0) {
|
||||
WriteFdExactly(fd, "'adb root' is required for 'adb reboot sideload'.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* const recovery_dir = "/cache/recovery";
|
||||
const char* const command_file = "/cache/recovery/command";
|
||||
// Ensure /cache/recovery exists.
|
||||
if (adb_mkdir(recovery_dir, 0770) == -1 && errno != EEXIST) {
|
||||
D("Failed to create directory '%s': %s\n", recovery_dir, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool write_status = android::base::WriteStringToFile(
|
||||
auto_reboot ? "--sideload_auto_reboot" : "--sideload", command_file);
|
||||
if (!write_status) {
|
||||
return false;
|
||||
}
|
||||
|
||||
reboot_arg = "recovery";
|
||||
}
|
||||
|
||||
sync();
|
||||
|
||||
char property_val[PROPERTY_VALUE_MAX];
|
||||
int ret = snprintf(property_val, sizeof(property_val), "reboot,%s", reboot_arg);
|
||||
if (ret >= static_cast<int>(sizeof(property_val))) {
|
||||
WriteFdFmt(fd, "reboot string too long: %d\n", ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = property_set(ANDROID_RB_PROPERTY, property_val);
|
||||
if (ret < 0) {
|
||||
WriteFdFmt(fd, "reboot failed: %d\n", ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void reboot_service(int fd, void* arg)
|
||||
{
|
||||
if (reboot_service_impl(fd, static_cast<const char*>(arg))) {
|
||||
// Don't return early. Give the reboot command time to take effect
|
||||
// to avoid messing up scripts which do "adb reboot && adb wait-for-device"
|
||||
while (true) {
|
||||
pause();
|
||||
}
|
||||
}
|
||||
|
||||
free(arg);
|
||||
adb_close(fd);
|
||||
}
|
||||
|
||||
void reverse_service(int fd, void* arg)
|
||||
{
|
||||
const char* command = reinterpret_cast<const char*>(arg);
|
||||
|
||||
if (handle_forward_request(command, kTransportAny, NULL, fd) < 0) {
|
||||
SendFail(fd, "not a reverse forwarding command");
|
||||
}
|
||||
free(arg);
|
||||
adb_close(fd);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int create_service_thread(void (*func)(int, void *), void *cookie)
|
||||
{
|
||||
int s[2];
|
||||
if (adb_socketpair(s)) {
|
||||
printf("cannot create service socket pair\n");
|
||||
return -1;
|
||||
}
|
||||
D("socketpair: (%d,%d)", s[0], s[1]);
|
||||
|
||||
stinfo* sti = reinterpret_cast<stinfo*>(malloc(sizeof(stinfo)));
|
||||
if (sti == nullptr) {
|
||||
fatal("cannot allocate stinfo");
|
||||
}
|
||||
sti->func = func;
|
||||
sti->cookie = cookie;
|
||||
sti->fd = s[1];
|
||||
|
||||
adb_thread_t t;
|
||||
if (adb_thread_create(&t, service_bootstrap_func, sti)) {
|
||||
free(sti);
|
||||
adb_close(s[0]);
|
||||
adb_close(s[1]);
|
||||
printf("cannot create service thread\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
D("service thread started, %d:%d\n",s[0], s[1]);
|
||||
return s[0];
|
||||
}
|
||||
|
||||
#if !ADB_HOST
|
||||
|
||||
static void init_subproc_child()
|
||||
{
|
||||
setsid();
|
||||
|
||||
// Set OOM score adjustment to prevent killing
|
||||
int fd = adb_open("/proc/self/oom_score_adj", O_WRONLY | O_CLOEXEC);
|
||||
if (fd >= 0) {
|
||||
adb_write(fd, "0", 1);
|
||||
adb_close(fd);
|
||||
} else {
|
||||
D("adb: unable to update oom_score_adj\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int create_subproc_pty(const char *cmd, const char *arg0, const char *arg1, pid_t *pid)
|
||||
{
|
||||
D("create_subproc_pty(cmd=%s, arg0=%s, arg1=%s)\n", cmd, arg0, arg1);
|
||||
#if defined(_WIN32)
|
||||
fprintf(stderr, "error: create_subproc_pty not implemented on Win32 (%s %s %s)\n", cmd, arg0, arg1);
|
||||
return -1;
|
||||
#else
|
||||
int ptm;
|
||||
|
||||
ptm = unix_open("/dev/ptmx", O_RDWR | O_CLOEXEC); // | O_NOCTTY);
|
||||
if(ptm < 0){
|
||||
printf("[ cannot open /dev/ptmx - %s ]\n",strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
char devname[64];
|
||||
if(grantpt(ptm) || unlockpt(ptm) || ptsname_r(ptm, devname, sizeof(devname)) != 0) {
|
||||
printf("[ trouble with /dev/ptmx - %s ]\n", strerror(errno));
|
||||
adb_close(ptm);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*pid = fork();
|
||||
if(*pid < 0) {
|
||||
printf("- fork failed: %s -\n", strerror(errno));
|
||||
adb_close(ptm);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (*pid == 0) {
|
||||
init_subproc_child();
|
||||
|
||||
int pts = unix_open(devname, O_RDWR | O_CLOEXEC);
|
||||
if (pts < 0) {
|
||||
fprintf(stderr, "child failed to open pseudo-term slave: %s\n", devname);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
dup2(pts, STDIN_FILENO);
|
||||
dup2(pts, STDOUT_FILENO);
|
||||
dup2(pts, STDERR_FILENO);
|
||||
|
||||
adb_close(pts);
|
||||
adb_close(ptm);
|
||||
|
||||
execl(cmd, cmd, arg0, arg1, NULL);
|
||||
fprintf(stderr, "- exec '%s' failed: %s (%d) -\n",
|
||||
cmd, strerror(errno), errno);
|
||||
exit(-1);
|
||||
} else {
|
||||
return ptm;
|
||||
}
|
||||
#endif /* !defined(_WIN32) */
|
||||
}
|
||||
|
||||
static int create_subproc_raw(const char *cmd, const char *arg0, const char *arg1, pid_t *pid)
|
||||
{
|
||||
D("create_subproc_raw(cmd=%s, arg0=%s, arg1=%s)\n", cmd, arg0, arg1);
|
||||
#if defined(_WIN32)
|
||||
fprintf(stderr, "error: create_subproc_raw not implemented on Win32 (%s %s %s)\n", cmd, arg0, arg1);
|
||||
return -1;
|
||||
#else
|
||||
|
||||
// 0 is parent socket, 1 is child socket
|
||||
int sv[2];
|
||||
if (adb_socketpair(sv) < 0) {
|
||||
printf("[ cannot create socket pair - %s ]\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
D("socketpair: (%d,%d)", sv[0], sv[1]);
|
||||
|
||||
*pid = fork();
|
||||
if (*pid < 0) {
|
||||
printf("- fork failed: %s -\n", strerror(errno));
|
||||
adb_close(sv[0]);
|
||||
adb_close(sv[1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (*pid == 0) {
|
||||
adb_close(sv[0]);
|
||||
init_subproc_child();
|
||||
|
||||
dup2(sv[1], STDIN_FILENO);
|
||||
dup2(sv[1], STDOUT_FILENO);
|
||||
dup2(sv[1], STDERR_FILENO);
|
||||
|
||||
adb_close(sv[1]);
|
||||
|
||||
execl(cmd, cmd, arg0, arg1, NULL);
|
||||
fprintf(stderr, "- exec '%s' failed: %s (%d) -\n",
|
||||
cmd, strerror(errno), errno);
|
||||
exit(-1);
|
||||
} else {
|
||||
adb_close(sv[1]);
|
||||
return sv[0];
|
||||
}
|
||||
#endif /* !defined(_WIN32) */
|
||||
}
|
||||
#endif /* !ABD_HOST */
|
||||
|
||||
#if ADB_HOST
|
||||
#define SHELL_COMMAND "/bin/sh"
|
||||
#define ALTERNATE_SHELL_COMMAND ""
|
||||
#else
|
||||
#define SHELL_COMMAND "/system/bin/sh"
|
||||
#define ALTERNATE_SHELL_COMMAND "/sbin/sh"
|
||||
#endif
|
||||
|
||||
#if !ADB_HOST
|
||||
static void subproc_waiter_service(int fd, void *cookie)
|
||||
{
|
||||
pid_t pid = (pid_t) (uintptr_t) cookie;
|
||||
|
||||
D("entered. fd=%d of pid=%d\n", fd, pid);
|
||||
while (true) {
|
||||
int status;
|
||||
pid_t p = waitpid(pid, &status, 0);
|
||||
if (p == pid) {
|
||||
D("fd=%d, post waitpid(pid=%d) status=%04x\n", fd, p, status);
|
||||
if (WIFSIGNALED(status)) {
|
||||
D("*** Killed by signal %d\n", WTERMSIG(status));
|
||||
break;
|
||||
} else if (!WIFEXITED(status)) {
|
||||
D("*** Didn't exit!!. status %d\n", status);
|
||||
break;
|
||||
} else if (WEXITSTATUS(status) >= 0) {
|
||||
D("*** Exit code %d\n", WEXITSTATUS(status));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
D("shell exited fd=%d of pid=%d err=%d\n", fd, pid, errno);
|
||||
if (SHELL_EXIT_NOTIFY_FD >=0) {
|
||||
int res;
|
||||
res = WriteFdExactly(SHELL_EXIT_NOTIFY_FD, &fd, sizeof(fd)) ? 0 : -1;
|
||||
D("notified shell exit via fd=%d for pid=%d res=%d errno=%d\n",
|
||||
SHELL_EXIT_NOTIFY_FD, pid, res, errno);
|
||||
}
|
||||
}
|
||||
|
||||
static int create_subproc_thread(const char *name, const subproc_mode mode)
|
||||
{
|
||||
adb_thread_t t;
|
||||
int ret_fd;
|
||||
pid_t pid = -1;
|
||||
|
||||
const char *arg0, *arg1;
|
||||
if (name == 0 || *name == 0) {
|
||||
arg0 = "-"; arg1 = 0;
|
||||
} else {
|
||||
arg0 = "-c"; arg1 = name;
|
||||
}
|
||||
|
||||
const char* shell_command;
|
||||
struct stat filecheck;
|
||||
if (stat(ALTERNATE_SHELL_COMMAND, &filecheck) == 0) {
|
||||
shell_command = ALTERNATE_SHELL_COMMAND;
|
||||
}
|
||||
else {
|
||||
shell_command = SHELL_COMMAND;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case SUBPROC_PTY:
|
||||
ret_fd = create_subproc_pty(shell_command, arg0, arg1, &pid);
|
||||
break;
|
||||
case SUBPROC_RAW:
|
||||
ret_fd = create_subproc_raw(shell_command, arg0, arg1, &pid);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "invalid subproc_mode %d\n", mode);
|
||||
return -1;
|
||||
}
|
||||
D("create_subproc ret_fd=%d pid=%d\n", ret_fd, pid);
|
||||
|
||||
stinfo* sti = reinterpret_cast<stinfo*>(malloc(sizeof(stinfo)));
|
||||
if(sti == 0) fatal("cannot allocate stinfo");
|
||||
sti->func = subproc_waiter_service;
|
||||
sti->cookie = (void*) (uintptr_t) pid;
|
||||
sti->fd = ret_fd;
|
||||
|
||||
if (adb_thread_create(&t, service_bootstrap_func, sti)) {
|
||||
free(sti);
|
||||
adb_close(ret_fd);
|
||||
fprintf(stderr, "cannot create service thread\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
D("service thread started, fd=%d pid=%d\n", ret_fd, pid);
|
||||
return ret_fd;
|
||||
}
|
||||
#endif
|
||||
|
||||
int service_to_fd(const char *name)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
if(!strncmp(name, "tcp:", 4)) {
|
||||
int port = atoi(name + 4);
|
||||
name = strchr(name + 4, ':');
|
||||
if(name == 0) {
|
||||
ret = socket_loopback_client(port, SOCK_STREAM);
|
||||
if (ret >= 0)
|
||||
disable_tcp_nagle(ret);
|
||||
} else {
|
||||
#if ADB_HOST
|
||||
ret = socket_network_client(name + 1, port, SOCK_STREAM);
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
#ifndef HAVE_WINSOCK /* winsock doesn't implement unix domain sockets */
|
||||
} else if(!strncmp(name, "local:", 6)) {
|
||||
ret = socket_local_client(name + 6,
|
||||
ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
|
||||
} else if(!strncmp(name, "localreserved:", 14)) {
|
||||
ret = socket_local_client(name + 14,
|
||||
ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
|
||||
} else if(!strncmp(name, "localabstract:", 14)) {
|
||||
ret = socket_local_client(name + 14,
|
||||
ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
|
||||
} else if(!strncmp(name, "localfilesystem:", 16)) {
|
||||
ret = socket_local_client(name + 16,
|
||||
ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM);
|
||||
#endif
|
||||
#if !ADB_HOST
|
||||
} else if(!strncmp("dev:", name, 4)) {
|
||||
ret = unix_open(name + 4, O_RDWR | O_CLOEXEC);
|
||||
} else if(!strncmp(name, "framebuffer:", 12)) {
|
||||
ret = create_service_thread(framebuffer_service, 0);
|
||||
} else if (!strncmp(name, "jdwp:", 5)) {
|
||||
ret = create_jdwp_connection_fd(atoi(name+5));
|
||||
} else if(!HOST && !strncmp(name, "shell:", 6)) {
|
||||
ret = create_subproc_thread(name + 6, SUBPROC_PTY);
|
||||
} else if(!HOST && !strncmp(name, "exec:", 5)) {
|
||||
ret = create_subproc_thread(name + 5, SUBPROC_RAW);
|
||||
} else if(!strncmp(name, "sync:", 5)) {
|
||||
ret = create_service_thread(file_sync_service, NULL);
|
||||
} else if(!strncmp(name, "remount:", 8)) {
|
||||
ret = create_service_thread(remount_service, NULL);
|
||||
} else if(!strncmp(name, "reboot:", 7)) {
|
||||
void* arg = strdup(name + 7);
|
||||
if (arg == NULL) return -1;
|
||||
ret = create_service_thread(reboot_service, arg);
|
||||
} else if(!strncmp(name, "root:", 5)) {
|
||||
ret = create_service_thread(restart_root_service, NULL);
|
||||
} else if(!strncmp(name, "unroot:", 7)) {
|
||||
ret = create_service_thread(restart_unroot_service, NULL);
|
||||
} else if(!strncmp(name, "backup:", 7)) {
|
||||
ret = create_subproc_thread(android::base::StringPrintf("/system/bin/bu backup %s",
|
||||
(name + 7)).c_str(), SUBPROC_RAW);
|
||||
} else if(!strncmp(name, "restore:", 8)) {
|
||||
ret = create_subproc_thread("/system/bin/bu restore", SUBPROC_RAW);
|
||||
} else if(!strncmp(name, "tcpip:", 6)) {
|
||||
int port;
|
||||
if (sscanf(name + 6, "%d", &port) != 1) {
|
||||
port = 0;
|
||||
}
|
||||
ret = create_service_thread(restart_tcp_service, (void *) (uintptr_t) port);
|
||||
} else if(!strncmp(name, "usb:", 4)) {
|
||||
ret = create_service_thread(restart_usb_service, NULL);
|
||||
} else if (!strncmp(name, "reverse:", 8)) {
|
||||
char* cookie = strdup(name + 8);
|
||||
if (cookie == NULL) {
|
||||
ret = -1;
|
||||
} else {
|
||||
ret = create_service_thread(reverse_service, cookie);
|
||||
if (ret < 0) {
|
||||
free(cookie);
|
||||
}
|
||||
}
|
||||
} else if(!strncmp(name, "disable-verity:", 15)) {
|
||||
ret = create_service_thread(set_verity_enabled_state_service, (void*)0);
|
||||
} else if(!strncmp(name, "enable-verity:", 15)) {
|
||||
ret = create_service_thread(set_verity_enabled_state_service, (void*)1);
|
||||
#endif
|
||||
}
|
||||
if (ret >= 0) {
|
||||
close_on_exec(ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if ADB_HOST
|
||||
struct state_info {
|
||||
transport_type transport;
|
||||
char* serial;
|
||||
int state;
|
||||
};
|
||||
|
||||
static void wait_for_state(int fd, void* cookie)
|
||||
{
|
||||
state_info* sinfo = reinterpret_cast<state_info*>(cookie);
|
||||
|
||||
D("wait_for_state %d\n", sinfo->state);
|
||||
|
||||
std::string error_msg = "unknown error";
|
||||
atransport* t = acquire_one_transport(sinfo->state, sinfo->transport, sinfo->serial, &error_msg);
|
||||
if (t != 0) {
|
||||
SendOkay(fd);
|
||||
} else {
|
||||
SendFail(fd, error_msg);
|
||||
}
|
||||
|
||||
if (sinfo->serial)
|
||||
free(sinfo->serial);
|
||||
free(sinfo);
|
||||
adb_close(fd);
|
||||
D("wait_for_state is done\n");
|
||||
}
|
||||
|
||||
static void connect_device(const std::string& host, std::string* response) {
|
||||
if (host.empty()) {
|
||||
*response = "empty host name";
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string> pieces = android::base::Split(host, ":");
|
||||
const std::string& hostname = pieces[0];
|
||||
|
||||
int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
|
||||
if (pieces.size() > 1) {
|
||||
if (sscanf(pieces[1].c_str(), "%d", &port) != 1) {
|
||||
*response = android::base::StringPrintf("bad port number %s", pieces[1].c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// This may look like we're putting 'host' back together,
|
||||
// but we're actually inserting the default port if necessary.
|
||||
std::string serial = android::base::StringPrintf("%s:%d", hostname.c_str(), port);
|
||||
|
||||
int fd = socket_network_client_timeout(hostname.c_str(), port, SOCK_STREAM, 10);
|
||||
if (fd < 0) {
|
||||
*response = android::base::StringPrintf("unable to connect to %s:%d",
|
||||
hostname.c_str(), port);
|
||||
return;
|
||||
}
|
||||
|
||||
D("client: connected on remote on fd %d\n", fd);
|
||||
close_on_exec(fd);
|
||||
disable_tcp_nagle(fd);
|
||||
|
||||
int ret = register_socket_transport(fd, serial.c_str(), port, 0);
|
||||
if (ret < 0) {
|
||||
adb_close(fd);
|
||||
*response = android::base::StringPrintf("already connected to %s", serial.c_str());
|
||||
} else {
|
||||
*response = android::base::StringPrintf("connected to %s", serial.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void connect_emulator(const std::string& port_spec, std::string* response) {
|
||||
std::vector<std::string> pieces = android::base::Split(port_spec, ",");
|
||||
if (pieces.size() != 2) {
|
||||
*response = android::base::StringPrintf("unable to parse '%s' as <console port>,<adb port>",
|
||||
port_spec.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
int console_port = strtol(pieces[0].c_str(), NULL, 0);
|
||||
int adb_port = strtol(pieces[1].c_str(), NULL, 0);
|
||||
if (console_port <= 0 || adb_port <= 0) {
|
||||
*response = android::base::StringPrintf("Invalid port numbers: %s", port_spec.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the emulator is already known.
|
||||
// Note: There's a small but harmless race condition here: An emulator not
|
||||
// present just yet could be registered by another invocation right
|
||||
// after doing this check here. However, local_connect protects
|
||||
// against double-registration too. From here, a better error message
|
||||
// can be produced. In the case of the race condition, the very specific
|
||||
// error message won't be shown, but the data doesn't get corrupted.
|
||||
atransport* known_emulator = find_emulator_transport_by_adb_port(adb_port);
|
||||
if (known_emulator != nullptr) {
|
||||
*response = android::base::StringPrintf("Emulator already registered on port %d", adb_port);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if more emulators can be registered. Similar unproblematic
|
||||
// race condition as above.
|
||||
int candidate_slot = get_available_local_transport_index();
|
||||
if (candidate_slot < 0) {
|
||||
*response = "Cannot accept more emulators";
|
||||
return;
|
||||
}
|
||||
|
||||
// Preconditions met, try to connect to the emulator.
|
||||
if (!local_connect_arbitrary_ports(console_port, adb_port)) {
|
||||
*response = android::base::StringPrintf("Connected to emulator on ports %d,%d",
|
||||
console_port, adb_port);
|
||||
} else {
|
||||
*response = android::base::StringPrintf("Could not connect to emulator on ports %d,%d",
|
||||
console_port, adb_port);
|
||||
}
|
||||
}
|
||||
|
||||
static void connect_service(int fd, void* cookie)
|
||||
{
|
||||
char *host = reinterpret_cast<char*>(cookie);
|
||||
|
||||
std::string response;
|
||||
if (!strncmp(host, "emu:", 4)) {
|
||||
connect_emulator(host + 4, &response);
|
||||
} else {
|
||||
connect_device(host, &response);
|
||||
}
|
||||
|
||||
// Send response for emulator and device
|
||||
SendProtocolString(fd, response);
|
||||
adb_close(fd);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ADB_HOST
|
||||
asocket* host_service_to_socket(const char* name, const char *serial)
|
||||
{
|
||||
if (!strcmp(name,"track-devices")) {
|
||||
return create_device_tracker();
|
||||
} else if (!strncmp(name, "wait-for-", strlen("wait-for-"))) {
|
||||
auto sinfo = reinterpret_cast<state_info*>(malloc(sizeof(state_info)));
|
||||
if (sinfo == nullptr) {
|
||||
fprintf(stderr, "couldn't allocate state_info: %s", strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (serial)
|
||||
sinfo->serial = strdup(serial);
|
||||
else
|
||||
sinfo->serial = NULL;
|
||||
|
||||
name += strlen("wait-for-");
|
||||
|
||||
if (!strncmp(name, "local", strlen("local"))) {
|
||||
sinfo->transport = kTransportLocal;
|
||||
sinfo->state = CS_DEVICE;
|
||||
} else if (!strncmp(name, "usb", strlen("usb"))) {
|
||||
sinfo->transport = kTransportUsb;
|
||||
sinfo->state = CS_DEVICE;
|
||||
} else if (!strncmp(name, "any", strlen("any"))) {
|
||||
sinfo->transport = kTransportAny;
|
||||
sinfo->state = CS_DEVICE;
|
||||
} else {
|
||||
free(sinfo);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int fd = create_service_thread(wait_for_state, sinfo);
|
||||
return create_local_socket(fd);
|
||||
} else if (!strncmp(name, "connect:", 8)) {
|
||||
const char *host = name + 8;
|
||||
int fd = create_service_thread(connect_service, (void *)host);
|
||||
return create_local_socket(fd);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif /* ADB_HOST */
|
||||
207
set_verity_enable_state_service.cpp
Normal file
207
set_verity_enable_state_service.cpp
Normal file
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define TRACE_TAG TRACE_ADB
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "cutils/properties.h"
|
||||
|
||||
#include "adb.h"
|
||||
#include "adb_io.h"
|
||||
#include "ext4_sb.h"
|
||||
#include "fs_mgr.h"
|
||||
#include "remount_service.h"
|
||||
|
||||
#define FSTAB_PREFIX "/fstab."
|
||||
struct fstab *fstab;
|
||||
|
||||
#ifdef ALLOW_ADBD_DISABLE_VERITY
|
||||
static const bool kAllowDisableVerity = true;
|
||||
#else
|
||||
static const bool kAllowDisableVerity = false;
|
||||
#endif
|
||||
|
||||
static int get_target_device_size(int fd, const char *blk_device,
|
||||
uint64_t *device_size)
|
||||
{
|
||||
int data_device;
|
||||
struct ext4_super_block sb;
|
||||
struct fs_info info;
|
||||
|
||||
info.len = 0; /* Only len is set to 0 to ask the device for real size. */
|
||||
|
||||
data_device = adb_open(blk_device, O_RDONLY | O_CLOEXEC);
|
||||
if (data_device < 0) {
|
||||
WriteFdFmt(fd, "Error opening block device (%s)\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (lseek64(data_device, 1024, SEEK_SET) < 0) {
|
||||
WriteFdFmt(fd, "Error seeking to superblock\n");
|
||||
adb_close(data_device);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (adb_read(data_device, &sb, sizeof(sb)) != sizeof(sb)) {
|
||||
WriteFdFmt(fd, "Error reading superblock\n");
|
||||
adb_close(data_device);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ext4_parse_sb(&sb, &info);
|
||||
*device_size = info.len;
|
||||
|
||||
adb_close(data_device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Turn verity on/off */
|
||||
static int set_verity_enabled_state(int fd, const char *block_device,
|
||||
const char* mount_point, bool enable)
|
||||
{
|
||||
uint32_t magic_number;
|
||||
const uint32_t new_magic = enable ? VERITY_METADATA_MAGIC_NUMBER
|
||||
: VERITY_METADATA_MAGIC_DISABLE;
|
||||
uint64_t device_length = 0;
|
||||
int device = -1;
|
||||
int retval = -1;
|
||||
|
||||
if (!make_block_device_writable(block_device)) {
|
||||
WriteFdFmt(fd, "Could not make block device %s writable (%s).\n",
|
||||
block_device, strerror(errno));
|
||||
goto errout;
|
||||
}
|
||||
|
||||
device = adb_open(block_device, O_RDWR | O_CLOEXEC);
|
||||
if (device == -1) {
|
||||
WriteFdFmt(fd, "Could not open block device %s (%s).\n", block_device, strerror(errno));
|
||||
WriteFdFmt(fd, "Maybe run adb remount?\n");
|
||||
goto errout;
|
||||
}
|
||||
|
||||
// find the start of the verity metadata
|
||||
if (get_target_device_size(fd, (char*)block_device, &device_length) < 0) {
|
||||
WriteFdFmt(fd, "Could not get target device size.\n");
|
||||
goto errout;
|
||||
}
|
||||
|
||||
if (lseek64(device, device_length, SEEK_SET) < 0) {
|
||||
WriteFdFmt(fd, "Could not seek to start of verity metadata block.\n");
|
||||
goto errout;
|
||||
}
|
||||
|
||||
// check the magic number
|
||||
if (adb_read(device, &magic_number, sizeof(magic_number)) != sizeof(magic_number)) {
|
||||
WriteFdFmt(fd, "Couldn't read magic number!\n");
|
||||
goto errout;
|
||||
}
|
||||
|
||||
if (!enable && magic_number == VERITY_METADATA_MAGIC_DISABLE) {
|
||||
WriteFdFmt(fd, "Verity already disabled on %s\n", mount_point);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
if (enable && magic_number == VERITY_METADATA_MAGIC_NUMBER) {
|
||||
WriteFdFmt(fd, "Verity already enabled on %s\n", mount_point);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
if (magic_number != VERITY_METADATA_MAGIC_NUMBER
|
||||
&& magic_number != VERITY_METADATA_MAGIC_DISABLE) {
|
||||
WriteFdFmt(fd, "Couldn't find verity metadata at offset %" PRIu64 "!\n", device_length);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
if (lseek64(device, device_length, SEEK_SET) < 0) {
|
||||
WriteFdFmt(fd, "Could not seek to start of verity metadata block.\n");
|
||||
goto errout;
|
||||
}
|
||||
|
||||
if (adb_write(device, &new_magic, sizeof(new_magic)) != sizeof(new_magic)) {
|
||||
WriteFdFmt(fd, "Could not set verity %s flag on device %s with error %s\n",
|
||||
enable ? "enabled" : "disabled",
|
||||
block_device, strerror(errno));
|
||||
goto errout;
|
||||
}
|
||||
|
||||
WriteFdFmt(fd, "Verity %s on %s\n", enable ? "enabled" : "disabled", mount_point);
|
||||
retval = 0;
|
||||
errout:
|
||||
if (device != -1)
|
||||
adb_close(device);
|
||||
return retval;
|
||||
}
|
||||
|
||||
void set_verity_enabled_state_service(int fd, void* cookie)
|
||||
{
|
||||
bool enable = (cookie != NULL);
|
||||
if (kAllowDisableVerity) {
|
||||
char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
|
||||
char propbuf[PROPERTY_VALUE_MAX];
|
||||
int i;
|
||||
bool any_changed = false;
|
||||
|
||||
property_get("ro.secure", propbuf, "0");
|
||||
if (strcmp(propbuf, "1")) {
|
||||
WriteFdFmt(fd, "verity not enabled - ENG build\n");
|
||||
goto errout;
|
||||
}
|
||||
|
||||
property_get("ro.debuggable", propbuf, "0");
|
||||
if (strcmp(propbuf, "1")) {
|
||||
WriteFdFmt(fd, "verity cannot be disabled/enabled - USER build\n");
|
||||
goto errout;
|
||||
}
|
||||
|
||||
property_get("ro.hardware", propbuf, "");
|
||||
snprintf(fstab_filename, sizeof(fstab_filename), FSTAB_PREFIX"%s",
|
||||
propbuf);
|
||||
|
||||
fstab = fs_mgr_read_fstab(fstab_filename);
|
||||
if (!fstab) {
|
||||
WriteFdFmt(fd, "Failed to open %s\nMaybe run adb root?\n", fstab_filename);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
/* Loop through entries looking for ones that vold manages */
|
||||
for (i = 0; i < fstab->num_entries; i++) {
|
||||
if(fs_mgr_is_verified(&fstab->recs[i])) {
|
||||
if (!set_verity_enabled_state(fd, fstab->recs[i].blk_device,
|
||||
fstab->recs[i].mount_point,
|
||||
enable)) {
|
||||
any_changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (any_changed) {
|
||||
WriteFdFmt(fd, "Now reboot your device for settings to take effect\n");
|
||||
}
|
||||
} else {
|
||||
WriteFdFmt(fd, "%s-verity only works for userdebug builds\n",
|
||||
enable ? "enable" : "disable");
|
||||
}
|
||||
|
||||
errout:
|
||||
adb_close(fd);
|
||||
}
|
||||
902
sockets.cpp
Normal file
902
sockets.cpp
Normal file
@@ -0,0 +1,902 @@
|
||||
/*
|
||||
* 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 TRACE_SOCKETS
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if !ADB_HOST
|
||||
#include "cutils/properties.h"
|
||||
#endif
|
||||
|
||||
#include "adb.h"
|
||||
#include "adb_io.h"
|
||||
#include "transport.h"
|
||||
|
||||
ADB_MUTEX_DEFINE( socket_list_lock );
|
||||
|
||||
static void local_socket_close_locked(asocket *s);
|
||||
|
||||
static unsigned local_socket_next_id = 1;
|
||||
|
||||
static asocket local_socket_list = {
|
||||
.next = &local_socket_list,
|
||||
.prev = &local_socket_list,
|
||||
};
|
||||
|
||||
/* the the list of currently closing local sockets.
|
||||
** these have no peer anymore, but still packets to
|
||||
** write to their fd.
|
||||
*/
|
||||
static asocket local_socket_closing_list = {
|
||||
.next = &local_socket_closing_list,
|
||||
.prev = &local_socket_closing_list,
|
||||
};
|
||||
|
||||
// Parse the global list of sockets to find one with id |local_id|.
|
||||
// If |peer_id| is not 0, also check that it is connected to a peer
|
||||
// with id |peer_id|. Returns an asocket handle on success, NULL on failure.
|
||||
asocket *find_local_socket(unsigned local_id, unsigned peer_id)
|
||||
{
|
||||
asocket *s;
|
||||
asocket *result = NULL;
|
||||
|
||||
adb_mutex_lock(&socket_list_lock);
|
||||
for (s = local_socket_list.next; s != &local_socket_list; s = s->next) {
|
||||
if (s->id != local_id)
|
||||
continue;
|
||||
if (peer_id == 0 || (s->peer && s->peer->id == peer_id)) {
|
||||
result = s;
|
||||
}
|
||||
break;
|
||||
}
|
||||
adb_mutex_unlock(&socket_list_lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
insert_local_socket(asocket* s, asocket* list)
|
||||
{
|
||||
s->next = list;
|
||||
s->prev = s->next->prev;
|
||||
s->prev->next = s;
|
||||
s->next->prev = s;
|
||||
}
|
||||
|
||||
|
||||
void install_local_socket(asocket *s)
|
||||
{
|
||||
adb_mutex_lock(&socket_list_lock);
|
||||
|
||||
s->id = local_socket_next_id++;
|
||||
|
||||
// Socket ids should never be 0.
|
||||
if (local_socket_next_id == 0)
|
||||
local_socket_next_id = 1;
|
||||
|
||||
insert_local_socket(s, &local_socket_list);
|
||||
|
||||
adb_mutex_unlock(&socket_list_lock);
|
||||
}
|
||||
|
||||
void remove_socket(asocket *s)
|
||||
{
|
||||
// socket_list_lock should already be held
|
||||
if (s->prev && s->next)
|
||||
{
|
||||
s->prev->next = s->next;
|
||||
s->next->prev = s->prev;
|
||||
s->next = 0;
|
||||
s->prev = 0;
|
||||
s->id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void close_all_sockets(atransport *t)
|
||||
{
|
||||
asocket *s;
|
||||
|
||||
/* this is a little gross, but since s->close() *will* modify
|
||||
** the list out from under you, your options are limited.
|
||||
*/
|
||||
adb_mutex_lock(&socket_list_lock);
|
||||
restart:
|
||||
for(s = local_socket_list.next; s != &local_socket_list; s = s->next){
|
||||
if(s->transport == t || (s->peer && s->peer->transport == t)) {
|
||||
local_socket_close_locked(s);
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
adb_mutex_unlock(&socket_list_lock);
|
||||
}
|
||||
|
||||
static int local_socket_enqueue(asocket *s, apacket *p)
|
||||
{
|
||||
D("LS(%d): enqueue %d\n", s->id, p->len);
|
||||
|
||||
p->ptr = p->data;
|
||||
|
||||
/* if there is already data queue'd, we will receive
|
||||
** events when it's time to write. just add this to
|
||||
** the tail
|
||||
*/
|
||||
if(s->pkt_first) {
|
||||
goto enqueue;
|
||||
}
|
||||
|
||||
/* write as much as we can, until we
|
||||
** would block or there is an error/eof
|
||||
*/
|
||||
while(p->len > 0) {
|
||||
int r = adb_write(s->fd, p->ptr, p->len);
|
||||
if(r > 0) {
|
||||
p->len -= r;
|
||||
p->ptr += r;
|
||||
continue;
|
||||
}
|
||||
if((r == 0) || (errno != EAGAIN)) {
|
||||
D( "LS(%d): not ready, errno=%d: %s\n", s->id, errno, strerror(errno) );
|
||||
s->close(s);
|
||||
return 1; /* not ready (error) */
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(p->len == 0) {
|
||||
put_apacket(p);
|
||||
return 0; /* ready for more data */
|
||||
}
|
||||
|
||||
enqueue:
|
||||
p->next = 0;
|
||||
if(s->pkt_first) {
|
||||
s->pkt_last->next = p;
|
||||
} else {
|
||||
s->pkt_first = p;
|
||||
}
|
||||
s->pkt_last = p;
|
||||
|
||||
/* make sure we are notified when we can drain the queue */
|
||||
fdevent_add(&s->fde, FDE_WRITE);
|
||||
|
||||
return 1; /* not ready (backlog) */
|
||||
}
|
||||
|
||||
static void local_socket_ready(asocket *s)
|
||||
{
|
||||
/* far side is ready for data, pay attention to
|
||||
readable events */
|
||||
fdevent_add(&s->fde, FDE_READ);
|
||||
}
|
||||
|
||||
static void local_socket_close(asocket *s)
|
||||
{
|
||||
adb_mutex_lock(&socket_list_lock);
|
||||
local_socket_close_locked(s);
|
||||
adb_mutex_unlock(&socket_list_lock);
|
||||
}
|
||||
|
||||
// be sure to hold the socket list lock when calling this
|
||||
static void local_socket_destroy(asocket *s)
|
||||
{
|
||||
apacket *p, *n;
|
||||
int exit_on_close = s->exit_on_close;
|
||||
|
||||
D("LS(%d): destroying fde.fd=%d\n", s->id, s->fde.fd);
|
||||
|
||||
/* IMPORTANT: the remove closes the fd
|
||||
** that belongs to this socket
|
||||
*/
|
||||
fdevent_remove(&s->fde);
|
||||
|
||||
/* dispose of any unwritten data */
|
||||
for(p = s->pkt_first; p; p = n) {
|
||||
D("LS(%d): discarding %d bytes\n", s->id, p->len);
|
||||
n = p->next;
|
||||
put_apacket(p);
|
||||
}
|
||||
remove_socket(s);
|
||||
free(s);
|
||||
|
||||
if (exit_on_close) {
|
||||
D("local_socket_destroy: exiting\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void local_socket_close_locked(asocket *s)
|
||||
{
|
||||
D("entered local_socket_close_locked. LS(%d) fd=%d\n", s->id, s->fd);
|
||||
if(s->peer) {
|
||||
D("LS(%d): closing peer. peer->id=%d peer->fd=%d\n",
|
||||
s->id, s->peer->id, s->peer->fd);
|
||||
/* Note: it's important to call shutdown before disconnecting from
|
||||
* the peer, this ensures that remote sockets can still get the id
|
||||
* of the local socket they're connected to, to send a CLOSE()
|
||||
* protocol event. */
|
||||
if (s->peer->shutdown)
|
||||
s->peer->shutdown(s->peer);
|
||||
s->peer->peer = 0;
|
||||
// tweak to avoid deadlock
|
||||
if (s->peer->close == local_socket_close) {
|
||||
local_socket_close_locked(s->peer);
|
||||
} else {
|
||||
s->peer->close(s->peer);
|
||||
}
|
||||
s->peer = 0;
|
||||
}
|
||||
|
||||
/* If we are already closing, or if there are no
|
||||
** pending packets, destroy immediately
|
||||
*/
|
||||
if (s->closing || s->pkt_first == NULL) {
|
||||
int id = s->id;
|
||||
local_socket_destroy(s);
|
||||
D("LS(%d): closed\n", id);
|
||||
return;
|
||||
}
|
||||
|
||||
/* otherwise, put on the closing list
|
||||
*/
|
||||
D("LS(%d): closing\n", s->id);
|
||||
s->closing = 1;
|
||||
fdevent_del(&s->fde, FDE_READ);
|
||||
remove_socket(s);
|
||||
D("LS(%d): put on socket_closing_list fd=%d\n", s->id, s->fd);
|
||||
insert_local_socket(s, &local_socket_closing_list);
|
||||
}
|
||||
|
||||
static void local_socket_event_func(int fd, unsigned ev, void* _s)
|
||||
{
|
||||
asocket* s = reinterpret_cast<asocket*>(_s);
|
||||
D("LS(%d): event_func(fd=%d(==%d), ev=%04x)\n", s->id, s->fd, fd, ev);
|
||||
|
||||
/* put the FDE_WRITE processing before the FDE_READ
|
||||
** in order to simplify the code.
|
||||
*/
|
||||
if (ev & FDE_WRITE) {
|
||||
apacket* p;
|
||||
while ((p = s->pkt_first) != nullptr) {
|
||||
while (p->len > 0) {
|
||||
int r = adb_write(fd, p->ptr, p->len);
|
||||
if (r == -1) {
|
||||
/* returning here is ok because FDE_READ will
|
||||
** be processed in the next iteration loop
|
||||
*/
|
||||
if (errno == EAGAIN) {
|
||||
return;
|
||||
}
|
||||
} else if (r > 0) {
|
||||
p->ptr += r;
|
||||
p->len -= r;
|
||||
continue;
|
||||
}
|
||||
|
||||
D(" closing after write because r=%d and errno is %d\n", r, errno);
|
||||
s->close(s);
|
||||
return;
|
||||
}
|
||||
|
||||
if (p->len == 0) {
|
||||
s->pkt_first = p->next;
|
||||
if (s->pkt_first == 0) {
|
||||
s->pkt_last = 0;
|
||||
}
|
||||
put_apacket(p);
|
||||
}
|
||||
}
|
||||
|
||||
/* if we sent the last packet of a closing socket,
|
||||
** we can now destroy it.
|
||||
*/
|
||||
if (s->closing) {
|
||||
D(" closing because 'closing' is set after write\n");
|
||||
s->close(s);
|
||||
return;
|
||||
}
|
||||
|
||||
/* no more packets queued, so we can ignore
|
||||
** writable events again and tell our peer
|
||||
** to resume writing
|
||||
*/
|
||||
fdevent_del(&s->fde, FDE_WRITE);
|
||||
s->peer->ready(s->peer);
|
||||
}
|
||||
|
||||
|
||||
if (ev & FDE_READ) {
|
||||
apacket *p = get_apacket();
|
||||
unsigned char *x = p->data;
|
||||
size_t avail = MAX_PAYLOAD;
|
||||
int r;
|
||||
int is_eof = 0;
|
||||
|
||||
while (avail > 0) {
|
||||
r = adb_read(fd, x, avail);
|
||||
D("LS(%d): post adb_read(fd=%d,...) r=%d (errno=%d) avail=%zu\n",
|
||||
s->id, s->fd, r, r < 0 ? errno : 0, avail);
|
||||
if (r == -1) {
|
||||
if (errno == EAGAIN) {
|
||||
break;
|
||||
}
|
||||
} else if (r > 0) {
|
||||
avail -= r;
|
||||
x += r;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* r = 0 or unhandled error */
|
||||
is_eof = 1;
|
||||
break;
|
||||
}
|
||||
D("LS(%d): fd=%d post avail loop. r=%d is_eof=%d forced_eof=%d\n",
|
||||
s->id, s->fd, r, is_eof, s->fde.force_eof);
|
||||
if ((avail == MAX_PAYLOAD) || (s->peer == 0)) {
|
||||
put_apacket(p);
|
||||
} else {
|
||||
p->len = MAX_PAYLOAD - avail;
|
||||
|
||||
r = s->peer->enqueue(s->peer, p);
|
||||
D("LS(%d): fd=%d post peer->enqueue(). r=%d\n", s->id, s->fd,
|
||||
r);
|
||||
|
||||
if (r < 0) {
|
||||
/* error return means they closed us as a side-effect
|
||||
** and we must return immediately.
|
||||
**
|
||||
** note that if we still have buffered packets, the
|
||||
** socket will be placed on the closing socket list.
|
||||
** this handler function will be called again
|
||||
** to process FDE_WRITE events.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
if (r > 0) {
|
||||
/* if the remote cannot accept further events,
|
||||
** we disable notification of READs. They'll
|
||||
** be enabled again when we get a call to ready()
|
||||
*/
|
||||
fdevent_del(&s->fde, FDE_READ);
|
||||
}
|
||||
}
|
||||
/* Don't allow a forced eof if data is still there */
|
||||
if ((s->fde.force_eof && !r) || is_eof) {
|
||||
D(" closing because is_eof=%d r=%d s->fde.force_eof=%d\n",
|
||||
is_eof, r, s->fde.force_eof);
|
||||
s->close(s);
|
||||
}
|
||||
}
|
||||
|
||||
if (ev & FDE_ERROR){
|
||||
/* this should be caught be the next read or write
|
||||
** catching it here means we may skip the last few
|
||||
** bytes of readable data.
|
||||
*/
|
||||
D("LS(%d): FDE_ERROR (fd=%d)\n", s->id, s->fd);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
asocket *create_local_socket(int fd)
|
||||
{
|
||||
asocket *s = reinterpret_cast<asocket*>(calloc(1, sizeof(asocket)));
|
||||
if (s == NULL) fatal("cannot allocate socket");
|
||||
s->fd = fd;
|
||||
s->enqueue = local_socket_enqueue;
|
||||
s->ready = local_socket_ready;
|
||||
s->shutdown = NULL;
|
||||
s->close = local_socket_close;
|
||||
install_local_socket(s);
|
||||
|
||||
fdevent_install(&s->fde, fd, local_socket_event_func, s);
|
||||
D("LS(%d): created (fd=%d)\n", s->id, s->fd);
|
||||
return s;
|
||||
}
|
||||
|
||||
asocket *create_local_service_socket(const char *name)
|
||||
{
|
||||
#if !ADB_HOST
|
||||
if (!strcmp(name,"jdwp")) {
|
||||
return create_jdwp_service_socket();
|
||||
}
|
||||
if (!strcmp(name,"track-jdwp")) {
|
||||
return create_jdwp_tracker_service_socket();
|
||||
}
|
||||
#endif
|
||||
int fd = service_to_fd(name);
|
||||
if(fd < 0) return 0;
|
||||
|
||||
asocket* s = create_local_socket(fd);
|
||||
D("LS(%d): bound to '%s' via %d\n", s->id, name, fd);
|
||||
|
||||
#if !ADB_HOST
|
||||
char debug[PROPERTY_VALUE_MAX];
|
||||
if (!strncmp(name, "root:", 5))
|
||||
property_get("ro.debuggable", debug, "");
|
||||
|
||||
if ((!strncmp(name, "root:", 5) && getuid() != 0 && strcmp(debug, "1") == 0)
|
||||
|| (!strncmp(name, "unroot:", 7) && getuid() == 0)
|
||||
|| !strncmp(name, "usb:", 4)
|
||||
|| !strncmp(name, "tcpip:", 6)) {
|
||||
D("LS(%d): enabling exit_on_close\n", s->id);
|
||||
s->exit_on_close = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
#if ADB_HOST
|
||||
static asocket *create_host_service_socket(const char *name, const char* serial)
|
||||
{
|
||||
asocket *s;
|
||||
|
||||
s = host_service_to_socket(name, serial);
|
||||
|
||||
if (s != NULL) {
|
||||
D("LS(%d) bound to '%s'\n", s->id, name);
|
||||
return s;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
#endif /* ADB_HOST */
|
||||
|
||||
/* a Remote socket is used to send/receive data to/from a given transport object
|
||||
** it needs to be closed when the transport is forcibly destroyed by the user
|
||||
*/
|
||||
struct aremotesocket {
|
||||
asocket socket;
|
||||
adisconnect disconnect;
|
||||
};
|
||||
|
||||
static int remote_socket_enqueue(asocket *s, apacket *p)
|
||||
{
|
||||
D("entered remote_socket_enqueue RS(%d) WRITE fd=%d peer.fd=%d\n",
|
||||
s->id, s->fd, s->peer->fd);
|
||||
p->msg.command = A_WRTE;
|
||||
p->msg.arg0 = s->peer->id;
|
||||
p->msg.arg1 = s->id;
|
||||
p->msg.data_length = p->len;
|
||||
send_packet(p, s->transport);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void remote_socket_ready(asocket *s)
|
||||
{
|
||||
D("entered remote_socket_ready RS(%d) OKAY fd=%d peer.fd=%d\n",
|
||||
s->id, s->fd, s->peer->fd);
|
||||
apacket *p = get_apacket();
|
||||
p->msg.command = A_OKAY;
|
||||
p->msg.arg0 = s->peer->id;
|
||||
p->msg.arg1 = s->id;
|
||||
send_packet(p, s->transport);
|
||||
}
|
||||
|
||||
static void remote_socket_shutdown(asocket *s)
|
||||
{
|
||||
D("entered remote_socket_shutdown RS(%d) CLOSE fd=%d peer->fd=%d\n",
|
||||
s->id, s->fd, s->peer?s->peer->fd:-1);
|
||||
apacket *p = get_apacket();
|
||||
p->msg.command = A_CLSE;
|
||||
if(s->peer) {
|
||||
p->msg.arg0 = s->peer->id;
|
||||
}
|
||||
p->msg.arg1 = s->id;
|
||||
send_packet(p, s->transport);
|
||||
}
|
||||
|
||||
static void remote_socket_close(asocket *s)
|
||||
{
|
||||
if (s->peer) {
|
||||
s->peer->peer = 0;
|
||||
D("RS(%d) peer->close()ing peer->id=%d peer->fd=%d\n",
|
||||
s->id, s->peer->id, s->peer->fd);
|
||||
s->peer->close(s->peer);
|
||||
}
|
||||
D("entered remote_socket_close RS(%d) CLOSE fd=%d peer->fd=%d\n",
|
||||
s->id, s->fd, s->peer?s->peer->fd:-1);
|
||||
D("RS(%d): closed\n", s->id);
|
||||
remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect );
|
||||
free(s);
|
||||
}
|
||||
|
||||
static void remote_socket_disconnect(void* _s, atransport* t)
|
||||
{
|
||||
asocket* s = reinterpret_cast<asocket*>(_s);
|
||||
asocket* peer = s->peer;
|
||||
|
||||
D("remote_socket_disconnect RS(%d)\n", s->id);
|
||||
if (peer) {
|
||||
peer->peer = NULL;
|
||||
peer->close(peer);
|
||||
}
|
||||
remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect );
|
||||
free(s);
|
||||
}
|
||||
|
||||
/* Create an asocket to exchange packets with a remote service through transport
|
||||
|t|. Where |id| is the socket id of the corresponding service on the other
|
||||
side of the transport (it is allocated by the remote side and _cannot_ be 0).
|
||||
Returns a new non-NULL asocket handle. */
|
||||
asocket *create_remote_socket(unsigned id, atransport *t)
|
||||
{
|
||||
if (id == 0) fatal("invalid remote socket id (0)");
|
||||
asocket* s = reinterpret_cast<asocket*>(calloc(1, sizeof(aremotesocket)));
|
||||
adisconnect* dis = &reinterpret_cast<aremotesocket*>(s)->disconnect;
|
||||
|
||||
if (s == NULL) fatal("cannot allocate socket");
|
||||
s->id = id;
|
||||
s->enqueue = remote_socket_enqueue;
|
||||
s->ready = remote_socket_ready;
|
||||
s->shutdown = remote_socket_shutdown;
|
||||
s->close = remote_socket_close;
|
||||
s->transport = t;
|
||||
|
||||
dis->func = remote_socket_disconnect;
|
||||
dis->opaque = s;
|
||||
add_transport_disconnect( t, dis );
|
||||
D("RS(%d): created\n", s->id);
|
||||
return s;
|
||||
}
|
||||
|
||||
void connect_to_remote(asocket *s, const char *destination)
|
||||
{
|
||||
D("Connect_to_remote call RS(%d) fd=%d\n", s->id, s->fd);
|
||||
apacket *p = get_apacket();
|
||||
int len = strlen(destination) + 1;
|
||||
|
||||
if(len > (MAX_PAYLOAD-1)) {
|
||||
fatal("destination oversized");
|
||||
}
|
||||
|
||||
D("LS(%d): connect('%s')\n", s->id, destination);
|
||||
p->msg.command = A_OPEN;
|
||||
p->msg.arg0 = s->id;
|
||||
p->msg.data_length = len;
|
||||
strcpy((char*) p->data, destination);
|
||||
send_packet(p, s->transport);
|
||||
}
|
||||
|
||||
|
||||
/* this is used by magic sockets to rig local sockets to
|
||||
send the go-ahead message when they connect */
|
||||
static void local_socket_ready_notify(asocket *s)
|
||||
{
|
||||
s->ready = local_socket_ready;
|
||||
s->shutdown = NULL;
|
||||
s->close = local_socket_close;
|
||||
SendOkay(s->fd);
|
||||
s->ready(s);
|
||||
}
|
||||
|
||||
/* this is used by magic sockets to rig local sockets to
|
||||
send the failure message if they are closed before
|
||||
connected (to avoid closing them without a status message) */
|
||||
static void local_socket_close_notify(asocket *s)
|
||||
{
|
||||
s->ready = local_socket_ready;
|
||||
s->shutdown = NULL;
|
||||
s->close = local_socket_close;
|
||||
SendFail(s->fd, "closed");
|
||||
s->close(s);
|
||||
}
|
||||
|
||||
static unsigned unhex(unsigned char *s, int len)
|
||||
{
|
||||
unsigned n = 0, c;
|
||||
|
||||
while(len-- > 0) {
|
||||
switch((c = *s++)) {
|
||||
case '0': case '1': case '2':
|
||||
case '3': case '4': case '5':
|
||||
case '6': case '7': case '8':
|
||||
case '9':
|
||||
c -= '0';
|
||||
break;
|
||||
case 'a': case 'b': case 'c':
|
||||
case 'd': case 'e': case 'f':
|
||||
c = c - 'a' + 10;
|
||||
break;
|
||||
case 'A': case 'B': case 'C':
|
||||
case 'D': case 'E': case 'F':
|
||||
c = c - 'A' + 10;
|
||||
break;
|
||||
default:
|
||||
return 0xffffffff;
|
||||
}
|
||||
|
||||
n = (n << 4) | c;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
#if ADB_HOST
|
||||
|
||||
#define PREFIX(str) { str, sizeof(str) - 1 }
|
||||
static const struct prefix_struct {
|
||||
const char *str;
|
||||
const size_t len;
|
||||
} prefixes[] = {
|
||||
PREFIX("usb:"),
|
||||
PREFIX("product:"),
|
||||
PREFIX("model:"),
|
||||
PREFIX("device:"),
|
||||
};
|
||||
static const int num_prefixes = (sizeof(prefixes) / sizeof(prefixes[0]));
|
||||
|
||||
/* skip_host_serial return the position in a string
|
||||
skipping over the 'serial' parameter in the ADB protocol,
|
||||
where parameter string may be a host:port string containing
|
||||
the protocol delimiter (colon). */
|
||||
static char *skip_host_serial(char *service) {
|
||||
char *first_colon, *serial_end;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_prefixes; i++) {
|
||||
if (!strncmp(service, prefixes[i].str, prefixes[i].len))
|
||||
return strchr(service + prefixes[i].len, ':');
|
||||
}
|
||||
|
||||
first_colon = strchr(service, ':');
|
||||
if (!first_colon) {
|
||||
/* No colon in service string. */
|
||||
return NULL;
|
||||
}
|
||||
serial_end = first_colon;
|
||||
if (isdigit(serial_end[1])) {
|
||||
serial_end++;
|
||||
while ((*serial_end) && isdigit(*serial_end)) {
|
||||
serial_end++;
|
||||
}
|
||||
if ((*serial_end) != ':') {
|
||||
// Something other than numbers was found, reset the end.
|
||||
serial_end = first_colon;
|
||||
}
|
||||
}
|
||||
return serial_end;
|
||||
}
|
||||
|
||||
#endif // ADB_HOST
|
||||
|
||||
static int smart_socket_enqueue(asocket *s, apacket *p)
|
||||
{
|
||||
unsigned len;
|
||||
#if ADB_HOST
|
||||
char *service = NULL;
|
||||
char* serial = NULL;
|
||||
transport_type ttype = kTransportAny;
|
||||
#endif
|
||||
|
||||
D("SS(%d): enqueue %d\n", s->id, p->len);
|
||||
|
||||
if(s->pkt_first == 0) {
|
||||
s->pkt_first = p;
|
||||
s->pkt_last = p;
|
||||
} else {
|
||||
if((s->pkt_first->len + p->len) > MAX_PAYLOAD) {
|
||||
D("SS(%d): overflow\n", s->id);
|
||||
put_apacket(p);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
memcpy(s->pkt_first->data + s->pkt_first->len,
|
||||
p->data, p->len);
|
||||
s->pkt_first->len += p->len;
|
||||
put_apacket(p);
|
||||
|
||||
p = s->pkt_first;
|
||||
}
|
||||
|
||||
/* don't bother if we can't decode the length */
|
||||
if(p->len < 4) return 0;
|
||||
|
||||
len = unhex(p->data, 4);
|
||||
if((len < 1) || (len > 1024)) {
|
||||
D("SS(%d): bad size (%d)\n", s->id, len);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
D("SS(%d): len is %d\n", s->id, len );
|
||||
/* can't do anything until we have the full header */
|
||||
if((len + 4) > p->len) {
|
||||
D("SS(%d): waiting for %d more bytes\n", s->id, len+4 - p->len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
p->data[len + 4] = 0;
|
||||
|
||||
D("SS(%d): '%s'\n", s->id, (char*) (p->data + 4));
|
||||
|
||||
#if ADB_HOST
|
||||
service = (char *)p->data + 4;
|
||||
if(!strncmp(service, "host-serial:", strlen("host-serial:"))) {
|
||||
char* serial_end;
|
||||
service += strlen("host-serial:");
|
||||
|
||||
// serial number should follow "host:" and could be a host:port string.
|
||||
serial_end = skip_host_serial(service);
|
||||
if (serial_end) {
|
||||
*serial_end = 0; // terminate string
|
||||
serial = service;
|
||||
service = serial_end + 1;
|
||||
}
|
||||
} else if (!strncmp(service, "host-usb:", strlen("host-usb:"))) {
|
||||
ttype = kTransportUsb;
|
||||
service += strlen("host-usb:");
|
||||
} else if (!strncmp(service, "host-local:", strlen("host-local:"))) {
|
||||
ttype = kTransportLocal;
|
||||
service += strlen("host-local:");
|
||||
} else if (!strncmp(service, "host:", strlen("host:"))) {
|
||||
ttype = kTransportAny;
|
||||
service += strlen("host:");
|
||||
} else {
|
||||
service = NULL;
|
||||
}
|
||||
|
||||
if (service) {
|
||||
asocket *s2;
|
||||
|
||||
/* some requests are handled immediately -- in that
|
||||
** case the handle_host_request() routine has sent
|
||||
** the OKAY or FAIL message and all we have to do
|
||||
** is clean up.
|
||||
*/
|
||||
if(handle_host_request(service, ttype, serial, s->peer->fd, s) == 0) {
|
||||
/* XXX fail message? */
|
||||
D( "SS(%d): handled host service '%s'\n", s->id, service );
|
||||
goto fail;
|
||||
}
|
||||
if (!strncmp(service, "transport", strlen("transport"))) {
|
||||
D( "SS(%d): okay transport\n", s->id );
|
||||
p->len = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* try to find a local service with this name.
|
||||
** if no such service exists, we'll fail out
|
||||
** and tear down here.
|
||||
*/
|
||||
s2 = create_host_service_socket(service, serial);
|
||||
if(s2 == 0) {
|
||||
D( "SS(%d): couldn't create host service '%s'\n", s->id, service );
|
||||
SendFail(s->peer->fd, "unknown host service");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* we've connected to a local host service,
|
||||
** so we make our peer back into a regular
|
||||
** local socket and bind it to the new local
|
||||
** service socket, acknowledge the successful
|
||||
** connection, and close this smart socket now
|
||||
** that its work is done.
|
||||
*/
|
||||
SendOkay(s->peer->fd);
|
||||
|
||||
s->peer->ready = local_socket_ready;
|
||||
s->peer->shutdown = NULL;
|
||||
s->peer->close = local_socket_close;
|
||||
s->peer->peer = s2;
|
||||
s2->peer = s->peer;
|
||||
s->peer = 0;
|
||||
D( "SS(%d): okay\n", s->id );
|
||||
s->close(s);
|
||||
|
||||
/* initial state is "ready" */
|
||||
s2->ready(s2);
|
||||
return 0;
|
||||
}
|
||||
#else /* !ADB_HOST */
|
||||
if (s->transport == NULL) {
|
||||
std::string error_msg = "unknown failure";
|
||||
s->transport = acquire_one_transport(CS_ANY, kTransportAny, NULL, &error_msg);
|
||||
|
||||
if (s->transport == NULL) {
|
||||
SendFail(s->peer->fd, error_msg);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if(!(s->transport) || (s->transport->connection_state == CS_OFFLINE)) {
|
||||
/* if there's no remote we fail the connection
|
||||
** right here and terminate it
|
||||
*/
|
||||
SendFail(s->peer->fd, "device offline (x)");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
/* instrument our peer to pass the success or fail
|
||||
** message back once it connects or closes, then
|
||||
** detach from it, request the connection, and
|
||||
** tear down
|
||||
*/
|
||||
s->peer->ready = local_socket_ready_notify;
|
||||
s->peer->shutdown = NULL;
|
||||
s->peer->close = local_socket_close_notify;
|
||||
s->peer->peer = 0;
|
||||
/* give him our transport and upref it */
|
||||
s->peer->transport = s->transport;
|
||||
|
||||
connect_to_remote(s->peer, (char*) (p->data + 4));
|
||||
s->peer = 0;
|
||||
s->close(s);
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
/* we're going to close our peer as a side-effect, so
|
||||
** return -1 to signal that state to the local socket
|
||||
** who is enqueueing against us
|
||||
*/
|
||||
s->close(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void smart_socket_ready(asocket *s)
|
||||
{
|
||||
D("SS(%d): ready\n", s->id);
|
||||
}
|
||||
|
||||
static void smart_socket_close(asocket *s)
|
||||
{
|
||||
D("SS(%d): closed\n", s->id);
|
||||
if(s->pkt_first){
|
||||
put_apacket(s->pkt_first);
|
||||
}
|
||||
if(s->peer) {
|
||||
s->peer->peer = 0;
|
||||
s->peer->close(s->peer);
|
||||
s->peer = 0;
|
||||
}
|
||||
free(s);
|
||||
}
|
||||
|
||||
static asocket *create_smart_socket(void)
|
||||
{
|
||||
D("Creating smart socket \n");
|
||||
asocket *s = reinterpret_cast<asocket*>(calloc(1, sizeof(asocket)));
|
||||
if (s == NULL) fatal("cannot allocate socket");
|
||||
s->enqueue = smart_socket_enqueue;
|
||||
s->ready = smart_socket_ready;
|
||||
s->shutdown = NULL;
|
||||
s->close = smart_socket_close;
|
||||
|
||||
D("SS(%d)\n", s->id);
|
||||
return s;
|
||||
}
|
||||
|
||||
void connect_to_smartsocket(asocket *s)
|
||||
{
|
||||
D("Connecting to smart socket \n");
|
||||
asocket *ss = create_smart_socket();
|
||||
s->peer = ss;
|
||||
ss->peer = s;
|
||||
s->ready(s);
|
||||
}
|
||||
BIN
sockets.dia
Normal file
BIN
sockets.dia
Normal file
Binary file not shown.
525
sysdeps.h
Normal file
525
sysdeps.h
Normal file
@@ -0,0 +1,525 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* this file contains system-dependent definitions used by ADB
|
||||
* they're related to threads, sockets and file descriptors
|
||||
*/
|
||||
#ifndef _ADB_SYSDEPS_H
|
||||
#define _ADB_SYSDEPS_H
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
# undef _WIN32
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TEMP_FAILURE_RETRY is defined by some, but not all, versions of
|
||||
* <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
|
||||
* not already defined, then define it here.
|
||||
*/
|
||||
#ifndef TEMP_FAILURE_RETRY
|
||||
/* Used to retry syscalls that can return EINTR. */
|
||||
#define TEMP_FAILURE_RETRY(exp) ({ \
|
||||
typeof (exp) _rc; \
|
||||
do { \
|
||||
_rc = (exp); \
|
||||
} while (_rc == -1 && errno == EINTR); \
|
||||
_rc; })
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <ctype.h>
|
||||
#include <direct.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#include <process.h>
|
||||
#include <sys/stat.h>
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
#include "fdevent.h"
|
||||
|
||||
#define OS_PATH_SEPARATOR '\\'
|
||||
#define OS_PATH_SEPARATOR_STR "\\"
|
||||
#define ENV_PATH_SEPARATOR_STR ";"
|
||||
|
||||
typedef CRITICAL_SECTION adb_mutex_t;
|
||||
|
||||
#define ADB_MUTEX_DEFINE(x) adb_mutex_t x
|
||||
|
||||
/* declare all mutexes */
|
||||
/* For win32, adb_sysdeps_init() will do the mutex runtime initialization. */
|
||||
#define ADB_MUTEX(x) extern adb_mutex_t x;
|
||||
#include "mutex_list.h"
|
||||
|
||||
extern void adb_sysdeps_init(void);
|
||||
|
||||
static __inline__ void adb_mutex_lock( adb_mutex_t* lock )
|
||||
{
|
||||
EnterCriticalSection( lock );
|
||||
}
|
||||
|
||||
static __inline__ void adb_mutex_unlock( adb_mutex_t* lock )
|
||||
{
|
||||
LeaveCriticalSection( lock );
|
||||
}
|
||||
|
||||
typedef struct { unsigned tid; } adb_thread_t;
|
||||
|
||||
typedef void* (*adb_thread_func_t)(void* arg);
|
||||
|
||||
typedef void (*win_thread_func_t)(void* arg);
|
||||
|
||||
static __inline__ int adb_thread_create( adb_thread_t *thread, adb_thread_func_t func, void* arg)
|
||||
{
|
||||
thread->tid = _beginthread( (win_thread_func_t)func, 0, arg );
|
||||
if (thread->tid == (unsigned)-1L) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __inline__ unsigned long adb_thread_id()
|
||||
{
|
||||
return GetCurrentThreadId();
|
||||
}
|
||||
|
||||
static __inline__ void close_on_exec(int fd)
|
||||
{
|
||||
/* nothing really */
|
||||
}
|
||||
|
||||
#define lstat stat /* no symlinks on Win32 */
|
||||
|
||||
#define S_ISLNK(m) 0 /* no symlinks on Win32 */
|
||||
|
||||
static __inline__ int adb_unlink(const char* path)
|
||||
{
|
||||
int rc = unlink(path);
|
||||
|
||||
if (rc == -1 && errno == EACCES) {
|
||||
/* unlink returns EACCES when the file is read-only, so we first */
|
||||
/* try to make it writable, then unlink again... */
|
||||
rc = chmod(path, _S_IREAD|_S_IWRITE );
|
||||
if (rc == 0)
|
||||
rc = unlink(path);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
#undef unlink
|
||||
#define unlink ___xxx_unlink
|
||||
|
||||
static __inline__ int adb_mkdir(const char* path, int mode)
|
||||
{
|
||||
return _mkdir(path);
|
||||
}
|
||||
#undef mkdir
|
||||
#define mkdir ___xxx_mkdir
|
||||
|
||||
extern int adb_open(const char* path, int options);
|
||||
extern int adb_creat(const char* path, int mode);
|
||||
extern int adb_read(int fd, void* buf, int len);
|
||||
extern int adb_write(int fd, const void* buf, int len);
|
||||
extern int adb_lseek(int fd, int pos, int where);
|
||||
extern int adb_shutdown(int fd);
|
||||
extern int adb_close(int fd);
|
||||
|
||||
static __inline__ int unix_close(int fd)
|
||||
{
|
||||
return close(fd);
|
||||
}
|
||||
#undef close
|
||||
#define close ____xxx_close
|
||||
|
||||
extern int unix_read(int fd, void* buf, size_t len);
|
||||
|
||||
#undef read
|
||||
#define read ___xxx_read
|
||||
|
||||
static __inline__ int unix_write(int fd, const void* buf, size_t len)
|
||||
{
|
||||
return write(fd, buf, len);
|
||||
}
|
||||
#undef write
|
||||
#define write ___xxx_write
|
||||
|
||||
static __inline__ int adb_open_mode(const char* path, int options, int mode)
|
||||
{
|
||||
return adb_open(path, options);
|
||||
}
|
||||
|
||||
static __inline__ int unix_open(const char* path, int options,...)
|
||||
{
|
||||
if ((options & O_CREAT) == 0)
|
||||
{
|
||||
return open(path, options);
|
||||
}
|
||||
else
|
||||
{
|
||||
int mode;
|
||||
va_list args;
|
||||
va_start( args, options );
|
||||
mode = va_arg( args, int );
|
||||
va_end( args );
|
||||
return open(path, options, mode);
|
||||
}
|
||||
}
|
||||
#define open ___xxx_unix_open
|
||||
|
||||
|
||||
/* normally provided by <cutils/misc.h> */
|
||||
extern void* load_file(const char* pathname, unsigned* psize);
|
||||
|
||||
/* normally provided by <cutils/sockets.h> */
|
||||
extern int socket_loopback_client(int port, int type);
|
||||
extern int socket_network_client(const char *host, int port, int type);
|
||||
extern int socket_network_client_timeout(const char *host, int port, int type,
|
||||
int timeout);
|
||||
extern int socket_loopback_server(int port, int type);
|
||||
extern int socket_inaddr_any_server(int port, int type);
|
||||
|
||||
/* normally provided by "fdevent.h" */
|
||||
|
||||
#define FDE_READ 0x0001
|
||||
#define FDE_WRITE 0x0002
|
||||
#define FDE_ERROR 0x0004
|
||||
#define FDE_DONT_CLOSE 0x0080
|
||||
|
||||
typedef void (*fd_func)(int fd, unsigned events, void *userdata);
|
||||
|
||||
fdevent *fdevent_create(int fd, fd_func func, void *arg);
|
||||
void fdevent_destroy(fdevent *fde);
|
||||
void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg);
|
||||
void fdevent_remove(fdevent *item);
|
||||
void fdevent_set(fdevent *fde, unsigned events);
|
||||
void fdevent_add(fdevent *fde, unsigned events);
|
||||
void fdevent_del(fdevent *fde, unsigned events);
|
||||
void fdevent_loop();
|
||||
|
||||
static __inline__ void adb_sleep_ms( int mseconds )
|
||||
{
|
||||
Sleep( mseconds );
|
||||
}
|
||||
|
||||
extern int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen);
|
||||
|
||||
#undef accept
|
||||
#define accept ___xxx_accept
|
||||
|
||||
extern int adb_setsockopt(int fd, int level, int optname, const void* optval, socklen_t optlen);
|
||||
|
||||
#undef setsockopt
|
||||
#define setsockopt ___xxx_setsockopt
|
||||
|
||||
static __inline__ int adb_socket_setbufsize( int fd, int bufsize )
|
||||
{
|
||||
int opt = bufsize;
|
||||
return adb_setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const void*)&opt, sizeof(opt));
|
||||
}
|
||||
|
||||
static __inline__ void disable_tcp_nagle( int fd )
|
||||
{
|
||||
int on = 1;
|
||||
adb_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const void*)&on, sizeof(on));
|
||||
}
|
||||
|
||||
extern int adb_socketpair( int sv[2] );
|
||||
|
||||
static __inline__ char* adb_dirstart( const char* path )
|
||||
{
|
||||
char* p = strchr(path, '/');
|
||||
char* p2 = strchr(path, '\\');
|
||||
|
||||
if ( !p )
|
||||
p = p2;
|
||||
else if ( p2 && p2 > p )
|
||||
p = p2;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static __inline__ char* adb_dirstop( const char* path )
|
||||
{
|
||||
char* p = strrchr(path, '/');
|
||||
char* p2 = strrchr(path, '\\');
|
||||
|
||||
if ( !p )
|
||||
p = p2;
|
||||
else if ( p2 && p2 > p )
|
||||
p = p2;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static __inline__ int adb_is_absolute_host_path( const char* path )
|
||||
{
|
||||
return isalpha(path[0]) && path[1] == ':' && path[2] == '\\';
|
||||
}
|
||||
|
||||
#else /* !_WIN32 a.k.a. Unix */
|
||||
|
||||
#include "fdevent.h"
|
||||
#include <cutils/sockets.h>
|
||||
#include <cutils/misc.h>
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define OS_PATH_SEPARATOR '/'
|
||||
#define OS_PATH_SEPARATOR_STR "/"
|
||||
#define ENV_PATH_SEPARATOR_STR ":"
|
||||
|
||||
typedef pthread_mutex_t adb_mutex_t;
|
||||
|
||||
#define ADB_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
|
||||
#define adb_mutex_init pthread_mutex_init
|
||||
#define adb_mutex_lock pthread_mutex_lock
|
||||
#define adb_mutex_unlock pthread_mutex_unlock
|
||||
#define adb_mutex_destroy pthread_mutex_destroy
|
||||
|
||||
#define ADB_MUTEX_DEFINE(m) adb_mutex_t m = PTHREAD_MUTEX_INITIALIZER
|
||||
|
||||
#define adb_cond_t pthread_cond_t
|
||||
#define adb_cond_init pthread_cond_init
|
||||
#define adb_cond_wait pthread_cond_wait
|
||||
#define adb_cond_broadcast pthread_cond_broadcast
|
||||
#define adb_cond_signal pthread_cond_signal
|
||||
#define adb_cond_destroy pthread_cond_destroy
|
||||
|
||||
/* declare all mutexes */
|
||||
#define ADB_MUTEX(x) extern adb_mutex_t x;
|
||||
#include "mutex_list.h"
|
||||
|
||||
static __inline__ void close_on_exec(int fd)
|
||||
{
|
||||
fcntl( fd, F_SETFD, FD_CLOEXEC );
|
||||
}
|
||||
|
||||
static __inline__ int unix_open(const char* path, int options,...)
|
||||
{
|
||||
if ((options & O_CREAT) == 0)
|
||||
{
|
||||
return TEMP_FAILURE_RETRY( open(path, options) );
|
||||
}
|
||||
else
|
||||
{
|
||||
int mode;
|
||||
va_list args;
|
||||
va_start( args, options );
|
||||
mode = va_arg( args, int );
|
||||
va_end( args );
|
||||
return TEMP_FAILURE_RETRY( open( path, options, mode ) );
|
||||
}
|
||||
}
|
||||
|
||||
static __inline__ int adb_open_mode( const char* pathname, int options, int mode )
|
||||
{
|
||||
return TEMP_FAILURE_RETRY( open( pathname, options, mode ) );
|
||||
}
|
||||
|
||||
|
||||
static __inline__ int adb_open( const char* pathname, int options )
|
||||
{
|
||||
int fd = TEMP_FAILURE_RETRY( open( pathname, options ) );
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
close_on_exec( fd );
|
||||
return fd;
|
||||
}
|
||||
#undef open
|
||||
#define open ___xxx_open
|
||||
|
||||
static __inline__ int adb_shutdown(int fd)
|
||||
{
|
||||
return shutdown(fd, SHUT_RDWR);
|
||||
}
|
||||
#undef shutdown
|
||||
#define shutdown ____xxx_shutdown
|
||||
|
||||
static __inline__ int adb_close(int fd)
|
||||
{
|
||||
return close(fd);
|
||||
}
|
||||
#undef close
|
||||
#define close ____xxx_close
|
||||
|
||||
|
||||
static __inline__ int adb_read(int fd, void* buf, size_t len)
|
||||
{
|
||||
return TEMP_FAILURE_RETRY( read( fd, buf, len ) );
|
||||
}
|
||||
|
||||
#undef read
|
||||
#define read ___xxx_read
|
||||
|
||||
static __inline__ int adb_write(int fd, const void* buf, size_t len)
|
||||
{
|
||||
return TEMP_FAILURE_RETRY( write( fd, buf, len ) );
|
||||
}
|
||||
#undef write
|
||||
#define write ___xxx_write
|
||||
|
||||
static __inline__ int adb_lseek(int fd, int pos, int where)
|
||||
{
|
||||
return lseek(fd, pos, where);
|
||||
}
|
||||
#undef lseek
|
||||
#define lseek ___xxx_lseek
|
||||
|
||||
static __inline__ int adb_unlink(const char* path)
|
||||
{
|
||||
return unlink(path);
|
||||
}
|
||||
#undef unlink
|
||||
#define unlink ___xxx_unlink
|
||||
|
||||
static __inline__ int adb_creat(const char* path, int mode)
|
||||
{
|
||||
int fd = TEMP_FAILURE_RETRY( creat( path, mode ) );
|
||||
|
||||
if ( fd < 0 )
|
||||
return -1;
|
||||
|
||||
close_on_exec(fd);
|
||||
return fd;
|
||||
}
|
||||
#undef creat
|
||||
#define creat ___xxx_creat
|
||||
|
||||
static __inline__ int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = TEMP_FAILURE_RETRY( accept( serverfd, addr, addrlen ) );
|
||||
if (fd >= 0)
|
||||
close_on_exec(fd);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
#undef accept
|
||||
#define accept ___xxx_accept
|
||||
|
||||
#define unix_read adb_read
|
||||
#define unix_write adb_write
|
||||
#define unix_close adb_close
|
||||
|
||||
typedef pthread_t adb_thread_t;
|
||||
|
||||
typedef void* (*adb_thread_func_t)( void* arg );
|
||||
|
||||
static __inline__ int adb_thread_create( adb_thread_t *pthread, adb_thread_func_t start, void* arg )
|
||||
{
|
||||
pthread_attr_t attr;
|
||||
|
||||
pthread_attr_init (&attr);
|
||||
pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
|
||||
|
||||
return pthread_create( pthread, &attr, start, arg );
|
||||
}
|
||||
|
||||
static __inline__ int adb_socket_setbufsize( int fd, int bufsize )
|
||||
{
|
||||
int opt = bufsize;
|
||||
return setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
|
||||
}
|
||||
|
||||
static __inline__ void disable_tcp_nagle(int fd)
|
||||
{
|
||||
int on = 1;
|
||||
setsockopt( fd, IPPROTO_TCP, TCP_NODELAY, (void*)&on, sizeof(on) );
|
||||
}
|
||||
|
||||
static __inline__ int adb_setsockopt( int fd, int level, int optname, const void* optval, socklen_t optlen )
|
||||
{
|
||||
return setsockopt( fd, level, optname, optval, optlen );
|
||||
}
|
||||
|
||||
#undef setsockopt
|
||||
#define setsockopt ___xxx_setsockopt
|
||||
|
||||
static __inline__ int unix_socketpair( int d, int type, int protocol, int sv[2] )
|
||||
{
|
||||
return socketpair( d, type, protocol, sv );
|
||||
}
|
||||
|
||||
static __inline__ int adb_socketpair( int sv[2] )
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = unix_socketpair( AF_UNIX, SOCK_STREAM, 0, sv );
|
||||
if (rc < 0)
|
||||
return -1;
|
||||
|
||||
close_on_exec( sv[0] );
|
||||
close_on_exec( sv[1] );
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef socketpair
|
||||
#define socketpair ___xxx_socketpair
|
||||
|
||||
static __inline__ void adb_sleep_ms( int mseconds )
|
||||
{
|
||||
usleep( mseconds*1000 );
|
||||
}
|
||||
|
||||
static __inline__ int adb_mkdir(const char* path, int mode)
|
||||
{
|
||||
return mkdir(path, mode);
|
||||
}
|
||||
#undef mkdir
|
||||
#define mkdir ___xxx_mkdir
|
||||
|
||||
static __inline__ void adb_sysdeps_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
static __inline__ char* adb_dirstart(const char* path)
|
||||
{
|
||||
return strchr(path, '/');
|
||||
}
|
||||
|
||||
static __inline__ char* adb_dirstop(const char* path)
|
||||
{
|
||||
return strrchr(path, '/');
|
||||
}
|
||||
|
||||
static __inline__ int adb_is_absolute_host_path( const char* path )
|
||||
{
|
||||
return path[0] == '/';
|
||||
}
|
||||
|
||||
static __inline__ unsigned long adb_thread_id()
|
||||
{
|
||||
return (unsigned long)pthread_self();
|
||||
}
|
||||
|
||||
#endif /* !_WIN32 */
|
||||
|
||||
#endif /* _ADB_SYSDEPS_H */
|
||||
3053
sysdeps_win32.cpp
Normal file
3053
sysdeps_win32.cpp
Normal file
File diff suppressed because it is too large
Load Diff
68
test_track_devices.cpp
Normal file
68
test_track_devices.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
// TODO: replace this with a shell/python script.
|
||||
|
||||
/* a simple test program, connects to ADB server, and opens a track-devices session */
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <memory.h>
|
||||
|
||||
#include <base/file.h>
|
||||
|
||||
static void
|
||||
panic( const char* msg )
|
||||
{
|
||||
fprintf(stderr, "PANIC: %s: %s\n", msg, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
const char* request = "host:track-devices";
|
||||
|
||||
if (argv[1] && strcmp(argv[1], "--jdwp") == 0) {
|
||||
request = "track-jdwp";
|
||||
}
|
||||
|
||||
int ret;
|
||||
struct sockaddr_in server;
|
||||
char buffer[1024];
|
||||
|
||||
memset( &server, 0, sizeof(server) );
|
||||
server.sin_family = AF_INET;
|
||||
server.sin_port = htons(5037);
|
||||
server.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
|
||||
int s = socket( PF_INET, SOCK_STREAM, 0 );
|
||||
ret = connect( s, (struct sockaddr*) &server, sizeof(server) );
|
||||
if (ret < 0) panic( "could not connect to server" );
|
||||
|
||||
/* send the request */
|
||||
int len = snprintf(buffer, sizeof(buffer), "%04zx%s", strlen(request), request);
|
||||
if (!android::base::WriteFully(s, buffer, len))
|
||||
panic( "could not send request" );
|
||||
|
||||
/* read the OKAY answer */
|
||||
if (!android::base::ReadFully(s, buffer, 4))
|
||||
panic( "could not read request" );
|
||||
|
||||
printf( "server answer: %.*s\n", 4, buffer );
|
||||
|
||||
/* now loop */
|
||||
while (true) {
|
||||
char head[5] = "0000";
|
||||
|
||||
if (!android::base::ReadFully(s, head, 4))
|
||||
panic("could not read length");
|
||||
|
||||
int len;
|
||||
if (sscanf(head, "%04x", &len) != 1 )
|
||||
panic("could not decode length");
|
||||
|
||||
if (!android::base::ReadFully(s, buffer, len))
|
||||
panic("could not read data");
|
||||
|
||||
printf( "received header %.*s (%d bytes):\n%.*s", 4, head, len, len, buffer );
|
||||
}
|
||||
close(s);
|
||||
}
|
||||
439
tests/test_adb.py
Executable file
439
tests/test_adb.py
Executable file
@@ -0,0 +1,439 @@
|
||||
#!/usr/bin/env python2
|
||||
"""Simple conformance test for adb.
|
||||
|
||||
This script will use the available adb in path and run simple
|
||||
tests that attempt to touch all accessible attached devices.
|
||||
"""
|
||||
import hashlib
|
||||
import os
|
||||
import pipes
|
||||
import random
|
||||
import re
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
|
||||
def trace(cmd):
|
||||
"""Print debug message if tracing enabled."""
|
||||
if False:
|
||||
print >> sys.stderr, cmd
|
||||
|
||||
|
||||
def call(cmd_str):
|
||||
"""Run process and return output tuple (stdout, stderr, ret code)."""
|
||||
trace(cmd_str)
|
||||
process = subprocess.Popen(shlex.split(cmd_str),
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
stdout, stderr = process.communicate()
|
||||
return stdout, stderr, process.returncode
|
||||
|
||||
|
||||
def call_combined(cmd_str):
|
||||
"""Run process and return output tuple (stdout+stderr, ret code)."""
|
||||
trace(cmd_str)
|
||||
process = subprocess.Popen(shlex.split(cmd_str),
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT)
|
||||
stdout, _ = process.communicate()
|
||||
return stdout, process.returncode
|
||||
|
||||
|
||||
def call_checked(cmd_str):
|
||||
"""Run process and get stdout+stderr, raise an exception on trouble."""
|
||||
trace(cmd_str)
|
||||
return subprocess.check_output(shlex.split(cmd_str),
|
||||
stderr=subprocess.STDOUT)
|
||||
|
||||
|
||||
def call_checked_list(cmd_str):
|
||||
return call_checked(cmd_str).split('\n')
|
||||
|
||||
|
||||
def call_checked_list_skip(cmd_str):
|
||||
out_list = call_checked_list(cmd_str)
|
||||
|
||||
def is_init_line(line):
|
||||
if (len(line) >= 3) and (line[0] == "*") and (line[-2] == "*"):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
return [line for line in out_list if not is_init_line(line)]
|
||||
|
||||
|
||||
def get_device_list():
|
||||
output = call_checked_list_skip("adb devices")
|
||||
dev_list = []
|
||||
for line in output[1:]:
|
||||
if line.strip() == "":
|
||||
continue
|
||||
device, _ = line.split()
|
||||
dev_list.append(device)
|
||||
return dev_list
|
||||
|
||||
|
||||
def get_attached_device_count():
|
||||
return len(get_device_list())
|
||||
|
||||
|
||||
def compute_md5(string):
|
||||
hsh = hashlib.md5()
|
||||
hsh.update(string)
|
||||
return hsh.hexdigest()
|
||||
|
||||
|
||||
class HostFile(object):
|
||||
def __init__(self, handle, md5):
|
||||
self.handle = handle
|
||||
self.md5 = md5
|
||||
self.full_path = handle.name
|
||||
self.base_name = os.path.basename(self.full_path)
|
||||
|
||||
|
||||
class DeviceFile(object):
|
||||
def __init__(self, md5, full_path):
|
||||
self.md5 = md5
|
||||
self.full_path = full_path
|
||||
self.base_name = os.path.basename(self.full_path)
|
||||
|
||||
|
||||
def make_random_host_files(in_dir, num_files, rand_size=True):
|
||||
files = {}
|
||||
min_size = 1 * (1 << 10)
|
||||
max_size = 16 * (1 << 10)
|
||||
fixed_size = min_size
|
||||
|
||||
for _ in range(num_files):
|
||||
file_handle = tempfile.NamedTemporaryFile(dir=in_dir)
|
||||
|
||||
if rand_size:
|
||||
size = random.randrange(min_size, max_size, 1024)
|
||||
else:
|
||||
size = fixed_size
|
||||
rand_str = os.urandom(size)
|
||||
file_handle.write(rand_str)
|
||||
file_handle.flush()
|
||||
|
||||
md5 = compute_md5(rand_str)
|
||||
files[file_handle.name] = HostFile(file_handle, md5)
|
||||
return files
|
||||
|
||||
|
||||
def make_random_device_files(adb, in_dir, num_files, rand_size=True):
|
||||
files = {}
|
||||
min_size = 1 * (1 << 10)
|
||||
max_size = 16 * (1 << 10)
|
||||
fixed_size = min_size
|
||||
|
||||
for i in range(num_files):
|
||||
if rand_size:
|
||||
size = random.randrange(min_size, max_size, 1024)
|
||||
else:
|
||||
size = fixed_size
|
||||
|
||||
base_name = "device_tmpfile" + str(i)
|
||||
full_path = in_dir + "/" + base_name
|
||||
|
||||
adb.shell("dd if=/dev/urandom of={} bs={} count=1".format(full_path,
|
||||
size))
|
||||
dev_md5, _ = adb.shell("md5sum {}".format(full_path)).split()
|
||||
|
||||
files[full_path] = DeviceFile(dev_md5, full_path)
|
||||
return files
|
||||
|
||||
|
||||
class AdbWrapper(object):
|
||||
"""Convenience wrapper object for the adb command."""
|
||||
def __init__(self, device=None, out_dir=None):
|
||||
self.device = device
|
||||
self.out_dir = out_dir
|
||||
self.adb_cmd = "adb "
|
||||
if self.device:
|
||||
self.adb_cmd += "-s {} ".format(device)
|
||||
if self.out_dir:
|
||||
self.adb_cmd += "-p {} ".format(out_dir)
|
||||
|
||||
def shell(self, cmd):
|
||||
return call_checked(self.adb_cmd + "shell " + cmd)
|
||||
|
||||
def shell_nocheck(self, cmd):
|
||||
return call_combined(self.adb_cmd + "shell " + cmd)
|
||||
|
||||
def install(self, filename):
|
||||
return call_checked(self.adb_cmd + "install {}".format(pipes.quote(filename)))
|
||||
|
||||
def push(self, local, remote):
|
||||
return call_checked(self.adb_cmd + "push {} {}".format(local, remote))
|
||||
|
||||
def pull(self, remote, local):
|
||||
return call_checked(self.adb_cmd + "pull {} {}".format(remote, local))
|
||||
|
||||
def sync(self, directory=""):
|
||||
return call_checked(self.adb_cmd + "sync {}".format(directory))
|
||||
|
||||
def forward(self, local, remote):
|
||||
return call_checked(self.adb_cmd + "forward {} {}".format(local,
|
||||
remote))
|
||||
|
||||
def tcpip(self, port):
|
||||
return call_checked(self.adb_cmd + "tcpip {}".format(port))
|
||||
|
||||
def usb(self):
|
||||
return call_checked(self.adb_cmd + "usb")
|
||||
|
||||
def root(self):
|
||||
return call_checked(self.adb_cmd + "root")
|
||||
|
||||
def unroot(self):
|
||||
return call_checked(self.adb_cmd + "unroot")
|
||||
|
||||
def forward_remove(self, local):
|
||||
return call_checked(self.adb_cmd + "forward --remove {}".format(local))
|
||||
|
||||
def forward_remove_all(self):
|
||||
return call_checked(self.adb_cmd + "forward --remove-all")
|
||||
|
||||
def connect(self, host):
|
||||
return call_checked(self.adb_cmd + "connect {}".format(host))
|
||||
|
||||
def disconnect(self, host):
|
||||
return call_checked(self.adb_cmd + "disconnect {}".format(host))
|
||||
|
||||
def reverse(self, remote, local):
|
||||
return call_checked(self.adb_cmd + "reverse {} {}".format(remote,
|
||||
local))
|
||||
|
||||
def reverse_remove_all(self):
|
||||
return call_checked(self.adb_cmd + "reverse --remove-all")
|
||||
|
||||
def reverse_remove(self, remote):
|
||||
return call_checked(
|
||||
self.adb_cmd + "reverse --remove {}".format(remote))
|
||||
|
||||
def wait(self):
|
||||
return call_checked(self.adb_cmd + "wait-for-device")
|
||||
|
||||
|
||||
class AdbBasic(unittest.TestCase):
|
||||
def test_shell(self):
|
||||
"""Check that we can at least cat a file."""
|
||||
adb = AdbWrapper()
|
||||
out = adb.shell("cat /proc/uptime")
|
||||
self.assertEqual(len(out.split()), 2)
|
||||
self.assertGreater(float(out.split()[0]), 0.0)
|
||||
self.assertGreater(float(out.split()[1]), 0.0)
|
||||
|
||||
def test_help(self):
|
||||
"""Make sure we get _something_ out of help."""
|
||||
out = call_checked("adb help")
|
||||
self.assertTrue(len(out) > 0)
|
||||
|
||||
def test_version(self):
|
||||
"""Get a version number out of the output of adb."""
|
||||
out = call_checked("adb version").split()
|
||||
version_num = False
|
||||
for item in out:
|
||||
if re.match(r"[\d+\.]*\d", item):
|
||||
version_num = True
|
||||
self.assertTrue(version_num)
|
||||
|
||||
def _test_root(self):
|
||||
adb = AdbWrapper()
|
||||
adb.root()
|
||||
adb.wait()
|
||||
self.assertEqual("root", adb.shell("id -un").strip())
|
||||
|
||||
def _test_unroot(self):
|
||||
adb = AdbWrapper()
|
||||
adb.unroot()
|
||||
adb.wait()
|
||||
self.assertEqual("shell", adb.shell("id -un").strip())
|
||||
|
||||
def test_root_unroot(self):
|
||||
"""Make sure that adb root and adb unroot work, using id(1)."""
|
||||
adb = AdbWrapper()
|
||||
original_user = adb.shell("id -un").strip()
|
||||
try:
|
||||
if original_user == "root":
|
||||
self._test_unroot()
|
||||
self._test_root()
|
||||
elif original_user == "shell":
|
||||
self._test_root()
|
||||
self._test_unroot()
|
||||
finally:
|
||||
if original_user == "root":
|
||||
adb.root()
|
||||
else:
|
||||
adb.unroot()
|
||||
adb.wait()
|
||||
|
||||
def test_argument_escaping(self):
|
||||
"""Make sure that argument escaping is somewhat sane."""
|
||||
adb = AdbWrapper()
|
||||
|
||||
# http://b/19734868
|
||||
# Note that this actually matches ssh(1)'s behavior --- it's
|
||||
# converted to "sh -c echo hello; echo world" which sh interprets
|
||||
# as "sh -c echo" (with an argument to that shell of "hello"),
|
||||
# and then "echo world" back in the first shell.
|
||||
result = adb.shell("sh -c 'echo hello; echo world'").splitlines()
|
||||
self.assertEqual(["", "world"], result)
|
||||
# If you really wanted "hello" and "world", here's what you'd do:
|
||||
result = adb.shell("echo hello\;echo world").splitlines()
|
||||
self.assertEqual(["hello", "world"], result)
|
||||
|
||||
# http://b/15479704
|
||||
self.assertEqual('t', adb.shell("'true && echo t'").strip())
|
||||
self.assertEqual('t', adb.shell("sh -c 'true && echo t'").strip())
|
||||
|
||||
# http://b/20564385
|
||||
self.assertEqual('t', adb.shell("FOO=a BAR=b echo t").strip())
|
||||
self.assertEqual('123Linux', adb.shell("echo -n 123\;uname").strip())
|
||||
|
||||
def test_install_argument_escaping(self):
|
||||
"""Make sure that install argument escaping works."""
|
||||
adb = AdbWrapper()
|
||||
|
||||
# http://b/20323053
|
||||
tf = tempfile.NamedTemporaryFile("w", suffix="-text;ls;1.apk")
|
||||
self.assertIn("-text;ls;1.apk", adb.install(tf.name))
|
||||
|
||||
# http://b/3090932
|
||||
tf = tempfile.NamedTemporaryFile("w", suffix="-Live Hold'em.apk")
|
||||
self.assertIn("-Live Hold'em.apk", adb.install(tf.name))
|
||||
|
||||
|
||||
class AdbFile(unittest.TestCase):
|
||||
SCRATCH_DIR = "/data/local/tmp"
|
||||
DEVICE_TEMP_FILE = SCRATCH_DIR + "/adb_test_file"
|
||||
DEVICE_TEMP_DIR = SCRATCH_DIR + "/adb_test_dir"
|
||||
|
||||
def test_push(self):
|
||||
"""Push a randomly generated file to specified device."""
|
||||
kbytes = 512
|
||||
adb = AdbWrapper()
|
||||
with tempfile.NamedTemporaryFile(mode="w") as tmp:
|
||||
rand_str = os.urandom(1024 * kbytes)
|
||||
tmp.write(rand_str)
|
||||
tmp.flush()
|
||||
|
||||
host_md5 = compute_md5(rand_str)
|
||||
adb.shell_nocheck("rm -r {}".format(AdbFile.DEVICE_TEMP_FILE))
|
||||
try:
|
||||
adb.push(local=tmp.name, remote=AdbFile.DEVICE_TEMP_FILE)
|
||||
dev_md5, _ = adb.shell(
|
||||
"md5sum {}".format(AdbFile.DEVICE_TEMP_FILE)).split()
|
||||
self.assertEqual(host_md5, dev_md5)
|
||||
finally:
|
||||
adb.shell_nocheck("rm {}".format(AdbFile.DEVICE_TEMP_FILE))
|
||||
|
||||
# TODO: write push directory test.
|
||||
|
||||
def test_pull(self):
|
||||
"""Pull a randomly generated file from specified device."""
|
||||
kbytes = 512
|
||||
adb = AdbWrapper()
|
||||
adb.shell_nocheck("rm -r {}".format(AdbFile.DEVICE_TEMP_FILE))
|
||||
try:
|
||||
adb.shell("dd if=/dev/urandom of={} bs=1024 count={}".format(
|
||||
AdbFile.DEVICE_TEMP_FILE, kbytes))
|
||||
dev_md5, _ = adb.shell(
|
||||
"md5sum {}".format(AdbFile.DEVICE_TEMP_FILE)).split()
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode="w") as tmp_write:
|
||||
adb.pull(remote=AdbFile.DEVICE_TEMP_FILE, local=tmp_write.name)
|
||||
with open(tmp_write.name) as tmp_read:
|
||||
host_contents = tmp_read.read()
|
||||
host_md5 = compute_md5(host_contents)
|
||||
self.assertEqual(dev_md5, host_md5)
|
||||
finally:
|
||||
adb.shell_nocheck("rm {}".format(AdbFile.DEVICE_TEMP_FILE))
|
||||
|
||||
def test_pull_dir(self):
|
||||
"""Pull a randomly generated directory of files from the device."""
|
||||
adb = AdbWrapper()
|
||||
temp_files = {}
|
||||
host_dir = None
|
||||
try:
|
||||
# create temporary host directory
|
||||
host_dir = tempfile.mkdtemp()
|
||||
|
||||
# create temporary dir on device
|
||||
adb.shell_nocheck("rm -r {}".format(AdbFile.DEVICE_TEMP_DIR))
|
||||
adb.shell("mkdir -p {}".format(AdbFile.DEVICE_TEMP_DIR))
|
||||
|
||||
# populate device dir with random files
|
||||
temp_files = make_random_device_files(
|
||||
adb, in_dir=AdbFile.DEVICE_TEMP_DIR, num_files=32)
|
||||
|
||||
adb.pull(remote=AdbFile.DEVICE_TEMP_DIR, local=host_dir)
|
||||
|
||||
for device_full_path in temp_files:
|
||||
host_path = os.path.join(
|
||||
host_dir, temp_files[device_full_path].base_name)
|
||||
with open(host_path) as host_file:
|
||||
host_md5 = compute_md5(host_file.read())
|
||||
self.assertEqual(host_md5,
|
||||
temp_files[device_full_path].md5)
|
||||
finally:
|
||||
for dev_file in temp_files.values():
|
||||
host_path = os.path.join(host_dir, dev_file.base_name)
|
||||
os.remove(host_path)
|
||||
adb.shell_nocheck("rm -r {}".format(AdbFile.DEVICE_TEMP_DIR))
|
||||
if host_dir:
|
||||
os.removedirs(host_dir)
|
||||
|
||||
def test_sync(self):
|
||||
"""Sync a randomly generated directory of files to specified device."""
|
||||
try:
|
||||
adb = AdbWrapper()
|
||||
temp_files = {}
|
||||
|
||||
# create temporary host directory
|
||||
base_dir = tempfile.mkdtemp()
|
||||
|
||||
# create mirror device directory hierarchy within base_dir
|
||||
full_dir_path = base_dir + AdbFile.DEVICE_TEMP_DIR
|
||||
os.makedirs(full_dir_path)
|
||||
|
||||
# create 32 random files within the host mirror
|
||||
temp_files = make_random_host_files(in_dir=full_dir_path,
|
||||
num_files=32)
|
||||
|
||||
# clean up any trash on the device
|
||||
adb = AdbWrapper(out_dir=base_dir)
|
||||
adb.shell_nocheck("rm -r {}".format(AdbFile.DEVICE_TEMP_DIR))
|
||||
|
||||
# issue the sync
|
||||
adb.sync("data")
|
||||
|
||||
# confirm that every file on the device mirrors that on the host
|
||||
for host_full_path in temp_files.keys():
|
||||
device_full_path = os.path.join(
|
||||
AdbFile.DEVICE_TEMP_DIR,
|
||||
temp_files[host_full_path].base_name)
|
||||
dev_md5, _ = adb.shell(
|
||||
"md5sum {}".format(device_full_path)).split()
|
||||
self.assertEqual(temp_files[host_full_path].md5, dev_md5)
|
||||
|
||||
finally:
|
||||
adb.shell_nocheck("rm -r {}".format(AdbFile.DEVICE_TEMP_DIR))
|
||||
if temp_files:
|
||||
for tf in temp_files.values():
|
||||
tf.handle.close()
|
||||
if base_dir:
|
||||
os.removedirs(base_dir + AdbFile.DEVICE_TEMP_DIR)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
random.seed(0)
|
||||
dev_count = get_attached_device_count()
|
||||
if dev_count:
|
||||
suite = unittest.TestLoader().loadTestsFromName(__name__)
|
||||
unittest.TextTestRunner(verbosity=3).run(suite)
|
||||
else:
|
||||
print "Test suite must be run with attached devices"
|
||||
1094
transport.cpp
Normal file
1094
transport.cpp
Normal file
File diff suppressed because it is too large
Load Diff
70
transport.h
Normal file
70
transport.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
#ifndef __TRANSPORT_H
|
||||
#define __TRANSPORT_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "adb.h"
|
||||
|
||||
/*
|
||||
* Obtain a transport from the available transports.
|
||||
* If state is != CS_ANY, only transports in that state are considered.
|
||||
* If serial is non-NULL then only the device with that serial will be chosen.
|
||||
* If no suitable transport is found, error is set.
|
||||
*/
|
||||
atransport* acquire_one_transport(int state, transport_type ttype,
|
||||
const char* serial, std::string* error_out);
|
||||
void add_transport_disconnect(atransport* t, adisconnect* dis);
|
||||
void remove_transport_disconnect(atransport* t, adisconnect* dis);
|
||||
void kick_transport(atransport* t);
|
||||
void run_transport_disconnects(atransport* t);
|
||||
void update_transports(void);
|
||||
|
||||
/* transports are ref-counted
|
||||
** get_device_transport does an acquire on your behalf before returning
|
||||
*/
|
||||
void init_transport_registration(void);
|
||||
std::string list_transports(bool long_listing);
|
||||
atransport* find_transport(const char* serial);
|
||||
|
||||
void register_usb_transport(usb_handle* h, const char* serial,
|
||||
const char* devpath, unsigned writeable);
|
||||
|
||||
/* cause new transports to be init'd and added to the list */
|
||||
int register_socket_transport(int s, const char* serial, int port, int local);
|
||||
|
||||
/* this should only be used for transports with connection_state == CS_NOPERM */
|
||||
void unregister_usb_transport(usb_handle* usb);
|
||||
|
||||
/* these should only be used for the "adb disconnect" command */
|
||||
void unregister_transport(atransport* t);
|
||||
void unregister_all_tcp_transports();
|
||||
|
||||
int check_header(apacket* p);
|
||||
int check_data(apacket* p);
|
||||
|
||||
/* for MacOS X cleanup */
|
||||
void close_usb_devices();
|
||||
|
||||
void send_packet(apacket* p, atransport* t);
|
||||
|
||||
asocket* create_device_tracker(void);
|
||||
|
||||
#endif /* __TRANSPORT_H */
|
||||
422
transport_local.cpp
Normal file
422
transport_local.cpp
Normal file
@@ -0,0 +1,422 @@
|
||||
/*
|
||||
* 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 TRACE_TRANSPORT
|
||||
|
||||
#include "sysdeps.h"
|
||||
#include "transport.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <base/stringprintf.h>
|
||||
|
||||
#if !ADB_HOST
|
||||
#include "cutils/properties.h"
|
||||
#endif
|
||||
|
||||
#include "adb.h"
|
||||
#include "adb_io.h"
|
||||
|
||||
#if ADB_HOST
|
||||
/* we keep a list of opened transports. The atransport struct knows to which
|
||||
* local transport it is connected. The list is used to detect when we're
|
||||
* trying to connect twice to a given local transport.
|
||||
*/
|
||||
#define ADB_LOCAL_TRANSPORT_MAX 64
|
||||
|
||||
ADB_MUTEX_DEFINE( local_transports_lock );
|
||||
|
||||
static atransport* local_transports[ ADB_LOCAL_TRANSPORT_MAX ];
|
||||
#endif /* ADB_HOST */
|
||||
|
||||
static int remote_read(apacket *p, atransport *t)
|
||||
{
|
||||
if(!ReadFdExactly(t->sfd, &p->msg, sizeof(amessage))){
|
||||
D("remote local: read terminated (message)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(check_header(p)) {
|
||||
D("bad header: terminated (data)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!ReadFdExactly(t->sfd, p->data, p->msg.data_length)){
|
||||
D("remote local: terminated (data)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(check_data(p)) {
|
||||
D("bad data: terminated (data)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int remote_write(apacket *p, atransport *t)
|
||||
{
|
||||
int length = p->msg.data_length;
|
||||
|
||||
if(!WriteFdExactly(t->sfd, &p->msg, sizeof(amessage) + length)) {
|
||||
D("remote local: write terminated\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int local_connect(int port) {
|
||||
return local_connect_arbitrary_ports(port-1, port);
|
||||
}
|
||||
|
||||
int local_connect_arbitrary_ports(int console_port, int adb_port)
|
||||
{
|
||||
int fd = -1;
|
||||
|
||||
#if ADB_HOST
|
||||
const char *host = getenv("ADBHOST");
|
||||
if (host) {
|
||||
fd = socket_network_client(host, adb_port, SOCK_STREAM);
|
||||
}
|
||||
#endif
|
||||
if (fd < 0) {
|
||||
fd = socket_loopback_client(adb_port, SOCK_STREAM);
|
||||
}
|
||||
|
||||
if (fd >= 0) {
|
||||
D("client: connected on remote on fd %d\n", fd);
|
||||
close_on_exec(fd);
|
||||
disable_tcp_nagle(fd);
|
||||
std::string serial = android::base::StringPrintf("emulator-%d", console_port);
|
||||
register_socket_transport(fd, serial.c_str(), adb_port, 1);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static void *client_socket_thread(void *x)
|
||||
{
|
||||
#if ADB_HOST
|
||||
int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
|
||||
int count = ADB_LOCAL_TRANSPORT_MAX;
|
||||
|
||||
D("transport: client_socket_thread() starting\n");
|
||||
|
||||
/* try to connect to any number of running emulator instances */
|
||||
/* this is only done when ADB starts up. later, each new emulator */
|
||||
/* will send a message to ADB to indicate that is is starting up */
|
||||
for ( ; count > 0; count--, port += 2 ) {
|
||||
(void) local_connect(port);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *server_socket_thread(void * arg)
|
||||
{
|
||||
int serverfd, fd;
|
||||
struct sockaddr addr;
|
||||
socklen_t alen;
|
||||
int port = (int) (uintptr_t) arg;
|
||||
|
||||
D("transport: server_socket_thread() starting\n");
|
||||
serverfd = -1;
|
||||
for(;;) {
|
||||
if(serverfd == -1) {
|
||||
serverfd = socket_inaddr_any_server(port, SOCK_STREAM);
|
||||
if(serverfd < 0) {
|
||||
D("server: cannot bind socket yet: %s\n", strerror(errno));
|
||||
adb_sleep_ms(1000);
|
||||
continue;
|
||||
}
|
||||
close_on_exec(serverfd);
|
||||
}
|
||||
|
||||
alen = sizeof(addr);
|
||||
D("server: trying to get new connection from %d\n", port);
|
||||
fd = adb_socket_accept(serverfd, &addr, &alen);
|
||||
if(fd >= 0) {
|
||||
D("server: new connection on fd %d\n", fd);
|
||||
close_on_exec(fd);
|
||||
disable_tcp_nagle(fd);
|
||||
register_socket_transport(fd, "host", port, 1);
|
||||
}
|
||||
}
|
||||
D("transport: server_socket_thread() exiting\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This is relevant only for ADB daemon running inside the emulator. */
|
||||
#if !ADB_HOST
|
||||
/*
|
||||
* Redefine open and write for qemu_pipe.h that contains inlined references
|
||||
* to those routines. We will redifine them back after qemu_pipe.h inclusion.
|
||||
*/
|
||||
#undef open
|
||||
#undef write
|
||||
#define open adb_open
|
||||
#define write adb_write
|
||||
#include <hardware/qemu_pipe.h>
|
||||
#undef open
|
||||
#undef write
|
||||
#define open ___xxx_open
|
||||
#define write ___xxx_write
|
||||
|
||||
/* A worker thread that monitors host connections, and registers a transport for
|
||||
* every new host connection. This thread replaces server_socket_thread on
|
||||
* condition that adbd daemon runs inside the emulator, and emulator uses QEMUD
|
||||
* pipe to communicate with adbd daemon inside the guest. This is done in order
|
||||
* to provide more robust communication channel between ADB host and guest. The
|
||||
* main issue with server_socket_thread approach is that it runs on top of TCP,
|
||||
* and thus is sensitive to network disruptions. For instance, the
|
||||
* ConnectionManager may decide to reset all network connections, in which case
|
||||
* the connection between ADB host and guest will be lost. To make ADB traffic
|
||||
* independent from the network, we use here 'adb' QEMUD service to transfer data
|
||||
* between the host, and the guest. See external/qemu/android/adb-*.* that
|
||||
* implements the emulator's side of the protocol. Another advantage of using
|
||||
* QEMUD approach is that ADB will be up much sooner, since it doesn't depend
|
||||
* anymore on network being set up.
|
||||
* The guest side of the protocol contains the following phases:
|
||||
* - Connect with adb QEMUD service. In this phase a handle to 'adb' QEMUD service
|
||||
* is opened, and it becomes clear whether or not emulator supports that
|
||||
* protocol.
|
||||
* - Wait for the ADB host to create connection with the guest. This is done by
|
||||
* sending an 'accept' request to the adb QEMUD service, and waiting on
|
||||
* response.
|
||||
* - When new ADB host connection is accepted, the connection with adb QEMUD
|
||||
* service is registered as the transport, and a 'start' request is sent to the
|
||||
* adb QEMUD service, indicating that the guest is ready to receive messages.
|
||||
* Note that the guest will ignore messages sent down from the emulator before
|
||||
* the transport registration is completed. That's why we need to send the
|
||||
* 'start' request after the transport is registered.
|
||||
*/
|
||||
static void *qemu_socket_thread(void * arg)
|
||||
{
|
||||
/* 'accept' request to the adb QEMUD service. */
|
||||
static const char _accept_req[] = "accept";
|
||||
/* 'start' request to the adb QEMUD service. */
|
||||
static const char _start_req[] = "start";
|
||||
/* 'ok' reply from the adb QEMUD service. */
|
||||
static const char _ok_resp[] = "ok";
|
||||
|
||||
const int port = (int) (uintptr_t) arg;
|
||||
int res, fd;
|
||||
char tmp[256];
|
||||
char con_name[32];
|
||||
|
||||
D("transport: qemu_socket_thread() starting\n");
|
||||
|
||||
/* adb QEMUD service connection request. */
|
||||
snprintf(con_name, sizeof(con_name), "qemud:adb:%d", port);
|
||||
|
||||
/* Connect to the adb QEMUD service. */
|
||||
fd = qemu_pipe_open(con_name);
|
||||
if (fd < 0) {
|
||||
/* This could be an older version of the emulator, that doesn't
|
||||
* implement adb QEMUD service. Fall back to the old TCP way. */
|
||||
adb_thread_t thr;
|
||||
D("adb service is not available. Falling back to TCP socket.\n");
|
||||
adb_thread_create(&thr, server_socket_thread, arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for(;;) {
|
||||
/*
|
||||
* Wait till the host creates a new connection.
|
||||
*/
|
||||
|
||||
/* Send the 'accept' request. */
|
||||
res = adb_write(fd, _accept_req, strlen(_accept_req));
|
||||
if ((size_t)res == strlen(_accept_req)) {
|
||||
/* Wait for the response. In the response we expect 'ok' on success,
|
||||
* or 'ko' on failure. */
|
||||
res = adb_read(fd, tmp, sizeof(tmp));
|
||||
if (res != 2 || memcmp(tmp, _ok_resp, 2)) {
|
||||
D("Accepting ADB host connection has failed.\n");
|
||||
adb_close(fd);
|
||||
} else {
|
||||
/* Host is connected. Register the transport, and start the
|
||||
* exchange. */
|
||||
register_socket_transport(fd, "host", port, 1);
|
||||
adb_write(fd, _start_req, strlen(_start_req));
|
||||
}
|
||||
|
||||
/* Prepare for accepting of the next ADB host connection. */
|
||||
fd = qemu_pipe_open(con_name);
|
||||
if (fd < 0) {
|
||||
D("adb service become unavailable.\n");
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
D("Unable to send the '%s' request to ADB service.\n", _accept_req);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
D("transport: qemu_socket_thread() exiting\n");
|
||||
return 0;
|
||||
}
|
||||
#endif // !ADB_HOST
|
||||
|
||||
void local_init(int port)
|
||||
{
|
||||
adb_thread_t thr;
|
||||
void* (*func)(void *);
|
||||
|
||||
if(HOST) {
|
||||
func = client_socket_thread;
|
||||
} else {
|
||||
#if ADB_HOST
|
||||
func = server_socket_thread;
|
||||
#else
|
||||
/* For the adbd daemon in the system image we need to distinguish
|
||||
* between the device, and the emulator. */
|
||||
char is_qemu[PROPERTY_VALUE_MAX];
|
||||
property_get("ro.kernel.qemu", is_qemu, "");
|
||||
if (!strcmp(is_qemu, "1")) {
|
||||
/* Running inside the emulator: use QEMUD pipe as the transport. */
|
||||
func = qemu_socket_thread;
|
||||
} else {
|
||||
/* Running inside the device: use TCP socket as the transport. */
|
||||
func = server_socket_thread;
|
||||
}
|
||||
#endif // !ADB_HOST
|
||||
}
|
||||
|
||||
D("transport: local %s init\n", HOST ? "client" : "server");
|
||||
|
||||
if(adb_thread_create(&thr, func, (void *) (uintptr_t) port)) {
|
||||
fatal_errno("cannot create local socket %s thread",
|
||||
HOST ? "client" : "server");
|
||||
}
|
||||
}
|
||||
|
||||
static void remote_kick(atransport *t)
|
||||
{
|
||||
int fd = t->sfd;
|
||||
t->sfd = -1;
|
||||
adb_shutdown(fd);
|
||||
adb_close(fd);
|
||||
|
||||
#if ADB_HOST
|
||||
if(HOST) {
|
||||
int nn;
|
||||
adb_mutex_lock( &local_transports_lock );
|
||||
for (nn = 0; nn < ADB_LOCAL_TRANSPORT_MAX; nn++) {
|
||||
if (local_transports[nn] == t) {
|
||||
local_transports[nn] = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
adb_mutex_unlock( &local_transports_lock );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void remote_close(atransport *t)
|
||||
{
|
||||
adb_close(t->fd);
|
||||
}
|
||||
|
||||
|
||||
#if ADB_HOST
|
||||
/* Only call this function if you already hold local_transports_lock. */
|
||||
atransport* find_emulator_transport_by_adb_port_locked(int adb_port)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ADB_LOCAL_TRANSPORT_MAX; i++) {
|
||||
if (local_transports[i] && local_transports[i]->adb_port == adb_port) {
|
||||
return local_transports[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
atransport* find_emulator_transport_by_adb_port(int adb_port)
|
||||
{
|
||||
adb_mutex_lock( &local_transports_lock );
|
||||
atransport* result = find_emulator_transport_by_adb_port_locked(adb_port);
|
||||
adb_mutex_unlock( &local_transports_lock );
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Only call this function if you already hold local_transports_lock. */
|
||||
int get_available_local_transport_index_locked()
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ADB_LOCAL_TRANSPORT_MAX; i++) {
|
||||
if (local_transports[i] == NULL) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int get_available_local_transport_index()
|
||||
{
|
||||
adb_mutex_lock( &local_transports_lock );
|
||||
int result = get_available_local_transport_index_locked();
|
||||
adb_mutex_unlock( &local_transports_lock );
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
int init_socket_transport(atransport *t, int s, int adb_port, int local)
|
||||
{
|
||||
int fail = 0;
|
||||
|
||||
t->kick = remote_kick;
|
||||
t->close = remote_close;
|
||||
t->read_from_remote = remote_read;
|
||||
t->write_to_remote = remote_write;
|
||||
t->sfd = s;
|
||||
t->sync_token = 1;
|
||||
t->connection_state = CS_OFFLINE;
|
||||
t->type = kTransportLocal;
|
||||
t->adb_port = 0;
|
||||
|
||||
#if ADB_HOST
|
||||
if (HOST && local) {
|
||||
adb_mutex_lock( &local_transports_lock );
|
||||
{
|
||||
t->adb_port = adb_port;
|
||||
atransport* existing_transport =
|
||||
find_emulator_transport_by_adb_port_locked(adb_port);
|
||||
int index = get_available_local_transport_index_locked();
|
||||
if (existing_transport != NULL) {
|
||||
D("local transport for port %d already registered (%p)?\n",
|
||||
adb_port, existing_transport);
|
||||
fail = -1;
|
||||
} else if (index < 0) {
|
||||
// Too many emulators.
|
||||
D("cannot register more emulators. Maximum is %d\n",
|
||||
ADB_LOCAL_TRANSPORT_MAX);
|
||||
fail = -1;
|
||||
} else {
|
||||
local_transports[index] = t;
|
||||
}
|
||||
}
|
||||
adb_mutex_unlock( &local_transports_lock );
|
||||
}
|
||||
#endif
|
||||
return fail;
|
||||
}
|
||||
53
transport_test.cpp
Normal file
53
transport_test.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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 "transport.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "adb.h"
|
||||
|
||||
TEST(transport, kick_transport) {
|
||||
atransport t = {};
|
||||
// Mutate some member so we can test that the function is run.
|
||||
t.kick = [](atransport* trans) { trans->fd = 42; };
|
||||
atransport expected = t;
|
||||
expected.fd = 42;
|
||||
expected.kicked = 1;
|
||||
kick_transport(&t);
|
||||
ASSERT_EQ(42, t.fd);
|
||||
ASSERT_EQ(1, t.kicked);
|
||||
ASSERT_EQ(0, memcmp(&expected, &t, sizeof(atransport)));
|
||||
}
|
||||
|
||||
TEST(transport, kick_transport_already_kicked) {
|
||||
// Ensure that the transport is not modified if the transport has already been
|
||||
// kicked.
|
||||
atransport t = {};
|
||||
t.kicked = 1;
|
||||
t.kick = [](atransport*) { FAIL() << "Kick should not have been called"; };
|
||||
atransport expected = t;
|
||||
kick_transport(&t);
|
||||
ASSERT_EQ(0, memcmp(&expected, &t, sizeof(atransport)));
|
||||
}
|
||||
|
||||
// Disabled because the function currently segfaults for a zeroed atransport. I
|
||||
// want to make sure I understand how this is working at all before I try fixing
|
||||
// that.
|
||||
TEST(transport, DISABLED_run_transport_disconnects_zeroed_atransport) {
|
||||
atransport t = {};
|
||||
run_transport_disconnects(&t);
|
||||
}
|
||||
107
transport_usb.cpp
Normal file
107
transport_usb.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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 TRACE_TRANSPORT
|
||||
|
||||
#include "sysdeps.h"
|
||||
#include "transport.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "adb.h"
|
||||
|
||||
static int remote_read(apacket *p, atransport *t)
|
||||
{
|
||||
if(usb_read(t->usb, &p->msg, sizeof(amessage))){
|
||||
D("remote usb: read terminated (message)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(check_header(p)) {
|
||||
D("remote usb: check_header failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(p->msg.data_length) {
|
||||
if(usb_read(t->usb, p->data, p->msg.data_length)){
|
||||
D("remote usb: terminated (data)\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if(check_data(p)) {
|
||||
D("remote usb: check_data failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int remote_write(apacket *p, atransport *t)
|
||||
{
|
||||
unsigned size = p->msg.data_length;
|
||||
|
||||
if(usb_write(t->usb, &p->msg, sizeof(amessage))) {
|
||||
D("remote usb: 1 - write terminated\n");
|
||||
return -1;
|
||||
}
|
||||
if(p->msg.data_length == 0) return 0;
|
||||
if(usb_write(t->usb, &p->data, size)) {
|
||||
D("remote usb: 2 - write terminated\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void remote_close(atransport *t)
|
||||
{
|
||||
usb_close(t->usb);
|
||||
t->usb = 0;
|
||||
}
|
||||
|
||||
static void remote_kick(atransport *t)
|
||||
{
|
||||
usb_kick(t->usb);
|
||||
}
|
||||
|
||||
void init_usb_transport(atransport *t, usb_handle *h, int state)
|
||||
{
|
||||
D("transport: usb\n");
|
||||
t->close = remote_close;
|
||||
t->kick = remote_kick;
|
||||
t->read_from_remote = remote_read;
|
||||
t->write_to_remote = remote_write;
|
||||
t->sync_token = 1;
|
||||
t->connection_state = state;
|
||||
t->type = kTransportUsb;
|
||||
t->usb = h;
|
||||
|
||||
#if ADB_HOST
|
||||
HOST = 1;
|
||||
#else
|
||||
HOST = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if ADB_HOST
|
||||
int is_adb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_protocol)
|
||||
{
|
||||
return (usb_class == ADB_CLASS && usb_subclass == ADB_SUBCLASS && usb_protocol == ADB_PROTOCOL);
|
||||
}
|
||||
#endif
|
||||
684
usb_linux.cpp
Normal file
684
usb_linux.cpp
Normal file
@@ -0,0 +1,684 @@
|
||||
/*
|
||||
* 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 TRACE_USB
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.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/types.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
|
||||
#include <base/file.h>
|
||||
#include <base/stringprintf.h>
|
||||
#include <base/strings.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "transport.h"
|
||||
|
||||
/* usb scan debugging is waaaay too verbose */
|
||||
#define DBGX(x...)
|
||||
|
||||
ADB_MUTEX_DEFINE( usb_lock );
|
||||
|
||||
struct usb_handle
|
||||
{
|
||||
usb_handle *prev;
|
||||
usb_handle *next;
|
||||
|
||||
char fname[64];
|
||||
int desc;
|
||||
unsigned char ep_in;
|
||||
unsigned char ep_out;
|
||||
|
||||
unsigned zero_mask;
|
||||
unsigned writeable;
|
||||
|
||||
struct usbdevfs_urb urb_in;
|
||||
struct usbdevfs_urb urb_out;
|
||||
|
||||
int urb_in_busy;
|
||||
int urb_out_busy;
|
||||
int dead;
|
||||
|
||||
adb_cond_t notify;
|
||||
adb_mutex_t lock;
|
||||
|
||||
// for garbage collecting disconnected devices
|
||||
int mark;
|
||||
|
||||
// ID of thread currently in REAPURB
|
||||
pthread_t reaper_thread;
|
||||
};
|
||||
|
||||
static usb_handle handle_list = {
|
||||
.prev = &handle_list,
|
||||
.next = &handle_list,
|
||||
};
|
||||
|
||||
static int known_device(const char *dev_name)
|
||||
{
|
||||
usb_handle *usb;
|
||||
|
||||
adb_mutex_lock(&usb_lock);
|
||||
for(usb = handle_list.next; usb != &handle_list; usb = usb->next){
|
||||
if(!strcmp(usb->fname, dev_name)) {
|
||||
// set mark flag to indicate this device is still alive
|
||||
usb->mark = 1;
|
||||
adb_mutex_unlock(&usb_lock);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
adb_mutex_unlock(&usb_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kick_disconnected_devices()
|
||||
{
|
||||
usb_handle *usb;
|
||||
|
||||
adb_mutex_lock(&usb_lock);
|
||||
// kick any devices in the device list that were not found in the device scan
|
||||
for(usb = handle_list.next; usb != &handle_list; usb = usb->next){
|
||||
if (usb->mark == 0) {
|
||||
usb_kick(usb);
|
||||
} else {
|
||||
usb->mark = 0;
|
||||
}
|
||||
}
|
||||
adb_mutex_unlock(&usb_lock);
|
||||
|
||||
}
|
||||
|
||||
static inline int badname(const char *name)
|
||||
{
|
||||
while(*name) {
|
||||
if(!isdigit(*name++)) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void find_usb_device(const char *base,
|
||||
void (*register_device_callback)
|
||||
(const char *, const char *, unsigned char, unsigned char, int, int, unsigned))
|
||||
{
|
||||
char busname[32], devname[32];
|
||||
unsigned char local_ep_in, local_ep_out;
|
||||
DIR *busdir , *devdir ;
|
||||
struct dirent *de;
|
||||
int fd ;
|
||||
|
||||
busdir = opendir(base);
|
||||
if(busdir == 0) return;
|
||||
|
||||
while((de = readdir(busdir)) != 0) {
|
||||
if(badname(de->d_name)) continue;
|
||||
|
||||
snprintf(busname, sizeof busname, "%s/%s", base, de->d_name);
|
||||
devdir = opendir(busname);
|
||||
if(devdir == 0) continue;
|
||||
|
||||
// DBGX("[ scanning %s ]\n", busname);
|
||||
while((de = readdir(devdir))) {
|
||||
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;
|
||||
unsigned vid, pid;
|
||||
size_t desclength;
|
||||
|
||||
if(badname(de->d_name)) continue;
|
||||
snprintf(devname, sizeof devname, "%s/%s", busname, de->d_name);
|
||||
|
||||
if(known_device(devname)) {
|
||||
DBGX("skipping %s\n", devname);
|
||||
continue;
|
||||
}
|
||||
|
||||
// DBGX("[ scanning %s ]\n", devname);
|
||||
if((fd = unix_open(devname, O_RDONLY | O_CLOEXEC)) < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
desclength = adb_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\n", desclength);
|
||||
adb_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)) {
|
||||
adb_close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
vid = device->idVendor;
|
||||
pid = device->idProduct;
|
||||
DBGX("[ %s is V:%04x P:%04x ]\n", devname, 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\n");
|
||||
adb_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\n");
|
||||
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(vid, pid, interface->bInterfaceClass,
|
||||
interface->bInterfaceSubClass, interface->bInterfaceProtocol)) {
|
||||
|
||||
struct stat st;
|
||||
char pathbuf[128];
|
||||
char link[256];
|
||||
char *devpath = NULL;
|
||||
|
||||
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\n");
|
||||
break;
|
||||
}
|
||||
|
||||
// both endpoints should be bulk
|
||||
if (ep1->bmAttributes != USB_ENDPOINT_XFER_BULK ||
|
||||
ep2->bmAttributes != USB_ENDPOINT_XFER_BULK) {
|
||||
D("bulk endpoints not found\n");
|
||||
continue;
|
||||
}
|
||||
/* aproto 01 needs 0 termination */
|
||||
if(interface->bInterfaceProtocol == 0x01) {
|
||||
zero_mask = ep1->wMaxPacketSize - 1;
|
||||
}
|
||||
|
||||
// we have a match. now we just need to figure out which is in and which is 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)) {
|
||||
char *slash;
|
||||
ssize_t link_len;
|
||||
snprintf(pathbuf, sizeof(pathbuf), "/sys/dev/char/%d:%d",
|
||||
major(st.st_rdev), minor(st.st_rdev));
|
||||
link_len = readlink(pathbuf, link, sizeof(link) - 1);
|
||||
if (link_len > 0) {
|
||||
link[link_len] = '\0';
|
||||
slash = strrchr(link, '/');
|
||||
if (slash) {
|
||||
snprintf(pathbuf, sizeof(pathbuf),
|
||||
"usb:%s", slash + 1);
|
||||
devpath = pathbuf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
register_device_callback(devname, devpath,
|
||||
local_ep_in, local_ep_out,
|
||||
interface->bInterfaceNumber, device->iSerialNumber, zero_mask);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
bufptr += length;
|
||||
}
|
||||
} // end of while
|
||||
|
||||
adb_close(fd);
|
||||
} // end of devdir while
|
||||
closedir(devdir);
|
||||
} //end of busdir while
|
||||
closedir(busdir);
|
||||
}
|
||||
|
||||
void usb_cleanup()
|
||||
{
|
||||
}
|
||||
|
||||
static int usb_bulk_write(usb_handle *h, const void *data, int len)
|
||||
{
|
||||
struct usbdevfs_urb *urb = &h->urb_out;
|
||||
int res;
|
||||
struct timeval tv;
|
||||
struct timespec ts;
|
||||
|
||||
memset(urb, 0, sizeof(*urb));
|
||||
urb->type = USBDEVFS_URB_TYPE_BULK;
|
||||
urb->endpoint = h->ep_out;
|
||||
urb->status = -1;
|
||||
urb->buffer = (void*) data;
|
||||
urb->buffer_length = len;
|
||||
|
||||
D("++ write ++\n");
|
||||
|
||||
adb_mutex_lock(&h->lock);
|
||||
if(h->dead) {
|
||||
res = -1;
|
||||
goto fail;
|
||||
}
|
||||
do {
|
||||
res = ioctl(h->desc, USBDEVFS_SUBMITURB, urb);
|
||||
} while((res < 0) && (errno == EINTR));
|
||||
|
||||
if(res < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
res = -1;
|
||||
h->urb_out_busy = 1;
|
||||
for(;;) {
|
||||
/* time out after five seconds */
|
||||
gettimeofday(&tv, NULL);
|
||||
ts.tv_sec = tv.tv_sec + 5;
|
||||
ts.tv_nsec = tv.tv_usec * 1000L;
|
||||
res = pthread_cond_timedwait(&h->notify, &h->lock, &ts);
|
||||
if(res < 0 || h->dead) {
|
||||
break;
|
||||
}
|
||||
if(h->urb_out_busy == 0) {
|
||||
if(urb->status == 0) {
|
||||
res = urb->actual_length;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
fail:
|
||||
adb_mutex_unlock(&h->lock);
|
||||
D("-- write --\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
static int usb_bulk_read(usb_handle *h, void *data, int len)
|
||||
{
|
||||
struct usbdevfs_urb *urb = &h->urb_in;
|
||||
struct usbdevfs_urb *out = NULL;
|
||||
int res;
|
||||
|
||||
D("++ usb_bulk_read ++\n");
|
||||
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;
|
||||
|
||||
|
||||
adb_mutex_lock(&h->lock);
|
||||
if(h->dead) {
|
||||
res = -1;
|
||||
goto fail;
|
||||
}
|
||||
do {
|
||||
res = ioctl(h->desc, USBDEVFS_SUBMITURB, urb);
|
||||
} while((res < 0) && (errno == EINTR));
|
||||
|
||||
if(res < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
h->urb_in_busy = 1;
|
||||
for(;;) {
|
||||
D("[ reap urb - wait ]\n");
|
||||
h->reaper_thread = pthread_self();
|
||||
adb_mutex_unlock(&h->lock);
|
||||
res = ioctl(h->desc, USBDEVFS_REAPURB, &out);
|
||||
int saved_errno = errno;
|
||||
adb_mutex_lock(&h->lock);
|
||||
h->reaper_thread = 0;
|
||||
if(h->dead) {
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
if(res < 0) {
|
||||
if(saved_errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
D("[ reap urb - error ]\n");
|
||||
break;
|
||||
}
|
||||
D("[ urb @%p status = %d, actual = %d ]\n",
|
||||
out, out->status, out->actual_length);
|
||||
|
||||
if(out == &h->urb_in) {
|
||||
D("[ reap urb - IN complete ]\n");
|
||||
h->urb_in_busy = 0;
|
||||
if(urb->status == 0) {
|
||||
res = urb->actual_length;
|
||||
} else {
|
||||
res = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if(out == &h->urb_out) {
|
||||
D("[ reap urb - OUT compelete ]\n");
|
||||
h->urb_out_busy = 0;
|
||||
adb_cond_broadcast(&h->notify);
|
||||
}
|
||||
}
|
||||
fail:
|
||||
adb_mutex_unlock(&h->lock);
|
||||
D("-- usb_bulk_read --\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
int usb_write(usb_handle *h, const void *_data, int len)
|
||||
{
|
||||
unsigned char *data = (unsigned char*) _data;
|
||||
int n;
|
||||
int need_zero = 0;
|
||||
|
||||
D("++ usb_write ++\n");
|
||||
if(h->zero_mask) {
|
||||
/* if we need 0-markers and our transfer
|
||||
** is an even multiple of the packet size,
|
||||
** we make note of it
|
||||
*/
|
||||
if(!(len & h->zero_mask)) {
|
||||
need_zero = 1;
|
||||
}
|
||||
}
|
||||
|
||||
while(len > 0) {
|
||||
int xfer = (len > 4096) ? 4096 : len;
|
||||
|
||||
n = usb_bulk_write(h, data, xfer);
|
||||
if(n != xfer) {
|
||||
D("ERROR: n = %d, errno = %d (%s)\n",
|
||||
n, errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
len -= xfer;
|
||||
data += xfer;
|
||||
}
|
||||
|
||||
if(need_zero){
|
||||
n = usb_bulk_write(h, _data, 0);
|
||||
return n;
|
||||
}
|
||||
|
||||
D("-- usb_write --\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_read(usb_handle *h, void *_data, int len)
|
||||
{
|
||||
unsigned char *data = (unsigned char*) _data;
|
||||
int n;
|
||||
|
||||
D("++ usb_read ++\n");
|
||||
while(len > 0) {
|
||||
int xfer = (len > 4096) ? 4096 : len;
|
||||
|
||||
D("[ usb read %d fd = %d], fname=%s\n", xfer, h->desc, h->fname);
|
||||
n = usb_bulk_read(h, data, xfer);
|
||||
D("[ usb read %d ] = %d, fname=%s\n", xfer, n, h->fname);
|
||||
if(n != xfer) {
|
||||
if((errno == ETIMEDOUT) && (h->desc != -1)) {
|
||||
D("[ timeout ]\n");
|
||||
if(n > 0){
|
||||
data += n;
|
||||
len -= n;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
D("ERROR: n = %d, errno = %d (%s)\n",
|
||||
n, errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
len -= xfer;
|
||||
data += xfer;
|
||||
}
|
||||
|
||||
D("-- usb_read --\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_kick(usb_handle *h)
|
||||
{
|
||||
D("[ kicking %p (fd = %d) ]\n", h, h->desc);
|
||||
adb_mutex_lock(&h->lock);
|
||||
if(h->dead == 0) {
|
||||
h->dead = 1;
|
||||
|
||||
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->desc, USBDEVFS_DISCARDURB, &h->urb_in);
|
||||
ioctl(h->desc, USBDEVFS_DISCARDURB, &h->urb_out);
|
||||
h->urb_in.status = -ENODEV;
|
||||
h->urb_out.status = -ENODEV;
|
||||
h->urb_in_busy = 0;
|
||||
h->urb_out_busy = 0;
|
||||
adb_cond_broadcast(&h->notify);
|
||||
} else {
|
||||
unregister_usb_transport(h);
|
||||
}
|
||||
}
|
||||
adb_mutex_unlock(&h->lock);
|
||||
}
|
||||
|
||||
int usb_close(usb_handle *h)
|
||||
{
|
||||
D("++ usb close ++\n");
|
||||
adb_mutex_lock(&usb_lock);
|
||||
h->next->prev = h->prev;
|
||||
h->prev->next = h->next;
|
||||
h->prev = 0;
|
||||
h->next = 0;
|
||||
|
||||
adb_close(h->desc);
|
||||
D("-- usb closed %p (fd = %d) --\n", h, h->desc);
|
||||
adb_mutex_unlock(&usb_lock);
|
||||
|
||||
free(h);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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) {
|
||||
// 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 'o handles with a matching name, we
|
||||
// have no further work to do.
|
||||
adb_mutex_lock(&usb_lock);
|
||||
for (usb_handle* usb = handle_list.next; usb != &handle_list; usb = usb->next) {
|
||||
if (!strcmp(usb->fname, dev_name)) {
|
||||
adb_mutex_unlock(&usb_lock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
adb_mutex_unlock(&usb_lock);
|
||||
|
||||
D("[ usb located new device %s (%d/%d/%d) ]\n", dev_name, ep_in, ep_out, interface);
|
||||
usb_handle* usb = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
|
||||
if (usb == nullptr) fatal("couldn't allocate usb_handle");
|
||||
strcpy(usb->fname, dev_name);
|
||||
usb->ep_in = ep_in;
|
||||
usb->ep_out = ep_out;
|
||||
usb->zero_mask = zero_mask;
|
||||
usb->writeable = 1;
|
||||
|
||||
adb_cond_init(&usb->notify, 0);
|
||||
adb_mutex_init(&usb->lock, 0);
|
||||
// Initialize mark to 1 so we don't get garbage collected after the device
|
||||
// scan.
|
||||
usb->mark = 1;
|
||||
usb->reaper_thread = 0;
|
||||
|
||||
usb->desc = unix_open(usb->fname, O_RDWR | O_CLOEXEC);
|
||||
if (usb->desc == -1) {
|
||||
// Opening RW failed, so see if we have RO access.
|
||||
usb->desc = unix_open(usb->fname, O_RDONLY | O_CLOEXEC);
|
||||
if (usb->desc == -1) {
|
||||
D("[ usb open %s failed: %s]\n", usb->fname, strerror(errno));
|
||||
free(usb);
|
||||
return;
|
||||
}
|
||||
usb->writeable = 0;
|
||||
}
|
||||
|
||||
D("[ usb opened %s%s, fd=%d]\n", usb->fname,
|
||||
(usb->writeable ? "" : " (read-only)"), usb->desc);
|
||||
|
||||
if (usb->writeable) {
|
||||
if (ioctl(usb->desc, USBDEVFS_CLAIMINTERFACE, &interface) != 0) {
|
||||
D("[ usb ioctl(%d, USBDEVFS_CLAIMINTERFACE) failed: %s]\n",
|
||||
usb->desc, strerror(errno));
|
||||
adb_close(usb->desc);
|
||||
free(usb);
|
||||
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 ]\n", 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.
|
||||
adb_mutex_lock(&usb_lock);
|
||||
usb->next = &handle_list;
|
||||
usb->prev = handle_list.prev;
|
||||
usb->prev->next = usb;
|
||||
usb->next->prev = usb;
|
||||
adb_mutex_unlock(&usb_lock);
|
||||
|
||||
register_usb_transport(usb, serial.c_str(), dev_path, usb->writeable);
|
||||
}
|
||||
|
||||
static void* device_poll_thread(void* unused) {
|
||||
D("Created device thread\n");
|
||||
while (true) {
|
||||
// TODO: Use inotify.
|
||||
find_usb_device("/dev/bus/usb", register_device);
|
||||
kick_disconnected_devices();
|
||||
sleep(1);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void sigalrm_handler(int signo) {
|
||||
// don't need to do anything here
|
||||
}
|
||||
|
||||
void usb_init()
|
||||
{
|
||||
adb_thread_t tid;
|
||||
struct sigaction actions;
|
||||
|
||||
memset(&actions, 0, sizeof(actions));
|
||||
sigemptyset(&actions.sa_mask);
|
||||
actions.sa_flags = 0;
|
||||
actions.sa_handler = sigalrm_handler;
|
||||
sigaction(SIGALRM,& actions, NULL);
|
||||
|
||||
if(adb_thread_create(&tid, device_poll_thread, NULL)){
|
||||
fatal_errno("cannot create input thread");
|
||||
}
|
||||
}
|
||||
569
usb_linux_client.cpp
Normal file
569
usb_linux_client.cpp
Normal file
@@ -0,0 +1,569 @@
|
||||
/*
|
||||
* 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 TRACE_USB
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#include <cutils/properties.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/functionfs.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "transport.h"
|
||||
|
||||
#define MAX_PACKET_SIZE_FS 64
|
||||
#define MAX_PACKET_SIZE_HS 512
|
||||
#define MAX_PACKET_SIZE_SS 1024
|
||||
|
||||
#define cpu_to_le16(x) htole16(x)
|
||||
#define cpu_to_le32(x) htole32(x)
|
||||
|
||||
struct usb_handle
|
||||
{
|
||||
adb_cond_t notify;
|
||||
adb_mutex_t lock;
|
||||
|
||||
int (*write)(usb_handle *h, const void *data, int len);
|
||||
int (*read)(usb_handle *h, void *data, int len);
|
||||
void (*kick)(usb_handle *h);
|
||||
|
||||
// Legacy f_adb
|
||||
int fd;
|
||||
|
||||
// FunctionFS
|
||||
int control;
|
||||
int bulk_out; /* "out" from the host's perspective => source for adbd */
|
||||
int bulk_in; /* "in" from the host's perspective => sink for adbd */
|
||||
};
|
||||
|
||||
struct func_desc {
|
||||
struct usb_interface_descriptor intf;
|
||||
struct usb_endpoint_descriptor_no_audio source;
|
||||
struct usb_endpoint_descriptor_no_audio sink;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ss_func_desc {
|
||||
struct usb_interface_descriptor intf;
|
||||
struct usb_endpoint_descriptor_no_audio source;
|
||||
struct usb_ss_ep_comp_descriptor source_comp;
|
||||
struct usb_endpoint_descriptor_no_audio sink;
|
||||
struct usb_ss_ep_comp_descriptor sink_comp;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct desc_v1 {
|
||||
struct usb_functionfs_descs_head_v1 {
|
||||
__le32 magic;
|
||||
__le32 length;
|
||||
__le32 fs_count;
|
||||
__le32 hs_count;
|
||||
} __attribute__((packed)) header;
|
||||
struct func_desc fs_descs, hs_descs;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct desc_v2 {
|
||||
struct usb_functionfs_descs_head_v2 header;
|
||||
// The rest of the structure depends on the flags in the header.
|
||||
__le32 fs_count;
|
||||
__le32 hs_count;
|
||||
__le32 ss_count;
|
||||
struct func_desc fs_descs, hs_descs;
|
||||
struct ss_func_desc ss_descs;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct func_desc fs_descriptors = {
|
||||
.intf = {
|
||||
.bLength = sizeof(fs_descriptors.intf),
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bInterfaceNumber = 0,
|
||||
.bNumEndpoints = 2,
|
||||
.bInterfaceClass = ADB_CLASS,
|
||||
.bInterfaceSubClass = ADB_SUBCLASS,
|
||||
.bInterfaceProtocol = ADB_PROTOCOL,
|
||||
.iInterface = 1, /* first string from the provided table */
|
||||
},
|
||||
.source = {
|
||||
.bLength = sizeof(fs_descriptors.source),
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = 1 | USB_DIR_OUT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = MAX_PACKET_SIZE_FS,
|
||||
},
|
||||
.sink = {
|
||||
.bLength = sizeof(fs_descriptors.sink),
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = 2 | USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = MAX_PACKET_SIZE_FS,
|
||||
},
|
||||
};
|
||||
|
||||
struct func_desc hs_descriptors = {
|
||||
.intf = {
|
||||
.bLength = sizeof(hs_descriptors.intf),
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bInterfaceNumber = 0,
|
||||
.bNumEndpoints = 2,
|
||||
.bInterfaceClass = ADB_CLASS,
|
||||
.bInterfaceSubClass = ADB_SUBCLASS,
|
||||
.bInterfaceProtocol = ADB_PROTOCOL,
|
||||
.iInterface = 1, /* first string from the provided table */
|
||||
},
|
||||
.source = {
|
||||
.bLength = sizeof(hs_descriptors.source),
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = 1 | USB_DIR_OUT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = MAX_PACKET_SIZE_HS,
|
||||
},
|
||||
.sink = {
|
||||
.bLength = sizeof(hs_descriptors.sink),
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = 2 | USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = MAX_PACKET_SIZE_HS,
|
||||
},
|
||||
};
|
||||
|
||||
static struct ss_func_desc ss_descriptors = {
|
||||
.intf = {
|
||||
.bLength = sizeof(ss_descriptors.intf),
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bInterfaceNumber = 0,
|
||||
.bNumEndpoints = 2,
|
||||
.bInterfaceClass = ADB_CLASS,
|
||||
.bInterfaceSubClass = ADB_SUBCLASS,
|
||||
.bInterfaceProtocol = ADB_PROTOCOL,
|
||||
.iInterface = 1, /* first string from the provided table */
|
||||
},
|
||||
.source = {
|
||||
.bLength = sizeof(ss_descriptors.source),
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = 1 | USB_DIR_OUT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = MAX_PACKET_SIZE_SS,
|
||||
},
|
||||
.source_comp = {
|
||||
.bLength = sizeof(ss_descriptors.source_comp),
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||
},
|
||||
.sink = {
|
||||
.bLength = sizeof(ss_descriptors.sink),
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = 2 | USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = MAX_PACKET_SIZE_SS,
|
||||
},
|
||||
.sink_comp = {
|
||||
.bLength = sizeof(ss_descriptors.sink_comp),
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||
},
|
||||
};
|
||||
|
||||
#define STR_INTERFACE_ "ADB Interface"
|
||||
|
||||
static const struct {
|
||||
struct usb_functionfs_strings_head header;
|
||||
struct {
|
||||
__le16 code;
|
||||
const char str1[sizeof(STR_INTERFACE_)];
|
||||
} __attribute__((packed)) lang0;
|
||||
} __attribute__((packed)) strings = {
|
||||
.header = {
|
||||
.magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),
|
||||
.length = cpu_to_le32(sizeof(strings)),
|
||||
.str_count = cpu_to_le32(1),
|
||||
.lang_count = cpu_to_le32(1),
|
||||
},
|
||||
.lang0 = {
|
||||
cpu_to_le16(0x0409), /* en-us */
|
||||
STR_INTERFACE_,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
static void *usb_adb_open_thread(void *x)
|
||||
{
|
||||
struct usb_handle *usb = (struct usb_handle *)x;
|
||||
int fd;
|
||||
|
||||
while (true) {
|
||||
// wait until the USB device needs opening
|
||||
adb_mutex_lock(&usb->lock);
|
||||
while (usb->fd != -1)
|
||||
adb_cond_wait(&usb->notify, &usb->lock);
|
||||
adb_mutex_unlock(&usb->lock);
|
||||
|
||||
D("[ usb_thread - opening device ]\n");
|
||||
do {
|
||||
/* XXX use inotify? */
|
||||
fd = unix_open("/dev/android_adb", O_RDWR);
|
||||
if (fd < 0) {
|
||||
// to support older kernels
|
||||
fd = unix_open("/dev/android", O_RDWR);
|
||||
}
|
||||
if (fd < 0) {
|
||||
adb_sleep_ms(1000);
|
||||
}
|
||||
} while (fd < 0);
|
||||
D("[ opening device succeeded ]\n");
|
||||
|
||||
close_on_exec(fd);
|
||||
usb->fd = fd;
|
||||
|
||||
D("[ usb_thread - registering device ]\n");
|
||||
register_usb_transport(usb, 0, 0, 1);
|
||||
}
|
||||
|
||||
// never gets here
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usb_adb_write(usb_handle *h, const void *data, int len)
|
||||
{
|
||||
int n;
|
||||
|
||||
D("about to write (fd=%d, len=%d)\n", h->fd, len);
|
||||
n = adb_write(h->fd, data, len);
|
||||
if(n != len) {
|
||||
D("ERROR: fd = %d, n = %d, errno = %d (%s)\n",
|
||||
h->fd, n, errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
D("[ done fd=%d ]\n", h->fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usb_adb_read(usb_handle *h, void *data, int len)
|
||||
{
|
||||
int n;
|
||||
|
||||
D("about to read (fd=%d, len=%d)\n", h->fd, len);
|
||||
n = adb_read(h->fd, data, len);
|
||||
if(n != len) {
|
||||
D("ERROR: fd = %d, n = %d, errno = %d (%s)\n",
|
||||
h->fd, n, errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
D("[ done fd=%d ]\n", h->fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_adb_kick(usb_handle *h)
|
||||
{
|
||||
D("usb_kick\n");
|
||||
adb_mutex_lock(&h->lock);
|
||||
adb_close(h->fd);
|
||||
h->fd = -1;
|
||||
|
||||
// notify usb_adb_open_thread that we are disconnected
|
||||
adb_cond_signal(&h->notify);
|
||||
adb_mutex_unlock(&h->lock);
|
||||
}
|
||||
|
||||
static void usb_adb_init()
|
||||
{
|
||||
usb_handle* h = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
|
||||
if (h == nullptr) fatal("couldn't allocate usb_handle");
|
||||
|
||||
h->write = usb_adb_write;
|
||||
h->read = usb_adb_read;
|
||||
h->kick = usb_adb_kick;
|
||||
h->fd = -1;
|
||||
|
||||
adb_cond_init(&h->notify, 0);
|
||||
adb_mutex_init(&h->lock, 0);
|
||||
|
||||
// Open the file /dev/android_adb_enable to trigger
|
||||
// the enabling of the adb USB function in the kernel.
|
||||
// We never touch this file again - just leave it open
|
||||
// indefinitely so the kernel will know when we are running
|
||||
// and when we are not.
|
||||
int fd = unix_open("/dev/android_adb_enable", O_RDWR);
|
||||
if (fd < 0) {
|
||||
D("failed to open /dev/android_adb_enable\n");
|
||||
} else {
|
||||
close_on_exec(fd);
|
||||
}
|
||||
|
||||
D("[ usb_init - starting thread ]\n");
|
||||
adb_thread_t tid;
|
||||
if(adb_thread_create(&tid, usb_adb_open_thread, h)){
|
||||
fatal_errno("cannot create usb thread");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void init_functionfs(struct usb_handle *h)
|
||||
{
|
||||
ssize_t ret;
|
||||
struct desc_v1 v1_descriptor;
|
||||
struct desc_v2 v2_descriptor;
|
||||
|
||||
v2_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2);
|
||||
v2_descriptor.header.length = cpu_to_le32(sizeof(v2_descriptor));
|
||||
v2_descriptor.header.flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC |
|
||||
FUNCTIONFS_HAS_SS_DESC;
|
||||
v2_descriptor.fs_count = 3;
|
||||
v2_descriptor.hs_count = 3;
|
||||
v2_descriptor.ss_count = 5;
|
||||
v2_descriptor.fs_descs = fs_descriptors;
|
||||
v2_descriptor.hs_descs = hs_descriptors;
|
||||
v2_descriptor.ss_descs = ss_descriptors;
|
||||
|
||||
if (h->control < 0) { // might have already done this before
|
||||
D("OPENING %s\n", USB_FFS_ADB_EP0);
|
||||
h->control = adb_open(USB_FFS_ADB_EP0, O_RDWR);
|
||||
if (h->control < 0) {
|
||||
D("[ %s: cannot open control endpoint: errno=%d]\n", USB_FFS_ADB_EP0, errno);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = adb_write(h->control, &v2_descriptor, sizeof(v2_descriptor));
|
||||
if (ret < 0) {
|
||||
v1_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC);
|
||||
v1_descriptor.header.length = cpu_to_le32(sizeof(v1_descriptor));
|
||||
v1_descriptor.header.fs_count = 3;
|
||||
v1_descriptor.header.hs_count = 3;
|
||||
v1_descriptor.fs_descs = fs_descriptors;
|
||||
v1_descriptor.hs_descs = hs_descriptors;
|
||||
D("[ %s: Switching to V1_descriptor format errno=%d ]\n", USB_FFS_ADB_EP0, errno);
|
||||
ret = adb_write(h->control, &v1_descriptor, sizeof(v1_descriptor));
|
||||
if (ret < 0) {
|
||||
D("[ %s: write descriptors failed: errno=%d ]\n", USB_FFS_ADB_EP0, errno);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
ret = adb_write(h->control, &strings, sizeof(strings));
|
||||
if (ret < 0) {
|
||||
D("[ %s: writing strings failed: errno=%d]\n", USB_FFS_ADB_EP0, errno);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
h->bulk_out = adb_open(USB_FFS_ADB_OUT, O_RDWR);
|
||||
if (h->bulk_out < 0) {
|
||||
D("[ %s: cannot open bulk-out ep: errno=%d ]\n", USB_FFS_ADB_OUT, errno);
|
||||
goto err;
|
||||
}
|
||||
|
||||
h->bulk_in = adb_open(USB_FFS_ADB_IN, O_RDWR);
|
||||
if (h->bulk_in < 0) {
|
||||
D("[ %s: cannot open bulk-in ep: errno=%d ]\n", USB_FFS_ADB_IN, errno);
|
||||
goto err;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
err:
|
||||
if (h->bulk_in > 0) {
|
||||
adb_close(h->bulk_in);
|
||||
h->bulk_in = -1;
|
||||
}
|
||||
if (h->bulk_out > 0) {
|
||||
adb_close(h->bulk_out);
|
||||
h->bulk_out = -1;
|
||||
}
|
||||
if (h->control > 0) {
|
||||
adb_close(h->control);
|
||||
h->control = -1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static void *usb_ffs_open_thread(void *x)
|
||||
{
|
||||
struct usb_handle *usb = (struct usb_handle *)x;
|
||||
|
||||
while (true) {
|
||||
// wait until the USB device needs opening
|
||||
adb_mutex_lock(&usb->lock);
|
||||
while (usb->control != -1 && usb->bulk_in != -1 && usb->bulk_out != -1)
|
||||
adb_cond_wait(&usb->notify, &usb->lock);
|
||||
adb_mutex_unlock(&usb->lock);
|
||||
|
||||
while (true) {
|
||||
init_functionfs(usb);
|
||||
|
||||
if (usb->control >= 0 && usb->bulk_in >= 0 && usb->bulk_out >= 0)
|
||||
break;
|
||||
|
||||
adb_sleep_ms(1000);
|
||||
}
|
||||
property_set("sys.usb.ffs.ready", "1");
|
||||
|
||||
D("[ usb_thread - registering device ]\n");
|
||||
register_usb_transport(usb, 0, 0, 1);
|
||||
}
|
||||
|
||||
// never gets here
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bulk_write(int bulk_in, const uint8_t* buf, size_t length)
|
||||
{
|
||||
size_t count = 0;
|
||||
int ret;
|
||||
|
||||
do {
|
||||
ret = adb_write(bulk_in, buf + count, length - count);
|
||||
if (ret < 0) {
|
||||
if (errno != EINTR)
|
||||
return ret;
|
||||
} else {
|
||||
count += ret;
|
||||
}
|
||||
} while (count < length);
|
||||
|
||||
D("[ bulk_write done fd=%d ]\n", bulk_in);
|
||||
return count;
|
||||
}
|
||||
|
||||
static int usb_ffs_write(usb_handle* h, const void* data, int len)
|
||||
{
|
||||
D("about to write (fd=%d, len=%d)\n", h->bulk_in, len);
|
||||
int n = bulk_write(h->bulk_in, reinterpret_cast<const uint8_t*>(data), len);
|
||||
if (n != len) {
|
||||
D("ERROR: fd = %d, n = %d: %s\n", h->bulk_in, n, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
D("[ done fd=%d ]\n", h->bulk_in);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bulk_read(int bulk_out, uint8_t* buf, size_t length)
|
||||
{
|
||||
size_t count = 0;
|
||||
int ret;
|
||||
|
||||
do {
|
||||
ret = adb_read(bulk_out, buf + count, length - count);
|
||||
if (ret < 0) {
|
||||
if (errno != EINTR) {
|
||||
D("[ bulk_read failed fd=%d length=%zu count=%zu ]\n",
|
||||
bulk_out, length, count);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
count += ret;
|
||||
}
|
||||
} while (count < length);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int usb_ffs_read(usb_handle* h, void* data, int len)
|
||||
{
|
||||
D("about to read (fd=%d, len=%d)\n", h->bulk_out, len);
|
||||
int n = bulk_read(h->bulk_out, reinterpret_cast<uint8_t*>(data), len);
|
||||
if (n != len) {
|
||||
D("ERROR: fd = %d, n = %d: %s\n", h->bulk_out, n, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
D("[ done fd=%d ]\n", h->bulk_out);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_ffs_kick(usb_handle *h)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = ioctl(h->bulk_in, FUNCTIONFS_CLEAR_HALT);
|
||||
if (err < 0)
|
||||
D("[ kick: source (fd=%d) clear halt failed (%d) ]", h->bulk_in, errno);
|
||||
|
||||
err = ioctl(h->bulk_out, FUNCTIONFS_CLEAR_HALT);
|
||||
if (err < 0)
|
||||
D("[ kick: sink (fd=%d) clear halt failed (%d) ]", h->bulk_out, errno);
|
||||
|
||||
adb_mutex_lock(&h->lock);
|
||||
|
||||
// don't close ep0 here, since we may not need to reinitialize it with
|
||||
// the same descriptors again. if however ep1/ep2 fail to re-open in
|
||||
// init_functionfs, only then would we close and open ep0 again.
|
||||
adb_close(h->bulk_out);
|
||||
adb_close(h->bulk_in);
|
||||
h->bulk_out = h->bulk_in = -1;
|
||||
|
||||
// notify usb_ffs_open_thread that we are disconnected
|
||||
adb_cond_signal(&h->notify);
|
||||
adb_mutex_unlock(&h->lock);
|
||||
}
|
||||
|
||||
static void usb_ffs_init()
|
||||
{
|
||||
D("[ usb_init - using FunctionFS ]\n");
|
||||
|
||||
usb_handle* h = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
|
||||
if (h == nullptr) fatal("couldn't allocate usb_handle");
|
||||
|
||||
h->write = usb_ffs_write;
|
||||
h->read = usb_ffs_read;
|
||||
h->kick = usb_ffs_kick;
|
||||
h->control = -1;
|
||||
h->bulk_out = -1;
|
||||
h->bulk_out = -1;
|
||||
|
||||
adb_cond_init(&h->notify, 0);
|
||||
adb_mutex_init(&h->lock, 0);
|
||||
|
||||
D("[ usb_init - starting thread ]\n");
|
||||
adb_thread_t tid;
|
||||
if (adb_thread_create(&tid, usb_ffs_open_thread, h)){
|
||||
fatal_errno("[ cannot create usb thread ]\n");
|
||||
}
|
||||
}
|
||||
|
||||
void usb_init()
|
||||
{
|
||||
if (access(USB_FFS_ADB_EP0, F_OK) == 0)
|
||||
usb_ffs_init();
|
||||
else
|
||||
usb_adb_init();
|
||||
}
|
||||
|
||||
void usb_cleanup()
|
||||
{
|
||||
}
|
||||
|
||||
int usb_write(usb_handle *h, const void *data, int len)
|
||||
{
|
||||
return h->write(h, data, len);
|
||||
}
|
||||
|
||||
int usb_read(usb_handle *h, void *data, int len)
|
||||
{
|
||||
return h->read(h, data, len);
|
||||
}
|
||||
int usb_close(usb_handle *h)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_kick(usb_handle *h)
|
||||
{
|
||||
h->kick(h);
|
||||
}
|
||||
535
usb_osx.cpp
Normal file
535
usb_osx.cpp
Normal file
@@ -0,0 +1,535 @@
|
||||
/*
|
||||
* 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 TRACE_USB
|
||||
|
||||
#include "sysdeps.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 "adb.h"
|
||||
#include "transport.h"
|
||||
|
||||
#define DBG D
|
||||
|
||||
static IONotificationPortRef notificationPort = 0;
|
||||
static io_iterator_t notificationIterator;
|
||||
|
||||
struct usb_handle
|
||||
{
|
||||
UInt8 bulkIn;
|
||||
UInt8 bulkOut;
|
||||
IOUSBInterfaceInterface **interface;
|
||||
io_object_t usbNotification;
|
||||
unsigned int zero_mask;
|
||||
};
|
||||
|
||||
static CFRunLoopRef currentRunLoop = 0;
|
||||
static pthread_mutex_t start_lock;
|
||||
static pthread_cond_t start_cond;
|
||||
|
||||
|
||||
static void AndroidInterfaceAdded(void *refCon, io_iterator_t iterator);
|
||||
static void AndroidInterfaceNotify(void *refCon, io_iterator_t iterator,
|
||||
natural_t messageType,
|
||||
void *messageArgument);
|
||||
static usb_handle* CheckInterface(IOUSBInterfaceInterface **iface,
|
||||
UInt16 vendor, UInt16 product);
|
||||
|
||||
static int
|
||||
InitUSB()
|
||||
{
|
||||
CFMutableDictionaryRef matchingDict;
|
||||
CFRunLoopSourceRef runLoopSource;
|
||||
|
||||
//* To set up asynchronous notifications, create a notification port and
|
||||
//* add its run loop event source to the program's run loop
|
||||
notificationPort = IONotificationPortCreate(kIOMasterPortDefault);
|
||||
runLoopSource = IONotificationPortGetRunLoopSource(notificationPort);
|
||||
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode);
|
||||
|
||||
//* Create our matching dictionary to find the Android device's
|
||||
//* adb interface
|
||||
//* IOServiceAddMatchingNotification consumes the reference, so we do
|
||||
//* not need to release this
|
||||
matchingDict = IOServiceMatching(kIOUSBInterfaceClassName);
|
||||
|
||||
if (!matchingDict) {
|
||||
DBG("ERR: Couldn't create USB matching dictionary.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//* We have to get notifications for all potential candidates and test them
|
||||
//* at connection time because the matching rules don't allow for a
|
||||
//* USB interface class of 0xff for class+subclass+protocol matches
|
||||
//* See https://developer.apple.com/library/mac/qa/qa1076/_index.html
|
||||
IOServiceAddMatchingNotification(
|
||||
notificationPort,
|
||||
kIOFirstMatchNotification,
|
||||
matchingDict,
|
||||
AndroidInterfaceAdded,
|
||||
NULL,
|
||||
¬ificationIterator);
|
||||
|
||||
//* Iterate over set of matching interfaces to access already-present
|
||||
//* devices and to arm the notification
|
||||
AndroidInterfaceAdded(NULL, notificationIterator);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
AndroidInterfaceAdded(void *refCon, io_iterator_t iterator)
|
||||
{
|
||||
kern_return_t kr;
|
||||
io_service_t usbDevice;
|
||||
io_service_t usbInterface;
|
||||
IOCFPlugInInterface **plugInInterface = NULL;
|
||||
IOUSBInterfaceInterface220 **iface = NULL;
|
||||
IOUSBDeviceInterface197 **dev = NULL;
|
||||
HRESULT result;
|
||||
SInt32 score;
|
||||
UInt32 locationId;
|
||||
UInt8 if_class, subclass, protocol;
|
||||
UInt16 vendor;
|
||||
UInt16 product;
|
||||
UInt8 serialIndex;
|
||||
char serial[256];
|
||||
char devpathBuf[64];
|
||||
char *devpath = NULL;
|
||||
|
||||
while ((usbInterface = IOIteratorNext(iterator))) {
|
||||
//* Create an intermediate interface plugin
|
||||
kr = IOCreatePlugInInterfaceForService(usbInterface,
|
||||
kIOUSBInterfaceUserClientTypeID,
|
||||
kIOCFPlugInInterfaceID,
|
||||
&plugInInterface, &score);
|
||||
IOObjectRelease(usbInterface);
|
||||
if ((kIOReturnSuccess != kr) || (!plugInInterface)) {
|
||||
DBG("ERR: Unable to create an interface plug-in (%08x)\n", kr);
|
||||
continue;
|
||||
}
|
||||
|
||||
//* This gets us the interface object
|
||||
result = (*plugInInterface)->QueryInterface(
|
||||
plugInInterface,
|
||||
CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (LPVOID*)&iface);
|
||||
//* We only needed the plugin to get the interface, so discard it
|
||||
(*plugInInterface)->Release(plugInInterface);
|
||||
if (result || !iface) {
|
||||
DBG("ERR: Couldn't query the interface (%08x)\n", (int) result);
|
||||
continue;
|
||||
}
|
||||
|
||||
kr = (*iface)->GetInterfaceClass(iface, &if_class);
|
||||
kr = (*iface)->GetInterfaceSubClass(iface, &subclass);
|
||||
kr = (*iface)->GetInterfaceProtocol(iface, &protocol);
|
||||
if(if_class != ADB_CLASS || subclass != ADB_SUBCLASS || protocol != ADB_PROTOCOL) {
|
||||
// Ignore non-ADB devices.
|
||||
DBG("Ignoring interface with incorrect class/subclass/protocol - %d, %d, %d\n", 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) {
|
||||
DBG("ERR: Couldn't grab device from interface (%08x)\n", kr);
|
||||
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)) {
|
||||
DBG("ERR: Unable to create a device plug-in (%08x)\n", kr);
|
||||
continue;
|
||||
}
|
||||
|
||||
result = (*plugInInterface)->QueryInterface(plugInInterface,
|
||||
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*)&dev);
|
||||
//* only needed this to query the plugin
|
||||
(*plugInInterface)->Release(plugInInterface);
|
||||
if (result || !dev) {
|
||||
DBG("ERR: Couldn't create a device interface (%08x)\n",
|
||||
(int) result);
|
||||
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 == 0) {
|
||||
snprintf(devpathBuf, sizeof(devpathBuf), "usb:%" PRIu32 "X",
|
||||
(unsigned int)locationId);
|
||||
devpath = devpathBuf;
|
||||
}
|
||||
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);
|
||||
|
||||
DBG("INFO: Found vid=%04x pid=%04x serial=%s\n", vendor, product,
|
||||
serial);
|
||||
|
||||
usb_handle* handle = CheckInterface((IOUSBInterfaceInterface**)iface,
|
||||
vendor, product);
|
||||
if (handle == NULL) {
|
||||
DBG("ERR: Could not find device interface: %08x\n", kr);
|
||||
(*iface)->Release(iface);
|
||||
continue;
|
||||
}
|
||||
|
||||
DBG("AndroidDeviceAdded calling register_usb_transport\n");
|
||||
register_usb_transport(handle, (serial[0] ? serial : NULL), devpath, 1);
|
||||
|
||||
// Register for an interest notification of this device being removed.
|
||||
// Pass the reference to our private data as the refCon for the
|
||||
// notification.
|
||||
kr = IOServiceAddInterestNotification(notificationPort,
|
||||
usbInterface,
|
||||
kIOGeneralInterest,
|
||||
AndroidInterfaceNotify,
|
||||
handle,
|
||||
&handle->usbNotification);
|
||||
|
||||
if (kIOReturnSuccess != kr) {
|
||||
DBG("ERR: Unable to create interest notification (%08x)\n", kr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
AndroidInterfaceNotify(void *refCon, io_service_t service, natural_t messageType, void *messageArgument)
|
||||
{
|
||||
usb_handle *handle = (usb_handle *)refCon;
|
||||
|
||||
if (messageType == kIOMessageServiceIsTerminated) {
|
||||
if (!handle) {
|
||||
DBG("ERR: NULL handle\n");
|
||||
return;
|
||||
}
|
||||
DBG("AndroidInterfaceNotify\n");
|
||||
IOObjectRelease(handle->usbNotification);
|
||||
usb_kick(handle);
|
||||
}
|
||||
}
|
||||
|
||||
//* TODO: simplify this further since we only register to get ADB interface
|
||||
//* subclass+protocol events
|
||||
static usb_handle*
|
||||
CheckInterface(IOUSBInterfaceInterface **interface, UInt16 vendor, UInt16 product)
|
||||
{
|
||||
usb_handle* handle = NULL;
|
||||
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) {
|
||||
DBG("ERR: Could not open interface: (%08x)\n", kr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//* Get the number of endpoints associated with this interface
|
||||
kr = (*interface)->GetNumEndpoints(interface, &interfaceNumEndpoints);
|
||||
if (kr != kIOReturnSuccess) {
|
||||
DBG("ERR: Unable to get number of endpoints: (%08x)\n", 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) {
|
||||
DBG("ERR: Unable to get interface class, subclass and protocol\n");
|
||||
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(vendor, product, interfaceClass,
|
||||
interfaceSubClass, interfaceProtocol))
|
||||
goto err_bad_adb_interface;
|
||||
|
||||
handle = reinterpret_cast<usb_handle*>(calloc(1, sizeof(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 = 0; endpoint <= interfaceNumEndpoints; endpoint++) {
|
||||
UInt8 transferType;
|
||||
UInt16 maxPacketSize;
|
||||
UInt8 interval;
|
||||
UInt8 number;
|
||||
UInt8 direction;
|
||||
|
||||
kr = (*interface)->GetPipeProperties(interface, endpoint, &direction,
|
||||
&number, &transferType, &maxPacketSize, &interval);
|
||||
|
||||
if (kIOReturnSuccess == kr) {
|
||||
if (kUSBBulk != transferType)
|
||||
continue;
|
||||
|
||||
if (kUSBIn == direction)
|
||||
handle->bulkIn = endpoint;
|
||||
|
||||
if (kUSBOut == direction)
|
||||
handle->bulkOut = endpoint;
|
||||
|
||||
handle->zero_mask = maxPacketSize - 1;
|
||||
} else {
|
||||
DBG("ERR: FindDeviceInterface - could not get pipe properties\n");
|
||||
goto err_get_pipe_props;
|
||||
}
|
||||
}
|
||||
|
||||
handle->interface = interface;
|
||||
return handle;
|
||||
|
||||
err_get_pipe_props:
|
||||
free(handle);
|
||||
err_bad_adb_interface:
|
||||
err_get_interface_class:
|
||||
err_get_num_ep:
|
||||
(*interface)->USBInterfaceClose(interface);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void* RunLoopThread(void* unused)
|
||||
{
|
||||
InitUSB();
|
||||
|
||||
currentRunLoop = CFRunLoopGetCurrent();
|
||||
|
||||
// Signal the parent that we are running
|
||||
adb_mutex_lock(&start_lock);
|
||||
adb_cond_signal(&start_cond);
|
||||
adb_mutex_unlock(&start_lock);
|
||||
|
||||
CFRunLoopRun();
|
||||
currentRunLoop = 0;
|
||||
|
||||
IOObjectRelease(notificationIterator);
|
||||
IONotificationPortDestroy(notificationPort);
|
||||
|
||||
DBG("RunLoopThread done\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int initialized = 0;
|
||||
void usb_init()
|
||||
{
|
||||
if (!initialized)
|
||||
{
|
||||
adb_thread_t tid;
|
||||
|
||||
adb_mutex_init(&start_lock, NULL);
|
||||
adb_cond_init(&start_cond, NULL);
|
||||
|
||||
if(adb_thread_create(&tid, RunLoopThread, NULL))
|
||||
fatal_errno("cannot create input thread");
|
||||
|
||||
// Wait for initialization to finish
|
||||
adb_mutex_lock(&start_lock);
|
||||
adb_cond_wait(&start_cond, &start_lock);
|
||||
adb_mutex_unlock(&start_lock);
|
||||
|
||||
adb_mutex_destroy(&start_lock);
|
||||
adb_cond_destroy(&start_cond);
|
||||
|
||||
initialized = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void usb_cleanup()
|
||||
{
|
||||
DBG("usb_cleanup\n");
|
||||
close_usb_devices();
|
||||
if (currentRunLoop)
|
||||
CFRunLoopStop(currentRunLoop);
|
||||
}
|
||||
|
||||
int usb_write(usb_handle *handle, const void *buf, int len)
|
||||
{
|
||||
IOReturn result;
|
||||
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
if (!handle)
|
||||
return -1;
|
||||
|
||||
if (NULL == handle->interface) {
|
||||
DBG("ERR: usb_write interface was null\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (0 == handle->bulkOut) {
|
||||
DBG("ERR: bulkOut endpoint not assigned\n");
|
||||
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 (0 == result)
|
||||
return 0;
|
||||
|
||||
DBG("ERR: usb_write failed with status %d\n", result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int usb_read(usb_handle *handle, void *buf, int len)
|
||||
{
|
||||
IOReturn result;
|
||||
UInt32 numBytes = len;
|
||||
|
||||
if (!len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!handle) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (NULL == handle->interface) {
|
||||
DBG("ERR: usb_read interface was null\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (0 == handle->bulkIn) {
|
||||
DBG("ERR: bulkIn endpoint not assigned\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
result = (*handle->interface)->ReadPipe(handle->interface, handle->bulkIn, buf, &numBytes);
|
||||
|
||||
if (kIOUSBPipeStalled == result) {
|
||||
DBG(" 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 0;
|
||||
else {
|
||||
DBG("ERR: usb_read failed with status %x\n", result);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int usb_close(usb_handle *handle)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_kick(usb_handle *handle)
|
||||
{
|
||||
/* release the interface */
|
||||
if (!handle)
|
||||
return;
|
||||
|
||||
if (handle->interface)
|
||||
{
|
||||
(*handle->interface)->USBInterfaceClose(handle->interface);
|
||||
(*handle->interface)->Release(handle->interface);
|
||||
handle->interface = 0;
|
||||
}
|
||||
}
|
||||
519
usb_windows.cpp
Normal file
519
usb_windows.cpp
Normal file
@@ -0,0 +1,519 @@
|
||||
/*
|
||||
* 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 TRACE_USB
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#include <winsock2.h> // winsock.h *must* be included before windows.h.
|
||||
#include <adb_api.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <usb100.h>
|
||||
#include <windows.h>
|
||||
#include <winerror.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "transport.h"
|
||||
|
||||
/** 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.
|
||||
*/
|
||||
struct usb_handle {
|
||||
/// Previous entry in the list of opened usb handles
|
||||
usb_handle *prev;
|
||||
|
||||
/// Next entry in the list of opened usb handles
|
||||
usb_handle *next;
|
||||
|
||||
/// 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
|
||||
char* interface_name;
|
||||
|
||||
/// 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 usb_handle handle_list = {
|
||||
.prev = &handle_list,
|
||||
.next = &handle_list,
|
||||
};
|
||||
|
||||
/// Locker for the list of opened usb handles
|
||||
ADB_MUTEX_DEFINE( usb_lock );
|
||||
|
||||
/// Checks if there is opened usb handle in handle_list for this device.
|
||||
int known_device(const char* 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 char* 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();
|
||||
|
||||
/// Entry point for thread that polls (every second) for new usb interfaces.
|
||||
/// This routine calls find_devices in infinite loop.
|
||||
void* device_poll_thread(void* unused);
|
||||
|
||||
/// Initializes this module
|
||||
void usb_init();
|
||||
|
||||
/// Cleans up this module
|
||||
void usb_cleanup();
|
||||
|
||||
/// 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);
|
||||
|
||||
/// Gets interface (device) name for an opened usb handle
|
||||
const char *usb_name(usb_handle* handle);
|
||||
|
||||
int known_device_locked(const char* dev_name) {
|
||||
usb_handle* usb;
|
||||
|
||||
if (NULL != dev_name) {
|
||||
// Iterate through the list looking for the name match.
|
||||
for(usb = handle_list.next; usb != &handle_list; usb = usb->next) {
|
||||
// In Windows names are not case sensetive!
|
||||
if((NULL != usb->interface_name) &&
|
||||
(0 == stricmp(usb->interface_name, dev_name))) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int known_device(const char* dev_name) {
|
||||
int ret = 0;
|
||||
|
||||
if (NULL != dev_name) {
|
||||
adb_mutex_lock(&usb_lock);
|
||||
ret = known_device_locked(dev_name);
|
||||
adb_mutex_unlock(&usb_lock);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int register_new_device(usb_handle* handle) {
|
||||
if (NULL == handle)
|
||||
return 0;
|
||||
|
||||
adb_mutex_lock(&usb_lock);
|
||||
|
||||
// Check if device is already in the list
|
||||
if (known_device_locked(handle->interface_name)) {
|
||||
adb_mutex_unlock(&usb_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Not in the list. Add this handle to the list.
|
||||
handle->next = &handle_list;
|
||||
handle->prev = handle_list.prev;
|
||||
handle->prev->next = handle;
|
||||
handle->next->prev = handle;
|
||||
|
||||
adb_mutex_unlock(&usb_lock);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void* device_poll_thread(void* unused) {
|
||||
D("Created device thread\n");
|
||||
|
||||
while(1) {
|
||||
find_devices();
|
||||
adb_sleep_ms(1000);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void usb_init() {
|
||||
adb_thread_t tid;
|
||||
|
||||
if(adb_thread_create(&tid, device_poll_thread, NULL)) {
|
||||
fatal_errno("cannot create input thread");
|
||||
}
|
||||
}
|
||||
|
||||
void usb_cleanup() {
|
||||
}
|
||||
|
||||
usb_handle* do_usb_open(const wchar_t* interface_name) {
|
||||
// Allocate our handle
|
||||
usb_handle* ret = (usb_handle*)malloc(sizeof(usb_handle));
|
||||
if (NULL == ret)
|
||||
return NULL;
|
||||
|
||||
// Set linkers back to the handle
|
||||
ret->next = ret;
|
||||
ret->prev = ret;
|
||||
|
||||
// Create interface.
|
||||
ret->adb_interface = AdbCreateInterfaceByName(interface_name);
|
||||
|
||||
if (NULL == ret->adb_interface) {
|
||||
free(ret);
|
||||
errno = GetLastError();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Open read pipe (endpoint)
|
||||
ret->adb_read_pipe =
|
||||
AdbOpenDefaultBulkReadEndpoint(ret->adb_interface,
|
||||
AdbOpenAccessTypeReadWrite,
|
||||
AdbOpenSharingModeReadWrite);
|
||||
if (NULL != ret->adb_read_pipe) {
|
||||
// Open write pipe (endpoint)
|
||||
ret->adb_write_pipe =
|
||||
AdbOpenDefaultBulkWriteEndpoint(ret->adb_interface,
|
||||
AdbOpenAccessTypeReadWrite,
|
||||
AdbOpenSharingModeReadWrite);
|
||||
if (NULL != ret->adb_write_pipe) {
|
||||
// Save interface name
|
||||
unsigned long name_len = 0;
|
||||
|
||||
// First get expected name length
|
||||
AdbGetInterfaceName(ret->adb_interface,
|
||||
NULL,
|
||||
&name_len,
|
||||
true);
|
||||
if (0 != name_len) {
|
||||
ret->interface_name = (char*)malloc(name_len);
|
||||
|
||||
if (NULL != ret->interface_name) {
|
||||
// Now save the name
|
||||
if (AdbGetInterfaceName(ret->adb_interface,
|
||||
ret->interface_name,
|
||||
&name_len,
|
||||
true)) {
|
||||
// We're done at this point
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
SetLastError(ERROR_OUTOFMEMORY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Something went wrong.
|
||||
int saved_errno = GetLastError();
|
||||
usb_cleanup_handle(ret);
|
||||
free(ret);
|
||||
SetLastError(saved_errno);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int usb_write(usb_handle* handle, const void* data, int len) {
|
||||
unsigned long time_out = 5000;
|
||||
unsigned long written = 0;
|
||||
int ret;
|
||||
|
||||
D("usb_write %d\n", len);
|
||||
if (NULL != handle) {
|
||||
// Perform write
|
||||
ret = AdbWriteEndpointSync(handle->adb_write_pipe,
|
||||
(void*)data,
|
||||
(unsigned long)len,
|
||||
&written,
|
||||
time_out);
|
||||
int saved_errno = GetLastError();
|
||||
|
||||
if (ret) {
|
||||
// Make sure that we've written what we were asked to write
|
||||
D("usb_write got: %ld, expected: %d\n", written, len);
|
||||
if (written == (unsigned long)len) {
|
||||
if(handle->zero_mask && (len & handle->zero_mask) == 0) {
|
||||
// Send a zero length packet
|
||||
AdbWriteEndpointSync(handle->adb_write_pipe,
|
||||
(void*)data,
|
||||
0,
|
||||
&written,
|
||||
time_out);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
// assume ERROR_INVALID_HANDLE indicates we are disconnected
|
||||
if (saved_errno == ERROR_INVALID_HANDLE)
|
||||
usb_kick(handle);
|
||||
}
|
||||
errno = saved_errno;
|
||||
} else {
|
||||
D("usb_write NULL handle\n");
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
}
|
||||
|
||||
D("usb_write failed: %d\n", errno);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int usb_read(usb_handle *handle, void* data, int len) {
|
||||
unsigned long time_out = 0;
|
||||
unsigned long read = 0;
|
||||
int ret;
|
||||
|
||||
D("usb_read %d\n", len);
|
||||
if (NULL != handle) {
|
||||
while (len > 0) {
|
||||
int xfer = (len > 4096) ? 4096 : len;
|
||||
|
||||
ret = AdbReadEndpointSync(handle->adb_read_pipe,
|
||||
data,
|
||||
(unsigned long)xfer,
|
||||
&read,
|
||||
time_out);
|
||||
int saved_errno = GetLastError();
|
||||
D("usb_write got: %ld, expected: %d, errno: %d\n", read, xfer, saved_errno);
|
||||
if (ret) {
|
||||
data = (char *)data + read;
|
||||
len -= read;
|
||||
|
||||
if (len == 0)
|
||||
return 0;
|
||||
} else {
|
||||
// assume ERROR_INVALID_HANDLE indicates we are disconnected
|
||||
if (saved_errno == ERROR_INVALID_HANDLE)
|
||||
usb_kick(handle);
|
||||
break;
|
||||
}
|
||||
errno = saved_errno;
|
||||
}
|
||||
} else {
|
||||
D("usb_read NULL handle\n");
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
}
|
||||
|
||||
D("usb_read failed: %d\n", errno);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void usb_cleanup_handle(usb_handle* handle) {
|
||||
if (NULL != handle) {
|
||||
if (NULL != handle->interface_name)
|
||||
free(handle->interface_name);
|
||||
if (NULL != handle->adb_write_pipe)
|
||||
AdbCloseHandle(handle->adb_write_pipe);
|
||||
if (NULL != handle->adb_read_pipe)
|
||||
AdbCloseHandle(handle->adb_read_pipe);
|
||||
if (NULL != handle->adb_interface)
|
||||
AdbCloseHandle(handle->adb_interface);
|
||||
|
||||
handle->interface_name = NULL;
|
||||
handle->adb_write_pipe = NULL;
|
||||
handle->adb_read_pipe = NULL;
|
||||
handle->adb_interface = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void usb_kick(usb_handle* handle) {
|
||||
if (NULL != handle) {
|
||||
adb_mutex_lock(&usb_lock);
|
||||
|
||||
usb_cleanup_handle(handle);
|
||||
|
||||
adb_mutex_unlock(&usb_lock);
|
||||
} else {
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
errno = ERROR_INVALID_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
int usb_close(usb_handle* handle) {
|
||||
D("usb_close\n");
|
||||
|
||||
if (NULL != handle) {
|
||||
// Remove handle from the list
|
||||
adb_mutex_lock(&usb_lock);
|
||||
|
||||
if ((handle->next != handle) && (handle->prev != handle)) {
|
||||
handle->next->prev = handle->prev;
|
||||
handle->prev->next = handle->next;
|
||||
handle->prev = handle;
|
||||
handle->next = handle;
|
||||
}
|
||||
|
||||
adb_mutex_unlock(&usb_lock);
|
||||
|
||||
// Cleanup handle
|
||||
usb_cleanup_handle(handle);
|
||||
free(handle);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *usb_name(usb_handle* handle) {
|
||||
if (NULL == handle) {
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
errno = ERROR_INVALID_HANDLE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (const char*)handle->interface_name;
|
||||
}
|
||||
|
||||
int recognized_device(usb_handle* handle) {
|
||||
if (NULL == handle)
|
||||
return 0;
|
||||
|
||||
// Check vendor and product id first
|
||||
USB_DEVICE_DESCRIPTOR device_desc;
|
||||
|
||||
if (!AdbGetUsbDeviceDescriptor(handle->adb_interface,
|
||||
&device_desc)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Then check interface properties
|
||||
USB_INTERFACE_DESCRIPTOR interf_desc;
|
||||
|
||||
if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface,
|
||||
&interf_desc)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Must have two endpoints
|
||||
if (2 != interf_desc.bNumEndpoints) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (is_adb_interface(device_desc.idVendor, device_desc.idProduct,
|
||||
interf_desc.bInterfaceClass, interf_desc.bInterfaceSubClass, interf_desc.bInterfaceProtocol)) {
|
||||
|
||||
if(interf_desc.bInterfaceProtocol == 0x01) {
|
||||
AdbEndpointInformation endpoint_info;
|
||||
// assuming zero is a valid bulk endpoint ID
|
||||
if (AdbGetEndpointInformation(handle->adb_interface, 0, &endpoint_info)) {
|
||||
handle->zero_mask = endpoint_info.max_packet_size - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void find_devices() {
|
||||
usb_handle* handle = NULL;
|
||||
char entry_buffer[2048];
|
||||
char interf_name[2048];
|
||||
AdbInterfaceInfo* next_interface = (AdbInterfaceInfo*)(&entry_buffer[0]);
|
||||
unsigned long entry_buffer_size = sizeof(entry_buffer);
|
||||
char* copy_name;
|
||||
|
||||
// Enumerate all present and active interfaces.
|
||||
ADBAPIHANDLE enum_handle =
|
||||
AdbEnumInterfaces(usb_class_id, true, true, true);
|
||||
|
||||
if (NULL == enum_handle)
|
||||
return;
|
||||
|
||||
while (AdbNextInterface(enum_handle, next_interface, &entry_buffer_size)) {
|
||||
// TODO: FIXME - temp hack converting wchar_t into char.
|
||||
// It would be better to change AdbNextInterface so it will return
|
||||
// interface name as single char string.
|
||||
const wchar_t* wchar_name = next_interface->device_name;
|
||||
for(copy_name = interf_name;
|
||||
L'\0' != *wchar_name;
|
||||
wchar_name++, copy_name++) {
|
||||
*copy_name = (char)(*wchar_name);
|
||||
}
|
||||
*copy_name = '\0';
|
||||
|
||||
// Lets see if we already have this device in the list
|
||||
if (!known_device(interf_name)) {
|
||||
// This seems to be a new device. Open it!
|
||||
handle = do_usb_open(next_interface->device_name);
|
||||
if (NULL != handle) {
|
||||
// Lets see if this interface (device) belongs to us
|
||||
if (recognized_device(handle)) {
|
||||
D("adding a new device %s\n", interf_name);
|
||||
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, NULL, 1);
|
||||
} else {
|
||||
D("register_new_device failed for %s\n", interf_name);
|
||||
usb_cleanup_handle(handle);
|
||||
free(handle);
|
||||
}
|
||||
} else {
|
||||
D("cannot get serial number\n");
|
||||
usb_cleanup_handle(handle);
|
||||
free(handle);
|
||||
}
|
||||
} else {
|
||||
usb_cleanup_handle(handle);
|
||||
free(handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
entry_buffer_size = sizeof(entry_buffer);
|
||||
}
|
||||
|
||||
AdbCloseHandle(enum_handle);
|
||||
}
|
||||
Reference in New Issue
Block a user