Init from Omni 6.0 repo

Taken from system/core/adb on the omnirom/android-6.0 branch (2b143b247a69deba1577dd1b2ca92138be2b48dc)
This commit is contained in:
Zhuowei Zhang
2016-02-08 16:20:07 -08:00
commit 95eb55395d
61 changed files with 20566 additions and 0 deletions

11
.clang-format Normal file
View 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
View 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
View File

@@ -0,0 +1,2 @@
set noparent
filter=-build/header_guard,-build/include,-readability/function

0
MODULE_LICENSE_APACHE2 Normal file
View File

191
NOTICE Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

45
console.cpp Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

469
file_sync_service.cpp Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 */

View 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
View 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

Binary file not shown.

525
sysdeps.h Normal file
View 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

File diff suppressed because it is too large Load Diff

68
test_track_devices.cpp Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

70
transport.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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,
&notificationIterator);
//* 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
View 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);
}