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:
RiggiG 2023-06-22 21:24:55 -04:00 committed by Jerry
parent 1ee46970e3
commit 7b1ac46ab0
13 changed files with 36 additions and 5 deletions

View file

@ -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

View file

@ -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]'

View file

@ -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;

View file

@ -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,
}; };

View file

@ -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;

View file

@ -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 = {

View file

@ -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";

View file

@ -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 {

View file

@ -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 {

View file

@ -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;

View file

@ -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;

View file

@ -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() {

View file

@ -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);
} }