Compare commits

..

1 Commits

Author SHA1 Message Date
Romain Vimont
da8be4c8d5 Fix compilation errors with old SDL versions
SDL_PixelFormatEnum has been introduced in SDL 2.0.10:
<cc6a8ac87e>

SDL_PIXELFORMAT_BGR444 has been introduced in SDL 2.0.12:
<a1c11854f2>

Fixes #2777 <https://github.com/Genymobile/scrcpy/issues/2777>
2021-11-14 15:18:15 +01:00
26 changed files with 266 additions and 610 deletions

View File

@@ -416,27 +416,17 @@ autoadb scrcpy -s '{}'
To connect to a remote device, it is possible to connect a local `adb` client to To connect to a remote device, it is possible to connect a local `adb` client to
a remote `adb` server (provided they use the same version of the _adb_ a remote `adb` server (provided they use the same version of the _adb_
protocol). protocol):
First, make sure the ADB server is running on the remote computer:
```bash ```bash
adb start-server adb kill-server # kill the local adb server on 5037
``` ssh -CN -L5037:localhost:5037 -R27183:localhost:27183 your_remote_computer
Then, establish a SSH tunnel:
```bash
# local 5038 --> remote 5037
# local 27183 <-- remote 27183
ssh -CN -L5038:localhost:5037 -R27183:localhost:27183 your_remote_computer
# keep this open # keep this open
``` ```
From another terminal, run scrcpy: From another terminal:
```bash ```bash
export ADB_SERVER_SOCKET=tcp:localhost:5038
scrcpy scrcpy
``` ```
@@ -444,16 +434,14 @@ To avoid enabling remote port forwarding, you could force a forward connection
instead (notice the `-L` instead of `-R`): instead (notice the `-L` instead of `-R`):
```bash ```bash
# local 5038 --> remote 5037 adb kill-server # kill the local adb server on 5037
# local 27183 --> remote 27183 ssh -CN -L5037:localhost:5037 -L27183:localhost:27183 your_remote_computer
ssh -CN -L5038:localhost:5037 -L27183:localhost:27183 your_remote_computer
# keep this open # keep this open
``` ```
From another terminal, run scrcpy: From another terminal:
```bash ```bash
export ADB_SERVER_SOCKET=tcp:localhost:5038
scrcpy --force-adb-forward scrcpy --force-adb-forward
``` ```

View File

@@ -39,26 +39,16 @@ src = [
'src/util/tick.c', 'src/util/tick.c',
] ]
conf = configuration_data()
if host_machine.system() == 'windows' if host_machine.system() == 'windows'
src += [ src += [
'src/sys/win/file.c', 'src/sys/win/file.c',
'src/sys/win/process.c', 'src/sys/win/process.c',
] ]
conf.set('_WIN32_WINNT', '0x0600')
conf.set('WINVER', '0x0600')
else else
src += [ src += [
'src/sys/unix/file.c', 'src/sys/unix/file.c',
'src/sys/unix/process.c', 'src/sys/unix/process.c',
] ]
conf.set('_POSIX_C_SOURCE', '200809L')
conf.set('_XOPEN_SOURCE', '700')
conf.set('_GNU_SOURCE', true)
if host_machine.system() == 'darwin'
conf.set('_DARWIN_C_SOURCE', true)
endif
endif endif
v4l2_support = host_machine.system() == 'linux' v4l2_support = host_machine.system() == 'linux'
@@ -138,6 +128,8 @@ if host_machine.system() == 'windows'
dependencies += cc.find_library('ws2_32') dependencies += cc.find_library('ws2_32')
endif endif
conf = configuration_data()
foreach f : check_functions foreach f : check_functions
if cc.has_function(f) if cc.has_function(f)
define = 'HAVE_' + f.underscorify().to_upper() define = 'HAVE_' + f.underscorify().to_upper()

View File

@@ -7,7 +7,6 @@
#include "util/file.h" #include "util/file.h"
#include "util/log.h" #include "util/log.h"
#include "util/process_intr.h"
#include "util/str.h" #include "util/str.h"
static const char *adb_command; static const char *adb_command;
@@ -110,9 +109,9 @@ show_adb_err_msg(enum sc_process_result err, const char *const argv[]) {
free(buf); free(buf);
} }
static sc_pid sc_pid
adb_execute_p(const char *serial, const char *const adb_cmd[], adb_execute_p(const char *serial, const char *const adb_cmd[],
size_t len, sc_pipe *pout) { size_t len, sc_pipe *pin, sc_pipe *pout, sc_pipe *perr) {
int i; int i;
sc_pid pid; sc_pid pid;
@@ -133,7 +132,7 @@ adb_execute_p(const char *serial, const char *const adb_cmd[],
memcpy(&argv[i], adb_cmd, len * sizeof(const char *)); memcpy(&argv[i], adb_cmd, len * sizeof(const char *));
argv[len + i] = NULL; argv[len + i] = NULL;
enum sc_process_result r = enum sc_process_result r =
sc_process_execute_p(argv, &pid, NULL, pout, NULL); sc_process_execute_p(argv, &pid, pin, pout, perr);
if (r != SC_PROCESS_SUCCESS) { if (r != SC_PROCESS_SUCCESS) {
show_adb_err_msg(r, argv); show_adb_err_msg(r, argv);
pid = SC_PROCESS_NONE; pid = SC_PROCESS_NONE;
@@ -145,11 +144,11 @@ adb_execute_p(const char *serial, const char *const adb_cmd[],
sc_pid sc_pid
adb_execute(const char *serial, const char *const adb_cmd[], size_t len) { adb_execute(const char *serial, const char *const adb_cmd[], size_t len) {
return adb_execute_p(serial, adb_cmd, len, NULL); return adb_execute_p(serial, adb_cmd, len, NULL, NULL, NULL);
} }
static sc_pid sc_pid
adb_exec_forward(const char *serial, uint16_t local_port, adb_forward(const char *serial, uint16_t local_port,
const char *device_socket_name) { const char *device_socket_name) {
char local[4 + 5 + 1]; // tcp:PORT char local[4 + 5 + 1]; // tcp:PORT
char remote[108 + 14 + 1]; // localabstract:NAME char remote[108 + 14 + 1]; // localabstract:NAME
@@ -159,16 +158,16 @@ adb_exec_forward(const char *serial, uint16_t local_port,
return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd)); return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
} }
static sc_pid sc_pid
adb_exec_forward_remove(const char *serial, uint16_t local_port) { adb_forward_remove(const char *serial, uint16_t local_port) {
char local[4 + 5 + 1]; // tcp:PORT char local[4 + 5 + 1]; // tcp:PORT
sprintf(local, "tcp:%" PRIu16, local_port); sprintf(local, "tcp:%" PRIu16, local_port);
const char *const adb_cmd[] = {"forward", "--remove", local}; const char *const adb_cmd[] = {"forward", "--remove", local};
return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd)); return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
} }
static sc_pid sc_pid
adb_exec_reverse(const char *serial, const char *device_socket_name, adb_reverse(const char *serial, const char *device_socket_name,
uint16_t local_port) { uint16_t local_port) {
char local[4 + 5 + 1]; // tcp:PORT char local[4 + 5 + 1]; // tcp:PORT
char remote[108 + 14 + 1]; // localabstract:NAME char remote[108 + 14 + 1]; // localabstract:NAME
@@ -178,16 +177,16 @@ adb_exec_reverse(const char *serial, const char *device_socket_name,
return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd)); return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
} }
static sc_pid sc_pid
adb_exec_reverse_remove(const char *serial, const char *device_socket_name) { adb_reverse_remove(const char *serial, const char *device_socket_name) {
char remote[108 + 14 + 1]; // localabstract:NAME char remote[108 + 14 + 1]; // localabstract:NAME
snprintf(remote, sizeof(remote), "localabstract:%s", device_socket_name); snprintf(remote, sizeof(remote), "localabstract:%s", device_socket_name);
const char *const adb_cmd[] = {"reverse", "--remove", remote}; const char *const adb_cmd[] = {"reverse", "--remove", remote};
return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd)); return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
} }
static sc_pid sc_pid
adb_exec_push(const char *serial, const char *local, const char *remote) { adb_push(const char *serial, const char *local, const char *remote) {
#ifdef __WINDOWS__ #ifdef __WINDOWS__
// Windows will parse the string, so the paths must be quoted // Windows will parse the string, so the paths must be quoted
// (see sys/win/command.c) // (see sys/win/command.c)
@@ -213,8 +212,8 @@ adb_exec_push(const char *serial, const char *local, const char *remote) {
return pid; return pid;
} }
static sc_pid sc_pid
adb_exec_install(const char *serial, const char *local) { adb_install(const char *serial, const char *local) {
#ifdef __WINDOWS__ #ifdef __WINDOWS__
// Windows will parse the string, so the local name must be quoted // Windows will parse the string, so the local name must be quoted
// (see sys/win/command.c) // (see sys/win/command.c)
@@ -234,75 +233,45 @@ adb_exec_install(const char *serial, const char *local) {
return pid; return pid;
} }
static sc_pid static ssize_t
adb_exec_get_serialno(sc_pipe *pout) { adb_execute_for_output(const char *serial, const char *const adb_cmd[],
const char *const adb_cmd[] = {"get-serialno"}; size_t adb_cmd_len, char *buf, size_t buf_len,
return adb_execute_p(NULL, adb_cmd, ARRAY_LEN(adb_cmd), pout); const char *name) {
sc_pipe pout;
sc_pid pid = adb_execute_p(serial, adb_cmd, adb_cmd_len, NULL, &pout, NULL);
ssize_t r = sc_pipe_read_all(pout, buf, buf_len);
sc_pipe_close(pout);
if (!sc_process_check_success(pid, name, true)) {
return -1;
} }
bool return r;
adb_forward(struct sc_intr *intr, const char *serial, uint16_t local_port,
const char *device_socket_name) {
sc_pid pid = adb_exec_forward(serial, local_port, device_socket_name);
return sc_process_check_success_intr(intr, pid, "adb forward", true);
} }
bool static size_t
adb_forward_remove(struct sc_intr *intr, const char *serial, truncate_first_line(char *data, size_t len) {
uint16_t local_port) { data[len - 1] = '\0';
sc_pid pid = adb_exec_forward_remove(serial, local_port); char *eol = strpbrk(data, "\r\n");
return sc_process_check_success_intr(intr, pid, "adb forward --remove", if (eol) {
true); *eol = '\0';
len = eol - data;
} }
return len;
bool
adb_reverse(struct sc_intr *intr, const char *serial,
const char *device_socket_name, uint16_t local_port) {
sc_pid pid = adb_exec_reverse(serial, device_socket_name, local_port);
return sc_process_check_success_intr(intr, pid, "adb reverse", true);
}
bool
adb_reverse_remove(struct sc_intr *intr, const char *serial,
const char *device_socket_name) {
sc_pid pid = adb_exec_reverse_remove(serial, device_socket_name);
return sc_process_check_success_intr(intr, pid, "adb reverse --remove",
true);
}
bool
adb_push(struct sc_intr *intr, const char *serial, const char *local,
const char *remote) {
sc_pid pid = adb_exec_push(serial, local, remote);
return sc_process_check_success_intr(intr, pid, "adb push", true);
}
bool
adb_install(struct sc_intr *intr, const char *serial, const char *local) {
sc_pid pid = adb_exec_install(serial, local);
return sc_process_check_success_intr(intr, pid, "adb install", true);
} }
char * char *
adb_get_serialno(struct sc_intr *intr) { adb_get_serialno(void) {
sc_pipe pout;
sc_pid pid = adb_exec_get_serialno(&pout);
if (pid == SC_PROCESS_NONE) {
LOGE("Could not execute \"adb get-serialno\"");
return NULL;
}
char buf[128]; char buf[128];
ssize_t r = sc_pipe_read_all_intr(intr, pid, pout, buf, sizeof(buf));
sc_pipe_close(pout);
bool ok = const char *const adb_cmd[] = {"get-serialno"};
sc_process_check_success_intr(intr, pid, "adb get-serialno", true); ssize_t r = adb_execute_for_output(NULL, adb_cmd, ARRAY_LEN(adb_cmd),
if (!ok) { buf, sizeof(buf), "get-serialno");
if (r <= 0) {
return NULL; return NULL;
} }
sc_str_truncate(buf, r, " \r\n"); truncate_first_line(buf, r);
return strdup(buf); return strdup(buf);
} }

View File

@@ -6,40 +6,37 @@
#include <stdbool.h> #include <stdbool.h>
#include <inttypes.h> #include <inttypes.h>
#include "util/intr.h" #include "util/process.h"
sc_pid sc_pid
adb_execute(const char *serial, const char *const adb_cmd[], size_t len); adb_execute(const char *serial, const char *const adb_cmd[], size_t len);
bool sc_pid
adb_forward(struct sc_intr *intr, const char *serial, uint16_t local_port, adb_execute_p(const char *serial, const char *const adb_cmd[],
size_t len, sc_pipe *pin, sc_pipe *pout, sc_pipe *perr);
sc_pid
adb_forward(const char *serial, uint16_t local_port,
const char *device_socket_name); const char *device_socket_name);
bool sc_pid
adb_forward_remove(struct sc_intr *intr, const char *serial, adb_forward_remove(const char *serial, uint16_t local_port);
sc_pid
adb_reverse(const char *serial, const char *device_socket_name,
uint16_t local_port); uint16_t local_port);
bool sc_pid
adb_reverse(struct sc_intr *intr, const char *serial, adb_reverse_remove(const char *serial, const char *device_socket_name);
const char *device_socket_name, uint16_t local_port);
bool sc_pid
adb_reverse_remove(struct sc_intr *intr, const char *serial, adb_push(const char *serial, const char *local, const char *remote);
const char *device_socket_name);
bool sc_pid
adb_push(struct sc_intr *intr, const char *serial, const char *local, adb_install(const char *serial, const char *local);
const char *remote);
bool // Return the result of "adb get-serialno".
adb_install(struct sc_intr *intr, const char *serial, const char *local);
/**
* Execute `adb get-serialno`
*
* Return the result, to be freed by the caller, or NULL on error.
*/
char * char *
adb_get_serialno(struct sc_intr *intr); adb_get_serialno(void);
#endif #endif

View File

@@ -9,6 +9,33 @@
#define SC_SOCKET_NAME "scrcpy" #define SC_SOCKET_NAME "scrcpy"
static bool
enable_tunnel_reverse(struct sc_intr *intr, const char *serial,
uint16_t local_port) {
sc_pid pid = adb_reverse(serial, SC_SOCKET_NAME, local_port);
return sc_process_check_success_intr(intr, pid, "adb reverse");
}
static bool
disable_tunnel_reverse(struct sc_intr *intr, const char *serial) {
sc_pid pid = adb_reverse_remove(serial, SC_SOCKET_NAME);
return sc_process_check_success_intr(intr, pid, "adb reverse --remove");
}
static bool
enable_tunnel_forward(struct sc_intr *intr, const char *serial,
uint16_t local_port) {
sc_pid pid = adb_forward(serial, local_port, SC_SOCKET_NAME);
return sc_process_check_success_intr(intr, pid, "adb forward");
}
static bool
disable_tunnel_forward(struct sc_intr *intr, const char *serial,
uint16_t local_port) {
sc_pid pid = adb_forward_remove(serial, local_port);
return sc_process_check_success_intr(intr, pid, "adb forward --remove");
}
static bool static bool
listen_on_port(struct sc_intr *intr, sc_socket socket, uint16_t port) { listen_on_port(struct sc_intr *intr, sc_socket socket, uint16_t port) {
return net_listen_intr(intr, socket, IPV4_LOCALHOST, port, 1); return net_listen_intr(intr, socket, IPV4_LOCALHOST, port, 1);
@@ -20,7 +47,7 @@ enable_tunnel_reverse_any_port(struct sc_adb_tunnel *tunnel,
struct sc_port_range port_range) { struct sc_port_range port_range) {
uint16_t port = port_range.first; uint16_t port = port_range.first;
for (;;) { for (;;) {
if (!adb_reverse(intr, serial, SC_SOCKET_NAME, port)) { if (!enable_tunnel_reverse(intr, serial, port)) {
// the command itself failed, it will fail on any port // the command itself failed, it will fail on any port
return false; return false;
} }
@@ -51,7 +78,7 @@ enable_tunnel_reverse_any_port(struct sc_adb_tunnel *tunnel,
} }
// failure, disable tunnel and try another port // failure, disable tunnel and try another port
if (!adb_reverse_remove(intr, serial, SC_SOCKET_NAME)) { if (!disable_tunnel_reverse(intr, serial)) {
LOGW("Could not remove reverse tunnel on port %" PRIu16, port); LOGW("Could not remove reverse tunnel on port %" PRIu16, port);
} }
@@ -81,7 +108,7 @@ enable_tunnel_forward_any_port(struct sc_adb_tunnel *tunnel,
uint16_t port = port_range.first; uint16_t port = port_range.first;
for (;;) { for (;;) {
if (adb_forward(intr, serial, port, SC_SOCKET_NAME)) { if (enable_tunnel_forward(intr, serial, port)) {
// success // success
tunnel->local_port = port; tunnel->local_port = port;
tunnel->enabled = true; tunnel->enabled = true;
@@ -146,9 +173,9 @@ sc_adb_tunnel_close(struct sc_adb_tunnel *tunnel, struct sc_intr *intr,
bool ret; bool ret;
if (tunnel->forward) { if (tunnel->forward) {
ret = adb_forward_remove(intr, serial, tunnel->local_port); ret = disable_tunnel_forward(intr, serial, tunnel->local_port);
} else { } else {
ret = adb_reverse_remove(intr, serial, SC_SOCKET_NAME); ret = disable_tunnel_reverse(intr, serial);
assert(tunnel->server_socket != SC_SOCKET_NONE); assert(tunnel->server_socket != SC_SOCKET_NONE);
if (!net_close(tunnel->server_socket)) { if (!net_close(tunnel->server_socket)) {

View File

@@ -569,10 +569,6 @@ sc_getopt_adapter_create_longopts(void) {
size_t out_idx = 0; size_t out_idx = 0;
for (size_t i = 0; i < ARRAY_LEN(options); ++i) { for (size_t i = 0; i < ARRAY_LEN(options); ++i) {
const struct sc_option *in = &options[i]; const struct sc_option *in = &options[i];
// If longopt_id is set, then longopt must be set
assert(!in->longopt_id || in->longopt);
if (!in->longopt) { if (!in->longopt) {
// The longopts array must only contain long options // The longopts array must only contain long options
continue; continue;
@@ -675,12 +671,12 @@ print_option_usage_header(const struct sc_option *opt) {
} }
} }
printf("\n %s\n", buf.s); fprintf(stderr, "\n %s\n", buf.s);
free(buf.s); free(buf.s);
return; return;
error: error:
printf("<ERROR>\n"); fprintf(stderr, "<ERROR>\n");
} }
static void static void
@@ -696,11 +692,11 @@ print_option_usage(const struct sc_option *opt, unsigned cols) {
char *text = sc_str_wrap_lines(opt->text, cols, 8); char *text = sc_str_wrap_lines(opt->text, cols, 8);
if (!text) { if (!text) {
printf("<ERROR>\n"); fprintf(stderr, "<ERROR>\n");
return; return;
} }
printf("%s\n", text); fprintf(stderr, "%s\n", text);
free(text); free(text);
} }
@@ -711,11 +707,11 @@ print_shortcuts_intro(unsigned cols) {
"(left) Alt or (left) Super, but it can be configured by " "(left) Alt or (left) Super, but it can be configured by "
"--shortcut-mod (see above).", cols, 4); "--shortcut-mod (see above).", cols, 4);
if (!intro) { if (!intro) {
printf("<ERROR>\n"); fprintf(stderr, "<ERROR>\n");
return; return;
} }
printf("%s\n", intro); fprintf(stderr, "%s\n", intro);
free(intro); free(intro);
} }
@@ -725,21 +721,21 @@ print_shortcut(const struct sc_shortcut *shortcut, unsigned cols) {
assert(shortcut->shortcuts[0]); // At least one shortcut assert(shortcut->shortcuts[0]); // At least one shortcut
assert(shortcut->text); assert(shortcut->text);
printf("\n"); fprintf(stderr, "\n");
unsigned i = 0; unsigned i = 0;
while (shortcut->shortcuts[i]) { while (shortcut->shortcuts[i]) {
printf(" %s\n", shortcut->shortcuts[i]); fprintf(stderr, " %s\n", shortcut->shortcuts[i]);
++i; ++i;
}; };
char *text = sc_str_wrap_lines(shortcut->text, cols, 8); char *text = sc_str_wrap_lines(shortcut->text, cols, 8);
if (!text) { if (!text) {
printf("<ERROR>\n"); fprintf(stderr, "<ERROR>\n");
return; return;
} }
printf("%s\n", text); fprintf(stderr, "%s\n", text);
free(text); free(text);
} }
@@ -763,14 +759,14 @@ scrcpy_print_usage(const char *arg0) {
} }
} }
printf("Usage: %s [options]\n\n" fprintf(stderr, "Usage: %s [options]\n\n"
"Options:\n", arg0); "Options:\n", arg0);
for (size_t i = 0; i < ARRAY_LEN(options); ++i) { for (size_t i = 0; i < ARRAY_LEN(options); ++i) {
print_option_usage(&options[i], cols); print_option_usage(&options[i], cols);
} }
// Print shortcuts section // Print shortcuts section
printf("\nShortcuts:\n\n"); fprintf(stderr, "\nShortcuts:\n\n");
print_shortcuts_intro(cols); print_shortcuts_intro(cols);
for (size_t i = 0; i < ARRAY_LEN(shortcuts); ++i) { for (size_t i = 0; i < ARRAY_LEN(shortcuts); ++i) {
print_shortcut(&shortcuts[i], cols); print_shortcut(&shortcuts[i], cols);

View File

@@ -1,7 +1,12 @@
#ifndef COMPAT_H #ifndef COMPAT_H
#define COMPAT_H #define COMPAT_H
#include "config.h" #define _POSIX_C_SOURCE 200809L
#define _XOPEN_SOURCE 700
#define _GNU_SOURCE
#ifdef __APPLE__
# define _DARWIN_C_SOURCE
#endif
#include <libavformat/version.h> #include <libavformat/version.h>
#include <SDL2/SDL_version.h> #include <SDL2/SDL_version.h>

View File

@@ -5,7 +5,6 @@
#include "adb.h" #include "adb.h"
#include "util/log.h" #include "util/log.h"
#include "util/process_intr.h"
#define DEFAULT_PUSH_TARGET "/sdcard/Download/" #define DEFAULT_PUSH_TARGET "/sdcard/Download/"
@@ -17,7 +16,6 @@ file_handler_request_destroy(struct file_handler_request *req) {
bool bool
file_handler_init(struct file_handler *file_handler, const char *serial, file_handler_init(struct file_handler *file_handler, const char *serial,
const char *push_target) { const char *push_target) {
assert(serial);
cbuf_init(&file_handler->queue); cbuf_init(&file_handler->queue);
@@ -32,26 +30,23 @@ file_handler_init(struct file_handler *file_handler, const char *serial,
return false; return false;
} }
ok = sc_intr_init(&file_handler->intr); if (serial) {
if (!ok) {
LOGE("Could not create intr");
sc_cond_destroy(&file_handler->event_cond);
sc_mutex_destroy(&file_handler->mutex);
}
file_handler->serial = strdup(serial); file_handler->serial = strdup(serial);
if (!file_handler->serial) { if (!file_handler->serial) {
LOGE("Could not strdup serial"); LOGW("Could not strdup serial");
sc_intr_destroy(&file_handler->intr);
sc_cond_destroy(&file_handler->event_cond); sc_cond_destroy(&file_handler->event_cond);
sc_mutex_destroy(&file_handler->mutex); sc_mutex_destroy(&file_handler->mutex);
return false; return false;
} }
} else {
file_handler->serial = NULL;
}
// lazy initialization // lazy initialization
file_handler->initialized = false; file_handler->initialized = false;
file_handler->stopped = false; file_handler->stopped = false;
file_handler->current_process = SC_PROCESS_NONE;
file_handler->push_target = push_target ? push_target : DEFAULT_PUSH_TARGET; file_handler->push_target = push_target ? push_target : DEFAULT_PUSH_TARGET;
@@ -62,7 +57,6 @@ void
file_handler_destroy(struct file_handler *file_handler) { file_handler_destroy(struct file_handler *file_handler) {
sc_cond_destroy(&file_handler->event_cond); sc_cond_destroy(&file_handler->event_cond);
sc_mutex_destroy(&file_handler->mutex); sc_mutex_destroy(&file_handler->mutex);
sc_intr_destroy(&file_handler->intr);
free(file_handler->serial); free(file_handler->serial);
struct file_handler_request req; struct file_handler_request req;
@@ -71,6 +65,16 @@ file_handler_destroy(struct file_handler *file_handler) {
} }
} }
static sc_pid
install_apk(const char *serial, const char *file) {
return adb_install(serial, file);
}
static sc_pid
push_file(const char *serial, const char *file, const char *push_target) {
return adb_push(serial, file, push_target);
}
bool bool
file_handler_request(struct file_handler *file_handler, file_handler_request(struct file_handler *file_handler,
file_handler_action_t action, char *file) { file_handler_action_t action, char *file) {
@@ -102,16 +106,10 @@ file_handler_request(struct file_handler *file_handler,
static int static int
run_file_handler(void *data) { run_file_handler(void *data) {
struct file_handler *file_handler = data; struct file_handler *file_handler = data;
struct sc_intr *intr = &file_handler->intr;
const char *serial = file_handler->serial;
assert(serial);
const char *push_target = file_handler->push_target;
assert(push_target);
for (;;) { for (;;) {
sc_mutex_lock(&file_handler->mutex); sc_mutex_lock(&file_handler->mutex);
file_handler->current_process = SC_PROCESS_NONE;
while (!file_handler->stopped && cbuf_is_empty(&file_handler->queue)) { while (!file_handler->stopped && cbuf_is_empty(&file_handler->queue)) {
sc_cond_wait(&file_handler->event_cond, &file_handler->mutex); sc_cond_wait(&file_handler->event_cond, &file_handler->mutex);
} }
@@ -124,26 +122,43 @@ run_file_handler(void *data) {
bool non_empty = cbuf_take(&file_handler->queue, &req); bool non_empty = cbuf_take(&file_handler->queue, &req);
assert(non_empty); assert(non_empty);
(void) non_empty; (void) non_empty;
sc_pid pid;
if (req.action == ACTION_INSTALL_APK) {
LOGI("Installing %s...", req.file);
pid = install_apk(file_handler->serial, req.file);
} else {
LOGI("Pushing %s...", req.file);
pid = push_file(file_handler->serial, req.file,
file_handler->push_target);
}
file_handler->current_process = pid;
sc_mutex_unlock(&file_handler->mutex); sc_mutex_unlock(&file_handler->mutex);
if (req.action == ACTION_INSTALL_APK) { if (req.action == ACTION_INSTALL_APK) {
LOGI("Installing %s...", req.file); if (sc_process_check_success(pid, "adb install", false)) {
bool ok = adb_install(intr, serial, req.file);
if (ok) {
LOGI("%s successfully installed", req.file); LOGI("%s successfully installed", req.file);
} else { } else {
LOGE("Failed to install %s", req.file); LOGE("Failed to install %s", req.file);
} }
} else { } else {
LOGI("Pushing %s...", req.file); if (sc_process_check_success(pid, "adb push", false)) {
bool ok = adb_push(intr, serial, req.file, push_target); LOGI("%s successfully pushed to %s", req.file,
if (ok) { file_handler->push_target);
LOGI("%s successfully pushed to %s", req.file, push_target);
} else { } else {
LOGE("Failed to push %s to %s", req.file, push_target); LOGE("Failed to push %s to %s", req.file,
file_handler->push_target);
} }
} }
sc_mutex_lock(&file_handler->mutex);
// Close the process (it is necessarily already terminated)
// Execute this call with mutex locked to avoid race conditions with
// file_handler_stop()
sc_process_close(file_handler->current_process);
file_handler->current_process = SC_PROCESS_NONE;
sc_mutex_unlock(&file_handler->mutex);
file_handler_request_destroy(&req); file_handler_request_destroy(&req);
} }
return 0; return 0;
@@ -168,7 +183,11 @@ file_handler_stop(struct file_handler *file_handler) {
sc_mutex_lock(&file_handler->mutex); sc_mutex_lock(&file_handler->mutex);
file_handler->stopped = true; file_handler->stopped = true;
sc_cond_signal(&file_handler->event_cond); sc_cond_signal(&file_handler->event_cond);
sc_intr_interrupt(&file_handler->intr); if (file_handler->current_process != SC_PROCESS_NONE) {
if (!sc_process_terminate(file_handler->current_process)) {
LOGW("Could not terminate push/install process");
}
}
sc_mutex_unlock(&file_handler->mutex); sc_mutex_unlock(&file_handler->mutex);
} }

View File

@@ -8,7 +8,6 @@
#include "adb.h" #include "adb.h"
#include "util/cbuf.h" #include "util/cbuf.h"
#include "util/thread.h" #include "util/thread.h"
#include "util/intr.h"
typedef enum { typedef enum {
ACTION_INSTALL_APK, ACTION_INSTALL_APK,
@@ -30,9 +29,8 @@ struct file_handler {
sc_cond event_cond; sc_cond event_cond;
bool stopped; bool stopped;
bool initialized; bool initialized;
sc_pid current_process;
struct file_handler_request_queue queue; struct file_handler_request_queue queue;
struct sc_intr intr;
}; };
bool bool

View File

@@ -47,9 +47,6 @@ main(int argc, char *argv[]) {
setbuf(stderr, NULL); setbuf(stderr, NULL);
#endif #endif
printf("scrcpy " SCRCPY_VERSION
" <https://github.com/Genymobile/scrcpy>\n");
struct scrcpy_cli_args args = { struct scrcpy_cli_args args = {
.opts = scrcpy_options_default, .opts = scrcpy_options_default,
.help = false, .help = false,
@@ -76,6 +73,8 @@ main(int argc, char *argv[]) {
return 0; return 0;
} }
LOGI("scrcpy " SCRCPY_VERSION " <https://github.com/Genymobile/scrcpy>");
#ifdef SCRCPY_LAVF_REQUIRES_REGISTER_ALL #ifdef SCRCPY_LAVF_REQUIRES_REGISTER_ALL
av_register_all(); av_register_all();
#endif #endif

View File

@@ -394,11 +394,8 @@ scrcpy(struct scrcpy_options *options) {
// It is necessarily initialized here, since the device is connected // It is necessarily initialized here, since the device is connected
struct sc_server_info *info = &s->server.info; struct sc_server_info *info = &s->server.info;
const char *serial = s->server.params.serial;
assert(serial);
if (options->display && options->control) { if (options->display && options->control) {
if (!file_handler_init(&s->file_handler, serial, if (!file_handler_init(&s->file_handler, options->serial,
options->push_target)) { options->push_target)) {
goto end; goto end;
} }
@@ -519,7 +516,21 @@ scrcpy(struct scrcpy_options *options) {
#ifdef HAVE_AOA_HID #ifdef HAVE_AOA_HID
bool aoa_hid_ok = false; bool aoa_hid_ok = false;
char *serialno = NULL;
const char *serial = options->serial;
if (!serial) {
serialno = adb_get_serialno();
if (!serialno) {
LOGE("Could not get device serial");
goto aoa_hid_end;
}
serial = serialno;
LOGI("Device serial: %s", serial);
}
bool ok = sc_aoa_init(&s->aoa, serial); bool ok = sc_aoa_init(&s->aoa, serial);
free(serialno);
if (!ok) { if (!ok) {
goto aoa_hid_end; goto aoa_hid_end;
} }

View File

@@ -112,9 +112,9 @@ push_server(struct sc_intr *intr, const char *serial) {
free(server_path); free(server_path);
return false; return false;
} }
bool ok = adb_push(intr, serial, server_path, SC_DEVICE_SERVER_PATH); sc_pid pid = adb_push(serial, server_path, SC_DEVICE_SERVER_PATH);
free(server_path); free(server_path);
return ok; return sc_process_check_success_intr(intr, pid, "adb push");
} }
static const char * static const char *
@@ -234,12 +234,6 @@ connect_to_server(struct sc_server *server, uint32_t attempts, sc_tick delay) {
net_close(socket); net_close(socket);
} }
if (sc_intr_is_interrupted(&server->intr)) {
// Stop immediately
break;
}
if (attempts) { if (attempts) {
sc_mutex_lock(&server->mutex); sc_mutex_lock(&server->mutex);
sc_tick deadline = sc_tick_now() + delay; sc_tick deadline = sc_tick_now() + delay;
@@ -422,36 +416,12 @@ sc_server_on_terminated(void *userdata) {
LOGD("Server terminated"); LOGD("Server terminated");
} }
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
server->params.serial = adb_get_serialno(&server->intr);
if (!server->params.serial) {
LOGE("Could not get device serial");
return false;
}
}
return true;
}
static int static int
run_server(void *data) { run_server(void *data) {
struct sc_server *server = data; struct sc_server *server = data;
if (!sc_server_fill_serial(server)) {
goto error_connection_failed;
}
const struct sc_server_params *params = &server->params; const struct sc_server_params *params = &server->params;
LOGD("Device serial: %s", params->serial);
bool ok = push_server(&server->intr, params->serial); bool ok = push_server(&server->intr, params->serial);
if (!ok) { if (!ok) {
goto error_connection_failed; goto error_connection_failed;

View File

@@ -1,7 +1,5 @@
#include "util/process.h" #include "util/process.h"
#include <processthreadsapi.h>
#include <assert.h> #include <assert.h>
#include "util/log.h" #include "util/log.h"
@@ -28,9 +26,6 @@ sc_process_execute_p(const char *const argv[], HANDLE *handle,
HANDLE *pin, HANDLE *pout, HANDLE *perr) { HANDLE *pin, HANDLE *pout, HANDLE *perr) {
enum sc_process_result ret = SC_PROCESS_ERROR_GENERIC; enum sc_process_result ret = SC_PROCESS_ERROR_GENERIC;
// Add 1 per non-NULL pointer
unsigned handle_count = !!pin + !!pout + !!perr;
SECURITY_ATTRIBUTES sa; SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL; sa.lpSecurityDescriptor = NULL;
@@ -70,94 +65,45 @@ sc_process_execute_p(const char *const argv[], HANDLE *handle,
} }
} }
STARTUPINFOEXW si; STARTUPINFOW si;
PROCESS_INFORMATION pi; PROCESS_INFORMATION pi;
memset(&si, 0, sizeof(si)); memset(&si, 0, sizeof(si));
si.StartupInfo.cb = sizeof(si); si.cb = sizeof(si);
HANDLE handles[3]; if (pin || pout || perr) {
si.dwFlags = STARTF_USESTDHANDLES;
LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList = NULL;
if (handle_count) {
si.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
if (pin) { if (pin) {
si.StartupInfo.hStdInput = stdin_read_handle; si.hStdInput = stdin_read_handle;
} }
if (pout) { if (pout) {
si.StartupInfo.hStdOutput = stdout_write_handle; si.hStdOutput = stdout_write_handle;
} }
if (perr) { if (perr) {
si.StartupInfo.hStdError = stderr_write_handle; si.hStdError = stderr_write_handle;
} }
SIZE_T size;
// Call it once to know the required buffer size
BOOL ok =
InitializeProcThreadAttributeList(NULL, 1, 0, &size)
|| GetLastError() == ERROR_INSUFFICIENT_BUFFER;
if (!ok) {
goto error_close_stderr;
}
lpAttributeList = malloc(size);
if (!lpAttributeList) {
goto error_close_stderr;
}
ok = InitializeProcThreadAttributeList(lpAttributeList, 1, 0, &size);
if (!ok) {
free(lpAttributeList);
goto error_close_stderr;
}
// Explicitly pass the HANDLEs that must be inherited
unsigned i = 0;
if (pin) {
handles[i++] = stdin_read_handle;
}
if (pout) {
handles[i++] = stdout_write_handle;
}
if (perr) {
handles[i++] = stderr_write_handle;
}
ok = UpdateProcThreadAttribute(lpAttributeList, 0,
PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
handles, handle_count * sizeof(HANDLE),
NULL, NULL);
if (!ok) {
goto error_free_attribute_list;
}
si.lpAttributeList = lpAttributeList;
} }
char *cmd = malloc(CMD_MAX_LEN); char *cmd = malloc(CMD_MAX_LEN);
if (!cmd || !build_cmd(cmd, CMD_MAX_LEN, argv)) { if (!cmd || !build_cmd(cmd, CMD_MAX_LEN, argv)) {
goto error_free_attribute_list; *handle = NULL;
goto error_close_stderr;
} }
wchar_t *wide = sc_str_to_wchars(cmd); wchar_t *wide = sc_str_to_wchars(cmd);
free(cmd); free(cmd);
if (!wide) { if (!wide) {
LOGC("Could not allocate wide char string"); LOGC("Could not allocate wide char string");
goto error_free_attribute_list; goto error_close_stderr;
} }
BOOL bInheritHandles = handle_count > 0; if (!CreateProcessW(NULL, wide, NULL, NULL, TRUE, 0, NULL, NULL, &si,
DWORD dwCreationFlags = handle_count > 0 ? EXTENDED_STARTUPINFO_PRESENT : 0; &pi)) {
BOOL ok = CreateProcessW(NULL, wide, NULL, NULL, bInheritHandles,
dwCreationFlags, NULL, NULL, &si.StartupInfo, &pi);
free(wide); free(wide);
if (!ok) { *handle = NULL;
if (GetLastError() == ERROR_FILE_NOT_FOUND) { if (GetLastError() == ERROR_FILE_NOT_FOUND) {
ret = SC_PROCESS_ERROR_MISSING_BINARY; ret = SC_PROCESS_ERROR_MISSING_BINARY;
} }
goto error_free_attribute_list; goto error_close_stderr;
}
if (lpAttributeList) {
DeleteProcThreadAttributeList(lpAttributeList);
free(lpAttributeList);
} }
// These handles are used by the child process, close them for this process // These handles are used by the child process, close them for this process
@@ -171,15 +117,11 @@ sc_process_execute_p(const char *const argv[], HANDLE *handle,
CloseHandle(stderr_write_handle); CloseHandle(stderr_write_handle);
} }
free(wide);
*handle = pi.hProcess; *handle = pi.hProcess;
return SC_PROCESS_SUCCESS; return SC_PROCESS_SUCCESS;
error_free_attribute_list:
if (lpAttributeList) {
DeleteProcThreadAttributeList(lpAttributeList);
free(lpAttributeList);
}
error_close_stderr: error_close_stderr:
if (perr) { if (perr) {
CloseHandle(*perr); CloseHandle(*perr);
@@ -226,7 +168,7 @@ sc_process_close(HANDLE handle) {
} }
ssize_t ssize_t
sc_pipe_read(HANDLE pipe, char *data, size_t len) { sc_read_pipe(HANDLE pipe, char *data, size_t len) {
DWORD r; DWORD r;
if (!ReadFile(pipe, data, len, &r, NULL)) { if (!ReadFile(pipe, data, len, &r, NULL)) {
return -1; return -1;
@@ -235,7 +177,7 @@ sc_pipe_read(HANDLE pipe, char *data, size_t len) {
} }
void void
sc_pipe_close(HANDLE pipe) { sc_close_pipe(HANDLE pipe) {
if (!CloseHandle(pipe)) { if (!CloseHandle(pipe)) {
LOGW("Cannot close pipe"); LOGW("Cannot close pipe");
} }

View File

@@ -2,7 +2,7 @@
bool bool
sc_process_check_success_intr(struct sc_intr *intr, sc_pid pid, sc_process_check_success_intr(struct sc_intr *intr, sc_pid pid,
const char *name, bool close) { const char *name) {
if (!sc_intr_set_process(intr, pid)) { if (!sc_intr_set_process(intr, pid)) {
// Already interrupted // Already interrupted
return false; return false;
@@ -11,40 +11,6 @@ sc_process_check_success_intr(struct sc_intr *intr, sc_pid pid,
// Always pass close=false, interrupting would be racy otherwise // Always pass close=false, interrupting would be racy otherwise
bool ret = sc_process_check_success(pid, name, false); bool ret = sc_process_check_success(pid, name, false);
sc_intr_set_process(intr, SC_PROCESS_NONE);
if (close) {
// Close separately
sc_process_close(pid);
}
return ret;
}
ssize_t
sc_pipe_read_intr(struct sc_intr *intr, sc_pid pid, sc_pipe pipe, char *data,
size_t len) {
if (!sc_intr_set_process(intr, pid)) {
// Already interrupted
return false;
}
ssize_t ret = sc_pipe_read(pipe, data, len);
sc_intr_set_process(intr, SC_PROCESS_NONE);
return ret;
}
ssize_t
sc_pipe_read_all_intr(struct sc_intr *intr, sc_pid pid, sc_pipe pipe,
char *data, size_t len) {
if (!sc_intr_set_process(intr, pid)) {
// Already interrupted
return false;
}
ssize_t ret = sc_pipe_read_all(pipe, data, len);
sc_intr_set_process(intr, SC_PROCESS_NONE); sc_intr_set_process(intr, SC_PROCESS_NONE);
return ret; return ret;
} }

View File

@@ -8,14 +8,6 @@
bool bool
sc_process_check_success_intr(struct sc_intr *intr, sc_pid pid, sc_process_check_success_intr(struct sc_intr *intr, sc_pid pid,
const char *name, bool close); const char *name);
ssize_t
sc_pipe_read_intr(struct sc_intr *intr, sc_pid pid, sc_pipe pipe, char *data,
size_t len);
ssize_t
sc_pipe_read_all_intr(struct sc_intr *intr, sc_pid pid, sc_pipe pipe,
char *data, size_t len);
#endif #endif

View File

@@ -291,11 +291,3 @@ error:
free(buf.s); free(buf.s);
return NULL; return NULL;
} }
size_t
sc_str_truncate(char *data, size_t len, const char *endchars) {
data[len - 1] = '\0';
size_t idx = strcspn(data, endchars);
data[idx] = '\0';
return idx;
}

View File

@@ -103,15 +103,4 @@ sc_str_from_wchars(const wchar_t *s);
char * char *
sc_str_wrap_lines(const char *input, unsigned columns, unsigned indent); sc_str_wrap_lines(const char *input, unsigned columns, unsigned indent);
/**
* Truncate the data after any of the characters from `endchars`
*
* An '\0' is always written at the end of the data, even if no newline
* character is encountered.
*
* Return the size of the resulting line.
*/
size_t
sc_str_truncate(char *data, size_t len, const char *endchars);
#endif #endif

View File

@@ -337,32 +337,6 @@ static void test_wrap_lines(void) {
free(formatted); free(formatted);
} }
static void test_truncate(void) {
char s[] = "hello\nworld\n!";
size_t len = sc_str_truncate(s, sizeof(s), "\n");
assert(len == 5);
assert(!strcmp("hello", s));
char s2[] = "hello\r\nworkd\r\n!";
len = sc_str_truncate(s2, sizeof(s2), "\n\r");
assert(len == 5);
assert(!strcmp("hello", s));
char s3[] = "hello world\n!";
len = sc_str_truncate(s3, sizeof(s3), " \n\r");
assert(len == 5);
assert(!strcmp("hello", s3));
char s4[] = "hello ";
len = sc_str_truncate(s4, sizeof(s4), " \n\r");
assert(len == 5);
assert(!strcmp("hello", s4));
}
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
(void) argc; (void) argc;
(void) argv; (void) argv;
@@ -382,6 +356,5 @@ int main(int argc, char *argv[]) {
test_parse_integer_with_suffix(); test_parse_integer_with_suffix();
test_strlist_contains(); test_strlist_contains();
test_wrap_lines(); test_wrap_lines();
test_truncate();
return 0; return 0;
} }

View File

@@ -1,5 +1,6 @@
package com.genymobile.scrcpy; package com.genymobile.scrcpy;
import com.genymobile.scrcpy.wrappers.ContentProvider;
import com.genymobile.scrcpy.wrappers.ServiceManager; import com.genymobile.scrcpy.wrappers.ServiceManager;
import android.os.Parcel; import android.os.Parcel;
@@ -165,21 +166,14 @@ public final class CleanUp {
if (config.disableShowTouches || config.restoreStayOn != -1) { if (config.disableShowTouches || config.restoreStayOn != -1) {
ServiceManager serviceManager = new ServiceManager(); ServiceManager serviceManager = new ServiceManager();
Settings settings = new Settings(serviceManager); try (ContentProvider settings = serviceManager.getActivityManager().createSettingsProvider()) {
if (config.disableShowTouches) { if (config.disableShowTouches) {
Ln.i("Disabling \"show touches\""); Ln.i("Disabling \"show touches\"");
try { settings.putValue(ContentProvider.TABLE_SYSTEM, "show_touches", "0");
settings.putValue(Settings.TABLE_SYSTEM, "show_touches", "0");
} catch (SettingsException e) {
Ln.e("Could not restore \"show_touches\"", e);
}
} }
if (config.restoreStayOn != -1) { if (config.restoreStayOn != -1) {
Ln.i("Restoring \"stay awake\""); Ln.i("Restoring \"stay awake\"");
try { settings.putValue(ContentProvider.TABLE_GLOBAL, "stay_on_while_plugged_in", String.valueOf(config.restoreStayOn));
settings.putValue(Settings.TABLE_GLOBAL, "stay_on_while_plugged_in", String.valueOf(config.restoreStayOn));
} catch (SettingsException e) {
Ln.e("Could not restore \"stay_on_while_plugged_in\"", e);
} }
} }
} }

View File

@@ -1,33 +0,0 @@
package com.genymobile.scrcpy;
import java.io.IOException;
import java.util.Arrays;
import java.util.Scanner;
public final class Command {
private Command() {
// not instantiable
}
public static void exec(String... cmd) throws IOException, InterruptedException {
Process process = Runtime.getRuntime().exec(cmd);
int exitCode = process.waitFor();
if (exitCode != 0) {
throw new IOException("Command " + Arrays.toString(cmd) + " returned with value " + exitCode);
}
}
public static String execReadLine(String... cmd) throws IOException, InterruptedException {
String result = null;
Process process = Runtime.getRuntime().exec(cmd);
Scanner scanner = new Scanner(process.getInputStream());
if (scanner.hasNextLine()) {
result = scanner.nextLine();
}
int exitCode = process.waitFor();
if (exitCode != 0) {
throw new IOException("Command " + Arrays.toString(cmd) + " returned with value " + exitCode);
}
return result;
}
}

View File

@@ -1,6 +1,7 @@
package com.genymobile.scrcpy; package com.genymobile.scrcpy;
import com.genymobile.scrcpy.wrappers.ClipboardManager; import com.genymobile.scrcpy.wrappers.ClipboardManager;
import com.genymobile.scrcpy.wrappers.ContentProvider;
import com.genymobile.scrcpy.wrappers.InputManager; import com.genymobile.scrcpy.wrappers.InputManager;
import com.genymobile.scrcpy.wrappers.ServiceManager; import com.genymobile.scrcpy.wrappers.ServiceManager;
import com.genymobile.scrcpy.wrappers.SurfaceControl; import com.genymobile.scrcpy.wrappers.SurfaceControl;
@@ -28,7 +29,6 @@ public final class Device {
public static final int LOCK_VIDEO_ORIENTATION_INITIAL = -2; public static final int LOCK_VIDEO_ORIENTATION_INITIAL = -2;
private static final ServiceManager SERVICE_MANAGER = new ServiceManager(); private static final ServiceManager SERVICE_MANAGER = new ServiceManager();
private static final Settings SETTINGS = new Settings(SERVICE_MANAGER);
public interface RotationListener { public interface RotationListener {
void onRotationChanged(int rotation); void onRotationChanged(int rotation);
@@ -296,7 +296,7 @@ public final class Device {
} }
} }
public static Settings getSettings() { public static ContentProvider createSettingsProvider() {
return SETTINGS; return SERVICE_MANAGER.getActivityManager().createSettingsProvider();
} }
} }

View File

@@ -57,18 +57,11 @@ public final class Ln {
} }
} }
public static void w(String message, Throwable throwable) {
if (isEnabled(Level.WARN)) {
Log.w(TAG, message, throwable);
System.out.println(PREFIX + "WARN: " + message);
if (throwable != null) {
throwable.printStackTrace();
}
}
}
public static void w(String message) { public static void w(String message) {
w(message, null); if (isEnabled(Level.WARN)) {
Log.w(TAG, message);
System.out.println(PREFIX + "WARN: " + message);
}
} }
public static void e(String message, Throwable throwable) { public static void e(String message, Throwable throwable) {

View File

@@ -1,5 +1,7 @@
package com.genymobile.scrcpy; package com.genymobile.scrcpy;
import com.genymobile.scrcpy.wrappers.ContentProvider;
import android.graphics.Rect; import android.graphics.Rect;
import android.media.MediaCodec; import android.media.MediaCodec;
import android.media.MediaCodecInfo; import android.media.MediaCodecInfo;
@@ -17,25 +19,24 @@ public final class Server {
// not instantiable // not instantiable
} }
private static void initAndCleanUp(Options options) { private static void scrcpy(Options options) throws IOException {
Ln.i("Device: " + Build.MANUFACTURER + " " + Build.MODEL + " (Android " + Build.VERSION.RELEASE + ")");
final Device device = new Device(options);
List<CodecOption> codecOptions = CodecOption.parse(options.getCodecOptions());
boolean mustDisableShowTouchesOnCleanUp = false; boolean mustDisableShowTouchesOnCleanUp = false;
int restoreStayOn = -1; int restoreStayOn = -1;
if (options.getShowTouches() || options.getStayAwake()) { if (options.getShowTouches() || options.getStayAwake()) {
Settings settings = Device.getSettings(); try (ContentProvider settings = Device.createSettingsProvider()) {
if (options.getShowTouches()) { if (options.getShowTouches()) {
try { String oldValue = settings.getAndPutValue(ContentProvider.TABLE_SYSTEM, "show_touches", "1");
String oldValue = settings.getAndPutValue(Settings.TABLE_SYSTEM, "show_touches", "1");
// If "show touches" was disabled, it must be disabled back on clean up // If "show touches" was disabled, it must be disabled back on clean up
mustDisableShowTouchesOnCleanUp = !"1".equals(oldValue); mustDisableShowTouchesOnCleanUp = !"1".equals(oldValue);
} catch (SettingsException e) {
Ln.e("Could not change \"show_touches\"", e);
}
} }
if (options.getStayAwake()) { if (options.getStayAwake()) {
int stayOn = BatteryManager.BATTERY_PLUGGED_AC | BatteryManager.BATTERY_PLUGGED_USB | BatteryManager.BATTERY_PLUGGED_WIRELESS; int stayOn = BatteryManager.BATTERY_PLUGGED_AC | BatteryManager.BATTERY_PLUGGED_USB | BatteryManager.BATTERY_PLUGGED_WIRELESS;
try { String oldValue = settings.getAndPutValue(ContentProvider.TABLE_GLOBAL, "stay_on_while_plugged_in", String.valueOf(stayOn));
String oldValue = settings.getAndPutValue(Settings.TABLE_GLOBAL, "stay_on_while_plugged_in", String.valueOf(stayOn));
try { try {
restoreStayOn = Integer.parseInt(oldValue); restoreStayOn = Integer.parseInt(oldValue);
if (restoreStayOn == stayOn) { if (restoreStayOn == stayOn) {
@@ -45,25 +46,11 @@ public final class Server {
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
restoreStayOn = 0; restoreStayOn = 0;
} }
} catch (SettingsException e) {
Ln.e("Could not change \"stay_on_while_plugged_in\"", e);
} }
} }
} }
try {
CleanUp.configure(options.getDisplayId(), restoreStayOn, mustDisableShowTouchesOnCleanUp, true, options.getPowerOffScreenOnClose()); CleanUp.configure(options.getDisplayId(), restoreStayOn, mustDisableShowTouchesOnCleanUp, true, options.getPowerOffScreenOnClose());
} catch (IOException e) {
Ln.e("Could not configure cleanup", e);
}
}
private static void scrcpy(Options options) throws IOException {
Ln.i("Device: " + Build.MANUFACTURER + " " + Build.MODEL + " (Android " + Build.VERSION.RELEASE + ")");
final Device device = new Device(options);
List<CodecOption> codecOptions = CodecOption.parse(options.getCodecOptions());
Thread initThread = startInitThread(options);
boolean tunnelForward = options.isTunnelForward(); boolean tunnelForward = options.isTunnelForward();
@@ -95,7 +82,6 @@ public final class Server {
// this is expected on close // this is expected on close
Ln.d("Screen streaming stopped"); Ln.d("Screen streaming stopped");
} finally { } finally {
initThread.interrupt();
if (controllerThread != null) { if (controllerThread != null) {
controllerThread.interrupt(); controllerThread.interrupt();
} }
@@ -106,17 +92,6 @@ public final class Server {
} }
} }
private static Thread startInitThread(final Options options) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
initAndCleanUp(options);
}
});
thread.start();
return thread;
}
private static Thread startController(final Controller controller) { private static Thread startController(final Controller controller) {
Thread thread = new Thread(new Runnable() { Thread thread = new Thread(new Runnable() {
@Override @Override

View File

@@ -1,84 +0,0 @@
package com.genymobile.scrcpy;
import com.genymobile.scrcpy.wrappers.ContentProvider;
import com.genymobile.scrcpy.wrappers.ServiceManager;
import android.os.Build;
import java.io.IOException;
public class Settings {
public static final String TABLE_SYSTEM = ContentProvider.TABLE_SYSTEM;
public static final String TABLE_SECURE = ContentProvider.TABLE_SECURE;
public static final String TABLE_GLOBAL = ContentProvider.TABLE_GLOBAL;
private final ServiceManager serviceManager;
public Settings(ServiceManager serviceManager) {
this.serviceManager = serviceManager;
}
private static void execSettingsPut(String table, String key, String value) throws SettingsException {
try {
Command.exec("settings", "put", table, key, value);
} catch (IOException | InterruptedException e) {
throw new SettingsException("put", table, key, value, e);
}
}
private static String execSettingsGet(String table, String key) throws SettingsException {
try {
return Command.execReadLine("settings", "get", table, key);
} catch (IOException | InterruptedException e) {
throw new SettingsException("get", table, key, null, e);
}
}
public String getValue(String table, String key) throws SettingsException {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
// on Android >= 12, it always fails: <https://github.com/Genymobile/scrcpy/issues/2788>
try (ContentProvider provider = serviceManager.getActivityManager().createSettingsProvider()) {
return provider.getValue(table, key);
} catch (SettingsException e) {
Ln.w("Could not get settings value via ContentProvider, fallback to settings process", e);
}
}
return execSettingsGet(table, key);
}
public void putValue(String table, String key, String value) throws SettingsException {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
// on Android >= 12, it always fails: <https://github.com/Genymobile/scrcpy/issues/2788>
try (ContentProvider provider = serviceManager.getActivityManager().createSettingsProvider()) {
provider.putValue(table, key, value);
} catch (SettingsException e) {
Ln.w("Could not put settings value via ContentProvider, fallback to settings process", e);
}
}
execSettingsPut(table, key, value);
}
public String getAndPutValue(String table, String key, String value) throws SettingsException {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
// on Android >= 12, it always fails: <https://github.com/Genymobile/scrcpy/issues/2788>
try (ContentProvider provider = serviceManager.getActivityManager().createSettingsProvider()) {
String oldValue = provider.getValue(table, key);
if (!value.equals(oldValue)) {
provider.putValue(table, key, value);
}
return oldValue;
} catch (SettingsException e) {
Ln.w("Could not get and put settings value via ContentProvider, fallback to settings process", e);
}
}
String oldValue = getValue(table, key);
if (!value.equals(oldValue)) {
putValue(table, key, value);
}
return oldValue;
}
}

View File

@@ -1,11 +0,0 @@
package com.genymobile.scrcpy;
public class SettingsException extends Exception {
private static String createMessage(String method, String table, String key, String value) {
return "Could not access settings: " + method + " " + table + " " + key + (value != null ? " " + value : "");
}
public SettingsException(String method, String table, String key, String value, Throwable cause) {
super(createMessage(method, table, key, value), cause);
}
}

View File

@@ -1,7 +1,6 @@
package com.genymobile.scrcpy.wrappers; package com.genymobile.scrcpy.wrappers;
import com.genymobile.scrcpy.Ln; import com.genymobile.scrcpy.Ln;
import com.genymobile.scrcpy.SettingsException;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.os.Bundle; import android.os.Bundle;
@@ -88,8 +87,7 @@ public class ContentProvider implements Closeable {
return attributionSource; return attributionSource;
} }
private Bundle call(String callMethod, String arg, Bundle extras) private Bundle call(String callMethod, String arg, Bundle extras) {
throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
try { try {
Method method = getCallMethod(); Method method = getCallMethod();
Object[] args; Object[] args;
@@ -110,7 +108,7 @@ public class ContentProvider implements Closeable {
return (Bundle) method.invoke(provider, args); return (Bundle) method.invoke(provider, args);
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException | ClassNotFoundException | InstantiationException e) { } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException | ClassNotFoundException | InstantiationException e) {
Ln.e("Could not invoke method", e); Ln.e("Could not invoke method", e);
throw e; return null;
} }
} }
@@ -144,31 +142,30 @@ public class ContentProvider implements Closeable {
} }
} }
public String getValue(String table, String key) throws SettingsException { public String getValue(String table, String key) {
String method = getGetMethod(table); String method = getGetMethod(table);
Bundle arg = new Bundle(); Bundle arg = new Bundle();
arg.putInt(CALL_METHOD_USER_KEY, ServiceManager.USER_ID); arg.putInt(CALL_METHOD_USER_KEY, ServiceManager.USER_ID);
try {
Bundle bundle = call(method, key, arg); Bundle bundle = call(method, key, arg);
if (bundle == null) { if (bundle == null) {
return null; return null;
} }
return bundle.getString("value"); return bundle.getString("value");
} catch (Exception e) {
throw new SettingsException(table, "get", key, null, e);
} }
} public void putValue(String table, String key, String value) {
public void putValue(String table, String key, String value) throws SettingsException {
String method = getPutMethod(table); String method = getPutMethod(table);
Bundle arg = new Bundle(); Bundle arg = new Bundle();
arg.putInt(CALL_METHOD_USER_KEY, ServiceManager.USER_ID); arg.putInt(CALL_METHOD_USER_KEY, ServiceManager.USER_ID);
arg.putString(NAME_VALUE_TABLE_VALUE, value); arg.putString(NAME_VALUE_TABLE_VALUE, value);
try {
call(method, key, arg); call(method, key, arg);
} catch (Exception e) { }
throw new SettingsException(table, "put", key, value, e);
} public String getAndPutValue(String table, String key, String value) {
String oldValue = getValue(table, key);
if (!value.equals(oldValue)) {
putValue(table, key, value);
}
return oldValue;
} }
} }