Compare commits
1 Commits
server_thr
...
nosecurefl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01199084ad |
@@ -186,7 +186,7 @@ Enable "show touches" on start, restore the initial value on exit.
|
|||||||
It only shows physical touches (not clicks from scrcpy).
|
It only shows physical touches (not clicks from scrcpy).
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "\-\-v4l2-sink " /dev/videoN
|
.BI "\-\-v4l2_sink " /dev/videoN
|
||||||
Output to v4l2loopback device.
|
Output to v4l2loopback device.
|
||||||
|
|
||||||
It requires to lock the video orientation (see --lock-video-orientation).
|
It requires to lock the video orientation (see --lock-video-orientation).
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ scrcpy_print_usage(const char *arg0) {
|
|||||||
" It only shows physical touches (not clicks from scrcpy).\n"
|
" It only shows physical touches (not clicks from scrcpy).\n"
|
||||||
"\n"
|
"\n"
|
||||||
#ifdef HAVE_V4L2
|
#ifdef HAVE_V4L2
|
||||||
" --v4l2-sink /dev/videoN\n"
|
" --v4l2_sink /dev/videoN\n"
|
||||||
" Output to v4l2loopback device.\n"
|
" Output to v4l2loopback device.\n"
|
||||||
" It requires to lock the video orientation (see\n"
|
" It requires to lock the video orientation (see\n"
|
||||||
" --lock-video-orientation).\n"
|
" --lock-video-orientation).\n"
|
||||||
@@ -726,7 +726,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
|||||||
{"stay-awake", no_argument, NULL, 'w'},
|
{"stay-awake", no_argument, NULL, 'w'},
|
||||||
{"turn-screen-off", no_argument, NULL, 'S'},
|
{"turn-screen-off", no_argument, NULL, 'S'},
|
||||||
#ifdef HAVE_V4L2
|
#ifdef HAVE_V4L2
|
||||||
{"v4l2-sink", required_argument, NULL, OPT_V4L2_SINK},
|
{"v4l2_sink", required_argument, NULL, OPT_V4L2_SINK},
|
||||||
#endif
|
#endif
|
||||||
{"verbosity", required_argument, NULL, 'V'},
|
{"verbosity", required_argument, NULL, 'V'},
|
||||||
{"version", no_argument, NULL, 'v'},
|
{"version", no_argument, NULL, 'v'},
|
||||||
@@ -926,7 +926,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
|||||||
#ifdef HAVE_V4L2
|
#ifdef HAVE_V4L2
|
||||||
if (!opts->display && !opts->record_filename && !opts->v4l2_device) {
|
if (!opts->display && !opts->record_filename && !opts->v4l2_device) {
|
||||||
LOGE("-N/--no-display requires either screen recording (-r/--record)"
|
LOGE("-N/--no-display requires either screen recording (-r/--record)"
|
||||||
" or sink to v4l2loopback device (--v4l2-sink)");
|
" or sink to v4l2loopback device (--v4l2_sink)");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -111,8 +111,6 @@ decoder_push(struct decoder *decoder, const AVPacket *packet) {
|
|||||||
// A frame lost should not make the whole pipeline fail. The error, if
|
// A frame lost should not make the whole pipeline fail. The error, if
|
||||||
// any, is already logged.
|
// any, is already logged.
|
||||||
(void) ok;
|
(void) ok;
|
||||||
|
|
||||||
av_frame_unref(decoder->frame);
|
|
||||||
} else if (ret != AVERROR(EAGAIN)) {
|
} else if (ret != AVERROR(EAGAIN)) {
|
||||||
LOGE("Could not receive video frame: %d", ret);
|
LOGE("Could not receive video frame: %d", ret);
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -243,6 +243,10 @@ av_log_callback(void *avcl, int level, const char *fmt, va_list vl) {
|
|||||||
|
|
||||||
bool
|
bool
|
||||||
scrcpy(const struct scrcpy_options *options) {
|
scrcpy(const struct scrcpy_options *options) {
|
||||||
|
if (!server_init(&server)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
|
|
||||||
bool server_started = false;
|
bool server_started = false;
|
||||||
@@ -259,7 +263,6 @@ scrcpy(const struct scrcpy_options *options) {
|
|||||||
|
|
||||||
bool record = !!options->record_filename;
|
bool record = !!options->record_filename;
|
||||||
struct server_params params = {
|
struct server_params params = {
|
||||||
.serial = options->serial,
|
|
||||||
.log_level = options->log_level,
|
.log_level = options->log_level,
|
||||||
.crop = options->crop,
|
.crop = options->crop,
|
||||||
.port_range = options->port_range,
|
.port_range = options->port_range,
|
||||||
@@ -276,12 +279,7 @@ scrcpy(const struct scrcpy_options *options) {
|
|||||||
.force_adb_forward = options->force_adb_forward,
|
.force_adb_forward = options->force_adb_forward,
|
||||||
.power_off_on_close = options->power_off_on_close,
|
.power_off_on_close = options->power_off_on_close,
|
||||||
};
|
};
|
||||||
|
if (!server_start(&server, options->serial, ¶ms)) {
|
||||||
if (!server_init(&server, ¶ms)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!server_start(&server)) {
|
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,7 +311,7 @@ scrcpy(const struct scrcpy_options *options) {
|
|||||||
fps_counter_initialized = true;
|
fps_counter_initialized = true;
|
||||||
|
|
||||||
if (options->control) {
|
if (options->control) {
|
||||||
if (!file_handler_init(&file_handler, options->serial,
|
if (!file_handler_init(&file_handler, server.serial,
|
||||||
options->push_target)) {
|
options->push_target)) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|||||||
115
app/src/server.c
115
app/src/server.c
@@ -128,10 +128,9 @@ disable_tunnel_forward(const char *serial, uint16_t local_port) {
|
|||||||
static bool
|
static bool
|
||||||
disable_tunnel(struct server *server) {
|
disable_tunnel(struct server *server) {
|
||||||
if (server->tunnel_forward) {
|
if (server->tunnel_forward) {
|
||||||
return disable_tunnel_forward(server->params.serial,
|
return disable_tunnel_forward(server->serial, server->local_port);
|
||||||
server->local_port);
|
|
||||||
}
|
}
|
||||||
return disable_tunnel_reverse(server->params.serial);
|
return disable_tunnel_reverse(server->serial);
|
||||||
}
|
}
|
||||||
|
|
||||||
static socket_t
|
static socket_t
|
||||||
@@ -145,7 +144,7 @@ enable_tunnel_reverse_any_port(struct server *server,
|
|||||||
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 (!enable_tunnel_reverse(server->params.serial, port)) {
|
if (!enable_tunnel_reverse(server->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;
|
||||||
}
|
}
|
||||||
@@ -164,7 +163,7 @@ enable_tunnel_reverse_any_port(struct server *server,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// failure, disable tunnel and try another port
|
// failure, disable tunnel and try another port
|
||||||
if (!disable_tunnel_reverse(server->params.serial)) {
|
if (!disable_tunnel_reverse(server->serial)) {
|
||||||
LOGW("Could not remove reverse tunnel on port %" PRIu16, port);
|
LOGW("Could not remove reverse tunnel on port %" PRIu16, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,7 +191,7 @@ enable_tunnel_forward_any_port(struct server *server,
|
|||||||
server->tunnel_forward = true;
|
server->tunnel_forward = true;
|
||||||
uint16_t port = port_range.first;
|
uint16_t port = port_range.first;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (enable_tunnel_forward(server->params.serial, port)) {
|
if (enable_tunnel_forward(server->serial, port)) {
|
||||||
// success
|
// success
|
||||||
server->local_port = port;
|
server->local_port = port;
|
||||||
return true;
|
return true;
|
||||||
@@ -307,7 +306,7 @@ execute_server(struct server *server, const struct server_params *params) {
|
|||||||
// Port: 5005
|
// Port: 5005
|
||||||
// Then click on "Debug"
|
// Then click on "Debug"
|
||||||
#endif
|
#endif
|
||||||
return adb_execute(server->params.serial, cmd, ARRAY_LEN(cmd));
|
return adb_execute(server->serial, cmd, sizeof(cmd) / sizeof(cmd[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
static socket_t
|
static socket_t
|
||||||
@@ -353,75 +352,21 @@ close_socket(socket_t socket) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
server_params_destroy(struct server_params *params) {
|
|
||||||
// The server stores a copy of the params provided by the user
|
|
||||||
free((char *) params->crop);
|
|
||||||
free((char *) params->codec_options);
|
|
||||||
free((char *) params->encoder_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
server_params_copy(struct server_params *dst, const struct server_params *src) {
|
|
||||||
// params reference user-allocated memory, so we must copy them to handle
|
|
||||||
// them from a separate thread
|
|
||||||
|
|
||||||
*dst = *src;
|
|
||||||
|
|
||||||
dst->crop = NULL;
|
|
||||||
dst->codec_options = NULL;
|
|
||||||
dst->encoder_name = NULL;
|
|
||||||
|
|
||||||
if (src->crop) {
|
|
||||||
dst->crop = strdup(src->crop);
|
|
||||||
if (!dst->crop) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (src->codec_options) {
|
|
||||||
dst->codec_options = strdup(src->codec_options);
|
|
||||||
if (!dst->codec_options) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (src->encoder_name) {
|
|
||||||
dst->encoder_name = strdup(src->encoder_name);
|
|
||||||
if (!dst->encoder_name) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
error:
|
|
||||||
server_params_destroy(dst);
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
server_init(struct server *server, const struct server_params *params) {
|
server_init(struct server *server) {
|
||||||
if (!server_params_copy(&server->params, params)) {
|
server->serial = NULL;
|
||||||
LOGE("Could not copy server params");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
server->process = PROCESS_NONE;
|
server->process = PROCESS_NONE;
|
||||||
atomic_flag_clear_explicit(&server->server_socket_closed,
|
atomic_flag_clear_explicit(&server->server_socket_closed,
|
||||||
memory_order_relaxed);
|
memory_order_relaxed);
|
||||||
|
|
||||||
bool ok = sc_mutex_init(&server->mutex);
|
bool ok = sc_mutex_init(&server->mutex);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
server_params_destroy(&server->params);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ok = sc_cond_init(&server->process_terminated_cond);
|
ok = sc_cond_init(&server->process_terminated_cond);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
sc_mutex_destroy(&server->mutex);
|
sc_mutex_destroy(&server->mutex);
|
||||||
server_params_destroy(&server->params);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -461,41 +406,31 @@ run_wait_server(void *data) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
bool
|
||||||
run_server(void *data) {
|
server_start(struct server *server, const char *serial,
|
||||||
struct server *server = data;
|
const struct server_params *params) {
|
||||||
|
if (serial) {
|
||||||
|
server->serial = strdup(serial);
|
||||||
|
if (!server->serial) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const struct server_params *params = &server->params;
|
if (!push_server(serial)) {
|
||||||
const struct server_callbacks *cbs = &server->cbs;
|
goto error1;
|
||||||
void *userdata = server->userdata;
|
|
||||||
|
|
||||||
if (!push_server(params->serial)) {
|
|
||||||
cbs->on_connection_failed(server);
|
|
||||||
goto end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!enable_tunnel_any_port(server, params->port_range,
|
if (!enable_tunnel_any_port(server, params->port_range,
|
||||||
params->force_adb_forward)) {
|
params->force_adb_forward)) {
|
||||||
cbs->on_connection_failed(server);
|
goto error1;
|
||||||
goto end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// server will connect to our server socket
|
// server will connect to our server socket
|
||||||
server->process = execute_server(server, params);
|
server->process = execute_server(server, params);
|
||||||
if (server->process == PROCESS_NONE) {
|
if (server->process == PROCESS_NONE) {
|
||||||
cbs->on_connection_failed(server);
|
goto error2;
|
||||||
goto end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
process_wait(server->process, false); // ignore exit code
|
|
||||||
|
|
||||||
end:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
server_start(struct server *server) {
|
|
||||||
|
|
||||||
// If the server process dies before connecting to the server socket, then
|
// If the server process dies before connecting to the server socket, then
|
||||||
// the client will be stuck forever on accept(). To avoid the problem, we
|
// the client will be stuck forever on accept(). To avoid the problem, we
|
||||||
// must be able to wake up the accept() call when the server dies. To keep
|
// must be able to wake up the accept() call when the server dies. To keep
|
||||||
@@ -507,14 +442,14 @@ server_start(struct server *server) {
|
|||||||
if (!ok) {
|
if (!ok) {
|
||||||
process_terminate(server->process);
|
process_terminate(server->process);
|
||||||
process_wait(server->process, true); // ignore exit code
|
process_wait(server->process, true); // ignore exit code
|
||||||
goto error;
|
goto error2;
|
||||||
}
|
}
|
||||||
|
|
||||||
server->tunnel_enabled = true;
|
server->tunnel_enabled = true;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
error:
|
error2:
|
||||||
if (!server->tunnel_forward) {
|
if (!server->tunnel_forward) {
|
||||||
bool was_closed =
|
bool was_closed =
|
||||||
atomic_flag_test_and_set(&server->server_socket_closed);
|
atomic_flag_test_and_set(&server->server_socket_closed);
|
||||||
@@ -524,7 +459,8 @@ error:
|
|||||||
close_socket(server->server_socket);
|
close_socket(server->server_socket);
|
||||||
}
|
}
|
||||||
disable_tunnel(server);
|
disable_tunnel(server);
|
||||||
|
error1:
|
||||||
|
free(server->serial);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -622,5 +558,4 @@ server_destroy(struct server *server) {
|
|||||||
free(server->serial);
|
free(server->serial);
|
||||||
sc_cond_destroy(&server->process_terminated_cond);
|
sc_cond_destroy(&server->process_terminated_cond);
|
||||||
sc_mutex_destroy(&server->mutex);
|
sc_mutex_destroy(&server->mutex);
|
||||||
server_params_destroy(&server->params);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,25 +13,6 @@
|
|||||||
#include "util/net.h"
|
#include "util/net.h"
|
||||||
#include "util/thread.h"
|
#include "util/thread.h"
|
||||||
|
|
||||||
struct server_params {
|
|
||||||
enum sc_log_level log_level;
|
|
||||||
const char *serial;
|
|
||||||
const char *crop;
|
|
||||||
const char *codec_options;
|
|
||||||
const char *encoder_name;
|
|
||||||
struct sc_port_range port_range;
|
|
||||||
uint16_t max_size;
|
|
||||||
uint32_t bit_rate;
|
|
||||||
uint16_t max_fps;
|
|
||||||
int8_t lock_video_orientation;
|
|
||||||
bool control;
|
|
||||||
uint32_t display_id;
|
|
||||||
bool show_touches;
|
|
||||||
bool stay_awake;
|
|
||||||
bool force_adb_forward;
|
|
||||||
bool power_off_on_close;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct server {
|
struct server {
|
||||||
char *serial;
|
char *serial;
|
||||||
process_t process;
|
process_t process;
|
||||||
@@ -48,29 +29,34 @@ struct server {
|
|||||||
uint16_t local_port; // selected from port_range
|
uint16_t local_port; // selected from port_range
|
||||||
bool tunnel_enabled;
|
bool tunnel_enabled;
|
||||||
bool tunnel_forward; // use "adb forward" instead of "adb reverse"
|
bool tunnel_forward; // use "adb forward" instead of "adb reverse"
|
||||||
|
|
||||||
// The internal allocated strings are copies owned by the server
|
|
||||||
struct server_params params;
|
|
||||||
|
|
||||||
const struct server_callbacks *cbs;
|
|
||||||
void *userdata;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct server_callbacks {
|
struct server_params {
|
||||||
void (*on_connection_failed)(struct server *server);
|
enum sc_log_level log_level;
|
||||||
void (*on_connected)(struct server *server, const char *name,
|
const char *crop;
|
||||||
struct size size, void *userdata);
|
const char *codec_options;
|
||||||
void (*on_disconnected)(struct server *server, void *userdata);
|
const char *encoder_name;
|
||||||
|
struct sc_port_range port_range;
|
||||||
|
uint16_t max_size;
|
||||||
|
uint32_t bit_rate;
|
||||||
|
uint16_t max_fps;
|
||||||
|
int8_t lock_video_orientation;
|
||||||
|
bool control;
|
||||||
|
uint32_t display_id;
|
||||||
|
bool show_touches;
|
||||||
|
bool stay_awake;
|
||||||
|
bool force_adb_forward;
|
||||||
|
bool power_off_on_close;
|
||||||
};
|
};
|
||||||
|
|
||||||
// init server fields
|
// init default values
|
||||||
bool
|
bool
|
||||||
server_init(struct server *server, const struct server_params *params);
|
server_init(struct server *server);
|
||||||
|
|
||||||
// push, enable tunnel et start the server
|
// push, enable tunnel et start the server
|
||||||
bool
|
bool
|
||||||
server_start(struct server *server, const struct server_callbacks *cbs,
|
server_start(struct server *server, const char *serial,
|
||||||
void *userdata);
|
const struct server_params *params);
|
||||||
|
|
||||||
// block until the communication with the server is established
|
// block until the communication with the server is established
|
||||||
bool
|
bool
|
||||||
|
|||||||
@@ -92,11 +92,11 @@ encode_and_write_frame(struct sc_v4l2_sink *vs, const AVFrame *frame) {
|
|||||||
// A packet was received
|
// A packet was received
|
||||||
|
|
||||||
bool ok = write_packet(vs, packet);
|
bool ok = write_packet(vs, packet);
|
||||||
av_packet_unref(packet);
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
LOGW("Could not send packet to v4l2 sink");
|
LOGW("Could not send packet to v4l2 sink");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
av_packet_unref(packet);
|
||||||
} else if (ret != AVERROR(EAGAIN)) {
|
} else if (ret != AVERROR(EAGAIN)) {
|
||||||
LOGE("Could not receive v4l2 video packet: %d", ret);
|
LOGE("Could not receive v4l2 video packet: %d", ret);
|
||||||
return false;
|
return false;
|
||||||
@@ -125,7 +125,6 @@ run_v4l2_sink(void *data) {
|
|||||||
|
|
||||||
video_buffer_consume(&vs->vb, vs->frame);
|
video_buffer_consume(&vs->vb, vs->frame);
|
||||||
bool ok = encode_and_write_frame(vs, vs->frame);
|
bool ok = encode_and_write_frame(vs, vs->frame);
|
||||||
av_frame_unref(vs->frame);
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
LOGE("Could not send frame to v4l2 sink");
|
LOGE("Could not send frame to v4l2 sink");
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -3,10 +3,6 @@ package com.genymobile.scrcpy;
|
|||||||
import com.genymobile.scrcpy.wrappers.ContentProvider;
|
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.Parcelable;
|
|
||||||
import android.util.Base64;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
@@ -19,123 +15,25 @@ public final class CleanUp {
|
|||||||
|
|
||||||
public static final String SERVER_PATH = "/data/local/tmp/scrcpy-server.jar";
|
public static final String SERVER_PATH = "/data/local/tmp/scrcpy-server.jar";
|
||||||
|
|
||||||
// A simple struct to be passed from the main process to the cleanup process
|
|
||||||
public static class Config implements Parcelable {
|
|
||||||
|
|
||||||
public static final Creator<Config> CREATOR = new Creator<Config>() {
|
|
||||||
@Override
|
|
||||||
public Config createFromParcel(Parcel in) {
|
|
||||||
return new Config(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Config[] newArray(int size) {
|
|
||||||
return new Config[size];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private static final int FLAG_DISABLE_SHOW_TOUCHES = 1;
|
|
||||||
private static final int FLAG_RESTORE_NORMAL_POWER_MODE = 2;
|
|
||||||
private static final int FLAG_POWER_OFF_SCREEN = 4;
|
|
||||||
|
|
||||||
private int displayId;
|
|
||||||
|
|
||||||
// Restore the value (between 0 and 7), -1 to not restore
|
|
||||||
// <https://developer.android.com/reference/android/provider/Settings.Global#STAY_ON_WHILE_PLUGGED_IN>
|
|
||||||
private int restoreStayOn = -1;
|
|
||||||
|
|
||||||
private boolean disableShowTouches;
|
|
||||||
private boolean restoreNormalPowerMode;
|
|
||||||
private boolean powerOffScreen;
|
|
||||||
|
|
||||||
public Config() {
|
|
||||||
// Default constructor, the fields are initialized by CleanUp.configure()
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Config(Parcel in) {
|
|
||||||
displayId = in.readInt();
|
|
||||||
restoreStayOn = in.readInt();
|
|
||||||
byte options = in.readByte();
|
|
||||||
disableShowTouches = (options & FLAG_DISABLE_SHOW_TOUCHES) != 0;
|
|
||||||
restoreNormalPowerMode = (options & FLAG_RESTORE_NORMAL_POWER_MODE) != 0;
|
|
||||||
powerOffScreen = (options & FLAG_POWER_OFF_SCREEN) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeToParcel(Parcel dest, int flags) {
|
|
||||||
dest.writeInt(displayId);
|
|
||||||
dest.writeInt(restoreStayOn);
|
|
||||||
byte options = 0;
|
|
||||||
if (disableShowTouches) {
|
|
||||||
options |= FLAG_DISABLE_SHOW_TOUCHES;
|
|
||||||
}
|
|
||||||
if (restoreNormalPowerMode) {
|
|
||||||
options |= FLAG_RESTORE_NORMAL_POWER_MODE;
|
|
||||||
}
|
|
||||||
if (powerOffScreen) {
|
|
||||||
options |= FLAG_POWER_OFF_SCREEN;
|
|
||||||
}
|
|
||||||
dest.writeByte(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasWork() {
|
|
||||||
return disableShowTouches || restoreStayOn != -1 || restoreNormalPowerMode || powerOffScreen;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int describeContents() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] serialize() {
|
|
||||||
Parcel parcel = Parcel.obtain();
|
|
||||||
writeToParcel(parcel, 0);
|
|
||||||
byte[] bytes = parcel.marshall();
|
|
||||||
parcel.recycle();
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Config deserialize(byte[] bytes) {
|
|
||||||
Parcel parcel = Parcel.obtain();
|
|
||||||
parcel.unmarshall(bytes, 0, bytes.length);
|
|
||||||
parcel.setDataPosition(0);
|
|
||||||
return CREATOR.createFromParcel(parcel);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Config fromBase64(String base64) {
|
|
||||||
byte[] bytes = Base64.decode(base64, Base64.NO_WRAP);
|
|
||||||
return deserialize(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
String toBase64() {
|
|
||||||
byte[] bytes = serialize();
|
|
||||||
return Base64.encodeToString(bytes, Base64.NO_WRAP);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private CleanUp() {
|
private CleanUp() {
|
||||||
// not instantiable
|
// not instantiable
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void configure(int displayId, int restoreStayOn, boolean disableShowTouches, boolean restoreNormalPowerMode, boolean powerOffScreen)
|
public static void configure(boolean disableShowTouches, int restoreStayOn, boolean restoreNormalPowerMode, boolean powerOffScreen, int displayId)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
Config config = new Config();
|
boolean needProcess = disableShowTouches || restoreStayOn != -1 || restoreNormalPowerMode || powerOffScreen;
|
||||||
config.displayId = displayId;
|
if (needProcess) {
|
||||||
config.disableShowTouches = disableShowTouches;
|
startProcess(disableShowTouches, restoreStayOn, restoreNormalPowerMode, powerOffScreen, displayId);
|
||||||
config.restoreStayOn = restoreStayOn;
|
|
||||||
config.restoreNormalPowerMode = restoreNormalPowerMode;
|
|
||||||
config.powerOffScreen = powerOffScreen;
|
|
||||||
|
|
||||||
if (config.hasWork()) {
|
|
||||||
startProcess(config);
|
|
||||||
} else {
|
} else {
|
||||||
// There is no additional clean up to do when scrcpy dies
|
// There is no additional clean up to do when scrcpy dies
|
||||||
unlinkSelf();
|
unlinkSelf();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void startProcess(Config config) throws IOException {
|
private static void startProcess(boolean disableShowTouches, int restoreStayOn, boolean restoreNormalPowerMode, boolean powerOffScreen,
|
||||||
String[] cmd = {"app_process", "/", CleanUp.class.getName(), config.toBase64()};
|
int displayId) throws IOException {
|
||||||
|
String[] cmd = {"app_process", "/", CleanUp.class.getName(), String.valueOf(disableShowTouches), String.valueOf(
|
||||||
|
restoreStayOn), String.valueOf(restoreNormalPowerMode), String.valueOf(powerOffScreen), String.valueOf(displayId)};
|
||||||
|
|
||||||
ProcessBuilder builder = new ProcessBuilder(cmd);
|
ProcessBuilder builder = new ProcessBuilder(cmd);
|
||||||
builder.environment().put("CLASSPATH", SERVER_PATH);
|
builder.environment().put("CLASSPATH", SERVER_PATH);
|
||||||
@@ -162,27 +60,31 @@ public final class CleanUp {
|
|||||||
|
|
||||||
Ln.i("Cleaning up");
|
Ln.i("Cleaning up");
|
||||||
|
|
||||||
Config config = Config.fromBase64(args[0]);
|
boolean disableShowTouches = Boolean.parseBoolean(args[0]);
|
||||||
|
int restoreStayOn = Integer.parseInt(args[1]);
|
||||||
|
boolean restoreNormalPowerMode = Boolean.parseBoolean(args[2]);
|
||||||
|
boolean powerOffScreen = Boolean.parseBoolean(args[3]);
|
||||||
|
int displayId = Integer.parseInt(args[4]);
|
||||||
|
|
||||||
if (config.disableShowTouches || config.restoreStayOn != -1) {
|
if (disableShowTouches || restoreStayOn != -1) {
|
||||||
ServiceManager serviceManager = new ServiceManager();
|
ServiceManager serviceManager = new ServiceManager();
|
||||||
try (ContentProvider settings = serviceManager.getActivityManager().createSettingsProvider()) {
|
try (ContentProvider settings = serviceManager.getActivityManager().createSettingsProvider()) {
|
||||||
if (config.disableShowTouches) {
|
if (disableShowTouches) {
|
||||||
Ln.i("Disabling \"show touches\"");
|
Ln.i("Disabling \"show touches\"");
|
||||||
settings.putValue(ContentProvider.TABLE_SYSTEM, "show_touches", "0");
|
settings.putValue(ContentProvider.TABLE_SYSTEM, "show_touches", "0");
|
||||||
}
|
}
|
||||||
if (config.restoreStayOn != -1) {
|
if (restoreStayOn != -1) {
|
||||||
Ln.i("Restoring \"stay awake\"");
|
Ln.i("Restoring \"stay awake\"");
|
||||||
settings.putValue(ContentProvider.TABLE_GLOBAL, "stay_on_while_plugged_in", String.valueOf(config.restoreStayOn));
|
settings.putValue(ContentProvider.TABLE_GLOBAL, "stay_on_while_plugged_in", String.valueOf(restoreStayOn));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Device.isScreenOn()) {
|
if (Device.isScreenOn()) {
|
||||||
if (config.powerOffScreen) {
|
if (powerOffScreen) {
|
||||||
Ln.i("Power off screen");
|
Ln.i("Power off screen");
|
||||||
Device.powerOffScreen(config.displayId);
|
Device.powerOffScreen(displayId);
|
||||||
} else if (config.restoreNormalPowerMode) {
|
} else if (restoreNormalPowerMode) {
|
||||||
Ln.i("Restoring normal power mode");
|
Ln.i("Restoring normal power mode");
|
||||||
Device.setScreenPowerMode(Device.POWER_MODE_NORMAL);
|
Device.setScreenPowerMode(Device.POWER_MODE_NORMAL);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ public class Controller {
|
|||||||
public void control() throws IOException {
|
public void control() throws IOException {
|
||||||
// on start, power on the device
|
// on start, power on the device
|
||||||
if (!Device.isScreenOn()) {
|
if (!Device.isScreenOn()) {
|
||||||
device.pressReleaseKeycode(KeyEvent.KEYCODE_POWER);
|
device.injectKeycode(KeyEvent.KEYCODE_POWER);
|
||||||
|
|
||||||
// dirty hack
|
// dirty hack
|
||||||
// After POWER is injected, the device is powered on asynchronously.
|
// After POWER is injected, the device is powered on asynchronously.
|
||||||
@@ -273,7 +273,7 @@ public class Controller {
|
|||||||
if (keepPowerModeOff) {
|
if (keepPowerModeOff) {
|
||||||
schedulePowerModeOff();
|
schedulePowerModeOff();
|
||||||
}
|
}
|
||||||
return device.pressReleaseKeycode(KeyEvent.KEYCODE_POWER);
|
return device.injectKeycode(KeyEvent.KEYCODE_POWER);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean setClipboard(String text, boolean paste) {
|
private boolean setClipboard(String text, boolean paste) {
|
||||||
@@ -284,7 +284,7 @@ public class Controller {
|
|||||||
|
|
||||||
// On Android >= 7, also press the PASTE key if requested
|
// On Android >= 7, also press the PASTE key if requested
|
||||||
if (paste && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && device.supportsInputEvents()) {
|
if (paste && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && device.supportsInputEvents()) {
|
||||||
device.pressReleaseKeycode(KeyEvent.KEYCODE_PASTE);
|
device.injectKeycode(KeyEvent.KEYCODE_PASTE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ok;
|
return ok;
|
||||||
|
|||||||
@@ -164,39 +164,54 @@ public final class Device {
|
|||||||
return supportsInputEvents;
|
return supportsInputEvents;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean injectEvent(InputEvent inputEvent, int displayId) {
|
public static boolean injectEvent(InputEvent inputEvent, int mode, int displayId) {
|
||||||
if (!supportsInputEvents(displayId)) {
|
if (!supportsInputEvents(displayId)) {
|
||||||
throw new AssertionError("Could not inject input event if !supportsInputEvents()");
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (displayId != 0 && !InputManager.setDisplayId(inputEvent, displayId)) {
|
if (displayId != 0 && !InputManager.setDisplayId(inputEvent, displayId)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return SERVICE_MANAGER.getInputManager().injectInputEvent(inputEvent, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
|
return SERVICE_MANAGER.getInputManager().injectInputEvent(inputEvent, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean injectEvent(InputEvent inputEvent, int mode) {
|
||||||
|
if (!supportsInputEvents()) {
|
||||||
|
throw new AssertionError("Could not inject input event if !supportsInputEvents()");
|
||||||
|
}
|
||||||
|
|
||||||
|
return injectEvent(inputEvent, mode, displayId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean injectEventOnDisplay(InputEvent event, int displayId) {
|
||||||
|
return injectEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC, displayId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean injectEvent(InputEvent event) {
|
public boolean injectEvent(InputEvent event) {
|
||||||
return injectEvent(event, displayId);
|
return injectEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean injectKeyEvent(int action, int keyCode, int repeat, int metaState, int displayId) {
|
public static boolean injectKeyEvent(int action, int keyCode, int repeat, int metaState, int displayId) {
|
||||||
long now = SystemClock.uptimeMillis();
|
long now = SystemClock.uptimeMillis();
|
||||||
KeyEvent event = new KeyEvent(now, now, action, keyCode, repeat, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0,
|
KeyEvent event = new KeyEvent(now, now, action, keyCode, repeat, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0,
|
||||||
InputDevice.SOURCE_KEYBOARD);
|
InputDevice.SOURCE_KEYBOARD);
|
||||||
return injectEvent(event, displayId);
|
return injectEventOnDisplay(event, displayId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean injectKeyEvent(int action, int keyCode, int repeat, int metaState) {
|
public boolean injectKeyEvent(int action, int keyCode, int repeat, int metaState) {
|
||||||
return injectKeyEvent(action, keyCode, repeat, metaState, displayId);
|
long now = SystemClock.uptimeMillis();
|
||||||
|
KeyEvent event = new KeyEvent(now, now, action, keyCode, repeat, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0,
|
||||||
|
InputDevice.SOURCE_KEYBOARD);
|
||||||
|
return injectEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean pressReleaseKeycode(int keyCode, int displayId) {
|
public static boolean injectKeycode(int keyCode, int displayId) {
|
||||||
return injectKeyEvent(KeyEvent.ACTION_DOWN, keyCode, 0, 0, displayId) && injectKeyEvent(KeyEvent.ACTION_UP, keyCode, 0, 0, displayId);
|
return injectKeyEvent(KeyEvent.ACTION_DOWN, keyCode, 0, 0, displayId) && injectKeyEvent(KeyEvent.ACTION_UP, keyCode, 0, 0, displayId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean pressReleaseKeycode(int keyCode) {
|
public boolean injectKeycode(int keyCode) {
|
||||||
return pressReleaseKeycode(keyCode, displayId);
|
return injectKeyEvent(KeyEvent.ACTION_DOWN, keyCode, 0, 0) && injectKeyEvent(KeyEvent.ACTION_UP, keyCode, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isScreenOn() {
|
public static boolean isScreenOn() {
|
||||||
@@ -272,7 +287,7 @@ public final class Device {
|
|||||||
if (!isScreenOn()) {
|
if (!isScreenOn()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return pressReleaseKeycode(KeyEvent.KEYCODE_POWER, displayId);
|
return injectKeycode(KeyEvent.KEYCODE_POWER, displayId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ public class ScreenEncoder implements Device.RotationListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static IBinder createDisplay() {
|
private static IBinder createDisplay() {
|
||||||
return SurfaceControl.createDisplay("scrcpy", true);
|
return SurfaceControl.createDisplay("scrcpy", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void configure(MediaCodec codec, MediaFormat format) {
|
private static void configure(MediaCodec codec, MediaFormat format) {
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ public final class Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CleanUp.configure(options.getDisplayId(), restoreStayOn, mustDisableShowTouchesOnCleanUp, true, options.getPowerOffScreenOnClose());
|
CleanUp.configure(mustDisableShowTouchesOnCleanUp, restoreStayOn, true, options.getPowerOffScreenOnClose(), options.getDisplayId());
|
||||||
|
|
||||||
boolean tunnelForward = options.isTunnelForward();
|
boolean tunnelForward = options.isTunnelForward();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user