Compare commits
8 Commits
nosecurefl
...
cleanup_se
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5369c4f754 | ||
|
|
233f8e6cc4 | ||
|
|
9a7d351d67 | ||
|
|
d00ee640c0 | ||
|
|
ae6ec7a194 | ||
|
|
84f17fdeab | ||
|
|
1cde68a1fa | ||
|
|
45e7280148 |
@@ -186,7 +186,7 @@ Enable "show touches" on start, restore the initial value on exit.
|
||||
It only shows physical touches (not clicks from scrcpy).
|
||||
|
||||
.TP
|
||||
.BI "\-\-v4l2_sink " /dev/videoN
|
||||
.BI "\-\-v4l2-sink " /dev/videoN
|
||||
Output to v4l2loopback device.
|
||||
|
||||
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"
|
||||
"\n"
|
||||
#ifdef HAVE_V4L2
|
||||
" --v4l2_sink /dev/videoN\n"
|
||||
" --v4l2-sink /dev/videoN\n"
|
||||
" Output to v4l2loopback device.\n"
|
||||
" It requires to lock the video orientation (see\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'},
|
||||
{"turn-screen-off", no_argument, NULL, 'S'},
|
||||
#ifdef HAVE_V4L2
|
||||
{"v4l2_sink", required_argument, NULL, OPT_V4L2_SINK},
|
||||
{"v4l2-sink", required_argument, NULL, OPT_V4L2_SINK},
|
||||
#endif
|
||||
{"verbosity", required_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
|
||||
if (!opts->display && !opts->record_filename && !opts->v4l2_device) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -111,6 +111,8 @@ decoder_push(struct decoder *decoder, const AVPacket *packet) {
|
||||
// A frame lost should not make the whole pipeline fail. The error, if
|
||||
// any, is already logged.
|
||||
(void) ok;
|
||||
|
||||
av_frame_unref(decoder->frame);
|
||||
} else if (ret != AVERROR(EAGAIN)) {
|
||||
LOGE("Could not receive video frame: %d", ret);
|
||||
return false;
|
||||
|
||||
@@ -92,11 +92,11 @@ encode_and_write_frame(struct sc_v4l2_sink *vs, const AVFrame *frame) {
|
||||
// A packet was received
|
||||
|
||||
bool ok = write_packet(vs, packet);
|
||||
av_packet_unref(packet);
|
||||
if (!ok) {
|
||||
LOGW("Could not send packet to v4l2 sink");
|
||||
return false;
|
||||
}
|
||||
av_packet_unref(packet);
|
||||
} else if (ret != AVERROR(EAGAIN)) {
|
||||
LOGE("Could not receive v4l2 video packet: %d", ret);
|
||||
return false;
|
||||
@@ -125,6 +125,7 @@ run_v4l2_sink(void *data) {
|
||||
|
||||
video_buffer_consume(&vs->vb, vs->frame);
|
||||
bool ok = encode_and_write_frame(vs, vs->frame);
|
||||
av_frame_unref(vs->frame);
|
||||
if (!ok) {
|
||||
LOGE("Could not send frame to v4l2 sink");
|
||||
break;
|
||||
|
||||
@@ -3,6 +3,10 @@ package com.genymobile.scrcpy;
|
||||
import com.genymobile.scrcpy.wrappers.ContentProvider;
|
||||
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.IOException;
|
||||
|
||||
@@ -15,25 +19,123 @@ public final class CleanUp {
|
||||
|
||||
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() {
|
||||
// not instantiable
|
||||
}
|
||||
|
||||
public static void configure(boolean disableShowTouches, int restoreStayOn, boolean restoreNormalPowerMode, boolean powerOffScreen, int displayId)
|
||||
public static void configure(int displayId, int restoreStayOn, boolean disableShowTouches, boolean restoreNormalPowerMode, boolean powerOffScreen)
|
||||
throws IOException {
|
||||
boolean needProcess = disableShowTouches || restoreStayOn != -1 || restoreNormalPowerMode || powerOffScreen;
|
||||
if (needProcess) {
|
||||
startProcess(disableShowTouches, restoreStayOn, restoreNormalPowerMode, powerOffScreen, displayId);
|
||||
Config config = new Config();
|
||||
config.displayId = displayId;
|
||||
config.disableShowTouches = disableShowTouches;
|
||||
config.restoreStayOn = restoreStayOn;
|
||||
config.restoreNormalPowerMode = restoreNormalPowerMode;
|
||||
config.powerOffScreen = powerOffScreen;
|
||||
|
||||
if (config.hasWork()) {
|
||||
startProcess(config);
|
||||
} else {
|
||||
// There is no additional clean up to do when scrcpy dies
|
||||
unlinkSelf();
|
||||
}
|
||||
}
|
||||
|
||||
private static void startProcess(boolean disableShowTouches, int restoreStayOn, boolean restoreNormalPowerMode, boolean powerOffScreen,
|
||||
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)};
|
||||
private static void startProcess(Config config) throws IOException {
|
||||
String[] cmd = {"app_process", "/", CleanUp.class.getName(), config.toBase64()};
|
||||
|
||||
ProcessBuilder builder = new ProcessBuilder(cmd);
|
||||
builder.environment().put("CLASSPATH", SERVER_PATH);
|
||||
@@ -60,31 +162,27 @@ public final class CleanUp {
|
||||
|
||||
Ln.i("Cleaning up");
|
||||
|
||||
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]);
|
||||
Config config = Config.fromBase64(args[0]);
|
||||
|
||||
if (disableShowTouches || restoreStayOn != -1) {
|
||||
if (config.disableShowTouches || config.restoreStayOn != -1) {
|
||||
ServiceManager serviceManager = new ServiceManager();
|
||||
try (ContentProvider settings = serviceManager.getActivityManager().createSettingsProvider()) {
|
||||
if (disableShowTouches) {
|
||||
if (config.disableShowTouches) {
|
||||
Ln.i("Disabling \"show touches\"");
|
||||
settings.putValue(ContentProvider.TABLE_SYSTEM, "show_touches", "0");
|
||||
}
|
||||
if (restoreStayOn != -1) {
|
||||
if (config.restoreStayOn != -1) {
|
||||
Ln.i("Restoring \"stay awake\"");
|
||||
settings.putValue(ContentProvider.TABLE_GLOBAL, "stay_on_while_plugged_in", String.valueOf(restoreStayOn));
|
||||
settings.putValue(ContentProvider.TABLE_GLOBAL, "stay_on_while_plugged_in", String.valueOf(config.restoreStayOn));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Device.isScreenOn()) {
|
||||
if (powerOffScreen) {
|
||||
if (config.powerOffScreen) {
|
||||
Ln.i("Power off screen");
|
||||
Device.powerOffScreen(displayId);
|
||||
} else if (restoreNormalPowerMode) {
|
||||
Device.powerOffScreen(config.displayId);
|
||||
} else if (config.restoreNormalPowerMode) {
|
||||
Ln.i("Restoring normal power mode");
|
||||
Device.setScreenPowerMode(Device.POWER_MODE_NORMAL);
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ public class Controller {
|
||||
public void control() throws IOException {
|
||||
// on start, power on the device
|
||||
if (!Device.isScreenOn()) {
|
||||
device.injectKeycode(KeyEvent.KEYCODE_POWER);
|
||||
device.pressReleaseKeycode(KeyEvent.KEYCODE_POWER);
|
||||
|
||||
// dirty hack
|
||||
// After POWER is injected, the device is powered on asynchronously.
|
||||
@@ -273,7 +273,7 @@ public class Controller {
|
||||
if (keepPowerModeOff) {
|
||||
schedulePowerModeOff();
|
||||
}
|
||||
return device.injectKeycode(KeyEvent.KEYCODE_POWER);
|
||||
return device.pressReleaseKeycode(KeyEvent.KEYCODE_POWER);
|
||||
}
|
||||
|
||||
private boolean setClipboard(String text, boolean paste) {
|
||||
@@ -284,7 +284,7 @@ public class Controller {
|
||||
|
||||
// On Android >= 7, also press the PASTE key if requested
|
||||
if (paste && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && device.supportsInputEvents()) {
|
||||
device.injectKeycode(KeyEvent.KEYCODE_PASTE);
|
||||
device.pressReleaseKeycode(KeyEvent.KEYCODE_PASTE);
|
||||
}
|
||||
|
||||
return ok;
|
||||
|
||||
@@ -164,54 +164,39 @@ public final class Device {
|
||||
return supportsInputEvents;
|
||||
}
|
||||
|
||||
public static boolean injectEvent(InputEvent inputEvent, int mode, int displayId) {
|
||||
public static boolean injectEvent(InputEvent inputEvent, int displayId) {
|
||||
if (!supportsInputEvents(displayId)) {
|
||||
return false;
|
||||
throw new AssertionError("Could not inject input event if !supportsInputEvents()");
|
||||
}
|
||||
|
||||
if (displayId != 0 && !InputManager.setDisplayId(inputEvent, displayId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
return SERVICE_MANAGER.getInputManager().injectInputEvent(inputEvent, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
|
||||
}
|
||||
|
||||
public boolean injectEvent(InputEvent event) {
|
||||
return injectEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
|
||||
return injectEvent(event, displayId);
|
||||
}
|
||||
|
||||
public static boolean injectKeyEvent(int action, int keyCode, int repeat, int metaState, int displayId) {
|
||||
long now = SystemClock.uptimeMillis();
|
||||
KeyEvent event = new KeyEvent(now, now, action, keyCode, repeat, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0,
|
||||
InputDevice.SOURCE_KEYBOARD);
|
||||
return injectEventOnDisplay(event, displayId);
|
||||
return injectEvent(event, displayId);
|
||||
}
|
||||
|
||||
public boolean injectKeyEvent(int action, int keyCode, int repeat, int metaState) {
|
||||
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);
|
||||
return injectKeyEvent(action, keyCode, repeat, metaState, displayId);
|
||||
}
|
||||
|
||||
public static boolean injectKeycode(int keyCode, int displayId) {
|
||||
public static boolean pressReleaseKeycode(int keyCode, int displayId) {
|
||||
return injectKeyEvent(KeyEvent.ACTION_DOWN, keyCode, 0, 0, displayId) && injectKeyEvent(KeyEvent.ACTION_UP, keyCode, 0, 0, displayId);
|
||||
}
|
||||
|
||||
public boolean injectKeycode(int keyCode) {
|
||||
return injectKeyEvent(KeyEvent.ACTION_DOWN, keyCode, 0, 0) && injectKeyEvent(KeyEvent.ACTION_UP, keyCode, 0, 0);
|
||||
public boolean pressReleaseKeycode(int keyCode) {
|
||||
return pressReleaseKeycode(keyCode, displayId);
|
||||
}
|
||||
|
||||
public static boolean isScreenOn() {
|
||||
@@ -287,7 +272,7 @@ public final class Device {
|
||||
if (!isScreenOn()) {
|
||||
return true;
|
||||
}
|
||||
return injectKeycode(KeyEvent.KEYCODE_POWER, displayId);
|
||||
return pressReleaseKeycode(KeyEvent.KEYCODE_POWER, displayId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -50,7 +50,7 @@ public final class Server {
|
||||
}
|
||||
}
|
||||
|
||||
CleanUp.configure(mustDisableShowTouchesOnCleanUp, restoreStayOn, true, options.getPowerOffScreenOnClose(), options.getDisplayId());
|
||||
CleanUp.configure(options.getDisplayId(), restoreStayOn, mustDisableShowTouchesOnCleanUp, true, options.getPowerOffScreenOnClose());
|
||||
|
||||
boolean tunnelForward = options.isTunnelForward();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user