change: execute server as root and create secure display
change: AID_GRAPHICS -> AID_SYSTEM add: fllag --root to launch server as root cleanup: phantom whitespace change: eliminate ambiguous description add: autocompletion for --root change: use getPackageName method rather than fixed constant for `--root` case change: at least try audio when running as AID_SYSTEM on older versions fixups: PACKAGE_SHELL constant where necessary, DRY cleanup: usage of PACKAGE_NAME + PACKAGE_SHELL
This commit is contained in:
parent
1ee46970e3
commit
7b1ac46ab0
13 changed files with 36 additions and 5 deletions
|
@ -53,6 +53,7 @@ _scrcpy() {
|
||||||
--record-format=
|
--record-format=
|
||||||
--render-driver=
|
--render-driver=
|
||||||
--require-audio
|
--require-audio
|
||||||
|
--root
|
||||||
--rotation=
|
--rotation=
|
||||||
-s --serial=
|
-s --serial=
|
||||||
-S --turn-screen-off
|
-S --turn-screen-off
|
||||||
|
|
|
@ -59,6 +59,7 @@ arguments=(
|
||||||
'--record-format=[Force recording format]:format:(mp4 mkv)'
|
'--record-format=[Force recording format]:format:(mp4 mkv)'
|
||||||
'--render-driver=[Request SDL to use the given render driver]:driver name:(direct3d opengl opengles2 opengles metal software)'
|
'--render-driver=[Request SDL to use the given render driver]:driver name:(direct3d opengl opengles2 opengles metal software)'
|
||||||
'--require-audio=[Make scrcpy fail if audio is enabled but does not work]'
|
'--require-audio=[Make scrcpy fail if audio is enabled but does not work]'
|
||||||
|
'--root[Launch the server as root]'
|
||||||
'--rotation=[Set the initial display rotation]:rotation values:(0 1 2 3)'
|
'--rotation=[Set the initial display rotation]:rotation values:(0 1 2 3)'
|
||||||
{-s,--serial=}'[The device serial number \(mandatory for multiple devices only\)]:serial:($("${ADB-adb}" devices | awk '\''$2 == "device" {print $1}'\''))'
|
{-s,--serial=}'[The device serial number \(mandatory for multiple devices only\)]:serial:($("${ADB-adb}" devices | awk '\''$2 == "device" {print $1}'\''))'
|
||||||
{-S,--turn-screen-off}'[Turn the device screen off immediately]'
|
{-S,--turn-screen-off}'[Turn the device screen off immediately]'
|
||||||
|
|
|
@ -79,6 +79,7 @@ enum {
|
||||||
OPT_AUDIO_SOURCE,
|
OPT_AUDIO_SOURCE,
|
||||||
OPT_KILL_ADB_ON_CLOSE,
|
OPT_KILL_ADB_ON_CLOSE,
|
||||||
OPT_TIME_LIMIT,
|
OPT_TIME_LIMIT,
|
||||||
|
OPT_ROOT,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sc_option {
|
struct sc_option {
|
||||||
|
@ -535,6 +536,11 @@ static const struct sc_option options[] = {
|
||||||
"Possible values are 0, 1, 2 and 3. Each increment adds a 90 "
|
"Possible values are 0, 1, 2 and 3. Each increment adds a 90 "
|
||||||
"degrees rotation counterclockwise.",
|
"degrees rotation counterclockwise.",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.longopt_id = OPT_ROOT,
|
||||||
|
.longopt = "root",
|
||||||
|
.text = "Launch the server as root (disabled by default).",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.shortopt = 's',
|
.shortopt = 's',
|
||||||
.longopt = "serial",
|
.longopt = "serial",
|
||||||
|
@ -1977,6 +1983,9 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case OPT_ROOT:
|
||||||
|
opts->root = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// getopt prints the error message on stderr
|
// getopt prints the error message on stderr
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -82,4 +82,5 @@ const struct scrcpy_options scrcpy_options_default = {
|
||||||
.list_encoders = false,
|
.list_encoders = false,
|
||||||
.list_displays = false,
|
.list_displays = false,
|
||||||
.kill_adb_on_close = false,
|
.kill_adb_on_close = false,
|
||||||
|
.root = false,
|
||||||
};
|
};
|
||||||
|
|
|
@ -182,6 +182,7 @@ struct scrcpy_options {
|
||||||
bool list_encoders;
|
bool list_encoders;
|
||||||
bool list_displays;
|
bool list_displays;
|
||||||
bool kill_adb_on_close;
|
bool kill_adb_on_close;
|
||||||
|
bool root;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const struct scrcpy_options scrcpy_options_default;
|
extern const struct scrcpy_options scrcpy_options_default;
|
||||||
|
|
|
@ -380,6 +380,7 @@ scrcpy(struct scrcpy_options *options) {
|
||||||
.list_encoders = options->list_encoders,
|
.list_encoders = options->list_encoders,
|
||||||
.list_displays = options->list_displays,
|
.list_displays = options->list_displays,
|
||||||
.kill_adb_on_close = options->kill_adb_on_close,
|
.kill_adb_on_close = options->kill_adb_on_close,
|
||||||
|
.root = options->root,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct sc_server_callbacks cbs = {
|
static const struct sc_server_callbacks cbs = {
|
||||||
|
|
|
@ -194,6 +194,13 @@ execute_server(struct sc_server *server,
|
||||||
cmd[count++] = "-s";
|
cmd[count++] = "-s";
|
||||||
cmd[count++] = serial;
|
cmd[count++] = serial;
|
||||||
cmd[count++] = "shell";
|
cmd[count++] = "shell";
|
||||||
|
|
||||||
|
if (params->root) {
|
||||||
|
cmd[count++] = "su";
|
||||||
|
cmd[count++] = "1000"; // AID_SYSTEM, AID_GRAPHICS is also supported for FLAG_SECURE but lacks other perms
|
||||||
|
cmd[count++] = "-c";
|
||||||
|
}
|
||||||
|
|
||||||
cmd[count++] = "CLASSPATH=" SC_DEVICE_SERVER_PATH;
|
cmd[count++] = "CLASSPATH=" SC_DEVICE_SERVER_PATH;
|
||||||
cmd[count++] = "app_process";
|
cmd[count++] = "app_process";
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,7 @@ struct sc_server_params {
|
||||||
bool list_encoders;
|
bool list_encoders;
|
||||||
bool list_displays;
|
bool list_displays;
|
||||||
bool kill_adb_on_close;
|
bool kill_adb_on_close;
|
||||||
|
bool root;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sc_server {
|
struct sc_server {
|
||||||
|
|
|
@ -74,12 +74,12 @@ public final class AudioCapture {
|
||||||
Intent intent = new Intent(Intent.ACTION_MAIN);
|
Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
intent.addCategory(Intent.CATEGORY_LAUNCHER);
|
intent.addCategory(Intent.CATEGORY_LAUNCHER);
|
||||||
intent.setComponent(new ComponentName(FakeContext.PACKAGE_NAME, "com.android.shell.HeapDumpActivity"));
|
intent.setComponent(new ComponentName(FakeContext.PACKAGE_SHELL, "com.android.shell.HeapDumpActivity"));
|
||||||
ServiceManager.getActivityManager().startActivityAsUserWithFeature(intent);
|
ServiceManager.getActivityManager().startActivityAsUserWithFeature(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void stopWorkaroundAndroid11() {
|
private static void stopWorkaroundAndroid11() {
|
||||||
ServiceManager.getActivityManager().forceStopPackage(FakeContext.PACKAGE_NAME);
|
ServiceManager.getActivityManager().forceStopPackage(FakeContext.PACKAGE_SHELL);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tryStartRecording(int attempts, int delayMs) throws AudioCaptureForegroundException {
|
private void tryStartRecording(int attempts, int delayMs) throws AudioCaptureForegroundException {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import android.os.Build;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.HandlerThread;
|
import android.os.HandlerThread;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
import android.system.Os;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
@ -170,7 +171,7 @@ public final class AudioEncoder implements AsyncProcessor {
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.M)
|
@TargetApi(Build.VERSION_CODES.M)
|
||||||
public void encode() throws IOException, ConfigurationException, AudioCaptureForegroundException {
|
public void encode() throws IOException, ConfigurationException, AudioCaptureForegroundException {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
|
if ((Os.getuid() == 2000) && (Build.VERSION.SDK_INT < Build.VERSION_CODES.R)) {
|
||||||
Ln.w("Audio disabled: it is not supported before Android 11");
|
Ln.w("Audio disabled: it is not supported before Android 11");
|
||||||
streamer.writeDisableStream(false);
|
streamer.writeDisableStream(false);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -2,6 +2,7 @@ package com.genymobile.scrcpy;
|
||||||
|
|
||||||
import android.media.MediaCodec;
|
import android.media.MediaCodec;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.system.Os;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
@ -22,7 +23,7 @@ public final class AudioRawRecorder implements AsyncProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void record() throws IOException, AudioCaptureForegroundException {
|
private void record() throws IOException, AudioCaptureForegroundException {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
|
if ((Os.getuid() == 2000) && (Build.VERSION.SDK_INT < Build.VERSION_CODES.R)) {
|
||||||
Ln.w("Audio disabled: it is not supported before Android 11");
|
Ln.w("Audio disabled: it is not supported before Android 11");
|
||||||
streamer.writeDisableStream(false);
|
streamer.writeDisableStream(false);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -5,10 +5,12 @@ import android.content.AttributionSource;
|
||||||
import android.content.MutableContextWrapper;
|
import android.content.MutableContextWrapper;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
|
import android.system.Os;
|
||||||
|
|
||||||
public final class FakeContext extends MutableContextWrapper {
|
public final class FakeContext extends MutableContextWrapper {
|
||||||
|
|
||||||
public static final String PACKAGE_NAME = "com.android.shell";
|
public static final String PACKAGE_NAME = Os.getuid() == 1000 ? "android" : "com.android.shell";
|
||||||
|
public static final String PACKAGE_SHELL = "com.android.shell";
|
||||||
public static final int ROOT_UID = 0; // Like android.os.Process.ROOT_UID, but before API 29
|
public static final int ROOT_UID = 0; // Like android.os.Process.ROOT_UID, but before API 29
|
||||||
|
|
||||||
private static final FakeContext INSTANCE = new FakeContext();
|
private static final FakeContext INSTANCE = new FakeContext();
|
||||||
|
@ -31,6 +33,7 @@ public final class FakeContext extends MutableContextWrapper {
|
||||||
return PACKAGE_NAME;
|
return PACKAGE_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.S)
|
@TargetApi(Build.VERSION_CODES.S)
|
||||||
@Override
|
@Override
|
||||||
public AttributionSource getAttributionSource() {
|
public AttributionSource getAttributionSource() {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import android.os.IBinder;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
|
import android.system.Os;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
@ -269,6 +270,9 @@ public class ScreenEncoder implements Device.RotationListener, Device.FoldListen
|
||||||
// On Android 12 preview, SDK_INT is still R (not S), but CODENAME is "S".
|
// On Android 12 preview, SDK_INT is still R (not S), but CODENAME is "S".
|
||||||
boolean secure = Build.VERSION.SDK_INT < Build.VERSION_CODES.R || (Build.VERSION.SDK_INT == Build.VERSION_CODES.R && !"S"
|
boolean secure = Build.VERSION.SDK_INT < Build.VERSION_CODES.R || (Build.VERSION.SDK_INT == Build.VERSION_CODES.R && !"S"
|
||||||
.equals(Build.VERSION.CODENAME));
|
.equals(Build.VERSION.CODENAME));
|
||||||
|
if (Os.getuid() < 2000) {
|
||||||
|
secure = true;
|
||||||
|
}
|
||||||
return SurfaceControl.createDisplay("scrcpy", secure);
|
return SurfaceControl.createDisplay("scrcpy", secure);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue