├── porting ├── adb │ ├── client │ │ ├── usb_libusb.cpp │ │ ├── usb_osx.cpp │ │ ├── adb_porting.h │ │ ├── file_sync_client.h │ │ ├── adb_porting.cpp │ │ ├── transport_usb.cpp │ │ ├── commandline.h │ │ ├── bugreport.cpp │ │ ├── main.cpp │ │ ├── adb_client.cpp │ │ └── adb_install.cpp │ ├── include │ │ ├── sys │ │ │ └── user.h │ │ └── adb_public.h │ ├── crypto │ │ └── include │ │ │ └── adb │ │ │ └── crypto │ │ │ ├── x509_generator.h │ │ │ └── key.h │ ├── fdevent │ │ ├── fdevent.h │ │ ├── fdevent_poll.cpp │ │ └── fdevent.cpp │ └── adb_listeners.cpp ├── scripts │ ├── make-zstd.sh │ ├── make-lz4.sh │ ├── make-brotli.sh │ ├── defines.sh │ ├── make-protobuf.sh │ └── make-adb.sh ├── CMakeLists.txt ├── Makefile ├── protobuf.rb ├── cmake │ └── CMakeLists.protobuf.txt └── protobuf │ └── src │ └── google │ └── protobuf │ └── map_probe_benchmark.cc ├── .gitignore ├── example └── adb-demo │ ├── adb-demo │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── ViewController.h │ ├── AppDelegate.h │ ├── SceneDelegate.h │ ├── main.m │ ├── Info.plist │ ├── AppDelegate.m │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── SceneDelegate.m │ └── ViewController.m │ └── adb-demo.xcodeproj │ ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcuserdata │ │ └── ethan.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ ├── xcuserdata │ └── ethan.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist │ └── project.pbxproj ├── .gitmodules ├── Makefile ├── README.zh-cn.md └── README.md /porting/adb/client/usb_libusb.cpp: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /porting/adb/include/sys/user.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /porting/adb/client/usb_osx.cpp: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | android-tools/build 2 | output 3 | cmake-build-debug 4 | .idea 5 | xcuserdata 6 | -------------------------------------------------------------------------------- /example/adb-demo/adb-demo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /example/adb-demo/adb-demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/adb-demo/adb-demo/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /example/adb-demo/adb-demo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // adb-demo 4 | // 5 | // Created by Ethan on 2022/6/1. 6 | // 7 | 8 | #import 9 | 10 | @interface ViewController : UIViewController 11 | 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /example/adb-demo/adb-demo.xcodeproj/project.xcworkspace/xcuserdata/ethan.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wsvn53/adb-mobile/HEAD/example/adb-demo/adb-demo.xcodeproj/project.xcworkspace/xcuserdata/ethan.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /example/adb-demo/adb-demo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // adb-demo 4 | // 5 | // Created by Ethan on 2022/6/1. 6 | // 7 | 8 | #import 9 | 10 | @interface AppDelegate : UIResponder 11 | 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /example/adb-demo/adb-demo/SceneDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.h 3 | // adb-demo 4 | // 5 | // Created by Ethan on 2022/6/1. 6 | // 7 | 8 | #import 9 | 10 | @interface SceneDelegate : UIResponder 11 | 12 | @property (strong, nonatomic) UIWindow * window; 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /example/adb-demo/adb-demo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/adb-demo/adb-demo.xcodeproj/xcuserdata/ethan.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | adb-demo.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /example/adb-demo/adb-demo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // adb-demo 4 | // 5 | // Created by Ethan on 2022/6/1. 6 | // 7 | 8 | #import 9 | #import "AppDelegate.h" 10 | 11 | int main(int argc, char * argv[]) { 12 | NSString * appDelegateClassName; 13 | @autoreleasepool { 14 | // Setup code that might create autoreleased objects goes here. 15 | appDelegateClassName = NSStringFromClass([AppDelegate class]); 16 | } 17 | return UIApplicationMain(argc, argv, nil, appDelegateClassName); 18 | } 19 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "android-tools"] 2 | path = android-tools 3 | url = https://github.com/nmeum/android-tools.git 4 | [submodule "ios-cmake"] 5 | path = ios-cmake 6 | url = https://github.com/leetal/ios-cmake.git 7 | [submodule "external/lz4"] 8 | path = external/lz4 9 | url = https://android.googlesource.com/platform/external/lz4 10 | [submodule "external/zstd"] 11 | path = external/zstd 12 | url = https://android.googlesource.com/platform/external/zstd 13 | [submodule "external/brotli"] 14 | path = external/brotli 15 | url = https://android.googlesource.com/platform/external/brotli 16 | [submodule "external/protobuf"] 17 | path = external/protobuf 18 | url = https://github.com/protocolbuffers/protobuf.git 19 | -------------------------------------------------------------------------------- /porting/scripts/make-zstd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . "$(dirname $0)/defines.sh"; 4 | 5 | cmake_root=$SOURCE_ROOT/external/zstd/build/cmake/out; 6 | 7 | # Clean built products 8 | [[ -d "$cmake_root" ]] && rm -rfv "$cmake_root"; 9 | mkdir -pv "$cmake_root"; 10 | 11 | cd "$cmake_root" 12 | 13 | echo "→ Running cmake..."; 14 | cmake .. -G Xcode -DCMAKE_TOOLCHAIN_FILE="$CMAKE_TOOLCHAIN_FILE" -DPLATFORM="$PLATFORM" \ 15 | -DDEPLOYMENT_TARGET="$DEPLOYMENT_TARGET" $CMAKE_COMPAT_FLAGS 16 | 17 | echo "→ Building..."; 18 | cmake --build . --config Debug --target libzstd_static --parallel 8 19 | 20 | echo "→ Copying output..."; 21 | find . -name "*.a" -exec cp -av {} "$FULL_OUTPUT" \; 22 | 23 | echo "✅ zstd build completed successfully!" 24 | -------------------------------------------------------------------------------- /porting/scripts/make-lz4.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . "$(dirname $0)/defines.sh"; 4 | 5 | cmake_root=$SOURCE_ROOT/external/lz4/contrib/cmake_unofficial/out; 6 | 7 | # Clean built products 8 | [[ -d "$cmake_root" ]] && rm -rfv "$cmake_root"; 9 | mkdir -pv "$cmake_root"; 10 | 11 | cd "$cmake_root" 12 | 13 | echo "→ Running cmake..."; 14 | cmake .. -G Xcode -DCMAKE_TOOLCHAIN_FILE="$CMAKE_TOOLCHAIN_FILE" -DPLATFORM="$PLATFORM" \ 15 | -DDEPLOYMENT_TARGET="$DEPLOYMENT_TARGET" -DBUILD_STATIC_LIBS=ON -DBUILD_SHARED_LIBS=OFF \ 16 | $CMAKE_COMPAT_FLAGS 17 | 18 | echo "→ Building..."; 19 | cmake --build . --config Debug --target lz4_static --parallel 8 20 | 21 | echo "→ Copying output..."; 22 | find . -name "*.a" -exec cp -av {} "$FULL_OUTPUT" \; 23 | 24 | echo "✅ lz4 build completed successfully!" 25 | -------------------------------------------------------------------------------- /example/adb-demo/adb-demo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | UIApplicationSceneManifest 6 | 7 | UIApplicationSupportsMultipleScenes 8 | 9 | UISceneConfigurations 10 | 11 | UIWindowSceneSessionRoleApplication 12 | 13 | 14 | UISceneConfigurationName 15 | Default Configuration 16 | UISceneDelegateClassName 17 | SceneDelegate 18 | UISceneStoryboardFile 19 | Main 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /porting/scripts/make-brotli.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . "$(dirname $0)/defines.sh"; 4 | 5 | cmake_root=$SOURCE_ROOT/external/brotli/out; 6 | 7 | # Clean built products 8 | [[ -d "$cmake_root" ]] && rm -rfv "$cmake_root"; 9 | mkdir -pv "$cmake_root"; 10 | 11 | cd "$cmake_root" 12 | 13 | echo "→ Running cmake..."; 14 | cmake .. -G Xcode -DCMAKE_TOOLCHAIN_FILE="$CMAKE_TOOLCHAIN_FILE" -DPLATFORM="$PLATFORM" \ 15 | -DDEPLOYMENT_TARGET="$DEPLOYMENT_TARGET" -DBROTLI_BUNDLED_MODE=1 $CMAKE_COMPAT_FLAGS 16 | 17 | echo "→ Building..."; 18 | cmake --build . --config Debug --parallel 8 \ 19 | --target brotlidec-static --target brotlienc-static --target brotlicommon-static 20 | 21 | echo "→ Copying output..."; 22 | find . -name "*.a" -exec cp -av {} "$FULL_OUTPUT" \; 23 | 24 | echo "✅ brotli build completed successfully!" 25 | -------------------------------------------------------------------------------- /porting/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | project(adb-porting) 4 | 5 | set(CMAKE_CXX_STANDARD 17) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | 8 | set(ADB_CLIENT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/adb/client) 9 | set(ADB_VENDOR_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../android-tools/vendor/adb) 10 | 11 | file(GLOB_RECURSE ADB_CLIENT_SOURCES ${ADB_CLIENT_DIR}/*.cpp ${ADB_CLIENT_DIR}/*.h) 12 | file(GLOB_RECURSE ADB_VENDOR_SOURCES ${ADB_VENDOR_DIR}/*.cpp ${ADB_VENDOR_DIR}/*.h) 13 | 14 | set(ADB_PORTING_SOURCES ${ADB_CLIENT_SOURCES} ${ADB_VENDOR_SOURCES}) 15 | add_library(adb-porting ${ADB_PORTING_SOURCES}) 16 | 17 | target_compile_definitions(adb-porting PUBLIC ADB_HOST) 18 | 19 | target_include_directories(adb-porting PUBLIC 20 | ${ADB_CLIENT_DIR} 21 | ${ADB_VENDOR_DIR} 22 | ${ADB_VENDOR_DIR}/client 23 | ${CMAKE_CURRENT_SOURCE_DIR}/../android-tools/vendor/libbase/include 24 | ${CMAKE_CURRENT_SOURCE_DIR}/../android-tools/vendor/core/include 25 | ${CMAKE_CURRENT_SOURCE_DIR}/../external/protobuf/src 26 | ) 27 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: check-protobuf-version libs libadb.a libadb.include 2 | 3 | brew-install-deps: 4 | brew install fmt cmake pkgconfig googletest 5 | 6 | check-protobuf-version: 7 | @grep $$(protoc --version | cut -d' ' -f2) porting/protobuf.rb || { echo "* To compile libadb please install protobuf version 28.3 by:" && echo "brew unlink protobuf && brew install porting/protobuf.rb" && echo "" && exit 1; } 8 | protoc --version 9 | exit 0 10 | 11 | check-golang: 12 | @which go || { echo "* To compile libadb please install golang by execute:"; echo ""; } 13 | 14 | libs: 15 | [[ -d output ]] || mkdir output && echo "mkdir output" 16 | make -C porting 17 | 18 | libadb.include: 19 | [[ ! -d output/include ]] && mkdir output/include || echo "make include"; 20 | cp -av porting/adb/include/*.h output/include; 21 | 22 | libadb.a: 23 | rm -fv output/*/*/libadb-full.a || echo "" 24 | libtool -static -o output/iphoneos/arm64/libadb-full.a output/iphoneos/arm64/*.a 25 | libtool -static -o output/iphonesimulator/x86_64/libadb-full.a output/iphonesimulator/x86_64/*.a 26 | libtool -static -o output/iphonesimulator/arm64/libadb-full.a output/iphonesimulator/arm64/*.a 27 | -------------------------------------------------------------------------------- /porting/adb/crypto/include/adb/crypto/x509_generator.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except 6 | * in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | 25 | // bssl::UniquePtr is provided by BoringSSL's openssl/base.h 26 | 27 | namespace adb { 28 | namespace crypto { 29 | 30 | // Generate a X.509 certificate based on the key |pkey|. 31 | bssl::UniquePtr GenerateX509Certificate(EVP_PKEY* pkey); 32 | 33 | // Convert X509* to PEM string format 34 | std::string X509ToPEMString(X509* x509); 35 | 36 | } // namespace crypto 37 | } // namespace adb 38 | -------------------------------------------------------------------------------- /porting/adb/client/adb_porting.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @brief this file contains the hijack functions for porting adb 3 | */ 4 | 5 | #ifndef ADB_PORTING 6 | #define ADB_PORTING 1 7 | 8 | #include 9 | #include 10 | 11 | #define __android_log_print(...) porting_log_print(__VA_ARGS__) 12 | #define __android_log_buf_print(...) porting_log_buf_print(__VA_ARGS__) 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | // enum AdbTrace { 19 | // ADB = 0, /* 0x001 */ 20 | // SOCKETS, 21 | // PACKETS, 22 | // TRANSPORT, 23 | // RWX, /* 0x010 */ 24 | // USB, 25 | // SYNC, 26 | // SYSDEPS, 27 | // JDWP, /* 0x100 */ 28 | // SERVICES, 29 | // AUTH, 30 | // FDEVENT, 31 | // SHELL, 32 | // INCREMENTAL, 33 | // } 34 | 35 | 36 | // capture printf output as adb command result 37 | int capture_printf(char **output_buffer, size_t *output_size, const char *format, ...); 38 | 39 | // new method to call adb_commandline for public 40 | int adb_commandline_porting(char **output_buffer, size_t *output_buffer_size, int argc, const char** argv); 41 | 42 | // for init adb trace 43 | void adb_trace_init_porting(char**); 44 | 45 | // for enable adb trace 46 | void adb_trace_enable_porting(int trace_tag); 47 | 48 | #ifdef __cplusplus 49 | } 50 | #endif 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /porting/adb/include/adb_public.h: -------------------------------------------------------------------------------- 1 | /** 2 | * adb.h used for export adb functions 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | enum AdbTrace { 9 | ADB = 0, /* 0x001 */ 10 | SOCKETS, 11 | PACKETS, 12 | TRANSPORT, 13 | RWX, /* 0x010 */ 14 | USB, 15 | SYNC, 16 | SYSDEPS, 17 | JDWP, /* 0x100 */ 18 | SERVICES, 19 | AUTH, 20 | FDEVENT, 21 | SHELL, 22 | INCREMENTAL, 23 | }; 24 | 25 | void adb_trace_init_porting(char**); 26 | void adb_trace_enable_porting(enum AdbTrace trace_tag); 27 | 28 | // execute adb_commoandline 29 | int adb_commandline_porting(char **output_buffer, size_t *output_buffer_size, int argc, const char** argv); 30 | 31 | // enable adb verbose trace 32 | static inline void adb_enable_trace(void) { 33 | setenv("ADB_TRACE", "all", 1); 34 | const char *argv[] = { "adb" }; 35 | adb_trace_init_porting((char **)argv); 36 | adb_trace_enable_porting(ADB); 37 | } 38 | 39 | // set home directory 40 | static inline void adb_set_home(const char *home_dir) { 41 | setenv("HOME", home_dir, 1); 42 | } 43 | 44 | // set adb daemon port 45 | static inline void adb_set_server_port(const char *port) { 46 | setenv("ANDROID_ADB_SERVER_PORT", port, 1); 47 | } 48 | 49 | // required function body 50 | void adb_connect_status_updated(const char *serial, const char *status); 51 | -------------------------------------------------------------------------------- /example/adb-demo/adb-demo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // adb-demo 4 | // 5 | // Created by Ethan on 2022/6/1. 6 | // 7 | 8 | #import "AppDelegate.h" 9 | 10 | @interface AppDelegate () 11 | 12 | @end 13 | 14 | @implementation AppDelegate 15 | 16 | 17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 18 | // Override point for customization after application launch. 19 | return YES; 20 | } 21 | 22 | 23 | #pragma mark - UISceneSession lifecycle 24 | 25 | 26 | - (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options { 27 | // Called when a new scene session is being created. 28 | // Use this method to select a configuration to create the new scene with. 29 | return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role]; 30 | } 31 | 32 | 33 | - (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet *)sceneSessions { 34 | // Called when the user discards a scene session. 35 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 36 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 37 | } 38 | 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /porting/adb/client/file_sync_client.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | 22 | #include "file_sync_protocol.h" 23 | 24 | bool do_sync_ls(char **output_buffer, size_t *output_size, const char* path); 25 | bool do_sync_push(char **output_buffer, size_t *output_size, const std::vector& srcs, const char* dst, bool sync, 26 | CompressionType compression, bool dry_run, bool quiet); 27 | bool do_sync_pull(char **output_buffer, size_t *output_size, const std::vector& srcs, const char* dst, bool copy_attrs, 28 | CompressionType compression, const char* name = nullptr, bool quiet = false); 29 | 30 | bool do_sync_sync(char **output_buffer, size_t *output_size, const std::string& lpath, const std::string& rpath, bool list_only, 31 | CompressionType compression, bool dry_run, bool quiet); 32 | -------------------------------------------------------------------------------- /porting/scripts/defines.sh: -------------------------------------------------------------------------------- 1 | # Exit immediately on error, treat unset variables as error, and fail on pipe errors 2 | set -euo pipefail 3 | 4 | # Common Supports: 5 | # - TARGET=lz4/iphoneos/arm64 6 | # - OUTPUT=... 7 | 8 | [[ -z "${TARGET:-}" ]] && echo "** ❌ ERROR: TARGET is REQUIRED." && exit 1; 9 | [[ -z "${OUTPUT:-}" ]] && echo "** ❌ ERROR: OUTPUT is REQUIRED." && exit 1; 10 | 11 | LIB_NAME=$(echo "$TARGET" | cut -d/ -f1); 12 | SDK_NAME=$(echo "$TARGET" | cut -d/ -f2); 13 | ARCH_NAME=$(echo "$TARGET" | cut -d/ -f3); 14 | 15 | # Src root 16 | SOURCE_ROOT=$(cd "$(dirname $0)/../.." && pwd); 17 | 18 | # Porting root 19 | PORTING_ROOT=$SOURCE_ROOT/porting; 20 | 21 | # Prepare output path 22 | FULL_OUTPUT=$(cd $OUTPUT && pwd)/$SDK_NAME/$ARCH_NAME; 23 | [[ ! -d $FULL_OUTPUT ]] && mkdir -p $FULL_OUTPUT; 24 | 25 | # For iphone, change to platform 26 | PLATFORM=$([[ $SDK_NAME == iphoneos ]] && echo "OS64" || echo "SIMULATOR64"); 27 | [[ $SDK_NAME == iphonesimulator ]] && [[ $ARCH_NAME == arm64 ]] && PLATFORM=SIMULATORARM64; 28 | 29 | # Setup iphone deploy target 30 | DEPLOYMENT_TARGET=12.0; 31 | 32 | # Setup toolchains 33 | if [[ $SDK_NAME == *iphone* ]]; then 34 | CMAKE_TOOLCHAIN_FILE=$SOURCE_ROOT/ios-cmake/ios.toolchain.cmake; 35 | fi 36 | 37 | # CMake compatibility flag for older CMakeLists.txt files 38 | CMAKE_COMPAT_FLAGS="-DCMAKE_POLICY_VERSION_MINIMUM=3.5" 39 | 40 | # Print summary 41 | echo " - Lib Name: $LIB_NAME"; 42 | echo " - SDK Name: $SDK_NAME"; 43 | echo " - Arch Name: $ARCH_NAME"; 44 | echo " - CMake Toolchain: $CMAKE_TOOLCHAIN_FILE"; 45 | echo " - CMake PLATFORM: $PLATFORM"; 46 | -------------------------------------------------------------------------------- /porting/adb/crypto/include/adb/crypto/key.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except 6 | * in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | 25 | #include "key_type.pb.h" 26 | 27 | // bssl::UniquePtr is provided by BoringSSL's openssl/base.h 28 | 29 | namespace adb { 30 | namespace crypto { 31 | 32 | // Class that represents a public/private key pair. 33 | class Key { 34 | public: 35 | explicit Key(bssl::UniquePtr&& pkey, adb::proto::KeyType type) 36 | : pkey_(std::move(pkey)), key_type_(type) {} 37 | Key(Key&&) = default; 38 | Key& operator=(Key&&) = default; 39 | 40 | EVP_PKEY* GetEvpPkey() const { return pkey_.get(); } 41 | adb::proto::KeyType GetKeyType() const { return key_type_; } 42 | static std::string ToPEMString(EVP_PKEY* pkey); 43 | 44 | private: 45 | bssl::UniquePtr pkey_; 46 | adb::proto::KeyType key_type_; 47 | }; // Key 48 | 49 | } // namespace crypto 50 | } // namespace adb 51 | -------------------------------------------------------------------------------- /porting/Makefile: -------------------------------------------------------------------------------- 1 | # Use bash with error checking 2 | SHELL := /bin/bash 3 | .SHELLFLAGS := -e -o pipefail -c 4 | 5 | # Stop on first error 6 | .POSIX: 7 | 8 | .PHONY: iphone lz4 zstd brotli protobuf adb clean 9 | 10 | iphone: lz4 zstd brotli protobuf adb 11 | @echo "✅ All builds completed successfully!" 12 | 13 | ## lz4 14 | lz4: lz4/iphoneos/arm64 lz4/iphonesimulator/arm64 lz4/iphonesimulator/x86_64 15 | lz4/%: 16 | @echo "🔨 Making $@ ..." 17 | TARGET=$@ OUTPUT=../output bash ./scripts/make-lz4.sh 18 | 19 | ## zstd 20 | zstd: zstd/iphoneos/arm64 zstd/iphonesimulator/arm64 zstd/iphonesimulator/x86_64 21 | zstd/%: 22 | @echo "🔨 Making $@ ..." 23 | TARGET=$@ OUTPUT=../output bash ./scripts/make-zstd.sh 24 | 25 | ## brotli 26 | brotli: brotli/iphoneos/arm64 brotli/iphonesimulator/arm64 brotli/iphonesimulator/x86_64 27 | brotli/%: 28 | @echo "🔨 Making $@ ..." 29 | TARGET=$@ OUTPUT=../output bash ./scripts/make-brotli.sh 30 | 31 | ## protobuf 32 | protobuf: protobuf/iphoneos/arm64 protobuf/iphonesimulator/arm64 protobuf/iphonesimulator/x86_64 33 | protobuf/%: 34 | @echo "🔨 Making $@ ..." 35 | TARGET=$@ OUTPUT=../output bash ./scripts/make-protobuf.sh 36 | 37 | ## adb 38 | adb: adb/iphoneos/arm64 adb/iphonesimulator/arm64 adb/iphonesimulator/x86_64 39 | adb/%: 40 | @echo "🔨 Making $@ ..." 41 | TARGET=$@ OUTPUT=../output bash ./scripts/make-adb.sh 42 | 43 | ## Clean all build artifacts 44 | clean: 45 | @echo "🧹 Cleaning build artifacts..." 46 | rm -rf ../output 47 | rm -rf ../android-tools/out 48 | rm -rf ../external/lz4/contrib/cmake_unofficial/out 49 | rm -rf ../external/zstd/build/cmake/out 50 | rm -rf ../external/brotli/out 51 | rm -rf ../external/protobuf/out 52 | @echo "✅ Clean completed!" 53 | -------------------------------------------------------------------------------- /porting/scripts/make-protobuf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . "$(dirname $0)/defines.sh"; 4 | 5 | cmake_root=$SOURCE_ROOT/external/protobuf/out; 6 | 7 | # Clean built products 8 | [[ -d "$cmake_root" ]] && rm -rfv "$cmake_root"; 9 | mkdir -pv "$cmake_root"; 10 | 11 | cd "$cmake_root" 12 | 13 | which protoc || { 14 | echo "** ❌ ERROR: protoc not installed"; 15 | exit 1; 16 | } 17 | protoc_version=$(protoc --version | cut -d' ' -f2); 18 | echo "→ Using protobuf v$protoc_version"; 19 | 20 | # Switch to version 21 | echo "→ Checking out protobuf v$protoc_version..."; 22 | (cd "$SOURCE_ROOT/external/protobuf" && git clean -f && git checkout "v$protoc_version" && git submodule update --init --recursive) 23 | 24 | # Fix absl version 25 | echo "→ Fixing abseil-cpp version..."; 26 | (cd "$SOURCE_ROOT/external/protobuf/third_party/abseil-cpp" && git checkout 20240722.0) 27 | 28 | # Copy CMakeLists.txt to source folder 29 | echo "→ Copying CMakeLists.txt..."; 30 | cp "$SOURCE_ROOT/porting/cmake/CMakeLists.protobuf.txt" "$SOURCE_ROOT/external/protobuf/CMakeLists.txt" 31 | 32 | echo "→ Running cmake..."; 33 | cmake .. -G Xcode -DCMAKE_TOOLCHAIN_FILE="$CMAKE_TOOLCHAIN_FILE" -DPLATFORM="$PLATFORM" \ 34 | -DDEPLOYMENT_TARGET="$DEPLOYMENT_TARGET" $CMAKE_COMPAT_FLAGS 35 | 36 | # Hack files 37 | echo "→ Patching source files..."; 38 | cp -av "$PORTING_ROOT/protobuf/src/google/protobuf/map_probe_benchmark.cc" \ 39 | "$SOURCE_ROOT/external/protobuf/src/google/protobuf/map_probe_benchmark.cc" 40 | 41 | echo "→ Building..."; 42 | cmake --build . --target protobuf --config Debug --parallel 8 43 | 44 | echo "→ Copying output..."; 45 | find . -name "*.a" -exec cp -av {} "$FULL_OUTPUT" \; 46 | 47 | echo "✅ protobuf build completed successfully!" 48 | -------------------------------------------------------------------------------- /example/adb-demo/adb-demo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /porting/adb/client/adb_porting.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "commandline.h" 5 | #include "adb_porting.h" 6 | #include "adb_trace.h" 7 | 8 | int capture_printf(char **output_buffer, size_t *output_size, const char *format, ...) { 9 | if (output_buffer == nullptr || output_size == nullptr || format == nullptr) { 10 | return -1; 11 | } 12 | 13 | va_list args; 14 | va_start(args, format); 15 | 16 | // Calculate the required buffer size. 17 | va_list args_copy; 18 | va_copy(args_copy, args); 19 | const int required_size = vsnprintf(nullptr, 0, format, args_copy) + 1; // +1 for null terminator 20 | va_end(args_copy); 21 | 22 | // If the buffer is empty or not large enough, expand the buffer. 23 | size_t current_used_size = *output_size; // Used output buffer size 24 | *output_buffer = static_cast(realloc(*output_buffer, current_used_size + required_size)); 25 | if (*output_buffer == nullptr) { 26 | perror("realloc"); 27 | va_end(args); 28 | return -1; 29 | } 30 | 31 | // Append new content to the end of the buffer. 32 | vsnprintf(*output_buffer + current_used_size, required_size, format, args); 33 | *output_size = current_used_size + required_size - 1; // Update the used buffer size (excluding null terminator) 34 | 35 | va_end(args); 36 | 37 | // return size of this output 38 | return required_size - 1; 39 | } 40 | 41 | int adb_commandline_porting(char **output_buffer, size_t *output_buffer_size, int argc, const char** argv) { 42 | return adb_commandline(output_buffer, output_buffer_size, argc, argv); 43 | } 44 | 45 | void adb_trace_init_porting(char** argv) { 46 | adb_trace_init(argv); 47 | } 48 | 49 | void adb_trace_enable_porting(int trace_tag) { 50 | adb_trace_enable((AdbTrace)trace_tag); 51 | } 52 | -------------------------------------------------------------------------------- /README.zh-cn.md: -------------------------------------------------------------------------------- 1 | # adb-mobile 2 | 3 | Android Debug Bridge (adb) 移植到移动端的版本,目标是支持 iOS App 和 Android App 通过集成本项目生成的库可以连接开发者模式 (TCP/IP Mode) 的 Android 设备,并执行支持的 ADB 命令。 4 | 5 | 当前仅支持 iOS 平台,Android 版本正在开发中。 6 | 7 | [英文文档](README.md) 8 | 9 | ## 编译库 10 | 11 | 项目已经适配好了编译参数和脚本,仅需通过简单几步即可生成 libadb: 12 | 13 | ```sh 14 | # 1. 检出依赖的 submodules 15 | git submodule update --init --recursive 16 | 17 | # 2. 编译 18 | make 19 | ``` 20 | 21 | 生成的 libadb 产物可以在项目目录下的 output 文件夹里找到,iOS 为 libadb.a。 22 | 23 | ## 集成到工程 24 | 25 | iOS 工程: 26 | 27 | ```ini 28 | # 设置头文件路径 - Header Search Paths 29 | HEADER_SEARCH_PATHS = $(SRCROOT)/../../output/include 30 | 31 | # 设置库搜索路径 - Library Search Paths 32 | LIBRARY_SEARCH_PATHS = $(SRCROOT)/../../output 33 | 34 | # 设置连接器参数 - Other Linker Flags 35 | OTHER_LDFLAGS = -ladb -lc++ -lz 36 | ``` 37 | 38 | ## 使用说明 39 | 40 | iOS App: 41 | 42 | ```objc 43 | #import "adb_puiblic.h" 44 | 45 | // 开启调试信息输出,有助于排查问题 46 | adb_enable_trace(); 47 | 48 | // 设置 ADB 的监听端口,默认为 5037 49 | adb_set_server_port("15037"); 50 | 51 | // 设置 ADB 私钥和数据存放目录,一般为 App 的 Documents 目录 52 | NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 53 | const char *document_home = documentPaths.lastObject.UTF8String; 54 | adb_set_home(document_home); 55 | 56 | // 连接 Android 设备 57 | const char *argv[] = { "connect", "x.x.x." }; 58 | const char *output = null; 59 | int ret = adb_commandline_porting(2, argv, &output); 60 | 61 | // 执行 ADB 命令 62 | const char *argv[] = { "shell", "ip", "route" }; 63 | const char *output = null; 64 | int ret = adb_commandline_porting(2, argv, &output); 65 | ``` 66 | 67 | ADB 状态回调: 68 | 69 | ```objc 70 | // 实现 adb_connect_status_updated 回调方法,可以获取 adb 连接状态 71 | void adb_connect_status_updated(const char *serial, const char *status) { 72 | NSLog(@"ADB Status: %s, %s", serial, status); 73 | } 74 | ``` 75 | 76 | 也可以通过 example/adb-demo 工程查看具体使用方法。 -------------------------------------------------------------------------------- /example/adb-demo/adb-demo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "2x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "83.5x83.5" 82 | }, 83 | { 84 | "idiom" : "ios-marketing", 85 | "scale" : "1x", 86 | "size" : "1024x1024" 87 | } 88 | ], 89 | "info" : { 90 | "author" : "xcode", 91 | "version" : 1 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /example/adb-demo/adb-demo/SceneDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.m 3 | // adb-demo 4 | // 5 | // Created by Ethan on 2022/6/1. 6 | // 7 | 8 | #import "SceneDelegate.h" 9 | 10 | @interface SceneDelegate () 11 | 12 | @end 13 | 14 | @implementation SceneDelegate 15 | 16 | 17 | - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions { 18 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 19 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 20 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 21 | } 22 | 23 | 24 | - (void)sceneDidDisconnect:(UIScene *)scene { 25 | // Called as the scene is being released by the system. 26 | // This occurs shortly after the scene enters the background, or when its session is discarded. 27 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 28 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). 29 | } 30 | 31 | 32 | - (void)sceneDidBecomeActive:(UIScene *)scene { 33 | // Called when the scene has moved from an inactive state to an active state. 34 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 35 | } 36 | 37 | 38 | - (void)sceneWillResignActive:(UIScene *)scene { 39 | // Called when the scene will move from an active state to an inactive state. 40 | // This may occur due to temporary interruptions (ex. an incoming phone call). 41 | } 42 | 43 | 44 | - (void)sceneWillEnterForeground:(UIScene *)scene { 45 | // Called as the scene transitions from the background to the foreground. 46 | // Use this method to undo the changes made on entering the background. 47 | } 48 | 49 | 50 | - (void)sceneDidEnterBackground:(UIScene *)scene { 51 | // Called as the scene transitions from the foreground to the background. 52 | // Use this method to save data, release shared resources, and store enough scene-specific state information 53 | // to restore the scene back to its current state. 54 | } 55 | 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /porting/adb/client/transport_usb.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #define TRACE_TAG TRANSPORT 18 | 19 | #include "sysdeps.h" 20 | 21 | #include "client/usb.h" 22 | 23 | #include 24 | 25 | #include "sysdeps.h" 26 | #include "transport.h" 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #include "adb.h" 33 | 34 | #if defined(__APPLE__) 35 | #define CHECK_PACKET_OVERFLOW 0 36 | #else 37 | #define CHECK_PACKET_OVERFLOW 1 38 | #endif 39 | 40 | // Call usb_read using a buffer having a multiple of usb_get_max_packet_size() bytes 41 | // to avoid overflow. See http://libusb.sourceforge.net/api-1.0/packetoverflow.html. 42 | static int UsbReadMessage(usb_handle* h, amessage* msg) { 43 | D("UsbReadMessage"); 44 | return 0; 45 | } 46 | 47 | // Call usb_read using a buffer having a multiple of usb_get_max_packet_size() bytes 48 | // to avoid overflow. See http://libusb.sourceforge.net/api-1.0/packetoverflow.html. 49 | static int UsbReadPayload(usb_handle* h, apacket* p) { 50 | return 0; 51 | } 52 | 53 | static int remote_read(apacket* p, usb_handle* usb) { 54 | return 0; 55 | } 56 | 57 | UsbConnection::~UsbConnection() { 58 | } 59 | 60 | bool UsbConnection::Read(apacket* packet) { 61 | return false; 62 | } 63 | 64 | bool UsbConnection::Write(apacket* packet) { 65 | return false; 66 | } 67 | 68 | bool UsbConnection::DoTlsHandshake(RSA* key, std::string* auth_key) { 69 | // TODO: support TLS for usb connections 70 | LOG(FATAL) << "Not supported yet."; 71 | return false; 72 | } 73 | 74 | void UsbConnection::Reset() { 75 | } 76 | 77 | void UsbConnection::Close() { 78 | } 79 | 80 | void init_usb_transport(atransport* t, usb_handle* h) { 81 | D("transport: usb"); 82 | } 83 | 84 | bool is_adb_interface(int usb_class, int usb_subclass, int usb_protocol) { 85 | return false; 86 | } 87 | 88 | bool is_libusb_enabled() { 89 | return false; 90 | } 91 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # adb-mobile 2 | 3 | Android Debug Bridge (adb) ported version for mobile apps, the goal of this project is supporting both iOS App and Android App to connect to Android devices in developer mode (TCP/IP Mode) and execute supported ADB commands by integrating the libraries generated by this project. 4 | 5 | Currently only iOS platform is supported, Android version is under development. 6 | 7 | Update Log: 8 | 9 | * 2024-11: Upgrade porting project to ADB 35.0.2, and some API changes. 10 | 11 | [中文文档](README.zh-cn.md) 12 | 13 | ## Build Library 14 | 15 | The project has already finished the build scripts to generate libadb in a few simple steps: 16 | 17 | ```sh 18 | # 1. checkout all the submodules 19 | git submodule update --init --recursive 20 | 21 | # 2. build 22 | make 23 | ``` 24 | 25 | The generated libadb product can be found in the output folder of the project directory, for iOS is "libadb.a" that you can located at output folder. 26 | 27 | ## Integrate 28 | 29 | iOS Project: 30 | 31 | ```ini 32 | # Build Settings - Header Search Paths 33 | HEADER_SEARCH_PATHS = $(SRCROOT)/../../output/include 34 | 35 | # Build Settings - Library Search Paths 36 | LIBRARY_SEARCH_PATHS = $(SRCROOT)/../../output 37 | 38 | # Build Settings - Other Linker Flags 39 | OTHER_LDFLAGS = -ladb -lc++ -lz 40 | ``` 41 | 42 | ## API Usage 43 | 44 | iOS App: 45 | 46 | ```objc 47 | #import "adb_puiblic.h" 48 | 49 | // Enable trace debug message 50 | adb_enable_trace(); 51 | 52 | // Setup the ADB listening port, the default is 5037 53 | adb_set_server_port("15037"); 54 | 55 | // Setup the ADB private key and data directory, which is usually the Documents directory of the App 56 | NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 57 | const char *document_home = documentPaths.lastObject.UTF8String; 58 | adb_set_home(document_home); 59 | 60 | // Connecting Android devices 61 | const char *argv[] = { "connect", "x.x.x." }; 62 | 63 | // Initialize the output buffer 64 | char *output = NULL; 65 | size_t output_size = 0; 66 | int ret = adb_commandline_porting(&output, &output_size, 2, argv); 67 | 68 | // Execute the ADB commands 69 | const char *argv[] = { "shell", "ip", "route" }; 70 | char *output = NULL; 71 | size_t output_size = 0; 72 | int ret = adb_commandline_porting((&output, &output_size, 2, argv); 73 | ``` 74 | 75 | ADB Status Callback: 76 | 77 | ```objc 78 | // By define the adb_connect_status_updated callback method to get the adb connection status 79 | void adb_connect_status_updated(const char *serial, const char *status) { 80 | NSLog(@"ADB Status: %s, %s", serial, status); 81 | } 82 | ``` 83 | 84 | You can also see how to use this library in the example/adb-demo project. 85 | -------------------------------------------------------------------------------- /porting/protobuf.rb: -------------------------------------------------------------------------------- 1 | class Protobuf < Formula 2 | desc "Protocol buffers (Google's data interchange format)" 3 | homepage "https://protobuf.dev/" 4 | url "https://github.com/protocolbuffers/protobuf/releases/download/v28.3/protobuf-28.3.tar.gz" 5 | sha256 "7c3ebd7aaedd86fa5dc479a0fda803f602caaf78d8aff7ce83b89e1b8ae7442a" 6 | license "BSD-3-Clause" 7 | 8 | livecheck do 9 | url :stable 10 | strategy :github_latest 11 | end 12 | 13 | bottle do 14 | sha256 cellar: :any, arm64_sequoia: "cec9ebadfa0ff65482e3e195660a13ca43844792ed98822ad10b2bca4ef8e45c" 15 | sha256 cellar: :any, arm64_sonoma: "537ab66678b04aa2e770bc65aa710d63f37a1b5a1c65717c2e8954cdbb94d883" 16 | sha256 cellar: :any, arm64_ventura: "4a0a0e6db20bd8444491b35c34d6c51e42b493bcb3a3e6b309bdbd06bd85eab9" 17 | sha256 cellar: :any, sonoma: "446d7adcff7604c570a50f2aca5484612a32481cbe677e1bccdbe62989062de4" 18 | sha256 cellar: :any, ventura: "e52757d69a1f986314de2dd508eae336a544fb96838038fb76d0915b017bcc32" 19 | sha256 cellar: :any_skip_relocation, x86_64_linux: "b62500bd2fcf54e820720441d969bca0204ce95bab6e16dcb247bb28398356bd" 20 | end 21 | 22 | depends_on "cmake" => :build 23 | depends_on "abseil" 24 | uses_from_macos "zlib" 25 | 26 | on_macos do 27 | # We currently only run tests on macOS. 28 | # Running them on Linux requires rebuilding googletest with `-fPIC`. 29 | depends_on "googletest" => :build 30 | end 31 | 32 | patch do 33 | url "https://github.com/protocolbuffers/protobuf/commit/e490bff517916495ed3a900aa85791be01f674f5.patch?full_index=1" 34 | sha256 "7e89d0c379d89b24cb6fe795cd9d68e72f0b83fcc95dd91af721d670ad466022" 35 | end 36 | 37 | def install 38 | # Keep `CMAKE_CXX_STANDARD` in sync with the same variable in `abseil.rb`. 39 | abseil_cxx_standard = 17 40 | cmake_args = %W[ 41 | -DBUILD_SHARED_LIBS=ON 42 | -Dprotobuf_BUILD_LIBPROTOC=ON 43 | -Dprotobuf_BUILD_SHARED_LIBS=ON 44 | -Dprotobuf_INSTALL_EXAMPLES=ON 45 | -Dprotobuf_BUILD_TESTS=#{OS.mac? ? "ON" : "OFF"} 46 | -Dprotobuf_USE_EXTERNAL_GTEST=ON 47 | -Dprotobuf_ABSL_PROVIDER=package 48 | -Dprotobuf_JSONCPP_PROVIDER=package 49 | ] 50 | cmake_args << "-DCMAKE_CXX_STANDARD=#{abseil_cxx_standard}" 51 | 52 | system "cmake", "-S", ".", "-B", "build", *cmake_args, *std_cmake_args 53 | system "cmake", "--build", "build" 54 | system "ctest", "--test-dir", "build", "--verbose" if OS.mac? 55 | system "cmake", "--install", "build" 56 | 57 | (share/"vim/vimfiles/syntax").install "editors/proto.vim" 58 | elisp.install "editors/protobuf-mode.el" 59 | end 60 | 61 | test do 62 | testdata = <<~EOS 63 | syntax = "proto3"; 64 | package test; 65 | message TestCase { 66 | string name = 4; 67 | } 68 | message Test { 69 | repeated TestCase case = 1; 70 | } 71 | EOS 72 | (testpath/"test.proto").write testdata 73 | system bin/"protoc", "test.proto", "--cpp_out=." 74 | end 75 | end 76 | -------------------------------------------------------------------------------- /porting/scripts/make-adb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . "$(dirname $0)/defines.sh"; 4 | 5 | cmake_root=$SOURCE_ROOT/android-tools/out; 6 | 7 | # Init update android tools submodules FIRST (before creating output dir) 8 | # Force checkout to discard local changes 9 | echo "→ Reset android tools and submodules"; 10 | (cd "$SOURCE_ROOT/android-tools" && git checkout -f . && git clean -fd -e out) 11 | 12 | # Force reset all submodules 13 | for dep_repo in "$SOURCE_ROOT"/android-tools/vendor/*; do 14 | [[ -d "$dep_repo" ]] || continue; 15 | echo "→ Force reset $dep_repo"; 16 | (cd "$dep_repo" && git reset --hard HEAD && git clean -fd && git am --abort 2>/dev/null || true) 17 | done 18 | 19 | # Now update submodules 20 | echo "→ Updating submodules..."; 21 | (cd "$SOURCE_ROOT/android-tools" && git submodule update --init --recursive --force) 22 | 23 | # Clean and create build directory AFTER git operations 24 | [[ -d "$cmake_root" ]] && rm -rfv "$cmake_root"; 25 | mkdir -pv "$cmake_root"; 26 | 27 | cd "$cmake_root" 28 | 29 | echo "→ Running cmake..."; 30 | # Point OpenSSL to BoringSSL and exclude Homebrew's OpenSSL from include paths 31 | BORINGSSL_DIR="$SOURCE_ROOT/android-tools/vendor/boringssl" 32 | 33 | cmake -DCMAKE_OSX_SYSROOT="$SDK_NAME" -DCMAKE_OSX_ARCHITECTURES="$ARCH_NAME" \ 34 | -DCMAKE_OSX_DEPLOYMENT_TARGET="$DEPLOYMENT_TARGET" -DCMAKE_BUILD_TYPE=Debug \ 35 | -DOPENSSL_ROOT_DIR="$BORINGSSL_DIR" \ 36 | -DOPENSSL_INCLUDE_DIR="$BORINGSSL_DIR/include" \ 37 | -DCMAKE_DISABLE_FIND_PACKAGE_OpenSSL=ON \ 38 | -DANDROID_TOOLS_USE_BUNDLED_FMT=ON \ 39 | -DANDROID_TOOLS_USE_BUNDLED_LIBUSB=ON \ 40 | $CMAKE_COMPAT_FLAGS .. 41 | 42 | # Patch generated Makefiles to prioritize BoringSSL includes 43 | # Prepend BoringSSL include to CXX_INCLUDES so it's searched first for openssl headers 44 | echo "→ Patching include paths to prioritize BoringSSL..." 45 | find "$cmake_root" -name "flags.make" -exec sed -i '' \ 46 | "s|^CXX_INCLUDES = |CXX_INCLUDES = -I$BORINGSSL_DIR/include |g" {} \; 47 | 48 | # Hack to fix build files, these actions must execute after cmake, otherwise may cause file conflict 49 | # copy sys/user.h for libbase 50 | echo "→ Copying porting files..."; 51 | cp -av "$PORTING_ROOT/adb/include/sys" "$SOURCE_ROOT/android-tools/vendor/libbase/include/" 52 | ln -sfv "$SOURCE_ROOT/android-tools/vendor/core/diagnose_usb/include/diagnose_usb.h" \ 53 | "$SOURCE_ROOT/android-tools/vendor/core/include" 54 | 55 | # Hack source codes 56 | cp -av "$PORTING_ROOT/adb/"*.cpp "$SOURCE_ROOT/android-tools/vendor/adb/" 57 | cp -av "$PORTING_ROOT/adb/client/"* "$SOURCE_ROOT/android-tools/vendor/adb/client/" 58 | cp -av "$PORTING_ROOT/adb/fdevent/"* "$SOURCE_ROOT/android-tools/vendor/adb/fdevent/" 59 | cp -av "$PORTING_ROOT/adb/crypto/include/adb/crypto/"* "$SOURCE_ROOT/android-tools/vendor/adb/crypto/include/adb/crypto/" 60 | 61 | # Remove abort 62 | echo "→ Patching libbase logging.cpp..."; 63 | grep -v "abort();" "$SOURCE_ROOT/android-tools/vendor/libbase/logging.cpp" > "$SOURCE_ROOT/android-tools/vendor/libbase/logging.cpp.tmp" 64 | mv -fv "$SOURCE_ROOT/android-tools/vendor/libbase/logging.cpp.tmp" \ 65 | "$SOURCE_ROOT/android-tools/vendor/libbase/logging.cpp" 66 | 67 | echo "→ Building..."; 68 | make -j16 libadb crypto decrepit libcutils libzip libdiagnoseusb libbase \ 69 | libadb_crypto_defaults libcrypto libadb_tls_connection_defaults 70 | 71 | echo "→ Copying output..."; 72 | find . -name "*.a" -exec cp -av {} "$FULL_OUTPUT" \; 73 | 74 | echo "✅ Build completed successfully!" 75 | -------------------------------------------------------------------------------- /porting/cmake/CMakeLists.protobuf.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4.1) 2 | set(CMAKE_CXX_STANDARD 17) 3 | project(protobuf C CXX) 4 | set(PROJECT_NAME protobuf) 5 | 6 | message(STATUS "CMake Path: ${CMAKE_CURRENT_SOURCE_DIR}") 7 | message(STATUS "Platform: ${PLATFORM}") 8 | 9 | include_directories( 10 | ../../external/protobuf/src 11 | ../../external/protobuf/third_party/abseil-cpp 12 | ../../external/protobuf/third_party/utf8_range 13 | ../../external/protobuf/third_party/googletest/googletest/include 14 | ../../external/protobuf/third_party/googletest/googlemock/include 15 | ) 16 | 17 | file(GLOB protobuf_srcs 18 | ../../external/protobuf/src/google/protobuf/*.cc 19 | ../../external/protobuf/src/google/protobuf/stubs/*.cc 20 | ../../external/protobuf/src/google/protobuf/io/*.cc 21 | ../../external/protobuf/src/google/protobuf/util/*cc 22 | ../../external/protobuf/src/google/protobuf/util/internal/*.cc 23 | ../../external/protobuf/third_party/abseil-cpp/absl/strings/*.cc 24 | ../../external/protobuf/third_party/abseil-cpp/absl/strings/internal/*.cc 25 | ../../external/protobuf/third_party/abseil-cpp/absl/strings/internal/str_format/*.cc 26 | ../../external/protobuf/third_party/abseil-cpp/absl/types/*.cc 27 | ../../external/protobuf/third_party/abseil-cpp/absl/flags/*.cc 28 | ../../external/protobuf/third_party/abseil-cpp/absl/synchronization/*.cc 29 | ../../external/protobuf/third_party/abseil-cpp/absl/synchronization/internal/*.cc 30 | ../../external/protobuf/third_party/abseil-cpp/absl/hash/*.cc 31 | ../../external/protobuf/third_party/abseil-cpp/absl/hash/internal/*.cc 32 | ../../external/protobuf/third_party/abseil-cpp/absl/debugging/*.cc 33 | ../../external/protobuf/third_party/abseil-cpp/absl/debugging/internal/*.cc 34 | ../../external/protobuf/third_party/abseil-cpp/absl/crc/*.cc 35 | ../../external/protobuf/third_party/abseil-cpp/absl/crc/internal/*.cc 36 | ../../external/protobuf/third_party/abseil-cpp/absl/status/*.cc 37 | ../../external/protobuf/third_party/abseil-cpp/absl/status/internal/*.cc 38 | ../../external/protobuf/third_party/abseil-cpp/absl/time/*.cc 39 | ../../external/protobuf/third_party/abseil-cpp/absl/time/internal/*.cc 40 | ../../external/protobuf/third_party/abseil-cpp/absl/time/internal/cctz/src/*.cc 41 | ../../external/protobuf/third_party/abseil-cpp/absl/container/*.cc 42 | ../../external/protobuf/third_party/abseil-cpp/absl/container/internal/*.cc 43 | ../../external/protobuf/third_party/abseil-cpp/absl/numeric/*.cc 44 | ../../external/protobuf/third_party/abseil-cpp/absl/profiling/internal/*.cc 45 | ../../external/protobuf/third_party/abseil-cpp/absl/log/*.cc 46 | ../../external/protobuf/third_party/abseil-cpp/absl/log/internal/*.cc 47 | ../../external/protobuf/third_party/abseil-cpp/absl/random/*.cc 48 | ../../external/protobuf/third_party/abseil-cpp/absl/random/internal/*.cc 49 | ../../external/protobuf/third_party/abseil-cpp/absl/functional/*.cc 50 | ../../external/protobuf/third_party/abseil-cpp/absl/base/*.cc 51 | ../../external/protobuf/third_party/abseil-cpp/absl/base/internal/*.cc 52 | ../../external/protobuf/third_party/abseil-cpp/absl/debugging/*.cc 53 | ../../external/protobuf/third_party/abseil-cpp/absl/debugging/internal/*.cc 54 | ../../external/protobuf/third_party/utf8_range/*.cc 55 | ../../external/protobuf/third_party/utf8_range/utf8_range.c 56 | ) 57 | list(FILTER protobuf_srcs EXCLUDE REGEX "benchmarks.cc|.*_test\\.*|.*_unittest\\.*|.*_benchmark\\.*|.*test_util.*|.*_tester.*") 58 | 59 | foreach(src IN LISTS protobuf_srcs) 60 | message(STATUS "- ${src}") 61 | endforeach() 62 | 63 | add_library(protobuf STATIC 64 | ${protobuf_srcs} 65 | ) 66 | -------------------------------------------------------------------------------- /example/adb-demo/adb-demo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // adb-demo 4 | // 5 | // Created by Ethan on 2022/6/1. 6 | // 7 | 8 | #import "ViewController.h" 9 | #import "adb_public.h" 10 | 11 | void adb_connect_status_updated(const char *serial, const char *status) { 12 | NSLog(@"ADB Status: %s, %s", serial, status); 13 | } 14 | 15 | @interface ViewController () 16 | @property (weak, nonatomic) IBOutlet UITextField *adbHost; 17 | @property (weak, nonatomic) IBOutlet UIButton *adbConnect; 18 | @property (weak, nonatomic) IBOutlet UITextField *adbCommand; 19 | @property (weak, nonatomic) IBOutlet UIButton *adbExecute; 20 | 21 | @end 22 | 23 | @implementation ViewController 24 | 25 | - (void)viewDidLoad { 26 | [super viewDidLoad]; 27 | 28 | adb_enable_trace(); 29 | adb_set_server_port("15037"); 30 | 31 | NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 32 | const char *document_home = documentPaths.lastObject.UTF8String; 33 | adb_set_home(document_home); 34 | 35 | // Load defaults 36 | NSString *host = [NSUserDefaults.standardUserDefaults objectForKey:@"adbHost"]; 37 | if (host.length > 0) { 38 | self.adbHost.text = host; 39 | } 40 | NSString *command = [NSUserDefaults.standardUserDefaults objectForKey:@"adbCommand"]; 41 | if (command.length > 0) { 42 | self.adbCommand.text = command; 43 | } 44 | 45 | // Disable auto-correction 46 | self.adbHost.autocorrectionType = UITextAutocorrectionTypeNo; 47 | self.adbCommand.autocorrectionType = UITextAutocorrectionTypeNo; 48 | } 49 | 50 | -(NSString *)adbExecute:(NSArray *)commands success:(BOOL*)success { 51 | const char *argv[commands.count]; 52 | for (NSInteger i = 0; i < commands.count; i++) { 53 | argv[i] = commands[i].UTF8String; 54 | } 55 | 56 | char *output = NULL; 57 | size_t output_size = 0; 58 | int ret = adb_commandline_porting(&output, &output_size, (int)commands.count, argv); 59 | *success = ret == 0; 60 | 61 | if (output_size > 0) { 62 | return [NSString stringWithUTF8String:output]; 63 | } 64 | 65 | return @""; 66 | } 67 | 68 | -(void)showAlert:(NSString *)message { 69 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Message" message:message preferredStyle:(UIAlertControllerStyleAlert)]; 70 | [alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:(UIAlertActionStyleCancel) handler:nil]]; 71 | [self presentViewController:alertController animated:YES completion:nil]; 72 | } 73 | 74 | - (IBAction)connect:(id)sender { 75 | if (self.adbHost.text.length == 0) { 76 | [self showAlert:@"Host is empty!"]; 77 | return; 78 | } 79 | 80 | [self.adbHost endEditing:YES]; 81 | 82 | // Save text value into user defaults 83 | [NSUserDefaults.standardUserDefaults setObject:self.adbHost.text forKey:@"adbHost"]; 84 | [NSUserDefaults.standardUserDefaults synchronize]; 85 | 86 | NSDate *timeStart = [NSDate date]; 87 | 88 | BOOL success = NO; 89 | NSString *message = [self adbExecute:@[ @"connect", self.adbHost.text ] success:&success]; 90 | NSLog(@"Time cost: %0.4fs", -[timeStart timeIntervalSinceNow]); 91 | [self showAlert:[NSString stringWithFormat:@"Success: %@\nMessage: %@", success?@"YES":@"NO", message]]; 92 | } 93 | 94 | - (IBAction)execute:(id)sender { 95 | if (self.adbCommand.text.length == 0) { 96 | [self showAlert:@"Command is empty!"]; 97 | return; 98 | } 99 | 100 | [self.adbCommand endEditing:YES]; 101 | 102 | // Save text value into user defaults 103 | [[NSUserDefaults standardUserDefaults] setObject:self.adbCommand.text forKey:@"adbCommand"]; 104 | [[NSUserDefaults standardUserDefaults] synchronize]; 105 | 106 | BOOL success = NO; 107 | NSString *message = [self adbExecute:[self.adbCommand.text componentsSeparatedByString:@" "] success:&success]; 108 | [self showAlert:[NSString stringWithFormat:@"Success: %@\nMessage: %@", success?@"YES":@"NO", message]]; 109 | } 110 | 111 | @end 112 | -------------------------------------------------------------------------------- /porting/adb/client/commandline.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef COMMANDLINE_H 18 | #define COMMANDLINE_H 19 | 20 | #include 21 | 22 | #include 23 | 24 | #include "adb.h" 25 | #include "adb_client.h" 26 | #include "adb_unique_fd.h" 27 | #include "transport.h" 28 | 29 | // Callback used to handle the standard streams (stdout and stderr) sent by the 30 | // device's upon receiving a command. 31 | 32 | // 33 | class StandardStreamsCallbackInterface { 34 | public: 35 | StandardStreamsCallbackInterface() { 36 | } 37 | // Handles the stdout output from devices supporting the Shell protocol. 38 | // Returns true on success and false on failure. 39 | virtual bool OnStdout(const char* buffer, size_t length) = 0; 40 | 41 | // Handles the stderr output from devices supporting the Shell protocol. 42 | // Returns true on success and false on failure. 43 | virtual bool OnStderr(const char* buffer, size_t length) = 0; 44 | 45 | // Indicates the communication is finished and returns the appropriate error 46 | // code. 47 | // 48 | // |status| has the status code returning by the underlying communication 49 | // channels 50 | virtual int Done(int status) = 0; 51 | 52 | protected: 53 | static bool OnStream(std::string* string, FILE* stream, const char* buffer, size_t length, 54 | bool returnErrors) { 55 | if (string != nullptr) { 56 | string->append(buffer, length); 57 | return true; 58 | } else { 59 | bool okay = (fwrite(buffer, 1, length, stream) == length); 60 | fflush(stream); 61 | return returnErrors ? okay : true; 62 | } 63 | } 64 | 65 | private: 66 | DISALLOW_COPY_AND_ASSIGN(StandardStreamsCallbackInterface); 67 | }; 68 | 69 | // Default implementation that redirects the streams to the equivalent host 70 | // stream or to a string passed to the constructor. 71 | class DefaultStandardStreamsCallback : public StandardStreamsCallbackInterface { 72 | public: 73 | // If |stdout_str| is non-null, OnStdout will append to it. 74 | // If |stderr_str| is non-null, OnStderr will append to it. 75 | DefaultStandardStreamsCallback(std::string* stdout_str, std::string* stderr_str) 76 | : stdout_str_(stdout_str), stderr_str_(stderr_str), returnErrors_(false) { 77 | } 78 | DefaultStandardStreamsCallback(std::string* stdout_str, std::string* stderr_str, 79 | bool returnErrors) 80 | : stdout_str_(stdout_str), stderr_str_(stderr_str), returnErrors_(returnErrors) { 81 | } 82 | 83 | bool OnStdout(const char* buffer, size_t length) { 84 | return OnStream(stdout_str_, stdout, buffer, length, returnErrors_); 85 | } 86 | 87 | bool OnStderr(const char* buffer, size_t length) { 88 | return OnStream(stderr_str_, stderr, buffer, length, returnErrors_); 89 | } 90 | 91 | int Done(int status) { 92 | return status; 93 | } 94 | 95 | void ReturnErrors(bool returnErrors) { 96 | returnErrors_ = returnErrors; 97 | } 98 | 99 | private: 100 | std::string* stdout_str_; 101 | std::string* stderr_str_; 102 | bool returnErrors_; 103 | 104 | DISALLOW_COPY_AND_ASSIGN(DefaultStandardStreamsCallback); 105 | }; 106 | 107 | class SilentStandardStreamsCallbackInterface : public StandardStreamsCallbackInterface { 108 | public: 109 | SilentStandardStreamsCallbackInterface() = default; 110 | bool OnStdout(const char*, size_t) override final { return true; } 111 | bool OnStderr(const char*, size_t) override final { return true; } 112 | int Done(int status) override final { return status; } 113 | }; 114 | 115 | // Singleton. 116 | extern DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK; 117 | 118 | int adb_commandline(char** out_buf, size_t* out_buf_size, int argc, const char** argv); 119 | 120 | // Helper retrieval function. 121 | const std::optional& adb_get_feature_set_or_die(char** out_buf, size_t* out_buf_size); 122 | 123 | bool copy_to_file(int inFd, int outFd); 124 | 125 | // Connects to the device "shell" service with |command| and prints the 126 | // resulting output. 127 | // if |callback| is non-null, stdout/stderr output will be handled by it. 128 | int send_shell_command( 129 | const std::string& command, bool disable_shell_protocol = false, 130 | StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK); 131 | 132 | // Reads from |fd| and prints received data. If |use_shell_protocol| is true 133 | // this expects that incoming data will use the shell protocol, in which case 134 | // stdout/stderr are routed independently and the remote exit code will be 135 | // returned. 136 | // if |callback| is non-null, stdout/stderr output will be handled by it. 137 | int read_and_dump(char **output_buffer, size_t *output_size, borrowed_fd fd, bool use_shell_protocol = false, 138 | StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK); 139 | 140 | // Connects to the device "abb" service with |command| and returns the fd. 141 | template 142 | unique_fd send_abb_exec_command(const ContainerT& command_args, std::string* error) { 143 | std::string service_string = "abb_exec:" + android::base::Join(command_args, ABB_ARG_DELIMETER); 144 | 145 | unique_fd fd(adb_connect(service_string, error)); 146 | if (fd < 0) { 147 | fprintf(stderr, "adb: failed to run abb_exec. Error: %s\n", error->c_str()); 148 | return unique_fd{}; 149 | } 150 | return fd; 151 | } 152 | 153 | #endif // COMMANDLINE_H 154 | -------------------------------------------------------------------------------- /porting/adb/fdevent/fdevent.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2006 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef __FDEVENT_H 18 | #define __FDEVENT_H 19 | 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | 36 | #include "adb_unique_fd.h" 37 | 38 | // Events that may be observed 39 | #define FDE_READ 0x0001 40 | #define FDE_WRITE 0x0002 41 | #define FDE_ERROR 0x0004 42 | #define FDE_TIMEOUT 0x0008 43 | 44 | struct fdevent; 45 | 46 | typedef void (*fd_func)(int fd, unsigned events, void *userdata); 47 | typedef void (*fd_func2)(struct fdevent* fde, unsigned events, void* userdata); 48 | 49 | void invoke_fde(struct fdevent* fde, unsigned events); 50 | std::string dump_fde(const fdevent* fde); 51 | 52 | struct fdevent_event { 53 | fdevent* fde; 54 | unsigned events; 55 | fdevent_event(fdevent* pfde, unsigned ev) : fde(pfde), events(ev) {} 56 | }; 57 | 58 | struct fdevent final { 59 | uint64_t id; 60 | 61 | unique_fd fd; 62 | 63 | uint16_t state = 0; 64 | std::optional timeout; 65 | std::chrono::steady_clock::time_point last_active; 66 | 67 | std::variant func; 68 | void* arg = nullptr; 69 | }; 70 | 71 | struct fdevent_context { 72 | public: 73 | virtual ~fdevent_context() = default; 74 | 75 | // Allocate and initialize a new fdevent object. 76 | fdevent* Create(unique_fd fd, std::variant func, void* arg); 77 | 78 | // Deallocate an fdevent object, returning the file descriptor that was owned by it. 79 | // Note that this calls Set, which is a virtual method, so destructors that call this must be 80 | // final. 81 | unique_fd Destroy(fdevent* fde); 82 | 83 | protected: 84 | virtual void Register(fdevent*) = 0; 85 | virtual void Unregister(fdevent*) = 0; 86 | 87 | public: 88 | // Change which events should cause notifications. 89 | virtual void Set(fdevent* fde, unsigned events) = 0; 90 | void Add(fdevent* fde, unsigned events); 91 | void Del(fdevent* fde, unsigned events); 92 | 93 | // Set a timeout on an fdevent. 94 | // If no events are triggered by the timeout, an FDE_TIMEOUT will be generated. 95 | // Note timeouts are not defused automatically; if a timeout is set on an fdevent, it will 96 | // trigger repeatedly every |timeout| ms. 97 | void SetTimeout(fdevent* fde, std::optional timeout); 98 | 99 | protected: 100 | std::optional CalculatePollDuration(); 101 | void HandleEvents(const std::vector& events); 102 | 103 | private: 104 | // Run all pending functions enqueued via Run(). 105 | void FlushRunQueue() EXCLUDES(run_queue_mutex_); 106 | 107 | public: 108 | // Loop until TerminateLoop is called, handling events. 109 | // Implementations should call FlushRunQueue on every iteration, and check the value of 110 | // terminate_loop_ to determine whether to stop. 111 | virtual void Loop() = 0; 112 | 113 | // Assert that the caller is executing in the context of the execution 114 | // thread that invoked Loop(). 115 | void CheckLooperThread() const; 116 | 117 | // Queue an operation to be run on the looper thread. 118 | void Run(std::function fn); 119 | 120 | // Test-only functionality: 121 | void TerminateLoop(); 122 | virtual size_t InstalledCount() = 0; 123 | 124 | // Wait for the loop to actually exit (after TerminateLoop is called) 125 | void WaitForLoopExit(); 126 | 127 | // Notify waiting threads that the loop has exited (called from Loop implementations) 128 | void NotifyLoopExit(); 129 | 130 | protected: 131 | // Interrupt the run loop. 132 | virtual void Interrupt() = 0; 133 | 134 | std::optional looper_thread_id_ = std::nullopt; 135 | std::atomic terminate_loop_ = false; 136 | 137 | // Synchronization for waiting on loop exit 138 | std::mutex loop_exit_mutex_; 139 | std::condition_variable loop_exit_cv_; 140 | 141 | std::map installed_fdevents_; 142 | 143 | private: 144 | uint64_t fdevent_id_ = 0; 145 | std::mutex run_queue_mutex_; 146 | std::deque> run_queue_ GUARDED_BY(run_queue_mutex_); 147 | 148 | std::set fdevent_set_; 149 | }; 150 | 151 | // Backwards compatibility shims that forward to the global fdevent_context. 152 | fdevent* fdevent_create(int fd, fd_func func, void* arg); 153 | fdevent* fdevent_create(int fd, fd_func2 func, void* arg); 154 | 155 | unique_fd fdevent_release(fdevent* fde); 156 | void fdevent_destroy(fdevent* fde); 157 | 158 | void fdevent_set(fdevent *fde, unsigned events); 159 | void fdevent_add(fdevent *fde, unsigned events); 160 | void fdevent_del(fdevent *fde, unsigned events); 161 | void fdevent_set_timeout(fdevent* fde, std::optional timeout); 162 | void fdevent_loop(); 163 | 164 | // Delegates to the member function that checks for the initialization 165 | // of Loop() so that fdevent_context requests can be serially processed 166 | // by the global instance robustly. 167 | void fdevent_check_looper(); 168 | 169 | // Queue an operation to run on the looper event thread. 170 | void fdevent_run_on_looper(std::function fn); 171 | 172 | // The following functions are used only for tests. 173 | void fdevent_terminate_loop(); 174 | size_t fdevent_installed_count(); 175 | void fdevent_reset(); 176 | 177 | // Porting: Wait for the fdevent loop to exit after TerminateLoop is called 178 | void fdevent_wait_for_loop_exit(); 179 | 180 | // Porting: Get the global fdevent context 181 | fdevent_context* fdevent_get_ambient(); 182 | 183 | #endif 184 | -------------------------------------------------------------------------------- /example/adb-demo/adb-demo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 33 | 34 | 35 | 36 | 37 | 38 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /porting/adb/fdevent/fdevent_poll.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #define TRACE_TAG FDEVENT 18 | 19 | #include "sysdeps.h" 20 | #include "fdevent_poll.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #include "adb_io.h" 47 | #include "adb_trace.h" 48 | #include "adb_unique_fd.h" 49 | #include "adb_utils.h" 50 | #include "fdevent.h" 51 | #include "sysdeps/chrono.h" 52 | 53 | static void fdevent_interrupt(int fd, unsigned, void*) { 54 | char buf[BUFSIZ]; 55 | ssize_t rc = TEMP_FAILURE_RETRY(adb_read(fd, buf, sizeof(buf))); 56 | if (rc == -1) { 57 | PLOG(FATAL) << "failed to read from fdevent interrupt fd"; 58 | } 59 | } 60 | 61 | fdevent_context_poll::fdevent_context_poll() { 62 | int s[2]; 63 | if (adb_socketpair(s) != 0) { 64 | PLOG(FATAL) << "failed to create fdevent interrupt socketpair"; 65 | } 66 | 67 | if (!set_file_block_mode(s[0], false) || !set_file_block_mode(s[1], false)) { 68 | PLOG(FATAL) << "failed to make fdevent interrupt socket nonblocking"; 69 | } 70 | 71 | this->interrupt_fd_.reset(s[0]); 72 | fdevent* fde = this->Create(unique_fd(s[1]), fdevent_interrupt, nullptr); 73 | CHECK(fde != nullptr); 74 | this->Add(fde, FDE_READ); 75 | } 76 | 77 | fdevent_context_poll::~fdevent_context_poll() { 78 | // Destroy calls virtual methods, but this class is final, so that's okay. 79 | this->Destroy(this->interrupt_fde_); 80 | } 81 | 82 | void fdevent_context_poll::Set(fdevent* fde, unsigned events) { 83 | CheckLooperThread(); 84 | fde->state = events; 85 | D("fdevent_set: %s, events = %u", dump_fde(fde).c_str(), events); 86 | } 87 | 88 | static std::string dump_pollfds(const std::vector& pollfds) { 89 | std::string result; 90 | for (const auto& pollfd : pollfds) { 91 | std::string op; 92 | if (pollfd.events & POLLIN) { 93 | op += "R"; 94 | } 95 | if (pollfd.events & POLLOUT) { 96 | op += "W"; 97 | } 98 | android::base::StringAppendF(&result, " %d(%s)", pollfd.fd, op.c_str()); 99 | } 100 | return result; 101 | } 102 | 103 | void fdevent_context_poll::Loop() { 104 | looper_thread_id_ = android::base::GetThreadId(); 105 | 106 | std::vector pollfds; 107 | std::vector poll_events; 108 | 109 | while (true) { 110 | if (terminate_loop_) { 111 | break; 112 | } 113 | 114 | D("--- --- waiting for events"); 115 | pollfds.clear(); 116 | for (const auto& [fd, fde] : this->installed_fdevents_) { 117 | adb_pollfd pfd; 118 | pfd.fd = fd; 119 | pfd.events = 0; 120 | if (fde.state & FDE_READ) { 121 | pfd.events |= POLLIN; 122 | } 123 | if (fde.state & FDE_WRITE) { 124 | pfd.events |= POLLOUT; 125 | } 126 | if (fde.state & FDE_ERROR) { 127 | pfd.events |= POLLERR; 128 | } 129 | #if defined(__linux__) 130 | pfd.events |= POLLRDHUP; 131 | #endif 132 | pfd.revents = 0; 133 | pollfds.push_back(pfd); 134 | } 135 | CHECK_GT(pollfds.size(), 0u); 136 | D("poll(), pollfds = %s", dump_pollfds(pollfds).c_str()); 137 | 138 | std::optional timeout = CalculatePollDuration(); 139 | int timeout_ms; 140 | if (!timeout) { 141 | timeout_ms = -1; 142 | } else { 143 | timeout_ms = timeout->count(); 144 | } 145 | 146 | int ret = adb_poll(pollfds.data(), pollfds.size(), timeout_ms); 147 | if (ret == -1) { 148 | PLOG(ERROR) << "poll(), ret = " << ret; 149 | return; 150 | } 151 | 152 | auto post_poll = std::chrono::steady_clock::now(); 153 | 154 | for (const auto& pollfd : pollfds) { 155 | unsigned events = 0; 156 | if (pollfd.revents & POLLIN) { 157 | events |= FDE_READ; 158 | } 159 | if (pollfd.revents & POLLOUT) { 160 | events |= FDE_WRITE; 161 | } 162 | if (pollfd.revents & (POLLERR | POLLHUP | POLLNVAL)) { 163 | // We fake a read, as the rest of the code assumes that errors will 164 | // be detected at that point. 165 | events |= FDE_READ | FDE_ERROR; 166 | } 167 | #if defined(__linux__) 168 | if (pollfd.revents & POLLRDHUP) { 169 | events |= FDE_READ | FDE_ERROR; 170 | } 171 | #endif 172 | 173 | auto it = this->installed_fdevents_.find(pollfd.fd); 174 | // CHECK(it != this->installed_fdevents_.end()); 175 | fdevent* fde = &it->second; 176 | 177 | if (events == 0) { 178 | if (fde->timeout) { 179 | auto deadline = fde->last_active + *fde->timeout; 180 | if (deadline < post_poll) { 181 | events |= FDE_TIMEOUT; 182 | } 183 | } 184 | } 185 | 186 | if (events != 0) { 187 | D("%s got events %x", dump_fde(fde).c_str(), events); 188 | poll_events.push_back({fde, events}); 189 | fde->last_active = post_poll; 190 | } 191 | } 192 | this->HandleEvents(poll_events); 193 | poll_events.clear(); 194 | } 195 | 196 | // Notify waiting threads that the loop has exited 197 | NotifyLoopExit(); 198 | } 199 | 200 | size_t fdevent_context_poll::InstalledCount() { 201 | // We always have an installed fde for interrupt. 202 | return this->installed_fdevents_.size() - 1; 203 | } 204 | 205 | void fdevent_context_poll::Interrupt() { 206 | int rc = adb_write(this->interrupt_fd_, "", 1); 207 | 208 | // It's possible that we get EAGAIN here, if lots of notifications came in while handling. 209 | if (rc == 0) { 210 | std::cout << "fdevent interrupt fd was closed?" << std::endl; 211 | } else if (rc == -1 && errno != EAGAIN) { 212 | std::cout << "failed to write to fdevent interrupt fd" << std::endl; 213 | } 214 | } 215 | 216 | void fdevent_context_poll::Register(fdevent*) {} 217 | 218 | void fdevent_context_poll::Unregister(fdevent*) {} 219 | -------------------------------------------------------------------------------- /porting/adb/fdevent/fdevent.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006, Brian Swetland 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #define TRACE_TAG FDEVENT 18 | 19 | #include "sysdeps.h" 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include "adb_utils.h" 28 | #include "fdevent.h" 29 | #include "fdevent_epoll.h" 30 | 31 | #if !defined(__linux__) 32 | #include "fdevent_poll.h" 33 | #endif 34 | 35 | using namespace std::chrono_literals; 36 | using std::chrono::duration_cast; 37 | 38 | void invoke_fde(struct fdevent* fde, unsigned events) { 39 | if (auto f = std::get_if(&fde->func)) { 40 | (*f)(fde->fd.get(), events, fde->arg); 41 | } else if (auto f = std::get_if(&fde->func)) { 42 | (*f)(fde, events, fde->arg); 43 | } else { 44 | __builtin_unreachable(); 45 | } 46 | } 47 | 48 | std::string dump_fde(const fdevent* fde) { 49 | std::string state; 50 | if (fde->state & FDE_READ) { 51 | state += "R"; 52 | } 53 | if (fde->state & FDE_WRITE) { 54 | state += "W"; 55 | } 56 | if (fde->state & FDE_ERROR) { 57 | state += "E"; 58 | } 59 | return android::base::StringPrintf("(fdevent %" PRIu64 ": fd %d %s)", fde->id, fde->fd.get(), 60 | state.c_str()); 61 | } 62 | 63 | fdevent* fdevent_context::Create(unique_fd fd, std::variant func, void* arg) { 64 | CheckLooperThread(); 65 | 66 | CHECK_GE(fd.get(), 0); 67 | 68 | int fd_num = fd.get(); 69 | 70 | auto [it, inserted] = this->installed_fdevents_.emplace(fd_num, fdevent{}); 71 | CHECK(inserted); 72 | 73 | fdevent* fde = &it->second; 74 | fde->id = fdevent_id_++; 75 | fde->state = 0; 76 | fde->fd = std::move(fd); 77 | fde->func = func; 78 | fde->arg = arg; 79 | if (!set_file_block_mode(fde->fd, false)) { 80 | // Here is not proper to handle the error. If it fails here, some error is 81 | // likely to be detected by poll(), then we can let the callback function 82 | // to handle it. 83 | LOG(ERROR) << "failed to set non-blocking mode for fd " << fde->fd.get(); 84 | } 85 | 86 | this->fdevent_set_.insert(fde); 87 | this->Register(fde); 88 | return fde; 89 | } 90 | 91 | unique_fd fdevent_context::Destroy(fdevent* fde) { 92 | // adb-mobile: disable threads check for prevent abort 93 | // CheckLooperThread(); 94 | 95 | if (!fde) { 96 | return {}; 97 | } 98 | 99 | this->Unregister(fde); 100 | 101 | unique_fd fd = std::move(fde->fd); 102 | 103 | auto erased = this->installed_fdevents_.erase(fd.get()); 104 | CHECK_EQ(1UL, erased); 105 | erased = this->fdevent_set_.erase(fde); 106 | CHECK_EQ(1UL, erased); 107 | 108 | return fd; 109 | } 110 | 111 | void fdevent_context::Add(fdevent* fde, unsigned events) { 112 | CHECK(!(events & FDE_TIMEOUT)); 113 | Set(fde, fde->state | events); 114 | } 115 | 116 | void fdevent_context::Del(fdevent* fde, unsigned events) { 117 | CHECK(!(events & FDE_TIMEOUT)); 118 | Set(fde, fde->state & ~events); 119 | } 120 | 121 | void fdevent_context::SetTimeout(fdevent* fde, std::optional timeout) { 122 | CheckLooperThread(); // Caller thread is expected to have already 123 | // initialized the looper thread instance variable. 124 | fde->timeout = timeout; 125 | fde->last_active = std::chrono::steady_clock::now(); 126 | } 127 | 128 | std::optional fdevent_context::CalculatePollDuration() { 129 | std::optional result = std::nullopt; 130 | auto now = std::chrono::steady_clock::now(); 131 | 132 | CheckLooperThread(); 133 | 134 | for (const auto& [fd, fde] : this->installed_fdevents_) { 135 | UNUSED(fd); 136 | auto timeout_opt = fde.timeout; 137 | if (timeout_opt) { 138 | auto deadline = fde.last_active + *timeout_opt; 139 | auto time_left = duration_cast(deadline - now); 140 | if (time_left < 0ms) { 141 | time_left = 0ms; 142 | } 143 | 144 | if (!result) { 145 | result = time_left; 146 | } else { 147 | result = std::min(*result, time_left); 148 | } 149 | } 150 | } 151 | 152 | return result; 153 | } 154 | 155 | void fdevent_context::HandleEvents(const std::vector& events) { 156 | for (const auto& event : events) { 157 | // Verify the fde is still installed before invoking it. It could have been unregistered 158 | // and destroyed inside an earlier event handler. 159 | if (this->fdevent_set_.contains(event.fde)) { 160 | invoke_fde(event.fde, event.events); 161 | break; 162 | } 163 | } 164 | FlushRunQueue(); 165 | } 166 | 167 | void fdevent_context::FlushRunQueue() { 168 | // We need to be careful around reentrancy here, since a function we call can queue up another 169 | // function. 170 | while (true) { 171 | std::function fn; 172 | { 173 | std::lock_guard lock(this->run_queue_mutex_); 174 | if (this->run_queue_.empty()) { 175 | break; 176 | } 177 | fn = std::move(this->run_queue_.front()); 178 | this->run_queue_.pop_front(); 179 | } 180 | fn(); 181 | } 182 | } 183 | 184 | void fdevent_context::CheckLooperThread() const { 185 | if (looper_thread_id_) { 186 | CHECK_EQ(*looper_thread_id_, android::base::GetThreadId()); 187 | } 188 | } 189 | 190 | void fdevent_context::Run(std::function fn) { 191 | { 192 | std::lock_guard lock(run_queue_mutex_); 193 | run_queue_.push_back(std::move(fn)); 194 | } 195 | 196 | Interrupt(); 197 | } 198 | 199 | void fdevent_context::TerminateLoop() { 200 | terminate_loop_ = true; 201 | Interrupt(); 202 | } 203 | 204 | void fdevent_context::WaitForLoopExit() { 205 | std::unique_lock lock(loop_exit_mutex_); 206 | // Wait until the loop has actually exited (looper_thread_id_ is reset) 207 | loop_exit_cv_.wait(lock, [this]() { return !looper_thread_id_.has_value(); }); 208 | } 209 | 210 | void fdevent_context::NotifyLoopExit() { 211 | { 212 | std::lock_guard lock(loop_exit_mutex_); 213 | looper_thread_id_.reset(); 214 | } 215 | loop_exit_cv_.notify_all(); 216 | } 217 | 218 | static std::unique_ptr fdevent_create_context() { 219 | #if defined(__linux__) 220 | return std::make_unique(); 221 | #else 222 | return std::make_unique(); 223 | #endif 224 | } 225 | 226 | static auto& g_ambient_fdevent_context() { 227 | static auto context = fdevent_create_context().release(); 228 | return context; 229 | } 230 | 231 | fdevent_context* fdevent_get_ambient() { 232 | return g_ambient_fdevent_context(); 233 | } 234 | 235 | fdevent* fdevent_create(int fd, fd_func func, void* arg) { 236 | unique_fd ufd(fd); 237 | return fdevent_get_ambient()->Create(std::move(ufd), func, arg); 238 | } 239 | 240 | fdevent* fdevent_create(int fd, fd_func2 func, void* arg) { 241 | unique_fd ufd(fd); 242 | return fdevent_get_ambient()->Create(std::move(ufd), func, arg); 243 | } 244 | 245 | unique_fd fdevent_release(fdevent* fde) { 246 | return fdevent_get_ambient()->Destroy(fde); 247 | } 248 | 249 | void fdevent_destroy(fdevent* fde) { 250 | fdevent_get_ambient()->Destroy(fde); 251 | } 252 | 253 | void fdevent_set(fdevent* fde, unsigned events) { 254 | fdevent_get_ambient()->Set(fde, events); 255 | } 256 | 257 | void fdevent_add(fdevent* fde, unsigned events) { 258 | fdevent_get_ambient()->Add(fde, events); 259 | } 260 | 261 | void fdevent_del(fdevent* fde, unsigned events) { 262 | fdevent_get_ambient()->Del(fde, events); 263 | } 264 | 265 | void fdevent_set_timeout(fdevent* fde, std::optional timeout) { 266 | fdevent_get_ambient()->SetTimeout(fde, timeout); 267 | } 268 | 269 | void fdevent_run_on_looper(std::function fn) { 270 | fdevent_get_ambient()->Run(std::move(fn)); 271 | } 272 | 273 | void fdevent_loop() { 274 | fdevent_get_ambient()->Loop(); 275 | } 276 | 277 | void fdevent_check_looper() { 278 | fdevent_get_ambient()->CheckLooperThread(); 279 | } 280 | 281 | void fdevent_terminate_loop() { 282 | fdevent_get_ambient()->TerminateLoop(); 283 | } 284 | 285 | void fdevent_wait_for_loop_exit() { 286 | fdevent_get_ambient()->WaitForLoopExit(); 287 | } 288 | 289 | size_t fdevent_installed_count() { 290 | return fdevent_get_ambient()->InstalledCount(); 291 | } 292 | 293 | void fdevent_reset() { 294 | auto old = std::exchange(g_ambient_fdevent_context(), fdevent_create_context().release()); 295 | delete old; 296 | } 297 | -------------------------------------------------------------------------------- /porting/protobuf/src/google/protobuf/map_probe_benchmark.cc: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2023 Google Inc. All rights reserved. 3 | // 4 | // Use of this source code is governed by a BSD-style 5 | // license that can be found in the LICENSE file or at 6 | // https://developers.google.com/open-source/licenses/bsd 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "absl/random/random.h" 16 | #include "absl/strings/str_cat.h" 17 | #include "absl/strings/str_format.h" 18 | #include "absl/strings/string_view.h" 19 | #include "google/protobuf/map.h" 20 | 21 | namespace google::protobuf::internal { 22 | struct MapBenchmarkPeer { 23 | template 24 | static double LoadFactor(const T& map) { 25 | return static_cast(map.size()) / 26 | static_cast(map.num_buckets_); 27 | } 28 | 29 | template 30 | static double GetMeanProbeLength(const T& map) { 31 | double total_probe_cost = 0; 32 | for (map_index_t b = 0; b < map.num_buckets_; ++b) { 33 | if (map.TableEntryIsList(b)) { 34 | auto* node = internal::TableEntryToNode(map.table_[b]); 35 | size_t cost = 0; 36 | while (node != nullptr) { 37 | total_probe_cost += static_cast(cost); 38 | cost++; 39 | node = node->next; 40 | } 41 | } else if (map.TableEntryIsTree(b)) { 42 | // Overhead factor to account for more costly binary search. 43 | constexpr double kTreeOverhead = 2.0; 44 | size_t tree_size = TableEntryToTree(map.table_[b])->size(); 45 | total_probe_cost += kTreeOverhead * static_cast(tree_size) * 46 | std::log2(tree_size); 47 | } 48 | } 49 | return total_probe_cost / map.size(); 50 | } 51 | 52 | template 53 | static double GetPercentTree(const T& map) { 54 | size_t total_tree_size = 0; 55 | for (map_index_t b = 0; b < map.num_buckets_; ++b) { 56 | if (map.TableEntryIsTree(b)) { 57 | total_tree_size += TableEntryToTree(map.table_[b])->size(); 58 | } 59 | } 60 | return static_cast(total_tree_size) / 61 | static_cast(map.size()); 62 | } 63 | }; 64 | } // namespace protobuf 65 | //} // namespace google::internal 66 | 67 | namespace { 68 | 69 | using Peer = google::protobuf::internal::MapBenchmarkPeer; 70 | 71 | absl::BitGen& GlobalBitGen() { 72 | static auto* value = new absl::BitGen; 73 | return *value; 74 | } 75 | 76 | template 77 | using Table = google::protobuf::Map; 78 | 79 | struct LoadSizes { 80 | size_t min_load; 81 | size_t max_load; 82 | }; 83 | 84 | LoadSizes GetMinMaxLoadSizes() { 85 | static const auto sizes = [] { 86 | Table t; 87 | 88 | // First, fill enough to have a good distribution. 89 | constexpr size_t kMinSize = 10000; 90 | while (t.size() < kMinSize) t[static_cast(t.size())]; 91 | 92 | const auto reach_min_load_factor = [&] { 93 | const double lf = Peer::LoadFactor(t); 94 | while (lf <= Peer::LoadFactor(t)) t[static_cast(t.size())]; 95 | }; 96 | 97 | // Then, insert until we reach min load factor. 98 | reach_min_load_factor(); 99 | const size_t min_load_size = t.size(); 100 | 101 | // Keep going until we hit min load factor again, then go back one. 102 | t[static_cast(t.size())]; 103 | reach_min_load_factor(); 104 | 105 | return LoadSizes{min_load_size, t.size() - 1}; 106 | }(); 107 | return sizes; 108 | } 109 | 110 | struct Ratios { 111 | double min_load; 112 | double avg_load; 113 | double max_load; 114 | double percent_tree; 115 | }; 116 | 117 | template 118 | Ratios CollectMeanProbeLengths() { 119 | const auto min_max_sizes = GetMinMaxLoadSizes(); 120 | 121 | ElemFn elem; 122 | using Key = decltype(elem()); 123 | Table t; 124 | 125 | Ratios result; 126 | while (t.size() < min_max_sizes.min_load) t[elem()]; 127 | result.min_load = Peer::GetMeanProbeLength(t); 128 | 129 | while (t.size() < (min_max_sizes.min_load + min_max_sizes.max_load) / 2) 130 | t[elem()]; 131 | result.avg_load = Peer::GetMeanProbeLength(t); 132 | 133 | while (t.size() < min_max_sizes.max_load) t[elem()]; 134 | result.max_load = Peer::GetMeanProbeLength(t); 135 | result.percent_tree = Peer::GetPercentTree(t); 136 | 137 | return result; 138 | } 139 | 140 | constexpr char kStringFormat[] = "/path/to/file/name-%07d-of-9999999.txt"; 141 | 142 | template 143 | struct String { 144 | std::string value; 145 | static std::string Make(uint32_t v) { 146 | return {small ? absl::StrCat(v) : absl::StrFormat(kStringFormat, v)}; 147 | } 148 | }; 149 | 150 | template 151 | struct Sequential { 152 | T operator()() const { return current++; } 153 | mutable T current{}; 154 | }; 155 | 156 | template 157 | struct Sequential> { 158 | std::string operator()() const { return String::Make(current++); } 159 | mutable uint32_t current = 0; 160 | }; 161 | 162 | template 163 | struct AlmostSequential { 164 | mutable Sequential current; 165 | 166 | auto operator()() const -> decltype(current()) { 167 | while (absl::Uniform(GlobalBitGen(), 0.0, 1.0) <= percent_skip / 100.) 168 | current(); 169 | return current(); 170 | } 171 | }; 172 | 173 | struct Uniform { 174 | template 175 | T operator()(T) const { 176 | return absl::Uniform(absl::IntervalClosed, GlobalBitGen(), T{0}, ~T{0}); 177 | } 178 | }; 179 | 180 | struct Gaussian { 181 | template 182 | T operator()(T) const { 183 | double d; 184 | do { 185 | d = absl::Gaussian(GlobalBitGen(), 1e6, 1e4); 186 | } while (d <= 0 || d > std::numeric_limits::max() / 2); 187 | return static_cast(d); 188 | } 189 | }; 190 | 191 | struct Zipf { 192 | template 193 | T operator()(T) const { 194 | return absl::Zipf(GlobalBitGen(), std::numeric_limits::max(), 1.6); 195 | } 196 | }; 197 | 198 | template 199 | struct Random { 200 | T operator()() const { return Dist{}(T{}); } 201 | }; 202 | 203 | template 204 | struct Random, Dist> { 205 | std::string operator()() const { 206 | return String::Make(Random{}()); 207 | } 208 | }; 209 | 210 | template 211 | std::string Name(); 212 | 213 | std::string Name(uint64_t*) { return "u64"; } 214 | 215 | template 216 | std::string Name(String*) { 217 | return small ? "StrS" : "StrL"; 218 | } 219 | 220 | template 221 | std::string Name(Sequential*) { 222 | return "Sequential"; 223 | } 224 | 225 | template 226 | std::string Name(AlmostSequential*) { 227 | return absl::StrCat("AlmostSeq_", P); 228 | } 229 | 230 | template 231 | std::string Name(Random*) { 232 | return "UnifRand"; 233 | } 234 | 235 | template 236 | std::string Name(Random*) { 237 | return "GausRand"; 238 | } 239 | 240 | template 241 | std::string Name(Random*) { 242 | return "ZipfRand"; 243 | } 244 | 245 | template 246 | std::string Name() { 247 | return Name(static_cast(nullptr)); 248 | } 249 | 250 | struct Result { 251 | std::string name; 252 | std::string dist_name; 253 | Ratios ratios; 254 | }; 255 | 256 | template 257 | void RunForTypeAndDistribution(std::vector& results) { 258 | results.push_back({Name(), Name(), CollectMeanProbeLengths()}); 259 | } 260 | 261 | template 262 | void RunForType(std::vector& results) { 263 | RunForTypeAndDistribution>(results); 264 | RunForTypeAndDistribution>(results); 265 | RunForTypeAndDistribution>(results); 266 | RunForTypeAndDistribution>(results); 267 | RunForTypeAndDistribution>(results); 268 | RunForTypeAndDistribution>(results); 269 | } 270 | 271 | } // namespace 272 | 273 | int main(int argc, char** argv) { 274 | std::vector results; 275 | RunForType(results); 276 | RunForType>(results); 277 | RunForType>(results); 278 | 279 | absl::PrintF("{\n"); 280 | absl::PrintF(" \"benchmarks\": [\n"); 281 | absl::string_view comma; 282 | for (const auto& result : results) { 283 | auto print = [&](absl::string_view stat, double Ratios::*val) { 284 | std::string name = 285 | absl::StrCat(result.name, "/", result.dist_name, "/", stat); 286 | absl::PrintF(" %s{\n", comma); 287 | absl::PrintF(" \"cpu_time\": 0,\n"); 288 | absl::PrintF(" \"real_time\": 0,\n"); 289 | absl::PrintF(" \"allocs_per_iter\": %f,\n", result.ratios.*val); 290 | 291 | absl::PrintF(" \"iterations\": 1,\n"); 292 | absl::PrintF(" \"name\": \"%s\",\n", name); 293 | absl::PrintF(" \"time_unit\": \"ns\"\n"); 294 | absl::PrintF(" }\n"); 295 | comma = ","; 296 | }; 297 | print("min", &Ratios::min_load); 298 | print("avg", &Ratios::avg_load); 299 | print("max", &Ratios::max_load); 300 | print("tree_percent", &Ratios::percent_tree); 301 | } 302 | absl::PrintF(" ],\n"); 303 | absl::PrintF(" \"context\": {\n"); 304 | absl::PrintF(" }\n"); 305 | absl::PrintF("}\n"); 306 | 307 | return 0; 308 | } 309 | -------------------------------------------------------------------------------- /porting/adb/adb_listeners.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "adb_listeners.h" 18 | 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "socket_spec.h" 32 | #include "sysdeps.h" 33 | #include "transport.h" 34 | 35 | // A listener is an entity which binds to a local port and, upon receiving a connection on that 36 | // port, creates an asocket to connect the new local connection to a specific remote service. They 37 | // are mostly used to implement forward and reverse-forward. 38 | // 39 | // Some listeners, called "smartsockets" read from the new connection to determine what exact 40 | // service to connect to on the far side. This is implemented with a different fdevent handler. 41 | class alistener { 42 | public: 43 | alistener(const std::string& _local_name, const std::string& _connect_to); 44 | ~alistener(); 45 | 46 | bool isSmartSocket() { return connect_to == kSmartSocketConnectTo; } 47 | 48 | fdevent* fde = nullptr; 49 | int fd = -1; 50 | 51 | std::string local_name; 52 | std::string connect_to; 53 | atransport* transport = nullptr; 54 | adisconnect disconnect; 55 | 56 | private: 57 | DISALLOW_COPY_AND_ASSIGN(alistener); 58 | }; 59 | 60 | alistener::alistener(const std::string& _local_name, const std::string& _connect_to) 61 | : local_name(_local_name), connect_to(_connect_to) { 62 | } 63 | 64 | alistener::~alistener() { 65 | // Closes the corresponding fd. 66 | fdevent_destroy(fde); 67 | 68 | if (transport) { 69 | transport->RemoveDisconnect(&disconnect); 70 | } 71 | } 72 | 73 | // listener_list retains ownership of all created alistener objects. Removing an alistener from 74 | // this list will cause it to be deleted. 75 | static auto& listener_list_mutex = *new std::mutex(); 76 | typedef std::list> ListenerList; 77 | static ListenerList& listener_list GUARDED_BY(listener_list_mutex) = *new ListenerList(); 78 | 79 | #if ADB_HOST 80 | static void ss_listener_event_func(int _fd, unsigned ev, void *_l) { 81 | if (ev & FDE_READ) { 82 | unique_fd fd(adb_socket_accept(_fd, nullptr, nullptr)); 83 | if (fd < 0) return; 84 | 85 | int rcv_buf_size = CHUNK_SIZE; 86 | adb_setsockopt(fd.get(), SOL_SOCKET, SO_RCVBUF, &rcv_buf_size, sizeof(rcv_buf_size)); 87 | 88 | asocket* s = create_local_socket(std::move(fd)); 89 | if (s) { 90 | connect_to_smartsocket(s); 91 | return; 92 | } 93 | } 94 | } 95 | #endif 96 | 97 | static void listener_event_func(int _fd, unsigned ev, void* _l) 98 | { 99 | alistener* listener = reinterpret_cast(_l); 100 | 101 | if (ev & FDE_READ) { 102 | unique_fd fd(adb_socket_accept(_fd, nullptr, nullptr)); 103 | if (fd < 0) { 104 | return; 105 | } 106 | 107 | asocket* s = create_local_socket(std::move(fd)); 108 | if (s) { 109 | s->transport = listener->transport; 110 | connect_to_remote(s, listener->connect_to); 111 | return; 112 | } 113 | } 114 | } 115 | 116 | // Called as a transport disconnect function. |arg| is the raw alistener*. 117 | static void listener_disconnect(void* arg, atransport*) EXCLUDES(listener_list_mutex) { 118 | std::lock_guard lock(listener_list_mutex); 119 | for (auto iter = listener_list.begin(); iter != listener_list.end(); ++iter) { 120 | if (iter->get() == arg) { 121 | (*iter)->transport = nullptr; 122 | listener_list.erase(iter); 123 | return; 124 | } 125 | } 126 | } 127 | 128 | // Write the list of current listeners (network redirections) into a string. 129 | std::string format_listeners() EXCLUDES(listener_list_mutex) { 130 | std::lock_guard lock(listener_list_mutex); 131 | std::string result; 132 | for (auto& l : listener_list) { 133 | if (l->isSmartSocket()) { 134 | continue; 135 | } 136 | // " " " " "\n" 137 | // Entries from "adb reverse" have no serial. 138 | android::base::StringAppendF( 139 | &result, "%s %s %s\n", 140 | !l->transport->serial.empty() ? l->transport->serial.c_str() : "(reverse)", 141 | l->local_name.c_str(), l->connect_to.c_str()); 142 | } 143 | return result; 144 | } 145 | 146 | InstallStatus remove_listener(const char* local_name, atransport* transport) 147 | EXCLUDES(listener_list_mutex) { 148 | std::lock_guard lock(listener_list_mutex); 149 | for (auto iter = listener_list.begin(); iter != listener_list.end(); ++iter) { 150 | if (local_name == (*iter)->local_name) { 151 | listener_list.erase(iter); 152 | return INSTALL_STATUS_OK; 153 | } 154 | } 155 | return INSTALL_STATUS_LISTENER_NOT_FOUND; 156 | } 157 | 158 | void remove_all_listeners() EXCLUDES(listener_list_mutex) { 159 | std::lock_guard lock(listener_list_mutex); 160 | auto iter = listener_list.begin(); 161 | while (iter != listener_list.end()) { 162 | // Never remove smart sockets. 163 | if ((*iter)->connect_to[0] == '*') { 164 | ++iter; 165 | } else { 166 | iter = listener_list.erase(iter); 167 | } 168 | } 169 | } 170 | 171 | #if ADB_HOST 172 | void enable_server_sockets() EXCLUDES(listener_list_mutex) { 173 | std::lock_guard lock(listener_list_mutex); 174 | for (auto& l : listener_list) { 175 | if (l->isSmartSocket()) { 176 | fdevent_set(l->fde, FDE_READ); 177 | } 178 | } 179 | } 180 | 181 | void close_smartsockets() EXCLUDES(listener_list_mutex) { 182 | std::lock_guard lock(listener_list_mutex); 183 | auto pred = [](const std::unique_ptr& listener) { 184 | return listener->isSmartSocket(); 185 | }; 186 | listener_list.remove_if(pred); 187 | } 188 | #endif 189 | 190 | InstallStatus install_listener(const std::string& local_name, const char* connect_to, 191 | atransport* transport, int flags, int* resolved_tcp_port, 192 | std::string* error) EXCLUDES(listener_list_mutex) { 193 | std::lock_guard lock(listener_list_mutex); 194 | for (auto& l : listener_list) { 195 | if (local_name == l->local_name) { 196 | // Can't repurpose a smartsocket. 197 | if (l->isSmartSocket()) { 198 | *error = "cannot repurpose smartsocket"; 199 | return INSTALL_STATUS_INTERNAL_ERROR; 200 | } 201 | 202 | // Can't repurpose a listener if INSTALL_LISTENER_NO_REBIND is set 203 | if (flags & INSTALL_LISTENER_NO_REBIND) { 204 | *error = "cannot rebind"; 205 | return INSTALL_STATUS_CANNOT_REBIND; 206 | } 207 | 208 | l->connect_to = connect_to; 209 | if (l->transport != transport) { 210 | l->transport->RemoveDisconnect(&l->disconnect); 211 | l->transport = transport; 212 | l->transport->AddDisconnect(&l->disconnect); 213 | } 214 | return INSTALL_STATUS_OK; 215 | } 216 | } 217 | 218 | auto listener = std::make_unique(local_name, connect_to); 219 | 220 | int resolved = 0; 221 | listener->fd = socket_spec_listen(listener->local_name, error, &resolved); 222 | printf("[adb_porting] install_listener: %s, fd: %d\n", listener->local_name.c_str(), listener->fd); 223 | if (listener->fd < 0) { 224 | return INSTALL_STATUS_CANNOT_BIND; 225 | } 226 | 227 | // If the caller requested port 0, update the listener name with the resolved port. 228 | if (resolved != 0) { 229 | listener->local_name = android::base::StringPrintf("tcp:%d", resolved); 230 | if (resolved_tcp_port) { 231 | *resolved_tcp_port = resolved; 232 | } 233 | } 234 | 235 | close_on_exec(listener->fd); 236 | if (listener->isSmartSocket()) { 237 | #if ADB_HOST 238 | listener->fde = fdevent_create(listener->fd, ss_listener_event_func, listener.get()); 239 | #else 240 | LOG(FATAL) << "attempted to connect to *smartsocket* in daemon"; 241 | #endif 242 | } else { 243 | listener->fde = fdevent_create(listener->fd, listener_event_func, listener.get()); 244 | } 245 | if ((flags & INSTALL_LISTENER_DISABLED) == 0) { 246 | fdevent_set(listener->fde, FDE_READ); 247 | } 248 | 249 | listener->transport = transport; 250 | 251 | if (transport) { 252 | listener->disconnect.opaque = listener.get(); 253 | listener->disconnect.func = listener_disconnect; 254 | transport->AddDisconnect(&listener->disconnect); 255 | } 256 | 257 | listener_list.push_back(std::move(listener)); 258 | return INSTALL_STATUS_OK; 259 | } 260 | 261 | void reinstall_all_listeners() EXCLUDES(listener_list_mutex) { 262 | std::lock_guard lock(listener_list_mutex); 263 | printf("[adb_porting] reinstall_all_listeners\n"); 264 | // Print listeners count 265 | printf("[adb_porting] listeners count: %zu\n", listener_list.size()); 266 | for (auto& l : listener_list) { 267 | printf("[adb_porting] reinstall_all_listeners: %s\n", l->local_name.c_str()); 268 | std::string* error = nullptr; 269 | int resolved = 0; 270 | l->fd = socket_spec_listen(l->local_name, error, &resolved); 271 | LOG(INFO) << "+ reinstalled listener: " << l->local_name << ", fd: " << l->fd; 272 | if (error) { 273 | LOG(ERROR) << "reinstall_all_listeners: " << *error; 274 | continue; 275 | } 276 | } 277 | } -------------------------------------------------------------------------------- /porting/adb/client/bugreport.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #define TRACE_TAG ADB 18 | 19 | #include "sysdeps.h" 20 | 21 | #include "bugreport.h" 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | #include "adb_client.h" 30 | #include "adb_utils.h" 31 | #include "client/file_sync_client.h" 32 | 33 | static constexpr char BUGZ_BEGIN_PREFIX[] = "BEGIN:"; 34 | static constexpr char BUGZ_PROGRESS_PREFIX[] = "PROGRESS:"; 35 | static constexpr char BUGZ_PROGRESS_SEPARATOR[] = "/"; 36 | static constexpr char BUGZ_OK_PREFIX[] = "OK:"; 37 | static constexpr char BUGZ_FAIL_PREFIX[] = "FAIL:"; 38 | 39 | // Custom callback used to handle the output of zipped bugreports. 40 | class BugreportStandardStreamsCallback : public StandardStreamsCallbackInterface { 41 | public: 42 | BugreportStandardStreamsCallback(const std::string& dest_dir, const std::string& dest_file, 43 | bool show_progress, Bugreport* br) 44 | : br_(br), 45 | src_file_(), 46 | dest_dir_(dest_dir), 47 | dest_file_(dest_file), 48 | line_message_(), 49 | invalid_lines_(), 50 | show_progress_(show_progress), 51 | status_(0), 52 | line_(), 53 | last_progress_percentage_(0) { 54 | SetLineMessage("generating"); 55 | } 56 | 57 | bool OnStdout(const char* buffer, size_t length) { 58 | for (size_t i = 0; i < length; i++) { 59 | char c = buffer[i]; 60 | if (c == '\n') { 61 | ProcessLine(line_); 62 | line_.clear(); 63 | } else { 64 | line_.append(1, c); 65 | } 66 | } 67 | return true; 68 | } 69 | 70 | bool OnStderr(const char* buffer, size_t length) { 71 | return OnStream(nullptr, stderr, buffer, length, false); 72 | } 73 | 74 | int Done(int unused_) { 75 | // Process remaining line, if any. 76 | ProcessLine(line_); 77 | 78 | // Warn about invalid lines, if any. 79 | if (!invalid_lines_.empty()) { 80 | fprintf(stderr, 81 | "WARNING: bugreportz generated %zu line(s) with unknown commands, " 82 | "device might not support zipped bugreports:\n", 83 | invalid_lines_.size()); 84 | for (const auto& line : invalid_lines_) { 85 | fprintf(stderr, "\t%s\n", line.c_str()); 86 | } 87 | fprintf(stderr, 88 | "If the zipped bugreport was not generated, try 'adb bugreport' instead.\n"); 89 | } 90 | 91 | // Pull the generated bug report. 92 | if (status_ == 0) { 93 | if (src_file_.empty()) { 94 | fprintf(stderr, "bugreportz did not return a '%s' or '%s' line\n", BUGZ_OK_PREFIX, 95 | BUGZ_FAIL_PREFIX); 96 | return -1; 97 | } 98 | std::string destination; 99 | if (dest_dir_.empty()) { 100 | destination = dest_file_; 101 | } else { 102 | destination = android::base::StringPrintf("%s%c%s", dest_dir_.c_str(), 103 | OS_PATH_SEPARATOR, dest_file_.c_str()); 104 | } 105 | std::vector srcs{src_file_.c_str()}; 106 | SetLineMessage("pulling"); 107 | status_ = 108 | br_->DoSyncPull(srcs, destination.c_str(), false, line_message_.c_str()) ? 0 : 1; 109 | if (status_ == 0) { 110 | printf("Bug report copied to %s\n", destination.c_str()); 111 | } else { 112 | fprintf(stderr, 113 | "Bug report finished but could not be copied to '%s'.\n" 114 | "Try to run 'adb pull %s '\n" 115 | "to copy it to a directory that can be written.\n", 116 | destination.c_str(), src_file_.c_str()); 117 | } 118 | } 119 | return status_; 120 | } 121 | 122 | private: 123 | void SetLineMessage(const std::string& action) { 124 | line_message_ = action + " " + android::base::Basename(dest_file_); 125 | } 126 | 127 | void SetSrcFile(const std::string path) { 128 | src_file_ = path; 129 | if (!dest_dir_.empty()) { 130 | // Only uses device-provided name when user passed a directory. 131 | dest_file_ = android::base::Basename(path); 132 | SetLineMessage("generating"); 133 | } 134 | } 135 | 136 | void ProcessLine(const std::string& line) { 137 | if (line.empty()) return; 138 | 139 | if (android::base::StartsWith(line, BUGZ_BEGIN_PREFIX)) { 140 | SetSrcFile(&line[strlen(BUGZ_BEGIN_PREFIX)]); 141 | } else if (android::base::StartsWith(line, BUGZ_OK_PREFIX)) { 142 | SetSrcFile(&line[strlen(BUGZ_OK_PREFIX)]); 143 | } else if (android::base::StartsWith(line, BUGZ_FAIL_PREFIX)) { 144 | const char* error_message = &line[strlen(BUGZ_FAIL_PREFIX)]; 145 | fprintf(stderr, "adb: device failed to take a zipped bugreport: %s\n", error_message); 146 | status_ = -1; 147 | } else if (show_progress_ && android::base::StartsWith(line, BUGZ_PROGRESS_PREFIX)) { 148 | // progress_line should have the following format: 149 | // 150 | // BUGZ_PROGRESS_PREFIX:PROGRESS/TOTAL 151 | // 152 | size_t idx1 = line.rfind(BUGZ_PROGRESS_PREFIX) + strlen(BUGZ_PROGRESS_PREFIX); 153 | size_t idx2 = line.rfind(BUGZ_PROGRESS_SEPARATOR); 154 | int progress = std::stoi(line.substr(idx1, (idx2 - idx1))); 155 | int total = std::stoi(line.substr(idx2 + 1)); 156 | int progress_percentage = (progress * 100 / total); 157 | if (progress_percentage != 0 && progress_percentage <= last_progress_percentage_) { 158 | // Ignore. 159 | return; 160 | } 161 | last_progress_percentage_ = progress_percentage; 162 | br_->UpdateProgress(line_message_, progress_percentage); 163 | } else { 164 | invalid_lines_.push_back(line); 165 | } 166 | } 167 | 168 | Bugreport* br_; 169 | 170 | // Path of bugreport on device. 171 | std::string src_file_; 172 | 173 | // Bugreport destination on host, depending on argument passed on constructor: 174 | // - if argument is a directory, dest_dir_ is set with it and dest_file_ will be the name 175 | // of the bugreport reported by the device. 176 | // - if argument is empty, dest_dir is set as the current directory and dest_file_ will be the 177 | // name of the bugreport reported by the device. 178 | // - otherwise, dest_dir_ is not set and dest_file_ is set with the value passed on constructor. 179 | std::string dest_dir_, dest_file_; 180 | 181 | // Message displayed on LinePrinter, it's updated every time the destination above change. 182 | std::string line_message_; 183 | 184 | // Lines sent by bugreportz that contain invalid commands; will be displayed at the end. 185 | std::vector invalid_lines_; 186 | 187 | // Whether PROGRESS_LINES should be interpreted as progress. 188 | bool show_progress_; 189 | 190 | // Overall process of the operation, as returned by Done(). 191 | int status_; 192 | 193 | // Temporary buffer containing the characters read since the last newline (\n). 194 | std::string line_; 195 | 196 | // Last displayed progress. 197 | // Since dumpstate progress can recede, only forward progress should be displayed 198 | int last_progress_percentage_; 199 | 200 | DISALLOW_COPY_AND_ASSIGN(BugreportStandardStreamsCallback); 201 | }; 202 | 203 | int Bugreport::DoIt(int argc, const char** argv) { 204 | if (argc > 2) error_exit("usage: adb bugreport [[PATH] | [--stream]]"); 205 | 206 | // Gets bugreportz version. 207 | std::string bugz_stdout, bugz_stderr; 208 | DefaultStandardStreamsCallback version_callback(&bugz_stdout, &bugz_stderr); 209 | int status = SendShellCommand("bugreportz -v", false, &version_callback); 210 | std::string bugz_version = android::base::Trim(bugz_stderr); 211 | std::string bugz_output = android::base::Trim(bugz_stdout); 212 | int bugz_ver_major = 0, bugz_ver_minor = 0; 213 | 214 | if (status != 0 || bugz_version.empty()) { 215 | D("'bugreportz' -v results: status=%d, stdout='%s', stderr='%s'", status, 216 | bugz_output.c_str(), bugz_version.c_str()); 217 | if (argc == 1) { 218 | // Device does not support bugreportz: if called as 'adb bugreport', just falls out to 219 | // the flat-file version. 220 | fprintf(stderr, 221 | "Failed to get bugreportz version, which is only available on devices " 222 | "running Android 7.0 or later.\nTrying a plain-text bug report instead.\n"); 223 | return SendShellCommand("bugreport", false); 224 | } 225 | 226 | // But if user explicitly asked for a zipped bug report, fails instead (otherwise calling 227 | // 'bugreport' would generate a lot of output the user might not be prepared to handle). 228 | fprintf(stderr, 229 | "Failed to get bugreportz version: 'bugreportz -v' returned '%s' (code %d).\n" 230 | "If the device does not run Android 7.0 or above, try this instead:\n" 231 | "\tadb bugreport > bugreport.txt\n", 232 | bugz_output.c_str(), status); 233 | return status != 0 ? status : -1; 234 | } 235 | std::sscanf(bugz_version.c_str(), "%d.%d", &bugz_ver_major, &bugz_ver_minor); 236 | 237 | std::string dest_file, dest_dir; 238 | 239 | if (argc == 1) { 240 | // No args - use current directory 241 | if (!getcwd(&dest_dir)) { 242 | perror("adb: getcwd failed"); 243 | return 1; 244 | } 245 | } else if (!strcmp(argv[1], "--stream")) { 246 | if (bugz_ver_major == 1 && bugz_ver_minor < 2) { 247 | fprintf(stderr, 248 | "Failed to stream bugreport: bugreportz does not support stream.\n"); 249 | } else { 250 | return SendShellCommand("bugreportz -s", false); 251 | } 252 | } else { 253 | // Check whether argument is a directory or file 254 | if (directory_exists(argv[1])) { 255 | dest_dir = argv[1]; 256 | } else { 257 | dest_file = argv[1]; 258 | } 259 | } 260 | 261 | if (dest_file.empty()) { 262 | // Uses a default value until device provides the proper name 263 | dest_file = "bugreport.zip"; 264 | } else { 265 | if (!android::base::EndsWithIgnoreCase(dest_file, ".zip")) { 266 | dest_file += ".zip"; 267 | } 268 | } 269 | 270 | bool show_progress = true; 271 | std::string bugz_command = "bugreportz -p"; 272 | if (bugz_version == "1.0") { 273 | // 1.0 does not support progress notifications, so print a disclaimer 274 | // message instead. 275 | fprintf(stderr, 276 | "Bugreport is in progress and it could take minutes to complete.\n" 277 | "Please be patient and do not cancel or disconnect your device " 278 | "until it completes.\n"); 279 | show_progress = false; 280 | bugz_command = "bugreportz"; 281 | } 282 | BugreportStandardStreamsCallback bugz_callback(dest_dir, dest_file, show_progress, this); 283 | return SendShellCommand(bugz_command, false, &bugz_callback); 284 | } 285 | 286 | void Bugreport::UpdateProgress(const std::string& message, int progress_percentage) { 287 | line_printer_.Print( 288 | android::base::StringPrintf("[%3d%%] %s", progress_percentage, message.c_str()), 289 | LinePrinter::INFO); 290 | } 291 | 292 | int Bugreport::SendShellCommand(const std::string& command, bool disable_shell_protocol, 293 | StandardStreamsCallbackInterface* callback) { 294 | return send_shell_command(command, disable_shell_protocol, callback); 295 | } 296 | 297 | bool Bugreport::DoSyncPull(const std::vector& srcs, const char* dst, bool copy_attrs, 298 | const char* name) { 299 | return do_sync_pull(nullptr, nullptr, srcs, dst, copy_attrs, CompressionType::None, name); 300 | } 301 | -------------------------------------------------------------------------------- /porting/adb/client/main.cpp: -------------------------------------------------------------------------------- 1 | // → adding adb_porting.cpp to build workflow 2 | 3 | #include "adb_porting.h" 4 | #include "adb_porting.cpp" 5 | 6 | #include "sysdeps.h" 7 | 8 | // Fix shutdown defined in sysdeps.h 9 | #undef shutdown 10 | #undef close 11 | 12 | // include additional source codes 13 | #include "sysdeps/env.cpp" 14 | 15 | // include all file from CMakeList.txt 16 | 17 | /** hijack launch_server **/ 18 | #define launch_server(...) launch_server_unused(__VA_ARGS__) 19 | /** hijack this func to handle adb connect status **/ 20 | #define update_transport_status(...) update_transport_status_real(__VA_ARGS__) 21 | #include "adb.cpp" 22 | #undef launch_server 23 | #undef update_transport_status 24 | 25 | #include "adb_io.cpp" 26 | #include "adb_utils.cpp" 27 | #include "adb_listeners.cpp" 28 | #include "shell_service_protocol.cpp" 29 | #include "adb_trace.cpp" 30 | #include "adb_unique_fd.cpp" 31 | #include "types.cpp" 32 | #include "fdevent/fdevent.cpp" 33 | #include "fdevent/fdevent_poll.cpp" 34 | #include "fdevent/fdevent_epoll.cpp" 35 | 36 | #include "sockets.cpp" 37 | #include "socket_spec.cpp" 38 | #include "transport.cpp" 39 | #include "transport_fd.cpp" 40 | #include "transport_usb.cpp" 41 | #include "transport_local.cpp" 42 | 43 | static void setup_daemon_logging() { 44 | const std::string log_file_path(GetLogFilePath()); 45 | int fd = unix_open(log_file_path, O_WRONLY | O_CREAT | O_APPEND, 0640); 46 | if (fd == -1) { 47 | PLOG(FATAL) << "cannot open " << log_file_path; 48 | } 49 | if (dup2(fd, STDOUT_FILENO) == -1) { 50 | PLOG(FATAL) << "cannot redirect stdout"; 51 | } 52 | if (dup2(fd, STDERR_FILENO) == -1) { 53 | PLOG(FATAL) << "cannot redirect stderr"; 54 | } 55 | unix_close(fd); 56 | 57 | fprintf(stderr, "--- adb starting (pid %d) ---\n", getpid()); 58 | LOG(INFO) << adb_version(); 59 | } 60 | 61 | void adb_server_cleanup() { 62 | // Upon exit, we want to clean up in the following order: 63 | // 1. close_smartsockets, so that we don't get any new clients 64 | // 2. kick_all_transports, to avoid writing only part of a packet to a transport. 65 | // 3. usb_cleanup, to tear down the USB stack. 66 | close_smartsockets(); 67 | kick_all_transports(); 68 | // Porting fix: disable usb 69 | // usb_cleanup(); 70 | } 71 | 72 | static void intentionally_leak() { 73 | void* p = ::operator new(1); 74 | // The analyzer is upset about this leaking. NOLINTNEXTLINE 75 | LOG(INFO) << "leaking pointer " << p; 76 | } 77 | 78 | // one_device: if null, server owns all devices, else server owns only 79 | // device where atransport::MatchesTarget(one_device) is true. 80 | int adb_server_main(int is_daemon, const std::string& socket_spec, const char* one_device, 81 | int ack_reply_fd) { 82 | #if defined(_WIN32) 83 | // adb start-server starts us up with stdout and stderr hooked up to 84 | // anonymous pipes. When the C Runtime sees this, it makes stderr and 85 | // stdout buffered, but to improve the chance that error output is seen, 86 | // unbuffer stdout and stderr just like if we were run at the console. 87 | // This also keeps stderr unbuffered when it is redirected to adb.log. 88 | if (is_daemon) { 89 | if (setvbuf(stdout, nullptr, _IONBF, 0) == -1) { 90 | PLOG(FATAL) << "cannot make stdout unbuffered"; 91 | } 92 | if (setvbuf(stderr, nullptr, _IONBF, 0) == -1) { 93 | PLOG(FATAL) << "cannot make stderr unbuffered"; 94 | } 95 | } 96 | 97 | // TODO: On Ctrl-C, consider trying to kill a starting up adb server (if we're in 98 | // launch_server) by calling GenerateConsoleCtrlEvent(). 99 | 100 | // On Windows, SIGBREAK is when Ctrl-Break is pressed or the console window is closed. It should 101 | // act like Ctrl-C. 102 | signal(SIGBREAK, [](int) { raise(SIGINT); }); 103 | #endif 104 | signal(SIGINT, [](int) { fdevent_run_on_looper([]() { exit(0); }); }); 105 | 106 | if (one_device) { 107 | transport_set_one_device(one_device); 108 | } 109 | 110 | const char* reject_kill_server = getenv("ADB_REJECT_KILL_SERVER"); 111 | if (reject_kill_server && strcmp(reject_kill_server, "1") == 0) { 112 | adb_set_reject_kill_server(true); 113 | } 114 | 115 | const char* leak = getenv("ADB_LEAK"); 116 | if (leak && strcmp(leak, "1") == 0) { 117 | intentionally_leak(); 118 | } 119 | 120 | if (is_daemon) { 121 | close_stdin(); 122 | setup_daemon_logging(); 123 | } 124 | 125 | atexit(adb_server_cleanup); 126 | 127 | init_reconnect_handler(); 128 | 129 | // Porting fix: disable usb 130 | // if (!getenv("ADB_USB") || strcmp(getenv("ADB_USB"), "0") != 0) { 131 | // if (is_libusb_enabled()) { 132 | // libusb::usb_init(); 133 | // } else { 134 | // usb_init(); 135 | // } 136 | // } else { 137 | adb_notify_device_scan_complete(); 138 | // } 139 | 140 | if (!getenv("ADB_EMU") || strcmp(getenv("ADB_EMU"), "0") != 0) { 141 | local_init(android::base::StringPrintf("tcp:%d", DEFAULT_ADB_LOCAL_TRANSPORT_PORT)); 142 | } 143 | 144 | std::string error; 145 | 146 | auto start = std::chrono::steady_clock::now(); 147 | 148 | // If we told a previous adb server to quit because of version mismatch, we can get to this 149 | // point before it's finished exiting. Retry for a while to give it some time. Don't actually 150 | // accept any connections until adb_wait_for_device_initialization finishes below. 151 | while (install_listener(socket_spec, kSmartSocketConnectTo, nullptr, INSTALL_LISTENER_DISABLED, 152 | nullptr, &error) != INSTALL_STATUS_OK) { 153 | if (std::chrono::steady_clock::now() - start > 0.5s) { 154 | LOG(FATAL) << "could not install *smartsocket* listener: " << error; 155 | } 156 | 157 | std::this_thread::sleep_for(100ms); 158 | } 159 | 160 | adb_auth_init(); 161 | 162 | if (is_daemon) { 163 | #if !defined(_WIN32) 164 | // Start a new session for the daemon. Do this here instead of after the fork so 165 | // that a ctrl-c between the "starting server" and "done starting server" messages 166 | // gets a chance to terminate the server. 167 | // setsid will fail with EPERM if it's already been a lead process of new session. 168 | // Ignore such error. 169 | if (setsid() == -1 && errno != EPERM) { 170 | PLOG(FATAL) << "setsid() failed"; 171 | } 172 | #endif 173 | } 174 | 175 | // Wait for the USB scan to complete before notifying the parent that we're up. 176 | // We need to perform this in a thread, because we would otherwise block the event loop. 177 | std::thread notify_thread([ack_reply_fd]() { 178 | adb_wait_for_device_initialization(); 179 | 180 | if (ack_reply_fd >= 0) { 181 | // Any error output written to stderr now goes to adb.log. We could 182 | // keep around a copy of the stderr fd and use that to write any errors 183 | // encountered by the following code, but that is probably overkill. 184 | #if defined(_WIN32) 185 | const HANDLE ack_reply_handle = cast_int_to_handle(ack_reply_fd); 186 | const CHAR ack[] = "OK\n"; 187 | const DWORD bytes_to_write = arraysize(ack) - 1; 188 | DWORD written = 0; 189 | if (!WriteFile(ack_reply_handle, ack, bytes_to_write, &written, NULL)) { 190 | LOG(FATAL) << "cannot write ACK to handle " << ack_reply_handle 191 | << android::base::SystemErrorCodeToString(GetLastError()); 192 | } 193 | if (written != bytes_to_write) { 194 | LOG(FATAL) << "cannot write " << bytes_to_write << " bytes of ACK: only wrote " 195 | << written << " bytes"; 196 | } 197 | CloseHandle(ack_reply_handle); 198 | #else 199 | // TODO(danalbert): Can't use SendOkay because we're sending "OK\n", not 200 | // "OKAY". 201 | if (!android::base::WriteStringToFd("OK\n", ack_reply_fd)) { 202 | PLOG(FATAL) << "error writing ACK to fd " << ack_reply_fd; 203 | } 204 | unix_close(ack_reply_fd); 205 | #endif 206 | } 207 | // We don't accept() client connections until this point: this way, clients 208 | // can't see wonky state early in startup even if they're connecting directly 209 | // to the server instead of going through the adb program. 210 | fdevent_run_on_looper([] { enable_server_sockets(); }); 211 | }); 212 | notify_thread.detach(); 213 | 214 | #if defined(__linux__) 215 | // Write our location to .android/adb.$PORT, so that older clients can exec us. 216 | std::string path; 217 | if (!android::base::Readlink("/proc/self/exe", &path)) { 218 | PLOG(ERROR) << "failed to readlink /proc/self/exe"; 219 | } 220 | 221 | std::optional server_executable_path = adb_get_server_executable_path(); 222 | if (server_executable_path) { 223 | if (!android::base::WriteStringToFile(path, *server_executable_path)) { 224 | PLOG(ERROR) << "failed to write server path to " << path; 225 | } 226 | } 227 | #endif 228 | 229 | D("Event loop starting"); 230 | fdevent_loop(); 231 | return 0; 232 | } 233 | 234 | 235 | /** 236 | * Rewrite launch_server to launch in thread but not by new process 237 | */ 238 | void fdevent_reset_porting(void) { 239 | fdevent_reset(); 240 | } 241 | 242 | void clear_listener_list(void) { 243 | listener_list.clear(); 244 | } 245 | 246 | void clear_transport_list(void) { 247 | std::lock_guard lock(transport_lock); 248 | for (auto t : transport_list) { 249 | t->Kick(); 250 | } 251 | transport_list.clear(); 252 | pending_list.clear(); 253 | } 254 | 255 | void launch_server_thread(int pipe_write, const char *thread_socket_spec) { 256 | // Launch server with pipe 257 | fcntl(pipe_write, F_SETFD, 0); 258 | printf("[adb_porting] launch_server_main: %d\n", pipe_write); 259 | 260 | // Start main server 261 | adb_server_main(false, thread_socket_spec, nullptr, pipe_write); 262 | } 263 | 264 | int launch_server(const std::string& socket_spec, const char* one_device) { 265 | // Before launch server, we need to clear all the transport list 266 | static bool not_first = false; 267 | if (not_first == true) { 268 | printf("[adb_porting] 🔄 terminating loop\n"); 269 | fdevent_get_ambient()->TerminateLoop(); 270 | 271 | // Wait for the fdevent loop to actually exit before destroying the context 272 | // This fixes the race condition where the loop thread was still accessing 273 | // installed_fdevents_ while we were destroying it 274 | printf("[adb_porting] ⏳ waiting for loop to exit\n"); 275 | fdevent_wait_for_loop_exit(); 276 | printf("[adb_porting] ✅ loop exited\n"); 277 | 278 | printf("[adb_porting] 🧹 cleanup sockets and threads\n"); 279 | adb_server_cleanup(); 280 | clear_transport_list(); 281 | fdevent_reset_porting(); 282 | } 283 | not_first = true; 284 | 285 | static char *thread_socket_spec; 286 | static int pipe_server[2]; 287 | 288 | printf("[adb_porting] socket %s\n", socket_spec.c_str()); 289 | thread_socket_spec = strdup(socket_spec.c_str()); 290 | 291 | // Reset last used pipe 292 | pipe(pipe_server); 293 | 294 | // set up a pipe so the child can tell us when it is ready. 295 | if (pipe_server[0] < 0 || pipe_server[1] < 0) { 296 | fprintf(stderr, "pipe failed in launch_server, errno: %d\n", errno); 297 | return -1; 298 | } 299 | 300 | std::string path = android::base::GetExecutablePath(); 301 | 302 | // launch server in new thread 303 | std::thread adb_thread([]() { 304 | launch_server_thread(pipe_server[1], thread_socket_spec); 305 | }); 306 | adb_thread.detach(); 307 | 308 | // parent side 309 | printf("[adb_porting] launch_server pipe_write: %d\n", pipe_server[0]); 310 | printf("[adb_porting] launch_server pipe_read: %d\n", pipe_server[1]); 311 | 312 | // wait for the "OK\n" message from the child 313 | char temp[3] = {}; 314 | int ret = adb_read(pipe_server[0], temp, 3); 315 | int saved_errno = errno; 316 | if (ret < 0) { 317 | fprintf(stderr, "[adb_porting] could not read ok from ADB Server, errno = %d\n", saved_errno); 318 | return -1; 319 | } 320 | if (ret != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') { 321 | ReportServerStartupFailure(0); 322 | return -1; 323 | } 324 | 325 | return 0; 326 | } 327 | 328 | extern "C" { 329 | 330 | __attribute__ ((weak)) 331 | void adb_connect_status_updated(const char *serial, const char *status) { 332 | printf("%s", __FUNCTION__); 333 | } 334 | 335 | int adb_auth_key_generate(const char* filename) { 336 | return adb_auth_keygen(filename); 337 | } 338 | 339 | } 340 | 341 | void update_transport_status() { 342 | update_transport_status_real(); 343 | 344 | // Check all the tcp transports connect status 345 | iterate_transports([](const atransport *t) { 346 | printf("adb connect status changed: %s -> %s\n", t->serial.c_str(), to_string(t->GetConnectionState()).c_str()); 347 | adb_connect_status_updated(t->serial.c_str(), to_string(t->GetConnectionState()).c_str()); 348 | return true; 349 | }); 350 | } 351 | -------------------------------------------------------------------------------- /example/adb-demo/adb-demo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 55; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | AA978A8B2847B15900854CD1 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = AA978A8A2847B15900854CD1 /* AppDelegate.m */; }; 11 | AA978A8E2847B15900854CD1 /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = AA978A8D2847B15900854CD1 /* SceneDelegate.m */; }; 12 | AA978A912847B15900854CD1 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = AA978A902847B15900854CD1 /* ViewController.m */; }; 13 | AA978A942847B15900854CD1 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA978A922847B15900854CD1 /* Main.storyboard */; }; 14 | AA978A962847B15B00854CD1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AA978A952847B15B00854CD1 /* Assets.xcassets */; }; 15 | AA978A992847B15B00854CD1 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA978A972847B15B00854CD1 /* LaunchScreen.storyboard */; }; 16 | AA978A9C2847B15B00854CD1 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = AA978A9B2847B15B00854CD1 /* main.m */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | AA978A862847B15900854CD1 /* adb-demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "adb-demo.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 21 | AA978A892847B15900854CD1 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 22 | AA978A8A2847B15900854CD1 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 23 | AA978A8C2847B15900854CD1 /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = ""; }; 24 | AA978A8D2847B15900854CD1 /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = ""; }; 25 | AA978A8F2847B15900854CD1 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 26 | AA978A902847B15900854CD1 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 27 | AA978A932847B15900854CD1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 28 | AA978A952847B15B00854CD1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 29 | AA978A982847B15B00854CD1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 30 | AA978A9A2847B15B00854CD1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 31 | AA978A9B2847B15B00854CD1 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 32 | /* End PBXFileReference section */ 33 | 34 | /* Begin PBXFrameworksBuildPhase section */ 35 | AA978A832847B15900854CD1 /* Frameworks */ = { 36 | isa = PBXFrameworksBuildPhase; 37 | buildActionMask = 2147483647; 38 | files = ( 39 | ); 40 | runOnlyForDeploymentPostprocessing = 0; 41 | }; 42 | /* End PBXFrameworksBuildPhase section */ 43 | 44 | /* Begin PBXGroup section */ 45 | AA978A7D2847B15900854CD1 = { 46 | isa = PBXGroup; 47 | children = ( 48 | AA978A882847B15900854CD1 /* adb-demo */, 49 | AA978A872847B15900854CD1 /* Products */, 50 | ); 51 | sourceTree = ""; 52 | }; 53 | AA978A872847B15900854CD1 /* Products */ = { 54 | isa = PBXGroup; 55 | children = ( 56 | AA978A862847B15900854CD1 /* adb-demo.app */, 57 | ); 58 | name = Products; 59 | sourceTree = ""; 60 | }; 61 | AA978A882847B15900854CD1 /* adb-demo */ = { 62 | isa = PBXGroup; 63 | children = ( 64 | AA978A892847B15900854CD1 /* AppDelegate.h */, 65 | AA978A8A2847B15900854CD1 /* AppDelegate.m */, 66 | AA978A8C2847B15900854CD1 /* SceneDelegate.h */, 67 | AA978A8D2847B15900854CD1 /* SceneDelegate.m */, 68 | AA978A8F2847B15900854CD1 /* ViewController.h */, 69 | AA978A902847B15900854CD1 /* ViewController.m */, 70 | AA978A922847B15900854CD1 /* Main.storyboard */, 71 | AA978A952847B15B00854CD1 /* Assets.xcassets */, 72 | AA978A972847B15B00854CD1 /* LaunchScreen.storyboard */, 73 | AA978A9A2847B15B00854CD1 /* Info.plist */, 74 | AA978A9B2847B15B00854CD1 /* main.m */, 75 | ); 76 | path = "adb-demo"; 77 | sourceTree = ""; 78 | }; 79 | /* End PBXGroup section */ 80 | 81 | /* Begin PBXNativeTarget section */ 82 | AA978A852847B15900854CD1 /* adb-demo */ = { 83 | isa = PBXNativeTarget; 84 | buildConfigurationList = AA978A9F2847B15B00854CD1 /* Build configuration list for PBXNativeTarget "adb-demo" */; 85 | buildPhases = ( 86 | AA978A822847B15900854CD1 /* Sources */, 87 | AA978A832847B15900854CD1 /* Frameworks */, 88 | AA978A842847B15900854CD1 /* Resources */, 89 | ); 90 | buildRules = ( 91 | ); 92 | dependencies = ( 93 | ); 94 | name = "adb-demo"; 95 | productName = "adb-demo"; 96 | productReference = AA978A862847B15900854CD1 /* adb-demo.app */; 97 | productType = "com.apple.product-type.application"; 98 | }; 99 | /* End PBXNativeTarget section */ 100 | 101 | /* Begin PBXProject section */ 102 | AA978A7E2847B15900854CD1 /* Project object */ = { 103 | isa = PBXProject; 104 | attributes = { 105 | BuildIndependentTargetsInParallel = 1; 106 | LastUpgradeCheck = 1330; 107 | TargetAttributes = { 108 | AA978A852847B15900854CD1 = { 109 | CreatedOnToolsVersion = 13.3.1; 110 | }; 111 | }; 112 | }; 113 | buildConfigurationList = AA978A812847B15900854CD1 /* Build configuration list for PBXProject "adb-demo" */; 114 | compatibilityVersion = "Xcode 13.0"; 115 | developmentRegion = en; 116 | hasScannedForEncodings = 0; 117 | knownRegions = ( 118 | en, 119 | Base, 120 | ); 121 | mainGroup = AA978A7D2847B15900854CD1; 122 | productRefGroup = AA978A872847B15900854CD1 /* Products */; 123 | projectDirPath = ""; 124 | projectRoot = ""; 125 | targets = ( 126 | AA978A852847B15900854CD1 /* adb-demo */, 127 | ); 128 | }; 129 | /* End PBXProject section */ 130 | 131 | /* Begin PBXResourcesBuildPhase section */ 132 | AA978A842847B15900854CD1 /* Resources */ = { 133 | isa = PBXResourcesBuildPhase; 134 | buildActionMask = 2147483647; 135 | files = ( 136 | AA978A992847B15B00854CD1 /* LaunchScreen.storyboard in Resources */, 137 | AA978A962847B15B00854CD1 /* Assets.xcassets in Resources */, 138 | AA978A942847B15900854CD1 /* Main.storyboard in Resources */, 139 | ); 140 | runOnlyForDeploymentPostprocessing = 0; 141 | }; 142 | /* End PBXResourcesBuildPhase section */ 143 | 144 | /* Begin PBXSourcesBuildPhase section */ 145 | AA978A822847B15900854CD1 /* Sources */ = { 146 | isa = PBXSourcesBuildPhase; 147 | buildActionMask = 2147483647; 148 | files = ( 149 | AA978A912847B15900854CD1 /* ViewController.m in Sources */, 150 | AA978A8B2847B15900854CD1 /* AppDelegate.m in Sources */, 151 | AA978A9C2847B15B00854CD1 /* main.m in Sources */, 152 | AA978A8E2847B15900854CD1 /* SceneDelegate.m in Sources */, 153 | ); 154 | runOnlyForDeploymentPostprocessing = 0; 155 | }; 156 | /* End PBXSourcesBuildPhase section */ 157 | 158 | /* Begin PBXVariantGroup section */ 159 | AA978A922847B15900854CD1 /* Main.storyboard */ = { 160 | isa = PBXVariantGroup; 161 | children = ( 162 | AA978A932847B15900854CD1 /* Base */, 163 | ); 164 | name = Main.storyboard; 165 | sourceTree = ""; 166 | }; 167 | AA978A972847B15B00854CD1 /* LaunchScreen.storyboard */ = { 168 | isa = PBXVariantGroup; 169 | children = ( 170 | AA978A982847B15B00854CD1 /* Base */, 171 | ); 172 | name = LaunchScreen.storyboard; 173 | sourceTree = ""; 174 | }; 175 | /* End PBXVariantGroup section */ 176 | 177 | /* Begin XCBuildConfiguration section */ 178 | AA978A9D2847B15B00854CD1 /* Debug */ = { 179 | isa = XCBuildConfiguration; 180 | buildSettings = { 181 | ALWAYS_SEARCH_USER_PATHS = NO; 182 | CLANG_ANALYZER_NONNULL = YES; 183 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 184 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 185 | CLANG_ENABLE_MODULES = YES; 186 | CLANG_ENABLE_OBJC_ARC = YES; 187 | CLANG_ENABLE_OBJC_WEAK = YES; 188 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 189 | CLANG_WARN_BOOL_CONVERSION = YES; 190 | CLANG_WARN_COMMA = YES; 191 | CLANG_WARN_CONSTANT_CONVERSION = YES; 192 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 193 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 194 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 195 | CLANG_WARN_EMPTY_BODY = YES; 196 | CLANG_WARN_ENUM_CONVERSION = YES; 197 | CLANG_WARN_INFINITE_RECURSION = YES; 198 | CLANG_WARN_INT_CONVERSION = YES; 199 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 200 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 201 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 202 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 203 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 204 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 205 | CLANG_WARN_STRICT_PROTOTYPES = YES; 206 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 207 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 208 | CLANG_WARN_UNREACHABLE_CODE = YES; 209 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 210 | COPY_PHASE_STRIP = NO; 211 | DEBUG_INFORMATION_FORMAT = dwarf; 212 | ENABLE_STRICT_OBJC_MSGSEND = YES; 213 | ENABLE_TESTABILITY = YES; 214 | GCC_C_LANGUAGE_STANDARD = gnu11; 215 | GCC_DYNAMIC_NO_PIC = NO; 216 | GCC_NO_COMMON_BLOCKS = YES; 217 | GCC_OPTIMIZATION_LEVEL = 0; 218 | GCC_PREPROCESSOR_DEFINITIONS = ( 219 | "DEBUG=1", 220 | "$(inherited)", 221 | ); 222 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 223 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 224 | GCC_WARN_UNDECLARED_SELECTOR = YES; 225 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 226 | GCC_WARN_UNUSED_FUNCTION = YES; 227 | GCC_WARN_UNUSED_VARIABLE = YES; 228 | IPHONEOS_DEPLOYMENT_TARGET = 15.4; 229 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 230 | MTL_FAST_MATH = YES; 231 | ONLY_ACTIVE_ARCH = YES; 232 | SDKROOT = iphoneos; 233 | }; 234 | name = Debug; 235 | }; 236 | AA978A9E2847B15B00854CD1 /* Release */ = { 237 | isa = XCBuildConfiguration; 238 | buildSettings = { 239 | ALWAYS_SEARCH_USER_PATHS = NO; 240 | CLANG_ANALYZER_NONNULL = YES; 241 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 242 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 243 | CLANG_ENABLE_MODULES = YES; 244 | CLANG_ENABLE_OBJC_ARC = YES; 245 | CLANG_ENABLE_OBJC_WEAK = YES; 246 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 247 | CLANG_WARN_BOOL_CONVERSION = YES; 248 | CLANG_WARN_COMMA = YES; 249 | CLANG_WARN_CONSTANT_CONVERSION = YES; 250 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 251 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 252 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 253 | CLANG_WARN_EMPTY_BODY = YES; 254 | CLANG_WARN_ENUM_CONVERSION = YES; 255 | CLANG_WARN_INFINITE_RECURSION = YES; 256 | CLANG_WARN_INT_CONVERSION = YES; 257 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 258 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 259 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 260 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 261 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 262 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 263 | CLANG_WARN_STRICT_PROTOTYPES = YES; 264 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 265 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 266 | CLANG_WARN_UNREACHABLE_CODE = YES; 267 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 268 | COPY_PHASE_STRIP = NO; 269 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 270 | ENABLE_NS_ASSERTIONS = NO; 271 | ENABLE_STRICT_OBJC_MSGSEND = YES; 272 | GCC_C_LANGUAGE_STANDARD = gnu11; 273 | GCC_NO_COMMON_BLOCKS = YES; 274 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 275 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 276 | GCC_WARN_UNDECLARED_SELECTOR = YES; 277 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 278 | GCC_WARN_UNUSED_FUNCTION = YES; 279 | GCC_WARN_UNUSED_VARIABLE = YES; 280 | IPHONEOS_DEPLOYMENT_TARGET = 15.4; 281 | MTL_ENABLE_DEBUG_INFO = NO; 282 | MTL_FAST_MATH = YES; 283 | SDKROOT = iphoneos; 284 | VALIDATE_PRODUCT = YES; 285 | }; 286 | name = Release; 287 | }; 288 | AA978AA02847B15B00854CD1 /* Debug */ = { 289 | isa = XCBuildConfiguration; 290 | buildSettings = { 291 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 292 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 293 | CODE_SIGN_STYLE = Automatic; 294 | CURRENT_PROJECT_VERSION = 1; 295 | DEVELOPMENT_TEAM = K8Q74NT2BG; 296 | ENABLE_BITCODE = NO; 297 | GENERATE_INFOPLIST_FILE = YES; 298 | HEADER_SEARCH_PATHS = "$(SRCROOT)/../../output/include"; 299 | INFOPLIST_FILE = "adb-demo/Info.plist"; 300 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 301 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; 302 | INFOPLIST_KEY_UIMainStoryboardFile = Main; 303 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 304 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 305 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 306 | LD_RUNPATH_SEARCH_PATHS = ( 307 | "$(inherited)", 308 | "@executable_path/Frameworks", 309 | ); 310 | LIBRARY_SEARCH_PATHS = "$(SRCROOT)/../../output/$(PLATFORM_NAME)/$(CURRENT_ARCH)"; 311 | MARKETING_VERSION = 1.0; 312 | OTHER_LDFLAGS = ( 313 | "-lssl", 314 | "-lvncclient", 315 | "-lcrypto", 316 | ); 317 | PRODUCT_BUNDLE_IDENTIFIER = "com.mobile.adb-demo"; 318 | PRODUCT_NAME = "$(TARGET_NAME)"; 319 | SWIFT_EMIT_LOC_STRINGS = YES; 320 | TARGETED_DEVICE_FAMILY = "1,2"; 321 | }; 322 | name = Debug; 323 | }; 324 | AA978AA12847B15B00854CD1 /* Release */ = { 325 | isa = XCBuildConfiguration; 326 | buildSettings = { 327 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 328 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 329 | CODE_SIGN_STYLE = Automatic; 330 | CURRENT_PROJECT_VERSION = 1; 331 | DEVELOPMENT_TEAM = K8Q74NT2BG; 332 | ENABLE_BITCODE = NO; 333 | GENERATE_INFOPLIST_FILE = YES; 334 | HEADER_SEARCH_PATHS = "$(SRCROOT)/../../output/include"; 335 | INFOPLIST_FILE = "adb-demo/Info.plist"; 336 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 337 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; 338 | INFOPLIST_KEY_UIMainStoryboardFile = Main; 339 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 340 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 341 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 342 | LD_RUNPATH_SEARCH_PATHS = ( 343 | "$(inherited)", 344 | "@executable_path/Frameworks", 345 | ); 346 | LIBRARY_SEARCH_PATHS = "$(SRCROOT)/../../output/$(PLATFORM_NAME)/$(CURRENT_ARCH)"; 347 | MARKETING_VERSION = 1.0; 348 | OTHER_LDFLAGS = ( 349 | "-lssl", 350 | "-lvncclient", 351 | "-lcrypto", 352 | ); 353 | PRODUCT_BUNDLE_IDENTIFIER = "com.mobile.adb-demo"; 354 | PRODUCT_NAME = "$(TARGET_NAME)"; 355 | SWIFT_EMIT_LOC_STRINGS = YES; 356 | TARGETED_DEVICE_FAMILY = "1,2"; 357 | }; 358 | name = Release; 359 | }; 360 | /* End XCBuildConfiguration section */ 361 | 362 | /* Begin XCConfigurationList section */ 363 | AA978A812847B15900854CD1 /* Build configuration list for PBXProject "adb-demo" */ = { 364 | isa = XCConfigurationList; 365 | buildConfigurations = ( 366 | AA978A9D2847B15B00854CD1 /* Debug */, 367 | AA978A9E2847B15B00854CD1 /* Release */, 368 | ); 369 | defaultConfigurationIsVisible = 0; 370 | defaultConfigurationName = Release; 371 | }; 372 | AA978A9F2847B15B00854CD1 /* Build configuration list for PBXNativeTarget "adb-demo" */ = { 373 | isa = XCConfigurationList; 374 | buildConfigurations = ( 375 | AA978AA02847B15B00854CD1 /* Debug */, 376 | AA978AA12847B15B00854CD1 /* Release */, 377 | ); 378 | defaultConfigurationIsVisible = 0; 379 | defaultConfigurationName = Release; 380 | }; 381 | /* End XCConfigurationList section */ 382 | }; 383 | rootObject = AA978A7E2847B15900854CD1 /* Project object */; 384 | } 385 | -------------------------------------------------------------------------------- /porting/adb/client/adb_client.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #define TRACE_TAG ADB 18 | 19 | #include "sysdeps.h" 20 | #include "adb_client.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #include "adb_io.h" 47 | #include "adb_utils.h" 48 | #include "socket_spec.h" 49 | #include "sysdeps/chrono.h" 50 | 51 | static TransportType __adb_transport = kTransportAny; 52 | static const char* __adb_serial = nullptr; 53 | static TransportId __adb_transport_id = 0; 54 | 55 | static const char* __adb_server_socket_spec; 56 | static const char* __adb_client_one_device; 57 | 58 | void adb_set_transport(TransportType type, const char* serial, TransportId transport_id) { 59 | __adb_transport = type; 60 | __adb_serial = serial; 61 | __adb_transport_id = transport_id; 62 | } 63 | 64 | void adb_get_transport(TransportType* type, const char** serial, TransportId* transport_id) { 65 | if (type) *type = __adb_transport; 66 | if (serial) *serial = __adb_serial; 67 | if (transport_id) *transport_id = __adb_transport_id; 68 | } 69 | 70 | void adb_set_socket_spec(const char* socket_spec) { 71 | if (__adb_server_socket_spec) { 72 | // Porting: prevent abort, which caused by diffrent processes archtiecture, we are now in the same process. 73 | // LOG(FATAL) << "attempted to reinitialize adb_server_socket_spec " << socket_spec << " (was " << __adb_server_socket_spec << ")"; 74 | } 75 | __adb_server_socket_spec = socket_spec; 76 | } 77 | 78 | void adb_set_one_device(const char* one_device) { 79 | __adb_client_one_device = one_device; 80 | } 81 | 82 | static std::optional switch_socket_transport(int fd, std::string* error) { 83 | TransportId result; 84 | bool read_transport = true; 85 | 86 | std::string service; 87 | if (__adb_transport_id) { 88 | read_transport = false; 89 | service += "host:transport-id:"; 90 | service += std::to_string(__adb_transport_id); 91 | result = __adb_transport_id; 92 | } else if (__adb_serial) { 93 | service += "host:tport:serial:"; 94 | service += __adb_serial; 95 | } else { 96 | const char* transport_type = "???"; 97 | switch (__adb_transport) { 98 | case kTransportUsb: 99 | transport_type = "usb"; 100 | break; 101 | case kTransportLocal: 102 | transport_type = "local"; 103 | break; 104 | case kTransportAny: 105 | transport_type = "any"; 106 | break; 107 | case kTransportHost: 108 | // no switch necessary 109 | return 0; 110 | } 111 | service += "host:tport:"; 112 | service += transport_type; 113 | } 114 | 115 | if (!SendProtocolString(fd, service)) { 116 | *error = perror_str("write failure during connection"); 117 | return std::nullopt; 118 | } 119 | 120 | LOG(DEBUG) << "Switch transport in progress: " << service; 121 | 122 | if (!adb_status(fd, error)) { 123 | D("Switch transport failed: %s", error->c_str()); 124 | return std::nullopt; 125 | } 126 | 127 | if (read_transport) { 128 | if (!ReadFdExactly(fd, &result, sizeof(result))) { 129 | *error = "failed to read transport id from server"; 130 | return std::nullopt; 131 | } 132 | } 133 | 134 | D("Switch transport success"); 135 | return result; 136 | } 137 | 138 | bool adb_status(borrowed_fd fd, std::string* error) { 139 | char buf[5]; 140 | if (!ReadFdExactly(fd, buf, 4)) { 141 | *error = perror_str("protocol fault (couldn't read status)"); 142 | return false; 143 | } 144 | 145 | if (!memcmp(buf, "OKAY", 4)) { 146 | return true; 147 | } 148 | 149 | if (memcmp(buf, "FAIL", 4)) { 150 | *error = android::base::StringPrintf("protocol fault (status %02x %02x %02x %02x?!)", 151 | buf[0], buf[1], buf[2], buf[3]); 152 | return false; 153 | } 154 | 155 | ReadProtocolString(fd, error, error); 156 | return false; 157 | } 158 | 159 | static int _adb_connect(std::string_view service, TransportId* transport, std::string* error, 160 | bool force_switch = false) { 161 | LOG(DEBUG) << "_adb_connect: " << service; 162 | if (service.empty() || service.size() > MAX_PAYLOAD) { 163 | *error = android::base::StringPrintf("bad service name length (%zd)", service.size()); 164 | return -1; 165 | } 166 | 167 | std::string reason; 168 | unique_fd fd; 169 | if (!socket_spec_connect(&fd, __adb_server_socket_spec, nullptr, nullptr, &reason)) { 170 | *error = android::base::StringPrintf("cannot connect to daemon at %s: %s", 171 | __adb_server_socket_spec, reason.c_str()); 172 | return -2; 173 | } 174 | 175 | if (!service.starts_with("host") || force_switch) { 176 | std::optional transport_result = switch_socket_transport(fd.get(), error); 177 | if (!transport_result) { 178 | return -1; 179 | } 180 | 181 | if (transport) { 182 | *transport = *transport_result; 183 | } 184 | } 185 | 186 | if (!SendProtocolString(fd.get(), service)) { 187 | *error = perror_str("write failure during connection"); 188 | return -1; 189 | } 190 | 191 | if (!adb_status(fd.get(), error)) { 192 | return -1; 193 | } 194 | 195 | D("_adb_connect: return fd %d", fd.get()); 196 | return fd.release(); 197 | } 198 | 199 | bool adb_kill_server() { 200 | D("adb_kill_server"); 201 | std::string reason; 202 | unique_fd fd; 203 | if (!socket_spec_connect(&fd, __adb_server_socket_spec, nullptr, nullptr, &reason)) { 204 | fprintf(stderr, "cannot connect to daemon at %s: %s\n", __adb_server_socket_spec, 205 | reason.c_str()); 206 | return true; 207 | } 208 | 209 | if (!SendProtocolString(fd.get(), "host:kill")) { 210 | fprintf(stderr, "error: write failure during connection: %s\n", strerror(errno)); 211 | return false; 212 | } 213 | 214 | char buf[4]; 215 | if (!ReadFdExactly(fd.get(), buf, 4)) { 216 | fprintf(stderr, "error: failed to read response from server\n"); 217 | return false; 218 | } 219 | 220 | if (memcmp(buf, "OKAY", 4) == 0) { 221 | // Nothing to do. 222 | } else if (memcmp(buf, "FAIL", 4) == 0) { 223 | std::string output, error; 224 | if (!ReadProtocolString(fd.get(), &output, &error)) { 225 | fprintf(stderr, "error: %s\n", error.c_str()); 226 | return false; 227 | } 228 | 229 | fprintf(stderr, "error: %s\n", output.c_str()); 230 | return false; 231 | } 232 | 233 | // Now that no more data is expected, wait for socket orderly shutdown or error, indicating 234 | // server death. 235 | ReadOrderlyShutdown(fd.get()); 236 | return true; 237 | } 238 | 239 | int adb_connect(std::string_view service, std::string* error) { 240 | return adb_connect(nullptr, service, error); 241 | } 242 | 243 | #if defined(__linux__) 244 | std::optional adb_get_server_executable_path() { 245 | int port; 246 | std::string error; 247 | if (!parse_tcp_socket_spec(__adb_server_socket_spec, nullptr, &port, nullptr, &error)) { 248 | return {}; 249 | } 250 | 251 | return adb_get_android_dir_path() + OS_PATH_SEPARATOR + "adb." + std::to_string(port); 252 | } 253 | #endif 254 | 255 | static bool __adb_check_server_version(std::string* error) { 256 | unique_fd fd(_adb_connect("host:version", nullptr, error)); 257 | 258 | bool local = is_local_socket_spec(__adb_server_socket_spec); 259 | if (fd == -2 && !local) { 260 | fprintf(stderr, "* cannot start server on remote host\n"); 261 | // error is the original network connection error 262 | return false; 263 | } else if (fd == -2) { 264 | fprintf(stderr, "* daemon not running; starting now at %s\n", __adb_server_socket_spec); 265 | start_server: 266 | // On a "one_device_required" system, the server will not start without the "one_device" 267 | // parameter. If "--one_device" was not provided (__adb_client_one_device), we attempt 268 | // to find the value from the "-s" parameter (__adb_serial). 269 | const char* one_device = __adb_client_one_device; 270 | if (!one_device && __adb_serial && is_one_device_mandatory()) { 271 | one_device = __adb_serial; 272 | } 273 | if (launch_server(__adb_server_socket_spec, one_device)) { 274 | fprintf(stderr, "* failed to start daemon\n"); 275 | // launch_server() has already printed detailed error info, so just 276 | // return a generic error string about the overall adb_connect() 277 | // that the caller requested. 278 | *error = "cannot connect to daemon"; 279 | return false; 280 | } else { 281 | fprintf(stderr, "* daemon started successfully\n"); 282 | } 283 | // The server will wait until it detects all of its connected devices before acking. 284 | // Fall through to _adb_connect. 285 | } else { 286 | // If a server is already running, check its version matches. 287 | int version = 0; 288 | 289 | // If we have a file descriptor, then parse version result. 290 | if (fd >= 0) { 291 | std::string version_string; 292 | if (!ReadProtocolString(fd, &version_string, error)) { 293 | return false; 294 | } 295 | 296 | ReadOrderlyShutdown(fd); 297 | 298 | if (sscanf(&version_string[0], "%04x", &version) != 1) { 299 | *error = android::base::StringPrintf("cannot parse version string: %s", 300 | version_string.c_str()); 301 | return false; 302 | } 303 | } else { 304 | // If fd is -1 check for "unknown host service" which would 305 | // indicate a version of adb that does not support the 306 | // version command, in which case we should fall-through to kill it. 307 | if (*error != "unknown host service") { 308 | return false; 309 | } 310 | } 311 | 312 | if (version != ADB_SERVER_VERSION) { 313 | #if defined(__linux__) 314 | if (version > ADB_SERVER_VERSION && local) { 315 | // Try to re-exec the existing adb server's binary. 316 | constexpr const char* adb_reexeced = "adb (re-execed)"; 317 | if (strcmp(adb_reexeced, *__adb_argv) != 0) { 318 | __adb_argv[0] = adb_reexeced; 319 | std::optional server_path_path = adb_get_server_executable_path(); 320 | std::string server_path; 321 | if (server_path_path && 322 | android::base::ReadFileToString(*server_path_path, &server_path)) { 323 | if (execve(server_path.c_str(), const_cast(__adb_argv), 324 | const_cast(__adb_envp)) == -1) { 325 | LOG(ERROR) << "failed to exec newer version at " << server_path; 326 | } 327 | 328 | // Fall-through to restarting the server. 329 | } 330 | } 331 | } 332 | #endif 333 | 334 | fprintf(stderr, "adb server version (%d) doesn't match this client (%d); killing...\n", 335 | version, ADB_SERVER_VERSION); 336 | adb_kill_server(); 337 | goto start_server; 338 | } 339 | } 340 | 341 | return true; 342 | } 343 | 344 | bool adb_check_server_version(std::string* error) { 345 | // Only check the version once per process, since this isn't atomic anyway. 346 | static std::once_flag once; 347 | static bool result; 348 | static std::string* err; 349 | // std::call_once(once, []() { 350 | err = new std::string(); 351 | result = __adb_check_server_version(err); 352 | // }); 353 | *error = *err; 354 | return result; 355 | } 356 | 357 | int adb_connect(TransportId* transport, std::string_view service, std::string* error, 358 | bool force_switch_device) { 359 | LOG(DEBUG) << "adb_connect: service: " << service; 360 | 361 | // Query the adb server's version. 362 | if (!adb_check_server_version(error)) { 363 | return -1; 364 | } 365 | 366 | // if the command is start-server, we are done. 367 | if (service == "host:start-server") { 368 | return 0; 369 | } 370 | 371 | unique_fd fd(_adb_connect(service, transport, error, force_switch_device)); 372 | if (fd == -1) { 373 | D("_adb_connect error: %s", error->c_str()); 374 | } else if(fd == -2) { 375 | fprintf(stderr, "* daemon still not running\n"); 376 | } 377 | D("adb_connect: return fd %d", fd.get()); 378 | 379 | return fd.release(); 380 | } 381 | 382 | bool adb_command(const std::string& service) { 383 | std::string error; 384 | unique_fd fd(adb_connect(service, &error)); 385 | if (fd < 0) { 386 | fprintf(stderr, "error: %s\n", error.c_str()); 387 | return false; 388 | } 389 | 390 | if (!adb_status(fd.get(), &error)) { 391 | fprintf(stderr, "error: %s\n", error.c_str()); 392 | return false; 393 | } 394 | 395 | ReadOrderlyShutdown(fd.get()); 396 | return true; 397 | } 398 | 399 | bool adb_query(const std::string& service, std::string* result, std::string* error, 400 | bool force_switch_device) { 401 | D("adb_query: %s", service.c_str()); 402 | unique_fd fd(adb_connect(nullptr, service, error, force_switch_device)); 403 | if (fd < 0) { 404 | return false; 405 | } 406 | 407 | result->clear(); 408 | if (!ReadProtocolString(fd.get(), result, error)) { 409 | return false; 410 | } 411 | 412 | ReadOrderlyShutdown(fd.get()); 413 | return true; 414 | } 415 | 416 | std::string format_host_command(const char* command) { 417 | if (__adb_transport_id) { 418 | return android::base::StringPrintf("host-transport-id:%" PRIu64 ":%s", __adb_transport_id, 419 | command); 420 | } else if (__adb_serial) { 421 | return android::base::StringPrintf("host-serial:%s:%s", __adb_serial, command); 422 | } 423 | 424 | const char* prefix = "host"; 425 | if (__adb_transport == kTransportUsb) { 426 | prefix = "host-usb"; 427 | } else if (__adb_transport == kTransportLocal) { 428 | prefix = "host-local"; 429 | } 430 | return android::base::StringPrintf("%s:%s", prefix, command); 431 | } 432 | 433 | const std::optional& adb_get_feature_set(std::string* error) { 434 | static std::mutex feature_mutex [[clang::no_destroy]]; 435 | static std::optional features [[clang::no_destroy]]; 436 | std::lock_guard lock(feature_mutex); 437 | if (!features) { 438 | std::string result; 439 | std::string err; 440 | if (adb_query(format_host_command("features"), &result, &err)) { 441 | features = StringToFeatureSet(result); 442 | } else { 443 | if (error) { 444 | *error = err; 445 | } 446 | } 447 | } 448 | return features; 449 | } 450 | 451 | [[noreturn]] static void error_exit_va(int error, const char* fmt, va_list va) { 452 | fflush(stdout); 453 | fprintf(stderr, "%s: ", android::base::Basename(android::base::GetExecutablePath()).c_str()); 454 | 455 | vfprintf(stderr, fmt, va); 456 | 457 | if (error != 0) { 458 | fprintf(stderr, ": %s", strerror(error)); 459 | } 460 | 461 | putc('\n', stderr); 462 | fflush(stderr); 463 | 464 | exit(EXIT_FAILURE); 465 | } 466 | 467 | void error_exit(const char* fmt, ...) { 468 | va_list va; 469 | va_start(va, fmt); 470 | error_exit_va(0, fmt, va); 471 | va_end(va); 472 | } 473 | 474 | void perror_exit(const char* fmt, ...) { 475 | va_list va; 476 | va_start(va, fmt); 477 | error_exit_va(errno, fmt, va); 478 | va_end(va); 479 | } 480 | -------------------------------------------------------------------------------- /porting/adb/client/adb_install.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "adb_install.h" 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "adb.h" 35 | #include "adb_client.h" 36 | #include "adb_unique_fd.h" 37 | #include "adb_utils.h" 38 | #include "client/file_sync_client.h" 39 | #include "commandline.h" 40 | #include "fastdeploy.h" 41 | #include "incremental.h" 42 | #include "sysdeps.h" 43 | 44 | using namespace std::literals; 45 | 46 | static constexpr int kFastDeployMinApi = 24; 47 | 48 | namespace { 49 | 50 | enum InstallMode { 51 | INSTALL_DEFAULT, 52 | INSTALL_PUSH, 53 | INSTALL_STREAM, 54 | INSTALL_INCREMENTAL, 55 | }; 56 | 57 | enum class CmdlineOption { None, Enable, Disable }; 58 | } 59 | 60 | static InstallMode best_install_mode() { 61 | auto&& features = adb_get_feature_set_or_die(nullptr, nullptr); 62 | if (CanUseFeature(*features, kFeatureCmd)) { 63 | return INSTALL_STREAM; 64 | } 65 | return INSTALL_PUSH; 66 | } 67 | 68 | static bool is_apex_supported() { 69 | auto&& features = adb_get_feature_set_or_die(nullptr, nullptr); 70 | return CanUseFeature(*features, kFeatureApex); 71 | } 72 | 73 | static bool is_abb_exec_supported() { 74 | auto&& features = adb_get_feature_set_or_die(nullptr, nullptr); 75 | return CanUseFeature(*features, kFeatureAbbExec); 76 | } 77 | 78 | static int pm_command(int argc, const char** argv) { 79 | std::string cmd = "pm"; 80 | 81 | while (argc-- > 0) { 82 | cmd += " " + escape_arg(*argv++); 83 | } 84 | 85 | return send_shell_command(cmd); 86 | } 87 | 88 | static int uninstall_app_streamed(int argc, const char** argv) { 89 | // 'adb uninstall' takes the same arguments as 'cmd package uninstall' on device 90 | std::string cmd = "cmd package"; 91 | while (argc-- > 0) { 92 | // deny the '-k' option until the remaining data/cache can be removed with adb/UI 93 | if (strcmp(*argv, "-k") == 0) { 94 | printf("The -k option uninstalls the application while retaining the " 95 | "data/cache.\n" 96 | "At the moment, there is no way to remove the remaining data.\n" 97 | "You will have to reinstall the application with the same " 98 | "signature, and fully " 99 | "uninstall it.\n" 100 | "If you truly wish to continue, execute 'adb shell cmd package " 101 | "uninstall -k'.\n"); 102 | return EXIT_FAILURE; 103 | } 104 | cmd += " " + escape_arg(*argv++); 105 | } 106 | 107 | return send_shell_command(cmd); 108 | } 109 | 110 | static int uninstall_app_legacy(int argc, const char** argv) { 111 | /* if the user choose the -k option, we refuse to do it until devices are 112 | out with the option to uninstall the remaining data somehow (adb/ui) */ 113 | for (int i = 1; i < argc; i++) { 114 | if (!strcmp(argv[i], "-k")) { 115 | printf("The -k option uninstalls the application while retaining the " 116 | "data/cache.\n" 117 | "At the moment, there is no way to remove the remaining data.\n" 118 | "You will have to reinstall the application with the same " 119 | "signature, and fully " 120 | "uninstall it.\n" 121 | "If you truly wish to continue, execute 'adb shell pm uninstall " 122 | "-k'\n."); 123 | return EXIT_FAILURE; 124 | } 125 | } 126 | 127 | /* 'adb uninstall' takes the same arguments as 'pm uninstall' on device */ 128 | return pm_command(argc, argv); 129 | } 130 | 131 | int uninstall_app(int argc, const char** argv) { 132 | if (best_install_mode() == INSTALL_PUSH) { 133 | return uninstall_app_legacy(argc, argv); 134 | } 135 | return uninstall_app_streamed(argc, argv); 136 | } 137 | 138 | static void read_status_line(int fd, char* buf, size_t count) { 139 | count--; 140 | while (count > 0) { 141 | int len = adb_read(fd, buf, count); 142 | if (len <= 0) { 143 | break; 144 | } 145 | 146 | buf += len; 147 | count -= len; 148 | } 149 | *buf = '\0'; 150 | } 151 | 152 | static unique_fd send_command(const std::vector& cmd_args, std::string* error) { 153 | if (is_abb_exec_supported()) { 154 | return send_abb_exec_command(cmd_args, error); 155 | } else { 156 | return unique_fd(adb_connect(android::base::Join(cmd_args, " "), error)); 157 | } 158 | } 159 | 160 | static int install_app_streamed(int argc, const char** argv, bool use_fastdeploy) { 161 | printf("Performing Streamed Install\n"); 162 | 163 | // The last argument must be the APK file 164 | const char* file = argv[argc - 1]; 165 | if (!android::base::EndsWithIgnoreCase(file, ".apk") && 166 | !android::base::EndsWithIgnoreCase(file, ".apex")) { 167 | error_exit("filename doesn't end .apk or .apex: %s", file); 168 | } 169 | 170 | bool is_apex = false; 171 | if (android::base::EndsWithIgnoreCase(file, ".apex")) { 172 | is_apex = true; 173 | } 174 | if (is_apex && !is_apex_supported()) { 175 | error_exit(".apex is not supported on the target device"); 176 | } 177 | 178 | if (is_apex && use_fastdeploy) { 179 | error_exit("--fastdeploy doesn't support .apex files"); 180 | } 181 | 182 | if (use_fastdeploy) { 183 | #if defined(ENABLE_FASTDEPLOY) 184 | auto metadata = extract_metadata(file); 185 | if (metadata.has_value()) { 186 | // pass all but 1st (command) and last (apk path) parameters through to pm for 187 | // session creation 188 | std::vector pm_args{argv + 1, argv + argc - 1}; 189 | auto patchFd = install_patch(pm_args.size(), pm_args.data()); 190 | return stream_patch(file, std::move(metadata.value()), std::move(patchFd)); 191 | } 192 | #else 193 | error_exit("fastdeploy is disabled"); 194 | #endif 195 | } 196 | 197 | struct stat sb; 198 | if (stat(file, &sb) == -1) { 199 | fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno)); 200 | return 1; 201 | } 202 | 203 | unique_fd local_fd(adb_open(file, O_RDONLY | O_CLOEXEC)); 204 | if (local_fd < 0) { 205 | fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno)); 206 | return 1; 207 | } 208 | 209 | #ifdef __linux__ 210 | posix_fadvise(local_fd.get(), 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE); 211 | #endif 212 | 213 | const bool use_abb_exec = is_abb_exec_supported(); 214 | std::string error; 215 | std::vector cmd_args = {use_abb_exec ? "package" : "exec:cmd package"}; 216 | cmd_args.reserve(argc + 3); 217 | 218 | // don't copy the APK name, but, copy the rest of the arguments as-is 219 | while (argc-- > 1) { 220 | if (use_abb_exec) { 221 | cmd_args.push_back(*argv++); 222 | } else { 223 | cmd_args.push_back(escape_arg(*argv++)); 224 | } 225 | } 226 | 227 | // add size parameter [required for streaming installs] 228 | // do last to override any user specified value 229 | cmd_args.push_back("-S"); 230 | cmd_args.push_back(android::base::StringPrintf("%" PRIu64, static_cast(sb.st_size))); 231 | 232 | if (is_apex) { 233 | cmd_args.push_back("--apex"); 234 | } 235 | 236 | unique_fd remote_fd = send_command(cmd_args, &error); 237 | if (remote_fd < 0) { 238 | fprintf(stderr, "adb: connect error for write: %s\n", error.c_str()); 239 | return 1; 240 | } 241 | 242 | if (!copy_to_file(local_fd.get(), remote_fd.get())) { 243 | fprintf(stderr, "adb: failed to install: copy_to_file: %s: %s", file, strerror(errno)); 244 | return 1; 245 | } 246 | 247 | char buf[BUFSIZ]; 248 | read_status_line(remote_fd.get(), buf, sizeof(buf)); 249 | if (strncmp("Success", buf, 7) != 0) { 250 | fprintf(stderr, "adb: failed to install %s: %s", file, buf); 251 | return 1; 252 | } 253 | 254 | fputs(buf, stdout); 255 | return 0; 256 | } 257 | 258 | static int install_app_legacy(int argc, const char** argv, bool use_fastdeploy) { 259 | printf("Performing Push Install\n"); 260 | 261 | // Find last APK argument. 262 | // All other arguments passed through verbatim. 263 | int last_apk = -1; 264 | for (int i = argc - 1; i >= 0; i--) { 265 | if (android::base::EndsWithIgnoreCase(argv[i], ".apex")) { 266 | error_exit("APEX packages are only compatible with Streamed Install"); 267 | } 268 | if (android::base::EndsWithIgnoreCase(argv[i], ".apk")) { 269 | last_apk = i; 270 | break; 271 | } 272 | } 273 | 274 | if (last_apk == -1) error_exit("need APK file on command line"); 275 | 276 | int result = -1; 277 | std::vector apk_file = {argv[last_apk]}; 278 | std::string apk_dest = "/data/local/tmp/" + android::base::Basename(argv[last_apk]); 279 | argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */ 280 | 281 | if (use_fastdeploy) { 282 | #if defined(ENABLE_FASTDEPLOY) 283 | auto metadata = extract_metadata(apk_file[0]); 284 | if (metadata.has_value()) { 285 | auto patchFd = apply_patch_on_device(apk_dest.c_str()); 286 | int status = stream_patch(apk_file[0], std::move(metadata.value()), std::move(patchFd)); 287 | 288 | result = pm_command(argc, argv); 289 | delete_device_file(apk_dest); 290 | 291 | return status; 292 | } 293 | #else 294 | error_exit("fastdeploy is disabled"); 295 | #endif 296 | } 297 | 298 | if (do_sync_push(nullptr, nullptr, apk_file, apk_dest.c_str(), false, CompressionType::Any, false, false)) { 299 | result = pm_command(argc, argv); 300 | delete_device_file(apk_dest); 301 | } 302 | 303 | return result; 304 | } 305 | 306 | template 307 | static int ms_between(TimePoint start, TimePoint end) { 308 | return std::chrono::duration_cast(end - start).count(); 309 | } 310 | 311 | static int install_app_incremental(int argc, const char** argv, bool wait, bool silent) { 312 | using clock = std::chrono::high_resolution_clock; 313 | const auto start = clock::now(); 314 | int first_apk = -1; 315 | int last_apk = -1; 316 | incremental::Args passthrough_args = {}; 317 | for (int i = 0; i < argc; ++i) { 318 | const auto arg = std::string_view(argv[i]); 319 | if (android::base::EndsWithIgnoreCase(arg, ".apk"sv)) { 320 | last_apk = i; 321 | if (first_apk == -1) { 322 | first_apk = i; 323 | } 324 | } else if (arg.starts_with("install"sv)) { 325 | // incremental installation command on the device is the same for all its variations in 326 | // the adb, e.g. install-multiple or install-multi-package 327 | } else { 328 | passthrough_args.push_back(arg); 329 | } 330 | } 331 | 332 | if (first_apk == -1) { 333 | if (!silent) { 334 | fprintf(stderr, "error: need at least one APK file on command line\n"); 335 | } 336 | return -1; 337 | } 338 | 339 | auto files = incremental::Files{argv + first_apk, argv + last_apk + 1}; 340 | if (silent) { 341 | // For a silent installation we want to do the lightweight check first and bail early and 342 | // quietly if it fails. 343 | if (!incremental::can_install(files)) { 344 | return -1; 345 | } 346 | } 347 | 348 | printf("Performing Incremental Install\n"); 349 | auto server_process = incremental::install(files, passthrough_args, silent); 350 | if (!server_process) { 351 | return -1; 352 | } 353 | 354 | const auto end = clock::now(); 355 | printf("Install command complete in %d ms\n", ms_between(start, end)); 356 | 357 | if (wait) { 358 | (*server_process).wait(); 359 | } 360 | 361 | return 0; 362 | } 363 | 364 | static std::pair> calculate_install_mode( 365 | InstallMode modeFromArgs, bool fastdeploy, CmdlineOption incremental_request) { 366 | if (incremental_request == CmdlineOption::Enable) { 367 | if (fastdeploy) { 368 | error_exit( 369 | "--incremental and --fast-deploy options are incompatible. " 370 | "Please choose one"); 371 | } 372 | } 373 | 374 | if (modeFromArgs != INSTALL_DEFAULT) { 375 | if (incremental_request == CmdlineOption::Enable) { 376 | error_exit("--incremental is not compatible with other installation modes"); 377 | } 378 | return {modeFromArgs, std::nullopt}; 379 | } 380 | 381 | if (incremental_request != CmdlineOption::Disable && !is_abb_exec_supported()) { 382 | if (incremental_request == CmdlineOption::None) { 383 | incremental_request = CmdlineOption::Disable; 384 | } else { 385 | error_exit("Device doesn't support incremental installations"); 386 | } 387 | } 388 | if (incremental_request == CmdlineOption::None) { 389 | // check if the host is ok with incremental by default 390 | if (const char* incrementalFromEnv = getenv("ADB_INSTALL_DEFAULT_INCREMENTAL")) { 391 | using namespace android::base; 392 | auto val = ParseBool(incrementalFromEnv); 393 | if (val == ParseBoolResult::kFalse) { 394 | incremental_request = CmdlineOption::Disable; 395 | } 396 | } 397 | } 398 | if (incremental_request == CmdlineOption::None) { 399 | // still ok: let's see if the device allows using incremental by default 400 | // it starts feeling like we're looking for an excuse to not to use incremental... 401 | std::string error; 402 | std::vector args = {"settings", "get", "global", 403 | "enable_adb_incremental_install_default"}; 404 | auto fd = send_abb_exec_command(args, &error); 405 | if (!fd.ok()) { 406 | fprintf(stderr, "adb: retrieving the default device installation mode failed: %s", 407 | error.c_str()); 408 | } else { 409 | char buf[BUFSIZ] = {}; 410 | read_status_line(fd.get(), buf, sizeof(buf)); 411 | using namespace android::base; 412 | auto val = ParseBool(buf); 413 | if (val == ParseBoolResult::kFalse) { 414 | incremental_request = CmdlineOption::Disable; 415 | } 416 | } 417 | } 418 | 419 | if (incremental_request == CmdlineOption::Enable) { 420 | // explicitly requested - no fallback 421 | return {INSTALL_INCREMENTAL, std::nullopt}; 422 | } 423 | const auto bestMode = best_install_mode(); 424 | if (incremental_request == CmdlineOption::None) { 425 | // no opinion - use incremental, fallback to regular on a failure. 426 | return {INSTALL_INCREMENTAL, bestMode}; 427 | } 428 | // incremental turned off - use the regular best mode without a fallback. 429 | return {bestMode, std::nullopt}; 430 | } 431 | 432 | static std::vector parse_install_mode(std::vector argv, 433 | InstallMode* install_mode, 434 | CmdlineOption* incremental_request, 435 | bool* incremental_wait) { 436 | *install_mode = INSTALL_DEFAULT; 437 | *incremental_request = CmdlineOption::None; 438 | *incremental_wait = false; 439 | 440 | std::vector passthrough; 441 | for (auto&& arg : argv) { 442 | if (arg == "--streaming"sv) { 443 | *install_mode = INSTALL_STREAM; 444 | } else if (arg == "--no-streaming"sv) { 445 | *install_mode = INSTALL_PUSH; 446 | } else if (strlen(arg) >= "--incr"sv.size() && "--incremental"sv.starts_with(arg)) { 447 | *incremental_request = CmdlineOption::Enable; 448 | } else if (strlen(arg) >= "--no-incr"sv.size() && "--no-incremental"sv.starts_with(arg)) { 449 | *incremental_request = CmdlineOption::Disable; 450 | } else if (arg == "--wait"sv) { 451 | *incremental_wait = true; 452 | } else { 453 | passthrough.push_back(arg); 454 | } 455 | } 456 | return passthrough; 457 | } 458 | 459 | static std::vector parse_fast_deploy_mode( 460 | std::vector argv, bool* use_fastdeploy, 461 | FastDeploy_AgentUpdateStrategy* agent_update_strategy) { 462 | *use_fastdeploy = false; 463 | *agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion; 464 | 465 | std::vector passthrough; 466 | for (auto&& arg : argv) { 467 | if (arg == "--fastdeploy"sv) { 468 | *use_fastdeploy = true; 469 | } else if (arg == "--no-fastdeploy"sv) { 470 | *use_fastdeploy = false; 471 | } else if (arg == "--force-agent"sv) { 472 | *agent_update_strategy = FastDeploy_AgentUpdateAlways; 473 | } else if (arg == "--date-check-agent"sv) { 474 | *agent_update_strategy = FastDeploy_AgentUpdateNewerTimeStamp; 475 | } else if (arg == "--version-check-agent"sv) { 476 | *agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion; 477 | } else { 478 | passthrough.push_back(arg); 479 | } 480 | } 481 | return passthrough; 482 | } 483 | 484 | int install_app(int argc, const char** argv) { 485 | InstallMode install_mode = INSTALL_DEFAULT; 486 | auto incremental_request = CmdlineOption::None; 487 | bool incremental_wait = false; 488 | 489 | bool use_fastdeploy = false; 490 | FastDeploy_AgentUpdateStrategy agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion; 491 | 492 | auto unused_argv = parse_install_mode({argv, argv + argc}, &install_mode, &incremental_request, 493 | &incremental_wait); 494 | auto passthrough_argv = 495 | parse_fast_deploy_mode(std::move(unused_argv), &use_fastdeploy, &agent_update_strategy); 496 | 497 | auto [primary_mode, fallback_mode] = 498 | calculate_install_mode(install_mode, use_fastdeploy, incremental_request); 499 | if ((primary_mode == INSTALL_STREAM || 500 | fallback_mode.value_or(INSTALL_PUSH) == INSTALL_STREAM) && 501 | best_install_mode() == INSTALL_PUSH) { 502 | error_exit("Attempting to use streaming install on unsupported device"); 503 | } 504 | 505 | #if defined(ENABLE_FASTDEPLOY) 506 | if (use_fastdeploy && get_device_api_level() < kFastDeployMinApi) { 507 | fprintf(stderr, 508 | "Fast Deploy is only compatible with devices of API version %d or higher, " 509 | "ignoring.\n", 510 | kFastDeployMinApi); 511 | use_fastdeploy = false; 512 | } 513 | fastdeploy_set_agent_update_strategy(agent_update_strategy); 514 | #endif 515 | 516 | if (passthrough_argv.size() < 2) { 517 | error_exit("install requires an apk argument"); 518 | } 519 | 520 | auto run_install_mode = [&](InstallMode install_mode, bool silent) { 521 | switch (install_mode) { 522 | case INSTALL_PUSH: 523 | return install_app_legacy(passthrough_argv.size(), passthrough_argv.data(), 524 | use_fastdeploy); 525 | case INSTALL_STREAM: 526 | return install_app_streamed(passthrough_argv.size(), passthrough_argv.data(), 527 | use_fastdeploy); 528 | case INSTALL_INCREMENTAL: 529 | return install_app_incremental(passthrough_argv.size(), passthrough_argv.data(), 530 | incremental_wait, silent); 531 | case INSTALL_DEFAULT: 532 | default: 533 | error_exit("invalid install mode"); 534 | } 535 | }; 536 | auto res = run_install_mode(primary_mode, fallback_mode.has_value()); 537 | if (res && fallback_mode.value_or(primary_mode) != primary_mode) { 538 | res = run_install_mode(*fallback_mode, false); 539 | } 540 | return res; 541 | } 542 | 543 | static int install_multiple_app_streamed(int argc, const char** argv) { 544 | // Find all APK arguments starting at end. 545 | // All other arguments passed through verbatim. 546 | int first_apk = -1; 547 | uint64_t total_size = 0; 548 | for (int i = argc - 1; i >= 0; i--) { 549 | const char* file = argv[i]; 550 | if (android::base::EndsWithIgnoreCase(argv[i], ".apex")) { 551 | error_exit("APEX packages are not compatible with install-multiple"); 552 | } 553 | 554 | if (android::base::EndsWithIgnoreCase(file, ".apk") || 555 | android::base::EndsWithIgnoreCase(file, ".dm") || 556 | android::base::EndsWithIgnoreCase(file, ".fsv_sig")) { 557 | struct stat sb; 558 | if (stat(file, &sb) == -1) perror_exit("failed to stat \"%s\"", file); 559 | total_size += sb.st_size; 560 | first_apk = i; 561 | } else { 562 | break; 563 | } 564 | } 565 | 566 | if (first_apk == -1) error_exit("need APK file on command line"); 567 | 568 | const bool use_abb_exec = is_abb_exec_supported(); 569 | const std::string install_cmd = 570 | use_abb_exec ? "package" 571 | : best_install_mode() == INSTALL_PUSH ? "exec:pm" : "exec:cmd package"; 572 | 573 | std::vector cmd_args = {install_cmd, "install-create", "-S", 574 | std::to_string(total_size)}; 575 | cmd_args.reserve(first_apk + 4); 576 | for (int i = 0; i < first_apk; i++) { 577 | if (use_abb_exec) { 578 | cmd_args.push_back(argv[i]); 579 | } else { 580 | cmd_args.push_back(escape_arg(argv[i])); 581 | } 582 | } 583 | 584 | // Create install session 585 | std::string error; 586 | char buf[BUFSIZ]; 587 | { 588 | unique_fd fd = send_command(cmd_args, &error); 589 | if (fd < 0) { 590 | fprintf(stderr, "adb: connect error for create: %s\n", error.c_str()); 591 | return EXIT_FAILURE; 592 | } 593 | read_status_line(fd.get(), buf, sizeof(buf)); 594 | } 595 | 596 | int session_id = -1; 597 | if (!strncmp("Success", buf, 7)) { 598 | char* start = strrchr(buf, '['); 599 | char* end = strrchr(buf, ']'); 600 | if (start && end) { 601 | *end = '\0'; 602 | session_id = strtol(start + 1, nullptr, 10); 603 | } 604 | } 605 | if (session_id < 0) { 606 | fprintf(stderr, "adb: failed to create session\n"); 607 | fputs(buf, stderr); 608 | return EXIT_FAILURE; 609 | } 610 | const auto session_id_str = std::to_string(session_id); 611 | 612 | // Valid session, now stream the APKs 613 | bool success = true; 614 | for (int i = first_apk; i < argc; i++) { 615 | const char* file = argv[i]; 616 | struct stat sb; 617 | if (stat(file, &sb) == -1) { 618 | fprintf(stderr, "adb: failed to stat \"%s\": %s\n", file, strerror(errno)); 619 | success = false; 620 | goto finalize_session; 621 | } 622 | 623 | std::vector cmd_args = { 624 | install_cmd, 625 | "install-write", 626 | "-S", 627 | std::to_string(sb.st_size), 628 | session_id_str, 629 | android::base::Basename(file), 630 | "-", 631 | }; 632 | 633 | unique_fd local_fd(adb_open(file, O_RDONLY | O_CLOEXEC)); 634 | if (local_fd < 0) { 635 | fprintf(stderr, "adb: failed to open \"%s\": %s\n", file, strerror(errno)); 636 | success = false; 637 | goto finalize_session; 638 | } 639 | 640 | std::string error; 641 | unique_fd remote_fd = send_command(cmd_args, &error); 642 | if (remote_fd < 0) { 643 | fprintf(stderr, "adb: connect error for write: %s\n", error.c_str()); 644 | success = false; 645 | goto finalize_session; 646 | } 647 | 648 | if (!copy_to_file(local_fd.get(), remote_fd.get())) { 649 | fprintf(stderr, "adb: failed to write \"%s\": %s\n", file, strerror(errno)); 650 | success = false; 651 | goto finalize_session; 652 | } 653 | 654 | read_status_line(remote_fd.get(), buf, sizeof(buf)); 655 | 656 | if (strncmp("Success", buf, 7)) { 657 | fprintf(stderr, "adb: failed to write \"%s\"\n", file); 658 | fputs(buf, stderr); 659 | success = false; 660 | goto finalize_session; 661 | } 662 | } 663 | 664 | finalize_session: 665 | // Commit session if we streamed everything okay; otherwise abandon. 666 | std::vector service_args = { 667 | install_cmd, 668 | success ? "install-commit" : "install-abandon", 669 | session_id_str, 670 | }; 671 | { 672 | unique_fd fd = send_command(service_args, &error); 673 | if (fd < 0) { 674 | fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str()); 675 | return EXIT_FAILURE; 676 | } 677 | read_status_line(fd.get(), buf, sizeof(buf)); 678 | } 679 | if (!success) return EXIT_FAILURE; 680 | 681 | if (strncmp("Success", buf, 7)) { 682 | fprintf(stderr, "adb: failed to finalize session\n"); 683 | fputs(buf, stderr); 684 | return EXIT_FAILURE; 685 | } 686 | 687 | fputs(buf, stdout); 688 | return EXIT_SUCCESS; 689 | } 690 | 691 | int install_multiple_app(int argc, const char** argv) { 692 | InstallMode install_mode = INSTALL_DEFAULT; 693 | auto incremental_request = CmdlineOption::None; 694 | bool incremental_wait = false; 695 | bool use_fastdeploy = false; 696 | 697 | auto passthrough_argv = parse_install_mode({argv + 1, argv + argc}, &install_mode, 698 | &incremental_request, &incremental_wait); 699 | 700 | auto [primary_mode, fallback_mode] = 701 | calculate_install_mode(install_mode, use_fastdeploy, incremental_request); 702 | if ((primary_mode == INSTALL_STREAM || 703 | fallback_mode.value_or(INSTALL_PUSH) == INSTALL_STREAM) && 704 | best_install_mode() == INSTALL_PUSH) { 705 | error_exit("Attempting to use streaming install on unsupported device"); 706 | } 707 | 708 | auto run_install_mode = [&](InstallMode install_mode, bool silent) { 709 | switch (install_mode) { 710 | case INSTALL_PUSH: 711 | case INSTALL_STREAM: 712 | return install_multiple_app_streamed(passthrough_argv.size(), 713 | passthrough_argv.data()); 714 | case INSTALL_INCREMENTAL: 715 | return install_app_incremental(passthrough_argv.size(), passthrough_argv.data(), 716 | incremental_wait, silent); 717 | case INSTALL_DEFAULT: 718 | default: 719 | error_exit("invalid install mode"); 720 | } 721 | }; 722 | auto res = run_install_mode(primary_mode, fallback_mode.has_value()); 723 | if (res && fallback_mode.value_or(primary_mode) != primary_mode) { 724 | res = run_install_mode(*fallback_mode, false); 725 | } 726 | return res; 727 | } 728 | 729 | int install_multi_package(int argc, const char** argv) { 730 | // Find all APK arguments starting at end. 731 | // All other arguments passed through verbatim. 732 | bool apex_found = false; 733 | int first_package = -1; 734 | for (int i = argc - 1; i >= 0; i--) { 735 | const char* file = argv[i]; 736 | if (android::base::EndsWithIgnoreCase(file, ".apk") || 737 | android::base::EndsWithIgnoreCase(file, ".apex")) { 738 | first_package = i; 739 | if (android::base::EndsWithIgnoreCase(file, ".apex")) { 740 | apex_found = true; 741 | } 742 | } else { 743 | break; 744 | } 745 | } 746 | 747 | if (first_package == -1) error_exit("need APK or APEX files on command line"); 748 | 749 | if (best_install_mode() == INSTALL_PUSH) { 750 | fprintf(stderr, "adb: multi-package install is not supported on this device\n"); 751 | return EXIT_FAILURE; 752 | } 753 | 754 | const bool use_abb_exec = is_abb_exec_supported(); 755 | const std::string install_cmd = use_abb_exec ? "package" : "exec:cmd package"; 756 | 757 | std::vector multi_package_cmd_args = {install_cmd, "install-create", 758 | "--multi-package"}; 759 | 760 | multi_package_cmd_args.reserve(first_package + 4); 761 | for (int i = 1; i < first_package; i++) { 762 | if (use_abb_exec) { 763 | multi_package_cmd_args.push_back(argv[i]); 764 | } else { 765 | multi_package_cmd_args.push_back(escape_arg(argv[i])); 766 | } 767 | } 768 | 769 | if (apex_found) { 770 | multi_package_cmd_args.emplace_back("--staged"); 771 | } 772 | 773 | // Create multi-package install session 774 | std::string error; 775 | char buf[BUFSIZ]; 776 | { 777 | unique_fd fd = send_command(multi_package_cmd_args, &error); 778 | if (fd < 0) { 779 | fprintf(stderr, "adb: connect error for create multi-package: %s\n", error.c_str()); 780 | return EXIT_FAILURE; 781 | } 782 | read_status_line(fd.get(), buf, sizeof(buf)); 783 | } 784 | 785 | int parent_session_id = -1; 786 | if (!strncmp("Success", buf, 7)) { 787 | char* start = strrchr(buf, '['); 788 | char* end = strrchr(buf, ']'); 789 | if (start && end) { 790 | *end = '\0'; 791 | parent_session_id = strtol(start + 1, nullptr, 10); 792 | } 793 | } 794 | if (parent_session_id < 0) { 795 | fprintf(stderr, "adb: failed to create multi-package session\n"); 796 | fputs(buf, stderr); 797 | return EXIT_FAILURE; 798 | } 799 | const auto parent_session_id_str = std::to_string(parent_session_id); 800 | 801 | fprintf(stdout, "Created parent session ID %d.\n", parent_session_id); 802 | 803 | std::vector session_ids; 804 | 805 | // Valid session, now create the individual sessions and stream the APKs 806 | int success = EXIT_FAILURE; 807 | std::vector individual_cmd_args = {install_cmd, "install-create"}; 808 | for (int i = 1; i < first_package; i++) { 809 | if (use_abb_exec) { 810 | individual_cmd_args.push_back(argv[i]); 811 | } else { 812 | individual_cmd_args.push_back(escape_arg(argv[i])); 813 | } 814 | } 815 | if (apex_found) { 816 | individual_cmd_args.emplace_back("--staged"); 817 | } 818 | 819 | std::vector individual_apex_cmd_args; 820 | if (apex_found) { 821 | individual_apex_cmd_args = individual_cmd_args; 822 | individual_apex_cmd_args.emplace_back("--apex"); 823 | } 824 | 825 | std::vector add_session_cmd_args = { 826 | install_cmd, 827 | "install-add-session", 828 | parent_session_id_str, 829 | }; 830 | 831 | for (int i = first_package; i < argc; i++) { 832 | const char* file = argv[i]; 833 | char buf[BUFSIZ]; 834 | { 835 | unique_fd fd; 836 | // Create individual install session 837 | if (android::base::EndsWithIgnoreCase(file, ".apex")) { 838 | fd = send_command(individual_apex_cmd_args, &error); 839 | } else { 840 | fd = send_command(individual_cmd_args, &error); 841 | } 842 | if (fd < 0) { 843 | fprintf(stderr, "adb: connect error for create: %s\n", error.c_str()); 844 | goto finalize_multi_package_session; 845 | } 846 | read_status_line(fd.get(), buf, sizeof(buf)); 847 | } 848 | 849 | int session_id = -1; 850 | if (!strncmp("Success", buf, 7)) { 851 | char* start = strrchr(buf, '['); 852 | char* end = strrchr(buf, ']'); 853 | if (start && end) { 854 | *end = '\0'; 855 | session_id = strtol(start + 1, nullptr, 10); 856 | } 857 | } 858 | if (session_id < 0) { 859 | fprintf(stderr, "adb: failed to create multi-package session\n"); 860 | fputs(buf, stderr); 861 | goto finalize_multi_package_session; 862 | } 863 | const auto session_id_str = std::to_string(session_id); 864 | 865 | fprintf(stdout, "Created child session ID %d.\n", session_id); 866 | session_ids.push_back(session_id); 867 | 868 | // Support splitAPKs by allowing the notation split1.apk:split2.apk:split3.apk as argument. 869 | // The character used as separator is OS-dependent, see ENV_PATH_SEPARATOR_STR. 870 | std::vector splits = android::base::Split(file, ENV_PATH_SEPARATOR_STR); 871 | 872 | for (const std::string& split : splits) { 873 | struct stat sb; 874 | if (stat(split.c_str(), &sb) == -1) { 875 | fprintf(stderr, "adb: failed to stat %s: %s\n", split.c_str(), strerror(errno)); 876 | goto finalize_multi_package_session; 877 | } 878 | 879 | std::vector cmd_args = { 880 | install_cmd, 881 | "install-write", 882 | "-S", 883 | std::to_string(sb.st_size), 884 | session_id_str, 885 | android::base::StringPrintf("%d_%s", i, android::base::Basename(split).c_str()), 886 | "-", 887 | }; 888 | 889 | unique_fd local_fd(adb_open(split.c_str(), O_RDONLY | O_CLOEXEC)); 890 | if (local_fd < 0) { 891 | fprintf(stderr, "adb: failed to open %s: %s\n", split.c_str(), strerror(errno)); 892 | goto finalize_multi_package_session; 893 | } 894 | 895 | std::string error; 896 | unique_fd remote_fd = send_command(cmd_args, &error); 897 | if (remote_fd < 0) { 898 | fprintf(stderr, "adb: connect error for write: %s\n", error.c_str()); 899 | goto finalize_multi_package_session; 900 | } 901 | 902 | if (!copy_to_file(local_fd.get(), remote_fd.get())) { 903 | fprintf(stderr, "adb: failed to write %s: %s\n", split.c_str(), strerror(errno)); 904 | goto finalize_multi_package_session; 905 | } 906 | 907 | read_status_line(remote_fd.get(), buf, sizeof(buf)); 908 | 909 | if (strncmp("Success", buf, 7)) { 910 | fprintf(stderr, "adb: failed to write %s\n", split.c_str()); 911 | fputs(buf, stderr); 912 | goto finalize_multi_package_session; 913 | } 914 | } 915 | add_session_cmd_args.push_back(std::to_string(session_id)); 916 | } 917 | 918 | { 919 | unique_fd fd = send_command(add_session_cmd_args, &error); 920 | if (fd < 0) { 921 | fprintf(stderr, "adb: connect error for install-add-session: %s\n", error.c_str()); 922 | goto finalize_multi_package_session; 923 | } 924 | read_status_line(fd.get(), buf, sizeof(buf)); 925 | } 926 | 927 | if (strncmp("Success", buf, 7)) { 928 | fprintf(stderr, "adb: failed to link sessions (%s)\n", 929 | android::base::Join(add_session_cmd_args, " ").c_str()); 930 | fputs(buf, stderr); 931 | goto finalize_multi_package_session; 932 | } 933 | 934 | // no failures means we can proceed with the assumption of success 935 | success = 0; 936 | 937 | finalize_multi_package_session: 938 | // Commit session if we streamed everything okay; otherwise abandon 939 | std::vector service_args; 940 | if (success == 0) { 941 | service_args.push_back(install_cmd); 942 | service_args.push_back("install-commit"); 943 | // If successful, we need to forward args to install-commit 944 | for (int i = 1; i < first_package - 1; i++) { 945 | if (strcmp(argv[i], "--staged-ready-timeout") == 0) { 946 | service_args.push_back(argv[i]); 947 | service_args.push_back(argv[i + 1]); 948 | i++; 949 | } 950 | } 951 | service_args.push_back(parent_session_id_str); 952 | } else { 953 | service_args = {install_cmd, "install-abandon", parent_session_id_str}; 954 | } 955 | 956 | { 957 | unique_fd fd = send_command(service_args, &error); 958 | if (fd < 0) { 959 | fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str()); 960 | return EXIT_FAILURE; 961 | } 962 | read_status_line(fd.get(), buf, sizeof(buf)); 963 | } 964 | 965 | if (!strncmp("Success", buf, 7)) { 966 | fputs(buf, stdout); 967 | if (success == 0) { 968 | return 0; 969 | } 970 | } else { 971 | fprintf(stderr, "adb: failed to finalize session\n"); 972 | fputs(buf, stderr); 973 | } 974 | 975 | session_ids.push_back(parent_session_id); 976 | // try to abandon all remaining sessions 977 | for (std::size_t i = 0; i < session_ids.size(); i++) { 978 | std::vector service_args = { 979 | install_cmd, 980 | "install-abandon", 981 | std::to_string(session_ids[i]), 982 | }; 983 | fprintf(stderr, "Attempting to abandon session ID %d\n", session_ids[i]); 984 | unique_fd fd = send_command(service_args, &error); 985 | if (fd < 0) { 986 | fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str()); 987 | continue; 988 | } 989 | read_status_line(fd.get(), buf, sizeof(buf)); 990 | } 991 | return EXIT_FAILURE; 992 | } 993 | 994 | int delete_device_file(const std::string& filename) { 995 | // http://b/17339227 "Sideloading a Readonly File Results in a Prompt to 996 | // Delete" caused us to add `-f` here, to avoid the equivalent of the `-i` 997 | // prompt that you get from BSD rm (used in Android 5) if you have a 998 | // non-writable file and stdin is a tty (which is true for old versions of 999 | // adbd). 1000 | // 1001 | // Unfortunately, `rm -f` requires Android 4.3, so that workaround broke 1002 | // earlier Android releases. This was reported as http://b/37704384 "adb 1003 | // install -r passes invalid argument to rm on Android 4.1" and 1004 | // http://b/37035817 "ADB Fails: rm failed for -f, No such file or 1005 | // directory". 1006 | // 1007 | // Testing on a variety of devices and emulators shows that redirecting 1008 | // stdin is sufficient to avoid the pseudo-`-i`, and works on toolbox, 1009 | // BSD, and toybox versions of rm. 1010 | return send_shell_command("rm " + escape_arg(filename) + "