2018-02-08 22:16:27 +08:00
|
|
|
#include "server.h"
|
2018-01-22 18:22:31 +08:00
|
|
|
|
2019-11-28 04:11:40 +08:00
|
|
|
#include <assert.h>
|
2018-01-22 18:22:31 +08:00
|
|
|
#include <errno.h>
|
Replace SDL_net by custom implementation
SDL_net is not very suitable for scrcpy.
For example, SDLNet_TCP_Accept() is non-blocking, so we have to wrap it
by calling many SDL_Net-specific functions to make it blocking.
But above all, SDLNet_TCP_Open() is a server socket only when no IP is
provided; otherwise, it's a client socket. Therefore, it is not possible
to create a server socket bound to localhost, so it accepts connections
from anywhere.
This is a problem for scrcpy, because on start, the application listens
for nearly 1 second until it accepts the first connection, supposedly
from the device. If someone on the local network manages to connect to
the server socket first, then they can stream arbitrary H.264 video.
This may be troublesome, for example during a public presentation ;-)
Provide our own simplified API (net.h) instead, implemented for the
different platforms.
2018-02-16 05:59:21 +08:00
|
|
|
#include <inttypes.h>
|
2018-05-13 21:33:13 +08:00
|
|
|
#include <stdio.h>
|
2018-03-12 15:35:51 +08:00
|
|
|
#include <SDL2/SDL_timer.h>
|
2019-12-14 14:34:49 +08:00
|
|
|
#include <SDL2/SDL_platform.h>
|
2018-02-13 17:10:18 +08:00
|
|
|
|
2021-01-03 21:55:15 +08:00
|
|
|
#include "adb.h"
|
2021-11-11 23:12:17 +08:00
|
|
|
#include "util/file.h"
|
2019-11-24 18:53:00 +08:00
|
|
|
#include "util/log.h"
|
2021-11-13 01:50:50 +08:00
|
|
|
#include "util/net_intr.h"
|
|
|
|
#include "util/process_intr.h"
|
2021-11-13 06:12:51 +08:00
|
|
|
#include "util/str.h"
|
2018-01-22 18:22:31 +08:00
|
|
|
|
2021-11-13 06:24:12 +08:00
|
|
|
#define SC_SERVER_FILENAME "scrcpy-server"
|
2018-01-23 22:46:34 +08:00
|
|
|
|
2021-11-13 06:24:12 +08:00
|
|
|
#define SC_SERVER_PATH_DEFAULT PREFIX "/share/scrcpy/" SC_SERVER_FILENAME
|
|
|
|
#define SC_DEVICE_SERVER_PATH "/data/local/tmp/scrcpy-server.jar"
|
2018-02-28 22:13:56 +08:00
|
|
|
|
2019-12-15 01:13:56 +08:00
|
|
|
static char *
|
2019-03-03 03:09:56 +08:00
|
|
|
get_server_path(void) {
|
2019-12-14 14:34:49 +08:00
|
|
|
#ifdef __WINDOWS__
|
|
|
|
const wchar_t *server_path_env = _wgetenv(L"SCRCPY_SERVER_PATH");
|
|
|
|
#else
|
2019-06-10 21:14:10 +08:00
|
|
|
const char *server_path_env = getenv("SCRCPY_SERVER_PATH");
|
2019-12-14 14:34:49 +08:00
|
|
|
#endif
|
2019-06-10 21:14:10 +08:00
|
|
|
if (server_path_env) {
|
|
|
|
// if the envvar is set, use it
|
2019-12-14 14:34:49 +08:00
|
|
|
#ifdef __WINDOWS__
|
2021-11-13 06:08:19 +08:00
|
|
|
char *server_path = sc_str_from_wchars(server_path_env);
|
2019-12-14 14:34:49 +08:00
|
|
|
#else
|
2021-01-24 22:14:53 +08:00
|
|
|
char *server_path = strdup(server_path_env);
|
2019-12-14 14:34:49 +08:00
|
|
|
#endif
|
2019-12-15 01:13:56 +08:00
|
|
|
if (!server_path) {
|
2021-11-25 05:06:11 +08:00
|
|
|
LOG_OOM();
|
2019-12-15 01:13:56 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
LOGD("Using SCRCPY_SERVER_PATH: %s", server_path);
|
|
|
|
return server_path;
|
2019-06-10 21:14:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef PORTABLE
|
2021-11-13 06:24:12 +08:00
|
|
|
LOGD("Using server: " SC_SERVER_PATH_DEFAULT);
|
|
|
|
char *server_path = strdup(SC_SERVER_PATH_DEFAULT);
|
2019-12-15 01:13:56 +08:00
|
|
|
if (!server_path) {
|
2021-11-25 05:06:11 +08:00
|
|
|
LOG_OOM();
|
2019-12-15 01:13:56 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2019-06-10 21:14:10 +08:00
|
|
|
#else
|
2021-11-13 06:24:12 +08:00
|
|
|
char *server_path = sc_file_get_local_path(SC_SERVER_FILENAME);
|
2018-02-02 16:31:44 +08:00
|
|
|
if (!server_path) {
|
2021-10-23 00:51:20 +08:00
|
|
|
LOGE("Could not get local file path, "
|
2021-11-13 06:24:12 +08:00
|
|
|
"using " SC_SERVER_FILENAME " from current directory");
|
|
|
|
return strdup(SC_SERVER_FILENAME);
|
2018-01-23 22:46:34 +08:00
|
|
|
}
|
2019-06-10 21:14:10 +08:00
|
|
|
|
|
|
|
LOGD("Using server (portable): %s", server_path);
|
|
|
|
#endif
|
2021-10-26 00:08:31 +08:00
|
|
|
|
|
|
|
return server_path;
|
2018-02-13 18:55:12 +08:00
|
|
|
}
|
|
|
|
|
2021-10-28 05:40:52 +08:00
|
|
|
static void
|
2021-11-13 06:24:12 +08:00
|
|
|
sc_server_params_destroy(struct sc_server_params *params) {
|
2021-10-28 05:40:52 +08:00
|
|
|
// The server stores a copy of the params provided by the user
|
|
|
|
free((char *) params->serial);
|
|
|
|
free((char *) params->crop);
|
|
|
|
free((char *) params->codec_options);
|
|
|
|
free((char *) params->encoder_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2021-11-13 06:24:12 +08:00
|
|
|
sc_server_params_copy(struct sc_server_params *dst,
|
|
|
|
const struct sc_server_params *src) {
|
2021-10-28 05:40:52 +08:00
|
|
|
*dst = *src;
|
|
|
|
|
|
|
|
// The params reference user-allocated memory, so we must copy them to
|
|
|
|
// handle them from another thread
|
|
|
|
|
|
|
|
#define COPY(FIELD) \
|
|
|
|
dst->FIELD = NULL; \
|
|
|
|
if (src->FIELD) { \
|
|
|
|
dst->FIELD = strdup(src->FIELD); \
|
|
|
|
if (!dst->FIELD) { \
|
|
|
|
goto error; \
|
|
|
|
} \
|
|
|
|
}
|
|
|
|
|
|
|
|
COPY(serial);
|
|
|
|
COPY(crop);
|
|
|
|
COPY(codec_options);
|
|
|
|
COPY(encoder_name);
|
|
|
|
#undef COPY
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
error:
|
2021-11-13 06:24:12 +08:00
|
|
|
sc_server_params_destroy(dst);
|
2021-10-28 05:40:52 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-03-03 06:52:22 +08:00
|
|
|
static bool
|
2021-11-13 01:50:50 +08:00
|
|
|
push_server(struct sc_intr *intr, const char *serial) {
|
2019-12-15 01:13:56 +08:00
|
|
|
char *server_path = get_server_path();
|
|
|
|
if (!server_path) {
|
|
|
|
return false;
|
|
|
|
}
|
2021-11-11 23:21:07 +08:00
|
|
|
if (!sc_file_is_regular(server_path)) {
|
2019-12-06 04:07:11 +08:00
|
|
|
LOGE("'%s' does not exist or is not a regular file\n", server_path);
|
2021-01-24 22:14:53 +08:00
|
|
|
free(server_path);
|
2019-12-06 04:07:11 +08:00
|
|
|
return false;
|
|
|
|
}
|
2021-11-19 05:11:19 +08:00
|
|
|
bool ok = adb_push(intr, serial, server_path, SC_DEVICE_SERVER_PATH);
|
2021-01-24 22:14:53 +08:00
|
|
|
free(server_path);
|
2021-11-19 05:11:19 +08:00
|
|
|
return ok;
|
2018-01-23 22:46:34 +08:00
|
|
|
}
|
|
|
|
|
2020-05-25 03:51:40 +08:00
|
|
|
static const char *
|
|
|
|
log_level_to_server_string(enum sc_log_level level) {
|
|
|
|
switch (level) {
|
2021-06-18 03:40:30 +08:00
|
|
|
case SC_LOG_LEVEL_VERBOSE:
|
|
|
|
return "verbose";
|
2020-05-25 03:51:40 +08:00
|
|
|
case SC_LOG_LEVEL_DEBUG:
|
|
|
|
return "debug";
|
|
|
|
case SC_LOG_LEVEL_INFO:
|
|
|
|
return "info";
|
|
|
|
case SC_LOG_LEVEL_WARN:
|
|
|
|
return "warn";
|
|
|
|
case SC_LOG_LEVEL_ERROR:
|
|
|
|
return "error";
|
|
|
|
default:
|
|
|
|
assert(!"unexpected log level");
|
|
|
|
return "(unknown)";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-12 00:48:41 +08:00
|
|
|
static sc_pid
|
2021-11-13 06:24:12 +08:00
|
|
|
execute_server(struct sc_server *server,
|
|
|
|
const struct sc_server_params *params) {
|
2021-11-25 04:10:18 +08:00
|
|
|
sc_pid pid = SC_PROCESS_NONE;
|
|
|
|
|
|
|
|
const char *cmd[128];
|
|
|
|
unsigned count = 0;
|
|
|
|
cmd[count++] = "shell";
|
|
|
|
cmd[count++] = "CLASSPATH=" SC_DEVICE_SERVER_PATH;
|
|
|
|
cmd[count++] = "app_process";
|
|
|
|
|
2019-11-04 02:31:56 +08:00
|
|
|
#ifdef SERVER_DEBUGGER
|
|
|
|
# define SERVER_DEBUGGER_PORT "5005"
|
2021-11-25 04:10:18 +08:00
|
|
|
cmd[count++] =
|
2020-03-20 02:15:43 +08:00
|
|
|
# ifdef SERVER_DEBUGGER_METHOD_NEW
|
|
|
|
/* Android 9 and above */
|
2021-07-16 00:07:39 +08:00
|
|
|
"-XjdwpProvider:internal -XjdwpOptions:transport=dt_socket,suspend=y,"
|
|
|
|
"server=y,address="
|
2020-03-20 02:15:43 +08:00
|
|
|
# else
|
|
|
|
/* Android 8 and below */
|
2019-11-04 02:31:56 +08:00
|
|
|
"-agentlib:jdwp=transport=dt_socket,suspend=y,server=y,address="
|
2020-03-20 02:15:43 +08:00
|
|
|
# endif
|
2021-11-25 04:10:18 +08:00
|
|
|
SERVER_DEBUGGER_PORT;
|
2019-11-04 02:31:56 +08:00
|
|
|
#endif
|
2021-11-25 04:10:18 +08:00
|
|
|
cmd[count++] = "/"; // unused
|
|
|
|
cmd[count++] = "com.genymobile.scrcpy.Server";
|
|
|
|
cmd[count++] = SCRCPY_VERSION;
|
|
|
|
|
|
|
|
unsigned dyn_idx = count; // from there, the strings are allocated
|
|
|
|
#define ADD_PARAM(fmt, ...) { \
|
|
|
|
char *p = (char *) &cmd[count]; \
|
|
|
|
if (asprintf(&p, fmt, ## __VA_ARGS__) == -1) { \
|
|
|
|
goto end; \
|
|
|
|
} \
|
|
|
|
cmd[count++] = p; \
|
|
|
|
}
|
|
|
|
#define STRBOOL(v) (v ? "true" : "false")
|
|
|
|
|
2021-11-25 04:23:20 +08:00
|
|
|
ADD_PARAM("log_level=%s", log_level_to_server_string(params->log_level));
|
|
|
|
ADD_PARAM("bit_rate=%" PRIu32, params->bit_rate);
|
2021-11-25 04:31:12 +08:00
|
|
|
|
|
|
|
if (params->max_size) {
|
|
|
|
ADD_PARAM("max_size=%" PRIu16, params->max_size);
|
|
|
|
}
|
|
|
|
if (params->max_fps) {
|
|
|
|
ADD_PARAM("max_fps=%" PRIu16, params->max_fps);
|
|
|
|
}
|
|
|
|
if (params->lock_video_orientation != SC_LOCK_VIDEO_ORIENTATION_UNLOCKED) {
|
|
|
|
ADD_PARAM("lock_video_orientation=%" PRIi8,
|
|
|
|
params->lock_video_orientation);
|
|
|
|
}
|
|
|
|
if (server->tunnel.forward) {
|
|
|
|
ADD_PARAM("tunnel_forward=%s", STRBOOL(server->tunnel.forward));
|
|
|
|
}
|
|
|
|
if (params->crop) {
|
|
|
|
ADD_PARAM("crop=%s", params->crop);
|
|
|
|
}
|
|
|
|
if (!params->control) {
|
|
|
|
// By default, control is true
|
|
|
|
ADD_PARAM("control=%s", STRBOOL(params->control));
|
|
|
|
}
|
|
|
|
if (params->display_id) {
|
|
|
|
ADD_PARAM("display_id=%" PRIu32, params->display_id);
|
|
|
|
}
|
|
|
|
if (params->show_touches) {
|
|
|
|
ADD_PARAM("show_touches=%s", STRBOOL(params->show_touches));
|
|
|
|
}
|
|
|
|
if (params->stay_awake) {
|
|
|
|
ADD_PARAM("stay_awake=%s", STRBOOL(params->stay_awake));
|
|
|
|
}
|
|
|
|
if (params->codec_options) {
|
|
|
|
ADD_PARAM("codec_options=%s", params->codec_options);
|
|
|
|
}
|
|
|
|
if (params->encoder_name) {
|
|
|
|
ADD_PARAM("encoder_name=%s", params->encoder_name);
|
|
|
|
}
|
|
|
|
if (params->power_off_on_close) {
|
|
|
|
ADD_PARAM("power_off_on_close=%s", STRBOOL(params->power_off_on_close));
|
|
|
|
}
|
|
|
|
if (!params->clipboard_autosync) {
|
|
|
|
// By defaut, clipboard_autosync is true
|
|
|
|
ADD_PARAM("clipboard_autosync=%s", STRBOOL(params->clipboard_autosync));
|
|
|
|
}
|
2021-11-25 04:10:18 +08:00
|
|
|
|
|
|
|
#undef ADD_PARAM
|
|
|
|
#undef STRBOOL
|
|
|
|
|
2019-11-04 02:31:56 +08:00
|
|
|
#ifdef SERVER_DEBUGGER
|
|
|
|
LOGI("Server debugger waiting for a client on device port "
|
|
|
|
SERVER_DEBUGGER_PORT "...");
|
|
|
|
// From the computer, run
|
|
|
|
// adb forward tcp:5005 tcp:5005
|
|
|
|
// Then, from Android Studio: Run > Debug > Edit configurations...
|
|
|
|
// On the left, click on '+', "Remote", with:
|
|
|
|
// Host: localhost
|
|
|
|
// Port: 5005
|
|
|
|
// Then click on "Debug"
|
|
|
|
#endif
|
2021-11-25 04:10:18 +08:00
|
|
|
pid = adb_execute(params->serial, cmd, count);
|
|
|
|
|
|
|
|
end:
|
|
|
|
for (unsigned i = dyn_idx; i < count; ++i) {
|
|
|
|
free((char *) cmd[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return pid;
|
2018-01-22 18:22:31 +08:00
|
|
|
}
|
|
|
|
|
2021-11-01 05:01:57 +08:00
|
|
|
static bool
|
2021-11-18 08:02:53 +08:00
|
|
|
connect_and_read_byte(struct sc_intr *intr, sc_socket socket,
|
|
|
|
uint32_t tunnel_host, uint16_t tunnel_port) {
|
|
|
|
bool ok = net_connect_intr(intr, socket, tunnel_host, tunnel_port);
|
2021-11-01 05:01:57 +08:00
|
|
|
if (!ok) {
|
|
|
|
return false;
|
2018-03-12 15:35:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2021-11-13 01:50:50 +08:00
|
|
|
if (net_recv_intr(intr, socket, &byte, 1) != 1) {
|
2018-03-12 15:35:51 +08:00
|
|
|
// the server is not listening yet behind the adb tunnel
|
2021-11-01 05:01:57 +08:00
|
|
|
return false;
|
2018-03-12 15:35:51 +08:00
|
|
|
}
|
2021-11-01 05:01:57 +08:00
|
|
|
|
|
|
|
return true;
|
2018-03-12 15:35:51 +08:00
|
|
|
}
|
|
|
|
|
2021-10-27 04:49:45 +08:00
|
|
|
static sc_socket
|
2021-11-26 04:54:08 +08:00
|
|
|
connect_to_server(struct sc_server *server, unsigned attempts, sc_tick delay,
|
2021-11-18 08:02:53 +08:00
|
|
|
uint32_t host, uint16_t port) {
|
2018-03-12 15:35:51 +08:00
|
|
|
do {
|
2021-11-26 04:54:08 +08:00
|
|
|
LOGD("Remaining connection attempts: %u", attempts);
|
2021-11-01 05:01:57 +08:00
|
|
|
sc_socket socket = net_socket();
|
2021-11-13 16:58:52 +08:00
|
|
|
if (socket != SC_SOCKET_NONE) {
|
2021-11-18 08:02:53 +08:00
|
|
|
bool ok = connect_and_read_byte(&server->intr, socket, host, port);
|
2021-11-01 05:01:57 +08:00
|
|
|
if (ok) {
|
|
|
|
// it worked!
|
|
|
|
return socket;
|
|
|
|
}
|
|
|
|
|
|
|
|
net_close(socket);
|
2018-03-12 15:35:51 +08:00
|
|
|
}
|
2021-11-14 22:39:20 +08:00
|
|
|
|
|
|
|
if (sc_intr_is_interrupted(&server->intr)) {
|
|
|
|
// Stop immediately
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-03-12 15:35:51 +08:00
|
|
|
if (attempts) {
|
2021-10-31 21:56:37 +08:00
|
|
|
sc_mutex_lock(&server->mutex);
|
|
|
|
sc_tick deadline = sc_tick_now() + delay;
|
|
|
|
bool timed_out = false;
|
|
|
|
while (!server->stopped && !timed_out) {
|
|
|
|
timed_out = !sc_cond_timedwait(&server->cond_stopped,
|
|
|
|
&server->mutex, deadline);
|
|
|
|
}
|
|
|
|
bool stopped = server->stopped;
|
|
|
|
sc_mutex_unlock(&server->mutex);
|
|
|
|
|
|
|
|
if (stopped) {
|
|
|
|
LOGI("Connection attempt stopped");
|
|
|
|
break;
|
|
|
|
}
|
2018-03-12 15:35:51 +08:00
|
|
|
}
|
2021-11-26 04:54:08 +08:00
|
|
|
} while (--attempts);
|
2021-11-13 16:58:52 +08:00
|
|
|
return SC_SOCKET_NONE;
|
2018-03-12 15:35:51 +08:00
|
|
|
}
|
|
|
|
|
2021-01-01 23:34:47 +08:00
|
|
|
bool
|
2021-11-13 06:24:12 +08:00
|
|
|
sc_server_init(struct sc_server *server, const struct sc_server_params *params,
|
|
|
|
const struct sc_server_callbacks *cbs, void *cbs_userdata) {
|
|
|
|
bool ok = sc_server_params_copy(&server->params, params);
|
2021-10-28 05:40:52 +08:00
|
|
|
if (!ok) {
|
2021-11-25 05:06:11 +08:00
|
|
|
LOG_OOM();
|
2021-10-28 05:40:52 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-10-31 21:56:37 +08:00
|
|
|
ok = sc_mutex_init(&server->mutex);
|
|
|
|
if (!ok) {
|
2021-11-13 06:24:12 +08:00
|
|
|
sc_server_params_destroy(&server->params);
|
2021-10-31 21:56:37 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
ok = sc_cond_init(&server->cond_stopped);
|
|
|
|
if (!ok) {
|
|
|
|
sc_mutex_destroy(&server->mutex);
|
2021-11-13 06:24:12 +08:00
|
|
|
sc_server_params_destroy(&server->params);
|
2021-10-31 21:56:37 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-11-13 01:50:50 +08:00
|
|
|
ok = sc_intr_init(&server->intr);
|
|
|
|
if (!ok) {
|
|
|
|
sc_cond_destroy(&server->cond_stopped);
|
|
|
|
sc_mutex_destroy(&server->mutex);
|
2021-11-13 06:24:12 +08:00
|
|
|
sc_server_params_destroy(&server->params);
|
2021-11-13 01:50:50 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-10-31 21:56:37 +08:00
|
|
|
server->stopped = false;
|
2021-01-01 23:34:47 +08:00
|
|
|
|
2021-11-13 16:58:52 +08:00
|
|
|
server->video_socket = SC_SOCKET_NONE;
|
|
|
|
server->control_socket = SC_SOCKET_NONE;
|
2021-01-01 23:34:47 +08:00
|
|
|
|
2021-11-13 05:32:29 +08:00
|
|
|
sc_adb_tunnel_init(&server->tunnel);
|
2021-01-01 23:34:47 +08:00
|
|
|
|
2021-10-30 21:33:23 +08:00
|
|
|
assert(cbs);
|
|
|
|
assert(cbs->on_connection_failed);
|
|
|
|
assert(cbs->on_connected);
|
|
|
|
assert(cbs->on_disconnected);
|
|
|
|
|
|
|
|
server->cbs = cbs;
|
|
|
|
server->cbs_userdata = cbs_userdata;
|
|
|
|
|
2021-01-01 23:34:47 +08:00
|
|
|
return true;
|
2018-02-08 22:16:27 +08:00
|
|
|
}
|
|
|
|
|
2021-05-09 22:52:22 +08:00
|
|
|
static bool
|
2021-11-13 01:50:50 +08:00
|
|
|
device_read_info(struct sc_intr *intr, sc_socket device_socket,
|
2021-11-13 06:24:12 +08:00
|
|
|
struct sc_server_info *info) {
|
|
|
|
unsigned char buf[SC_DEVICE_NAME_FIELD_LENGTH + 4];
|
2021-11-13 01:50:50 +08:00
|
|
|
ssize_t r = net_recv_all_intr(intr, device_socket, buf, sizeof(buf));
|
2021-11-13 06:24:12 +08:00
|
|
|
if (r < SC_DEVICE_NAME_FIELD_LENGTH + 4) {
|
2021-05-09 22:52:22 +08:00
|
|
|
LOGE("Could not retrieve device information");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// in case the client sends garbage
|
2021-11-13 06:24:12 +08:00
|
|
|
buf[SC_DEVICE_NAME_FIELD_LENGTH - 1] = '\0';
|
2021-10-31 01:07:35 +08:00
|
|
|
memcpy(info->device_name, (char *) buf, sizeof(info->device_name));
|
|
|
|
|
2021-11-13 06:24:12 +08:00
|
|
|
info->frame_size.width = (buf[SC_DEVICE_NAME_FIELD_LENGTH] << 8)
|
|
|
|
| buf[SC_DEVICE_NAME_FIELD_LENGTH + 1];
|
|
|
|
info->frame_size.height = (buf[SC_DEVICE_NAME_FIELD_LENGTH + 2] << 8)
|
|
|
|
| buf[SC_DEVICE_NAME_FIELD_LENGTH + 3];
|
2021-05-09 22:52:22 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-10-30 21:33:23 +08:00
|
|
|
static bool
|
2021-11-13 06:24:12 +08:00
|
|
|
sc_server_connect_to(struct sc_server *server, struct sc_server_info *info) {
|
2021-11-13 05:32:29 +08:00
|
|
|
struct sc_adb_tunnel *tunnel = &server->tunnel;
|
|
|
|
|
|
|
|
assert(tunnel->enabled);
|
|
|
|
|
|
|
|
const char *serial = server->params.serial;
|
2021-11-01 18:44:58 +08:00
|
|
|
|
2021-11-13 16:58:52 +08:00
|
|
|
sc_socket video_socket = SC_SOCKET_NONE;
|
|
|
|
sc_socket control_socket = SC_SOCKET_NONE;
|
2021-11-13 05:32:29 +08:00
|
|
|
if (!tunnel->forward) {
|
|
|
|
video_socket = net_accept_intr(&server->intr, tunnel->server_socket);
|
2021-11-13 16:58:52 +08:00
|
|
|
if (video_socket == SC_SOCKET_NONE) {
|
2021-11-01 18:44:58 +08:00
|
|
|
goto fail;
|
2019-05-29 03:03:54 +08:00
|
|
|
}
|
|
|
|
|
2021-11-13 05:32:29 +08:00
|
|
|
control_socket = net_accept_intr(&server->intr, tunnel->server_socket);
|
2021-11-13 16:58:52 +08:00
|
|
|
if (control_socket == SC_SOCKET_NONE) {
|
2021-11-01 18:44:58 +08:00
|
|
|
goto fail;
|
2019-05-29 03:02:57 +08:00
|
|
|
}
|
2018-03-12 15:35:51 +08:00
|
|
|
} else {
|
2021-11-18 08:02:53 +08:00
|
|
|
uint32_t tunnel_host = server->params.tunnel_host;
|
|
|
|
if (!tunnel_host) {
|
|
|
|
tunnel_host = IPV4_LOCALHOST;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t tunnel_port = server->params.tunnel_port;
|
|
|
|
if (!tunnel_port) {
|
|
|
|
tunnel_port = tunnel->local_port;
|
|
|
|
}
|
|
|
|
|
2021-11-26 04:54:08 +08:00
|
|
|
unsigned attempts = 100;
|
2021-10-31 21:56:37 +08:00
|
|
|
sc_tick delay = SC_TICK_FROM_MS(100);
|
2021-11-18 08:02:53 +08:00
|
|
|
video_socket = connect_to_server(server, attempts, delay, tunnel_host,
|
|
|
|
tunnel_port);
|
2021-11-13 16:58:52 +08:00
|
|
|
if (video_socket == SC_SOCKET_NONE) {
|
2021-11-01 18:44:58 +08:00
|
|
|
goto fail;
|
2019-05-29 03:03:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// we know that the device is listening, we don't need several attempts
|
2021-11-01 05:01:57 +08:00
|
|
|
control_socket = net_socket();
|
2021-11-13 16:58:52 +08:00
|
|
|
if (control_socket == SC_SOCKET_NONE) {
|
2021-11-01 18:44:58 +08:00
|
|
|
goto fail;
|
2019-05-29 03:02:57 +08:00
|
|
|
}
|
2021-11-18 08:02:53 +08:00
|
|
|
bool ok = net_connect_intr(&server->intr, control_socket, tunnel_host,
|
|
|
|
tunnel_port);
|
2021-11-01 05:01:57 +08:00
|
|
|
if (!ok) {
|
|
|
|
goto fail;
|
|
|
|
}
|
2018-03-12 15:35:51 +08:00
|
|
|
}
|
2018-02-09 00:38:38 +08:00
|
|
|
|
|
|
|
// we don't need the adb tunnel anymore
|
2021-11-13 05:32:29 +08:00
|
|
|
sc_adb_tunnel_close(tunnel, &server->intr, serial);
|
2018-02-09 00:38:38 +08:00
|
|
|
|
2021-05-09 22:52:22 +08:00
|
|
|
// The sockets will be closed on stop if device_read_info() fails
|
2021-11-13 01:50:50 +08:00
|
|
|
bool ok = device_read_info(&server->intr, video_socket, info);
|
2021-11-01 18:44:58 +08:00
|
|
|
if (!ok) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2021-11-13 16:58:52 +08:00
|
|
|
assert(video_socket != SC_SOCKET_NONE);
|
|
|
|
assert(control_socket != SC_SOCKET_NONE);
|
2021-11-01 18:44:58 +08:00
|
|
|
|
|
|
|
server->video_socket = video_socket;
|
|
|
|
server->control_socket = control_socket;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
fail:
|
2021-11-13 16:58:52 +08:00
|
|
|
if (video_socket != SC_SOCKET_NONE) {
|
2021-11-01 18:44:58 +08:00
|
|
|
if (!net_close(video_socket)) {
|
|
|
|
LOGW("Could not close video socket");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-13 16:58:52 +08:00
|
|
|
if (control_socket != SC_SOCKET_NONE) {
|
2021-11-01 18:44:58 +08:00
|
|
|
if (!net_close(control_socket)) {
|
|
|
|
LOGW("Could not close control socket");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-30 21:33:23 +08:00
|
|
|
// Always leave this function with tunnel disabled
|
2021-11-13 05:32:29 +08:00
|
|
|
sc_adb_tunnel_close(tunnel, &server->intr, serial);
|
2021-10-30 21:33:23 +08:00
|
|
|
|
2021-11-01 18:44:58 +08:00
|
|
|
return false;
|
2018-02-08 22:16:27 +08:00
|
|
|
}
|
|
|
|
|
2021-11-13 01:30:20 +08:00
|
|
|
static void
|
2021-11-13 06:24:12 +08:00
|
|
|
sc_server_on_terminated(void *userdata) {
|
|
|
|
struct sc_server *server = userdata;
|
2021-11-13 01:30:20 +08:00
|
|
|
|
2021-11-13 04:40:22 +08:00
|
|
|
// If the server process dies before connecting to the server socket,
|
|
|
|
// then the client will be stuck forever on accept(). To avoid the problem,
|
|
|
|
// wake up the accept() call (or any other) when the server dies, like on
|
|
|
|
// stop() (it is safe to call interrupt() twice).
|
|
|
|
sc_intr_interrupt(&server->intr);
|
2021-11-13 01:30:20 +08:00
|
|
|
|
2021-10-30 21:33:23 +08:00
|
|
|
server->cbs->on_disconnected(server, server->cbs_userdata);
|
|
|
|
|
2021-11-13 01:30:20 +08:00
|
|
|
LOGD("Server terminated");
|
|
|
|
}
|
|
|
|
|
2021-11-18 04:58:36 +08:00
|
|
|
static bool
|
|
|
|
sc_server_fill_serial(struct sc_server *server) {
|
|
|
|
// Retrieve the actual device immediately if not provided, so that all
|
|
|
|
// future adb commands are executed for this specific device, even if other
|
|
|
|
// devices are connected afterwards (without "more than one
|
|
|
|
// device/emulator" error)
|
|
|
|
if (!server->params.serial) {
|
|
|
|
// The serial is owned by sc_server_params, and will be freed on destroy
|
2021-11-19 05:11:19 +08:00
|
|
|
server->params.serial = adb_get_serialno(&server->intr);
|
2021-11-18 04:58:36 +08:00
|
|
|
if (!server->params.serial) {
|
|
|
|
LOGE("Could not get device serial");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-10-30 21:33:23 +08:00
|
|
|
static int
|
|
|
|
run_server(void *data) {
|
2021-11-13 06:24:12 +08:00
|
|
|
struct sc_server *server = data;
|
2021-10-30 21:33:23 +08:00
|
|
|
|
2021-11-18 04:58:36 +08:00
|
|
|
if (!sc_server_fill_serial(server)) {
|
|
|
|
goto error_connection_failed;
|
|
|
|
}
|
|
|
|
|
2021-11-13 06:24:12 +08:00
|
|
|
const struct sc_server_params *params = &server->params;
|
2021-11-13 01:30:20 +08:00
|
|
|
|
2021-11-18 04:58:36 +08:00
|
|
|
LOGD("Device serial: %s", params->serial);
|
|
|
|
|
2021-11-13 01:50:50 +08:00
|
|
|
bool ok = push_server(&server->intr, params->serial);
|
2021-10-30 21:33:23 +08:00
|
|
|
if (!ok) {
|
|
|
|
goto error_connection_failed;
|
2021-11-13 01:30:20 +08:00
|
|
|
}
|
|
|
|
|
2021-11-13 05:32:29 +08:00
|
|
|
ok = sc_adb_tunnel_open(&server->tunnel, &server->intr, params->serial,
|
|
|
|
params->port_range, params->force_adb_forward);
|
2021-10-30 21:33:23 +08:00
|
|
|
if (!ok) {
|
|
|
|
goto error_connection_failed;
|
2021-11-13 01:30:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// server will connect to our server socket
|
2021-10-30 21:33:23 +08:00
|
|
|
sc_pid pid = execute_server(server, params);
|
|
|
|
if (pid == SC_PROCESS_NONE) {
|
2021-11-13 05:32:29 +08:00
|
|
|
sc_adb_tunnel_close(&server->tunnel, &server->intr, params->serial);
|
2021-10-30 21:33:23 +08:00
|
|
|
goto error_connection_failed;
|
2021-11-13 01:30:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct sc_process_listener listener = {
|
2021-11-13 06:24:12 +08:00
|
|
|
.on_terminated = sc_server_on_terminated,
|
2021-11-13 01:30:20 +08:00
|
|
|
};
|
2021-10-30 21:33:23 +08:00
|
|
|
struct sc_process_observer observer;
|
|
|
|
ok = sc_process_observer_init(&observer, pid, &listener, server);
|
2021-11-13 01:30:20 +08:00
|
|
|
if (!ok) {
|
2021-10-30 21:33:23 +08:00
|
|
|
sc_process_terminate(pid);
|
|
|
|
sc_process_wait(pid, true); // ignore exit code
|
2021-11-13 05:32:29 +08:00
|
|
|
sc_adb_tunnel_close(&server->tunnel, &server->intr, params->serial);
|
2021-10-30 21:33:23 +08:00
|
|
|
goto error_connection_failed;
|
2021-11-13 01:30:20 +08:00
|
|
|
}
|
|
|
|
|
2021-11-13 06:24:12 +08:00
|
|
|
ok = sc_server_connect_to(server, &server->info);
|
2021-10-30 21:33:23 +08:00
|
|
|
// The tunnel is always closed by server_connect_to()
|
|
|
|
if (!ok) {
|
|
|
|
sc_process_terminate(pid);
|
|
|
|
sc_process_wait(pid, true); // ignore exit code
|
|
|
|
sc_process_observer_join(&observer);
|
|
|
|
sc_process_observer_destroy(&observer);
|
|
|
|
goto error_connection_failed;
|
|
|
|
}
|
2021-11-13 01:30:20 +08:00
|
|
|
|
2021-10-30 21:33:23 +08:00
|
|
|
// Now connected
|
|
|
|
server->cbs->on_connected(server, server->cbs_userdata);
|
2021-11-13 01:30:20 +08:00
|
|
|
|
2021-10-30 21:33:23 +08:00
|
|
|
// Wait for server_stop()
|
2021-10-31 21:56:37 +08:00
|
|
|
sc_mutex_lock(&server->mutex);
|
2021-10-30 21:33:23 +08:00
|
|
|
while (!server->stopped) {
|
|
|
|
sc_cond_wait(&server->cond_stopped, &server->mutex);
|
|
|
|
}
|
2021-10-31 21:56:37 +08:00
|
|
|
sc_mutex_unlock(&server->mutex);
|
|
|
|
|
2021-01-01 19:41:25 +08:00
|
|
|
// Give some delay for the server to terminate properly
|
2021-07-04 22:50:19 +08:00
|
|
|
#define WATCHDOG_DELAY SC_TICK_FROM_SEC(1)
|
2021-11-12 16:49:37 +08:00
|
|
|
sc_tick deadline = sc_tick_now() + WATCHDOG_DELAY;
|
2021-10-30 21:33:23 +08:00
|
|
|
bool terminated = sc_process_observer_timedwait(&observer, deadline);
|
2021-01-01 19:41:25 +08:00
|
|
|
|
|
|
|
// After this delay, kill the server if it's not dead already.
|
|
|
|
// On some devices, closing the sockets is not sufficient to wake up the
|
|
|
|
// blocking calls while the device is asleep.
|
2021-11-12 16:49:37 +08:00
|
|
|
if (!terminated) {
|
|
|
|
// The process may have terminated since the check, but it is not
|
|
|
|
// reaped (closed) yet, so its PID is still valid, and it is ok to call
|
|
|
|
// sc_process_terminate() even in that case.
|
2021-01-01 19:41:25 +08:00
|
|
|
LOGW("Killing the server...");
|
2021-10-30 21:33:23 +08:00
|
|
|
sc_process_terminate(pid);
|
2021-01-01 19:41:25 +08:00
|
|
|
}
|
|
|
|
|
2021-10-30 21:33:23 +08:00
|
|
|
sc_process_observer_join(&observer);
|
|
|
|
sc_process_observer_destroy(&observer);
|
|
|
|
|
|
|
|
sc_process_close(pid);
|
|
|
|
|
|
|
|
return 0;
|
2021-11-12 16:49:37 +08:00
|
|
|
|
2021-10-30 21:33:23 +08:00
|
|
|
error_connection_failed:
|
|
|
|
server->cbs->on_connection_failed(server, server->cbs_userdata);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2021-11-13 06:24:12 +08:00
|
|
|
sc_server_start(struct sc_server *server) {
|
2021-10-30 21:33:23 +08:00
|
|
|
bool ok = sc_thread_create(&server->thread, run_server, "server", server);
|
|
|
|
if (!ok) {
|
|
|
|
LOGE("Could not create server thread");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2021-11-13 06:24:12 +08:00
|
|
|
sc_server_stop(struct sc_server *server) {
|
2021-10-30 21:33:23 +08:00
|
|
|
sc_mutex_lock(&server->mutex);
|
|
|
|
server->stopped = true;
|
|
|
|
sc_cond_signal(&server->cond_stopped);
|
2021-11-13 01:50:50 +08:00
|
|
|
sc_intr_interrupt(&server->intr);
|
2021-10-30 21:33:23 +08:00
|
|
|
sc_mutex_unlock(&server->mutex);
|
|
|
|
|
|
|
|
sc_thread_join(&server->thread, NULL);
|
2018-02-08 22:16:27 +08:00
|
|
|
}
|
2018-02-09 19:59:36 +08:00
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
void
|
2021-11-13 06:24:12 +08:00
|
|
|
sc_server_destroy(struct sc_server *server) {
|
|
|
|
sc_server_params_destroy(&server->params);
|
2021-11-13 01:50:50 +08:00
|
|
|
sc_intr_destroy(&server->intr);
|
2021-10-31 21:56:37 +08:00
|
|
|
sc_cond_destroy(&server->cond_stopped);
|
|
|
|
sc_mutex_destroy(&server->mutex);
|
2018-02-09 19:59:36 +08:00
|
|
|
}
|