Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Draft] Add option to install the server as an APK #3517

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ conf.set_quoted('SCRCPY_VERSION', meson.project_version())
# the prefix used during configuration (meson --prefix=PREFIX)
conf.set_quoted('PREFIX', get_option('prefix'))

# build a "portable" version (with scrcpy-server accessible from the same
# build a "portable" version (with scrcpy-server.apk accessible from the same
# directory as the executable)
conf.set('PORTABLE', get_option('portable'))

Expand Down
8 changes: 8 additions & 0 deletions app/scrcpy.1
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ However, the option is only available when the HID keyboard is enabled (or a phy

Also see \fB\-\-hid\-mouse\fR.

.TP
.B \-\-install
Install the server (via "adb install") rather than pushing it to /data/local/tmp (via "adb push").

.TP
.B \-\-legacy\-paste
Inject computer clipboard text as a sequence of key events on Ctrl+v (like MOD+Shift+v).
Expand Down Expand Up @@ -242,6 +246,10 @@ option if set, or by the file extension (.mp4 or .mkv).
.BI "\-\-record\-format " format
Force recording format (either mp4 or mkv).

.TP
.B \-\-reinstall
Reinstall the server (via "adb install"), even if the correct version is already installed. Implies \fB\-\-install\fR.

.TP
.BI "\-\-render\-driver " name
Request SDL to use the given render driver (this is just a hint).
Expand Down
108 changes: 108 additions & 0 deletions app/src/adb/adb.c
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,17 @@ sc_adb_install(struct sc_intr *intr, const char *serial, const char *local,
return process_check_success_intr(intr, pid, "adb install", flags);
}

bool
sc_adb_uninstall(struct sc_intr *intr, const char *serial, const char *pkg,
unsigned flags) {
assert(serial);
const char *const argv[] =
SC_ADB_COMMAND("-s", serial, "uninstall", pkg);

sc_pid pid = sc_adb_execute(argv, flags);
return process_check_success_intr(intr, pid, "adb uninstall", flags);
}

bool
sc_adb_tcpip(struct sc_intr *intr, const char *serial, uint16_t port,
unsigned flags) {
Expand Down Expand Up @@ -435,6 +446,7 @@ sc_adb_list_devices(struct sc_intr *intr, unsigned flags,
"Please report an issue.");
return false;
}
#undef BUFSIZE

// It is parsed as a NUL-terminated string
buf[r] = '\0';
Expand Down Expand Up @@ -713,3 +725,99 @@ sc_adb_get_device_ip(struct sc_intr *intr, const char *serial, unsigned flags) {

return sc_adb_parse_device_ip(buf);
}

char *
sc_adb_get_installed_apk_path(struct sc_intr *intr, const char *serial,
unsigned flags) {
assert(serial);
const char *const argv[] =
SC_ADB_COMMAND("-s", serial, "shell", "pm", "list", "package", "-f",
SC_ANDROID_PACKAGE);

sc_pipe pout;
sc_pid pid = sc_adb_execute_p(argv, flags, &pout);
if (pid == SC_PROCESS_NONE) {
LOGD("Could not execute \"pm list packages\"");
return NULL;
}

// "pm list packages -f <package>" output should contain only one line, so
// the output should be short
char buf[1024];
ssize_t r = sc_pipe_read_all_intr(intr, pid, pout, buf, sizeof(buf) - 1);
sc_pipe_close(pout);

bool ok = process_check_success_intr(intr, pid, "pm list packages", flags);
if (!ok) {
return NULL;
}

if (r == -1) {
return NULL;
}

assert((size_t) r < sizeof(buf));
if (r == sizeof(buf) - 1) {
// The implementation assumes that the output of "pm list packages"
// fits in the buffer in a single pass
LOGW("Result of \"pm list package\" does not fit in 1Kb. "
"Please report an issue.");
return NULL;
}

// It is parsed as a NUL-terminated string
buf[r] = '\0';

return sc_adb_parse_installed_apk_path(buf);
}

char *
sc_adb_get_installed_apk_version(struct sc_intr *intr, const char *serial,
unsigned flags) {
assert(serial);
const char *const argv[] =
SC_ADB_COMMAND("-s", serial, "shell", "dumpsys", "package",
SC_ANDROID_PACKAGE);

sc_pipe pout;
sc_pid pid = sc_adb_execute_p(argv, flags, &pout);
if (pid == SC_PROCESS_NONE) {
LOGD("Could not execute \"dumpsys package\"");
return NULL;
}

// "dumpsys package" output can be huge (e.g. 16k), but versionName is at
// the beginning, typically in the first 1024 bytes (64k should be enough
// for the whole output anyway)
#define BUFSIZE 65536
char *buf = malloc(BUFSIZE);
if (!buf) {
return false;
}
ssize_t r = sc_pipe_read_all_intr(intr, pid, pout, buf, BUFSIZE - 1);
sc_pipe_close(pout);

bool ok = process_check_success_intr(intr, pid, "dumpsys package", flags);
if (!ok) {
free(buf);
return NULL;
}

if (r == -1) {
free(buf);
return NULL;
}

assert((size_t) r < BUFSIZE);
#undef BUFSIZE
// if r == sizeof(buf), then the output is truncated, but we don't care,
// versionName is at the beginning in practice, and is unlikely to be
// truncated at 64k

// It is parsed as a NUL-terminated string
buf[r] = '\0';

char *version = sc_adb_parse_installed_apk_version(buf);
free(buf);
return version;
}
20 changes: 20 additions & 0 deletions app/src/adb/adb.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

#define SC_ADB_SILENT (SC_ADB_NO_STDOUT | SC_ADB_NO_STDERR | SC_ADB_NO_LOGERR)

#define SC_ANDROID_PACKAGE "com.genymobile.scrcpy"

const char *
sc_adb_get_executable(void);

Expand Down Expand Up @@ -64,6 +66,10 @@ bool
sc_adb_install(struct sc_intr *intr, const char *serial, const char *local,
unsigned flags);

bool
sc_adb_uninstall(struct sc_intr *intr, const char *serial, const char *pkg,
unsigned flags);

/**
* Execute `adb tcpip <port>`
*/
Expand Down Expand Up @@ -114,4 +120,18 @@ sc_adb_getprop(struct sc_intr *intr, const char *serial, const char *prop,
char *
sc_adb_get_device_ip(struct sc_intr *intr, const char *serial, unsigned flags);

/**
* Return the path of the installed APK for com.genymobile.scrcpy (if any)
*/
char *
sc_adb_get_installed_apk_path(struct sc_intr *intr, const char *serial,
unsigned flags);

/**
* Return the version of the installed APK for com.genymobile.scrcpy (if any)
*/
char *
sc_adb_get_installed_apk_version(struct sc_intr *intr, const char *serial,
unsigned flags);

#endif
59 changes: 59 additions & 0 deletions app/src/adb/adb_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -225,3 +225,62 @@ sc_adb_parse_device_ip(char *str) {

return NULL;
}

char *
sc_adb_parse_installed_apk_path(char *str) {
// str is expected to look like:
// "package:/data/app/.../base.apk=com.genymobile.scrcpy"
// ^^^^^^^^^^^^^^^^^^^^^^
// We want to extract the path (which may contain '=', even in practice)

if (strncmp(str, "package:", 8)) {
// Does not start with "package:"
return NULL;
}

char *s = str + 8;
size_t len = strcspn(s, " \r\n");
s[len] = '\0';

char *p = strrchr(s, '=');
if (!p) {
// No '=' found
return NULL;
}

// Truncate at the last '='
*p = '\0';

return strdup(s);
}

char *
sc_adb_parse_installed_apk_version(const char *str) {
// str is the (beginning of the) output of `dumpsys package`
// We want to extract the version string from a line starting with 4 spaces
// then `versionName=` then the version string.

#define VERSION_NAME_PREFIX "\n versionName="
char *s = strstr(str, VERSION_NAME_PREFIX);
if (!s) {
// Not found
return NULL;
}

s+= sizeof(VERSION_NAME_PREFIX) - 1;

size_t len = strspn(s, "0123456789.");
if (!len) {
LOGW("Unexpected version name with no value");
return NULL;
}

char *version = malloc(len + 1);
if (!version) {
return NULL;
}

memcpy(version, s, len);
version[len] = '\0';
return version;
}
20 changes: 20 additions & 0 deletions app/src/adb/adb_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,24 @@ sc_adb_parse_devices(char *str, struct sc_vec_adb_devices *out_vec);
char *
sc_adb_parse_device_ip(char *str);

/**
* Parse the package path from the output of
* `adb shell pm list packages -f <package>`
*
* The parameter must be a NUL-terminated string.
*
* Warning: this function modifies the buffer for optimization purposes.
*/
char *
sc_adb_parse_installed_apk_path(char *str);

/**
* Parse the package version from the output of
* `adb shell dumpsys package <package>`
*
* The parameter must be a NUL-terminated string.
*/
char *
sc_adb_parse_installed_apk_version(const char *str);

#endif
22 changes: 22 additions & 0 deletions app/src/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
#define OPT_NO_CLEANUP 1037
#define OPT_PRINT_FPS 1038
#define OPT_NO_POWER_ON 1039
#define OPT_INSTALL 1040
#define OPT_REINSTALL 1041

struct sc_option {
char shortopt;
Expand Down Expand Up @@ -207,6 +209,12 @@ static const struct sc_option options[] = {
.longopt = "help",
.text = "Print this help.",
},
{
.longopt_id = OPT_INSTALL,
.longopt = "install",
.text = "Install the server (via 'adb install') rather than pushing "
"it to /data/local/tmp (via 'adb push').",
},
{
.longopt_id = OPT_LEGACY_PASTE,
.longopt = "legacy-paste",
Expand Down Expand Up @@ -378,6 +386,13 @@ static const struct sc_option options[] = {
.argdesc = "format",
.text = "Force recording format (either mp4 or mkv).",
},
{
.longopt_id = OPT_REINSTALL,
.longopt = "reinstall",
.text = "Reinstall the server (via 'adb install'), even if the correct "
"version is already installed.\n"
"Implies --install.",
},
{
.longopt_id = OPT_RENDER_DRIVER,
.longopt = "render-driver",
Expand Down Expand Up @@ -1610,6 +1625,13 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
case OPT_PRINT_FPS:
opts->start_fps_counter = true;
break;
case OPT_INSTALL:
opts->install = true;
break;
case OPT_REINSTALL:
opts->install = true;
opts->reinstall = true;
break;
case OPT_OTG:
#ifdef HAVE_USB
opts->otg = true;
Expand Down
2 changes: 2 additions & 0 deletions app/src/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,6 @@ const struct scrcpy_options scrcpy_options_default = {
.cleanup = true,
.start_fps_counter = false,
.power_on = true,
.install = false,
.reinstall = false,
};
2 changes: 2 additions & 0 deletions app/src/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ struct scrcpy_options {
bool cleanup;
bool start_fps_counter;
bool power_on;
bool install;
bool reinstall;
};

extern const struct scrcpy_options scrcpy_options_default;
Expand Down
2 changes: 2 additions & 0 deletions app/src/scrcpy.c
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,8 @@ scrcpy(struct scrcpy_options *options) {
.tcpip_dst = options->tcpip_dst,
.cleanup = options->cleanup,
.power_on = options->power_on,
.install = options->install,
.reinstall = options->reinstall,
};

static const struct sc_server_callbacks cbs = {
Expand Down
Loading