Compare commits

..
Author SHA1 Message Date
Romain Vimont 01199084ad quickfix secure flag DONOTMERGE 2021-04-26 08:17:25 +02:00
9 changed files with 55 additions and 141 deletions
+1 -1
View File
@@ -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).
+3 -3
View File
@@ -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;
} }
-2
View File
@@ -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;
+1 -2
View File
@@ -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();