Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
db85211653 | |||
c0e20c86d6 |
@ -434,6 +434,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';
|
||||
@ -712,3 +713,95 @@ 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) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (r == -1) {
|
||||
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';
|
||||
|
||||
return sc_adb_parse_installed_apk_version(buf);
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
@ -114,4 +116,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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -160,6 +160,8 @@ execute_server(struct sc_server *server,
|
||||
const char *serial = server->serial;
|
||||
assert(serial);
|
||||
|
||||
fprintf(stderr, "=== [%s]\n", sc_adb_get_installed_apk_version(&server->intr, serial, 0));
|
||||
|
||||
const char *cmd[128];
|
||||
unsigned count = 0;
|
||||
cmd[count++] = sc_adb_get_executable();
|
||||
|
@ -241,6 +241,54 @@ static void test_get_ip_truncated(void) {
|
||||
assert(!ip);
|
||||
}
|
||||
|
||||
static void test_apk_path(void) {
|
||||
char str[] = "package:/data/app/~~71mguyc6p-kNjQdNaNkToA==/com.genymobile."
|
||||
"scrcpy-l6fiqqUSU7Ok7QLg-rIyJA==/base.apk=com.genymobile."
|
||||
"scrcpy\n";
|
||||
|
||||
const char *expected = "/data/app/~~71mguyc6p-kNjQdNaNkToA==/com.genymobile"
|
||||
".scrcpy-l6fiqqUSU7Ok7QLg-rIyJA==/base.apk";
|
||||
char *path = sc_adb_parse_installed_apk_path(str);
|
||||
assert(!strcmp(path, expected));
|
||||
free(path);
|
||||
}
|
||||
|
||||
static void test_apk_path_invalid(void) {
|
||||
// Does not start with "package:"
|
||||
char str[] = "garbage:/data/app/~~71mguyc6p-kNjQdNaNkToA==/com.genymobile."
|
||||
"scrcpy-l6fiqqUSU7Ok7QLg-rIyJA==/base.apk=com.genymobile."
|
||||
"scrcpy\n";
|
||||
|
||||
char *path = sc_adb_parse_installed_apk_path(str);
|
||||
assert(!path);
|
||||
}
|
||||
|
||||
static void test_apk_version(void) {
|
||||
char str[] =
|
||||
"Key Set Manager:\n"
|
||||
" [com.genymobile.scrcpy]\n"
|
||||
" Signing KeySets: 128\n"
|
||||
"\n"
|
||||
"Packages:\n"
|
||||
" Package [com.genymobile.scrcpy] (89abcdef):\n"
|
||||
" userId=12345\n"
|
||||
" pkg=Package{012345 com.genymobile.scrcpy}\n"
|
||||
" codePath=/data/app/~~abcdef==/com.genymobile.scrcpy-012345==\n"
|
||||
" resourcePath=/data/app/~~abcdef==/com.genymobile.scrcpy-013245==\n"
|
||||
" primaryCpuAbi=null\n"
|
||||
" secondaryCpuAbi=null\n"
|
||||
" versionCode=12400 minSdk=21 targetSdk=31\n"
|
||||
" versionName=1.24\n"
|
||||
" splits=[base]\n"
|
||||
" apkSigningVersion=2\n"
|
||||
" applicationInfo=ApplicationInfo{012345 com.genymobile.scrcpy}\n";
|
||||
|
||||
const char *expected = "1.24";
|
||||
char *version = sc_adb_parse_installed_apk_version(str);
|
||||
assert(!strcmp(version, expected));
|
||||
free(version);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
@ -263,5 +311,9 @@ int main(int argc, char *argv[]) {
|
||||
test_get_ip_no_wlan_without_eol();
|
||||
test_get_ip_truncated();
|
||||
|
||||
test_apk_path();
|
||||
test_apk_path_invalid();
|
||||
test_apk_version();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Reference in New Issue
Block a user