Compare commits

..

7 Commits

Author SHA1 Message Date
Romain Vimont
ee93d2aac1 Configure init and cleanup asynchronously
Accessing the settings (like --show-touches) on start should not delay
screen mirroring.

PR #2802 <https://github.com/Genymobile/scrcpy/pull/2802>
2021-11-18 08:37:32 +01:00
Romain Vimont
c29a0bf675 Do not quit on cleanup configuration failure
Cleanup is used for some options like --show-touches to restore the
state on exit.

If the configuration fails, do not crash the whole process. Just log an
error.

PR #2802 <https://github.com/Genymobile/scrcpy/pull/2802>
2021-11-18 08:37:28 +01:00
Romain Vimont
411bb0d18e Move init and cleanup to a separate method
PR #2802 <https://github.com/Genymobile/scrcpy/pull/2802>
2021-11-18 08:37:26 +01:00
Romain Vimont
cc0902b13c Read/write settings via command on Android >= 12
Before Android 8, executing the "settings" command from a shell was
very slow (~1 second), because it spawned a new app_process to execute
Java code. Therefore, to access settings without performance issues,
scrcpy used private APIs to read from and write to settings.

However, since Android 12, this is not possible anymore, due to
permissions changes.

To make it work again, execute the "settings" command on Android 12 (or
on previous version if the other method failed). This method is faster
than before Android 8 (~100ms).

Fixes #2671 <https://github.com/Genymobile/scrcpy/issues/2671>
Fixes #2788 <https://github.com/Genymobile/scrcpy/issues/2788>
PR #2802 <https://github.com/Genymobile/scrcpy/pull/2802>
2021-11-18 08:37:22 +01:00
Romain Vimont
48b572c272 Add throwable parameter to Log.w()
When an exception occurs, we might want to log a warning instead of an
error.

PR #2802 <https://github.com/Genymobile/scrcpy/pull/2802>
2021-11-18 08:37:18 +01:00
Romain Vimont
94feae71f2 Report settings errors via Exceptions
Settings read/write errors were silently ignored. Report them via a
SettingsException so that the caller can handle them.

This allows to log a proper error message, and will also allow to
fallback to a different settings method in case of failure.

PR #2802 <https://github.com/Genymobile/scrcpy/pull/2802>
2021-11-18 08:37:02 +01:00
Romain Vimont
67170437f1 Wrap settings management into a Settings class
Until now, the code that needed to read/write the Android settings had
to explicitly open and close a ContentProvider.

Wrap these details into a Settings class.

This paves the way to provide an alternative implementation of settings
read/write for Android >= 12.

PR #2802 <https://github.com/Genymobile/scrcpy/pull/2802>
2021-11-18 08:36:48 +01:00
9 changed files with 64 additions and 142 deletions

View File

@@ -111,7 +111,7 @@ show_adb_err_msg(enum sc_process_result err, const char *const argv[]) {
sc_pid
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;
sc_pid pid;
@@ -132,7 +132,7 @@ adb_execute_p(const char *serial, const char *const adb_cmd[],
memcpy(&argv[i], adb_cmd, len * sizeof(const char *));
argv[len + i] = NULL;
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) {
show_adb_err_msg(r, argv);
pid = SC_PROCESS_NONE;
@@ -144,7 +144,7 @@ adb_execute_p(const char *serial, const char *const adb_cmd[],
sc_pid
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);
}
sc_pid
@@ -233,8 +233,45 @@ adb_install(const char *serial, const char *local) {
return pid;
}
sc_pid
adb_get_serialno(sc_pipe *pout) {
const char *const adb_cmd[] = {"get-serialno"};
return adb_execute_p(NULL, adb_cmd, ARRAY_LEN(adb_cmd), pout);
static ssize_t
adb_execute_for_output(const char *serial, const char *const adb_cmd[],
size_t adb_cmd_len, char *buf, size_t buf_len,
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;
}
return r;
}
static size_t
truncate_first_line(char *data, size_t len) {
data[len - 1] = '\0';
char *eol = strpbrk(data, "\r\n");
if (eol) {
*eol = '\0';
len = eol - data;
}
return len;
}
char *
adb_get_serialno(void) {
char buf[128];
const char *const adb_cmd[] = {"get-serialno"};
ssize_t r = adb_execute_for_output(NULL, adb_cmd, ARRAY_LEN(adb_cmd),
buf, sizeof(buf), "get-serialno");
if (r <= 0) {
return NULL;
}
truncate_first_line(buf, r);
return strdup(buf);
}

View File

@@ -12,8 +12,8 @@ sc_pid
adb_execute(const char *serial, const char *const adb_cmd[], size_t len);
sc_pid
adb_execute_p(const char *serial, const char *const adb_cmd[], size_t len,
sc_pipe *pout);
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,
@@ -35,8 +35,8 @@ adb_push(const char *serial, const char *local, const char *remote);
sc_pid
adb_install(const char *serial, const char *local);
// Execute "adb get-serialno" and give a pipe to get the result
sc_pid
adb_get_serialno(sc_pipe *pout);
// Return the result of "adb get-serialno".
char *
adb_get_serialno(void);
#endif

View File

@@ -394,11 +394,8 @@ scrcpy(struct scrcpy_options *options) {
// It is necessarily initialized here, since the device is connected
struct sc_server_info *info = &s->server.info;
const char *serial = s->server.params.serial;
assert(serial);
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)) {
goto end;
}
@@ -519,7 +516,21 @@ scrcpy(struct scrcpy_options *options) {
#ifdef HAVE_AOA_HID
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);
free(serialno);
if (!ok) {
goto aoa_hid_end;
}

View File

@@ -422,59 +422,12 @@ sc_server_on_terminated(void *userdata) {
LOGD("Server terminated");
}
static char *
sc_server_get_serialno(struct sc_intr *intr) {
sc_pipe pout;
sc_pid pid = adb_get_serialno(&pout);
if (pid == SC_PROCESS_NONE) {
return false;
}
char buf[128];
ssize_t r = sc_pipe_read_all_intr(intr, pid, pout, buf, sizeof(buf));
sc_pipe_close(pout);
bool ok = sc_process_check_success_intr(intr, pid, "adb get-serialno");
sc_process_close(pid);
if (!ok) {
return NULL;
}
sc_str_truncate(buf, r, " \r\n");
return strdup(buf);
}
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 = sc_server_get_serialno(&server->intr);
if (!server->params.serial) {
LOGE("Could not get device serial");
return false;
}
}
return true;
}
static int
run_server(void *data) {
struct sc_server *server = data;
if (!sc_server_fill_serial(server)) {
goto error_connection_failed;
}
const struct sc_server_params *params = &server->params;
LOGD("Device serial: %s", params->serial);
bool ok = push_server(&server->intr, params->serial);
if (!ok) {
goto error_connection_failed;

View File

@@ -14,31 +14,3 @@ sc_process_check_success_intr(struct sc_intr *intr, sc_pid pid,
sc_intr_set_process(intr, SC_PROCESS_NONE);
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);
return ret;
}

View File

@@ -10,12 +10,4 @@ bool
sc_process_check_success_intr(struct sc_intr *intr, sc_pid pid,
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

View File

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

View File

@@ -103,15 +103,4 @@ sc_str_from_wchars(const wchar_t *s);
char *
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

View File

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