@ -4,6 +4,7 @@
# include <inttypes.h>
# include <stdint.h>
# include <SDL2/SDL_assert.h>
# include <SDL2/SDL_timer.h>
# include "config.h"
# include "log.h"
@ -37,17 +38,45 @@ static SDL_bool remove_server(const char *serial) {
return process_check_success ( process , " adb shell rm " ) ;
}
static SDL_bool enable_tunnel ( const char * serial , Uint16 local_port ) {
static SDL_bool enable_tunnel _reverse ( const char * serial , Uint16 local_port ) {
process_t process = adb_reverse ( serial , SOCKET_NAME , local_port ) ;
return process_check_success ( process , " adb reverse " ) ;
}
static SDL_bool disable_tunnel ( const char * serial ) {
static SDL_bool disable_tunnel _reverse ( const char * serial ) {
process_t process = adb_reverse_remove ( serial , SOCKET_NAME ) ;
return process_check_success ( process , " adb reverse --remove " ) ;
}
static process_t execute_server ( const char * serial , Uint16 max_size , Uint32 bit_rate ) {
static SDL_bool enable_tunnel_forward ( const char * serial , Uint16 local_port ) {
process_t process = adb_forward ( serial , local_port , SOCKET_NAME ) ;
return process_check_success ( process , " adb forward " ) ;
}
static SDL_bool disable_tunnel_forward ( const char * serial , Uint16 local_port ) {
process_t process = adb_forward_remove ( serial , local_port ) ;
return process_check_success ( process , " adb forward --remove " ) ;
}
static SDL_bool enable_tunnel ( struct server * server ) {
if ( enable_tunnel_reverse ( server - > serial , server - > local_port ) ) {
return SDL_TRUE ;
}
LOGW ( " 'adb reverse' failed, fallback to 'adb forward' " ) ;
server - > tunnel_forward = SDL_TRUE ;
return enable_tunnel_forward ( server - > serial , server - > local_port ) ;
}
static SDL_bool disable_tunnel ( struct server * server ) {
if ( server - > tunnel_forward ) {
return disable_tunnel_forward ( server - > serial , server - > local_port ) ;
}
return disable_tunnel_reverse ( server - > serial ) ;
}
static process_t execute_server ( const char * serial ,
Uint16 max_size , Uint32 bit_rate , SDL_bool tunnel_forward ) {
char max_size_string [ 6 ] ;
char bit_rate_string [ 11 ] ;
sprintf ( max_size_string , " % " PRIu16 , max_size ) ;
@ -60,15 +89,48 @@ static process_t execute_server(const char *serial, Uint16 max_size, Uint32 bit_
" com.genymobile.scrcpy.Server " ,
max_size_string ,
bit_rate_string ,
tunnel_forward ? " true " : " false " ,
} ;
return adb_execute ( serial , cmd , sizeof ( cmd ) / sizeof ( cmd [ 0 ] ) ) ;
}
static socket_t listen_on_port ( Uint16 port ) {
# define IPV4_LOCALHOST 0x7F000001
static socket_t listen_on_port ( Uint16 port ) {
return net_listen ( IPV4_LOCALHOST , port , 1 ) ;
}
static socket_t connect_and_read_byte ( Uint16 port ) {
socket_t socket = net_connect ( IPV4_LOCALHOST , port ) ;
if ( socket = = INVALID_SOCKET ) {
return INVALID_SOCKET ;
}
char byte ;
// the connection may succeed even if the server behind the "adb tunnel"
// is not listening, so read one byte to detect a working connection
if ( net_recv_all ( socket , & byte , 1 ) ! = 1 ) {
// the server is not listening yet behind the adb tunnel
return INVALID_SOCKET ;
}
return socket ;
}
static socket_t connect_to_server ( Uint16 port , Uint32 attempts , Uint32 delay ) {
do {
LOGD ( " Remaining connection attempts: %d " , ( int ) attempts ) ;
socket_t socket = connect_and_read_byte ( port ) ;
if ( socket ! = INVALID_SOCKET ) {
// it worked!
return socket ;
}
if ( attempts ) {
SDL_Delay ( delay ) ;
}
} while ( - - attempts > 0 ) ;
return INVALID_SOCKET ;
}
static void close_socket ( socket_t * socket ) {
SDL_assert ( * socket ! = INVALID_SOCKET ) ;
net_shutdown ( * socket , SHUT_RDWR ) ;
@ -85,6 +147,8 @@ void server_init(struct server *server) {
SDL_bool server_start ( struct server * server , const char * serial , Uint16 local_port ,
Uint16 max_size , Uint32 bit_rate ) {
server - > local_port = local_port ;
if ( serial ) {
server - > serial = SDL_strdup ( serial ) ;
}
@ -95,52 +159,67 @@ SDL_bool server_start(struct server *server, const char *serial, Uint16 local_po
server - > server_copied_to_device = SDL_TRUE ;
if ( ! enable_tunnel ( ser ial, local_port ) ) {
if ( ! enable_tunnel ( ser ver ) ) {
return SDL_FALSE ;
}
// At the application level, the device part is "the server" because it
// serves video stream and control. However, at the network level, the
// client listens and the server connects to the client. That way, the
// client can listen before starting the server app, so there is no need to
// try to connect until the server socket is listening on the device.
server - > server_socket = listen_on_port ( local_port ) ;
if ( server - > server_socket = = INVALID_SOCKET ) {
LOGE ( " Could not listen on port % " PRIu16 , local_port ) ;
disable_tunnel ( serial ) ;
return SDL_FALSE ;
// if "adb reverse" does not work (e.g. over "adb connect"), it fallbacks to
// "adb forward", so the app socket is the client
if ( ! server - > tunnel_forward ) {
// At the application level, the device part is "the server" because it
// serves video stream and control. However, at the network level, the
// client listens and the server connects to the client. That way, the
// client can listen before starting the server app, so there is no need to
// try to connect until the server socket is listening on the device.
server - > server_socket = listen_on_port ( local_port ) ;
if ( server - > server_socket = = INVALID_SOCKET ) {
LOGE ( " Could not listen on port % " PRIu16 , local_port ) ;
disable_tunnel ( server ) ;
return SDL_FALSE ;
}
}
// server will connect to our server socket
server - > process = execute_server ( serial , max_size , bit_rate ) ;
server - > process = execute_server ( serial , max_size , bit_rate , server - > tunnel_forward );
if ( server - > process = = PROCESS_NONE ) {
close_socket ( & server - > server_socket ) ;
disable_tunnel ( serial ) ;
if ( ! server - > tunnel_forward ) {
close_socket ( & server - > server_socket ) ;
}
disable_tunnel ( server ) ;
return SDL_FALSE ;
}
server - > adb_reverse _enabled = SDL_TRUE ;
server - > tunnel _enabled = SDL_TRUE ;
return SDL_TRUE ;
}
socket_t server_connect_to ( struct server * server , Uint32 timeout_ms ) {
server - > device_socket = net_accept ( server - > server_socket ) ;
if ( ! server - > tunnel_forward ) {
server - > device_socket = net_accept ( server - > server_socket ) ;
} else {
Uint32 attempts = 10 ;
Uint32 delay = 100 ; // ms
server - > device_socket = connect_to_server ( server - > local_port , attempts , delay ) ;
}
if ( server - > device_socket = = INVALID_SOCKET ) {
return INVALID_SOCKET ;
}
// we don't need the server socket anymore
close_socket ( & server - > server_socket ) ;
if ( ! server - > tunnel_forward ) {
// we don't need the server socket anymore
close_socket ( & server - > server_socket ) ;
}
// the server is started, we can clean up the jar from the temporary folder
remove_server ( server - > serial ) ; // ignore failure
server - > server_copied_to_device = SDL_FALSE ;
// we don't need the adb tunnel anymore
disable_tunnel ( server - > serial ) ; // ignore failure
server - > adb_reverse _enabled = SDL_FALSE ;
disable_tunnel ( server ) ; // ignore failure
server - > tunnel _enabled = SDL_FALSE ;
return server - > device_socket ;
}
@ -155,9 +234,9 @@ void server_stop(struct server *server) {
cmd_simple_wait ( server - > process , NULL ) ; // ignore exit code
LOGD ( " Server terminated " ) ;
if ( server - > adb_reverse _enabled) {
if ( server - > tunnel _enabled) {
// ignore failure
disable_tunnel ( server - > serial ) ;
disable_tunnel ( server ) ;
}
if ( server - > server_copied_to_device ) {