From 31b965ca186968ee47395ae6cf268f715195fbf0 Mon Sep 17 00:00:00 2001 From: RiggiG <44820045+RiggiG@users.noreply.github.com> Date: Thu, 22 Jun 2023 21:24:55 -0400 Subject: [PATCH 01/10] change: execute server as root and create secure display --- app/src/server.c | 5 +++++ .../src/main/java/com/genymobile/scrcpy/ScreenCapture.java | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/src/server.c b/app/src/server.c index d4726c2af8..0e8344a60a 100644 --- a/app/src/server.c +++ b/app/src/server.c @@ -215,6 +215,11 @@ execute_server(struct sc_server *server, cmd[count++] = "-s"; cmd[count++] = serial; cmd[count++] = "shell"; +//# ifdef SERVER_ASROOT // make this conditional at some point, would require condition in the server as well + cmd[count++] = "su"; + cmd[count++] = "1003"; // AID_GRAPHICS, AID_SYSTEM is also supported for FLAG_SECURE + cmd[count++] = "-c"; +//# endif cmd[count++] = "CLASSPATH=" SC_DEVICE_SERVER_PATH; cmd[count++] = "app_process"; diff --git a/server/src/main/java/com/genymobile/scrcpy/ScreenCapture.java b/server/src/main/java/com/genymobile/scrcpy/ScreenCapture.java index e048354a93..3e5a560b08 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ScreenCapture.java +++ b/server/src/main/java/com/genymobile/scrcpy/ScreenCapture.java @@ -72,8 +72,9 @@ public void onRotationChanged(int rotation) { private static IBinder createDisplay() { // Since Android 12 (preview), secure displays could not be created with shell permissions anymore. // 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".equals( - Build.VERSION.CODENAME)); + boolean secure = true; //hardcoded for root testing, but should be conditional based on flag + //boolean secure = Build.VERSION.SDK_INT < Build.VERSION_CODES.R || (Build.VERSION.SDK_INT == Build.VERSION_CODES.R && !"S" + // .equals(Build.VERSION.CODENAME)); return SurfaceControl.createDisplay("scrcpy", secure); } From e0cb239481d676a5091c5718380248b1a84be8eb Mon Sep 17 00:00:00 2001 From: RiggiG <44820045+RiggiG@users.noreply.github.com> Date: Thu, 22 Jun 2023 22:45:56 -0400 Subject: [PATCH 02/10] change: AID_GRAPHICS -> AID_SYSTEM --- app/src/server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/server.c b/app/src/server.c index 0e8344a60a..263ee8c0e2 100644 --- a/app/src/server.c +++ b/app/src/server.c @@ -217,7 +217,7 @@ execute_server(struct sc_server *server, cmd[count++] = "shell"; //# ifdef SERVER_ASROOT // make this conditional at some point, would require condition in the server as well cmd[count++] = "su"; - cmd[count++] = "1003"; // AID_GRAPHICS, AID_SYSTEM is also supported for FLAG_SECURE + cmd[count++] = "1000"; // AID_SYSTEM, AID_GRAPHICS is also supported for FLAG_SECURE but lacks other perms cmd[count++] = "-c"; //# endif cmd[count++] = "CLASSPATH=" SC_DEVICE_SERVER_PATH; From 5d585ed011018aa32db191de767ca848a45d2dd1 Mon Sep 17 00:00:00 2001 From: RiggiG <44820045+RiggiG@users.noreply.github.com> Date: Thu, 29 Jun 2023 00:41:03 -0400 Subject: [PATCH 03/10] add: fllag --root to launch server as root --- app/src/cli.c | 9 +++++++++ app/src/options.c | 1 + app/src/options.h | 1 + app/src/scrcpy.c | 1 + app/src/server.c | 12 +++++++----- app/src/server.h | 1 + .../java/com/genymobile/scrcpy/ScreenCapture.java | 8 +++++--- .../java/com/genymobile/scrcpy/SurfaceEncoder.java | 3 ++- 8 files changed, 27 insertions(+), 9 deletions(-) diff --git a/app/src/cli.c b/app/src/cli.c index fd4525f557..37db8c6d08 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -93,6 +93,7 @@ enum { OPT_DISPLAY_ORIENTATION, OPT_RECORD_ORIENTATION, OPT_ORIENTATION, + OPT_ROOT, }; struct sc_option { @@ -650,6 +651,12 @@ static const struct sc_option options[] = { .longopt = "rotation", .argdesc = "value", }, + { + .longopt_id = OPT_ROOT, + .longopt = "root", + .text = "Try to launch the server as root.\n" + "Disabled by default.", + }, { .shortopt = 's', .longopt = "serial", @@ -2354,6 +2361,8 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[], break; case OPT_CAMERA_HIGH_SPEED: opts->camera_high_speed = true; + case OPT_ROOT: + opts->root = true; break; default: // getopt prints the error message on stderr diff --git a/app/src/options.c b/app/src/options.c index a13df585a6..5c08b74930 100644 --- a/app/src/options.c +++ b/app/src/options.c @@ -89,6 +89,7 @@ const struct scrcpy_options scrcpy_options_default = { .kill_adb_on_close = false, .camera_high_speed = false, .list = 0, + .root = false, }; enum sc_orientation diff --git a/app/src/options.h b/app/src/options.h index 11e64fa19e..7958ab3715 100644 --- a/app/src/options.h +++ b/app/src/options.h @@ -273,6 +273,7 @@ struct scrcpy_options { #define SC_OPTION_LIST_CAMERAS 0x4 #define SC_OPTION_LIST_CAMERA_SIZES 0x8 uint8_t list; + bool root; }; extern const struct scrcpy_options scrcpy_options_default; diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index cf2e7e4786..0d814ebb76 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -388,6 +388,7 @@ scrcpy(struct scrcpy_options *options) { .kill_adb_on_close = options->kill_adb_on_close, .camera_high_speed = options->camera_high_speed, .list = options->list, + .root = options->root, }; static const struct sc_server_callbacks cbs = { diff --git a/app/src/server.c b/app/src/server.c index 263ee8c0e2..2473ec291c 100644 --- a/app/src/server.c +++ b/app/src/server.c @@ -215,11 +215,13 @@ execute_server(struct sc_server *server, cmd[count++] = "-s"; cmd[count++] = serial; cmd[count++] = "shell"; -//# ifdef SERVER_ASROOT // make this conditional at some point, would require condition in the server as well - cmd[count++] = "su"; - cmd[count++] = "1000"; // AID_SYSTEM, AID_GRAPHICS is also supported for FLAG_SECURE but lacks other perms - cmd[count++] = "-c"; -//# endif + + 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++] = "app_process"; diff --git a/app/src/server.h b/app/src/server.h index 062af0a9eb..39c8e1a80e 100644 --- a/app/src/server.h +++ b/app/src/server.h @@ -65,6 +65,7 @@ struct sc_server_params { bool kill_adb_on_close; bool camera_high_speed; uint8_t list; + bool root; }; struct sc_server { diff --git a/server/src/main/java/com/genymobile/scrcpy/ScreenCapture.java b/server/src/main/java/com/genymobile/scrcpy/ScreenCapture.java index 3e5a560b08..f833c16f3d 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ScreenCapture.java +++ b/server/src/main/java/com/genymobile/scrcpy/ScreenCapture.java @@ -72,9 +72,11 @@ public void onRotationChanged(int rotation) { private static IBinder createDisplay() { // Since Android 12 (preview), secure displays could not be created with shell permissions anymore. // On Android 12 preview, SDK_INT is still R (not S), but CODENAME is "S". - boolean secure = true; //hardcoded for root testing, but should be conditional based on flag - //boolean secure = Build.VERSION.SDK_INT < Build.VERSION_CODES.R || (Build.VERSION.SDK_INT == Build.VERSION_CODES.R && !"S" - // .equals(Build.VERSION.CODENAME)); + boolean secure = Build.VERSION.SDK_INT < Build.VERSION_CODES.R || (Build.VERSION.SDK_INT == Build.VERSION_CODES.R && !"S" + .equals(Build.VERSION.CODENAME)); + if (Os.getuid() < 2000) { + secure = true; + } return SurfaceControl.createDisplay("scrcpy", secure); } diff --git a/server/src/main/java/com/genymobile/scrcpy/SurfaceEncoder.java b/server/src/main/java/com/genymobile/scrcpy/SurfaceEncoder.java index 28435c0988..5b494e78b0 100644 --- a/server/src/main/java/com/genymobile/scrcpy/SurfaceEncoder.java +++ b/server/src/main/java/com/genymobile/scrcpy/SurfaceEncoder.java @@ -6,6 +6,7 @@ import android.os.Looper; import android.os.SystemClock; import android.view.Surface; +import android.system.Os; import java.io.IOException; import java.nio.ByteBuffer; @@ -29,7 +30,7 @@ public class SurfaceEncoder implements AsyncProcessor { private final int videoBitRate; private final int maxFps; private final boolean downsizeOnError; - + private boolean firstFrameSent; private int consecutiveErrors; From 3d294231715a225ef858dfb612a061bd2d25fde5 Mon Sep 17 00:00:00 2001 From: RiggiG <44820045+RiggiG@users.noreply.github.com> Date: Thu, 29 Jun 2023 00:43:20 -0400 Subject: [PATCH 04/10] cleanup: phantom whitespace --- server/src/main/java/com/genymobile/scrcpy/SurfaceEncoder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/SurfaceEncoder.java b/server/src/main/java/com/genymobile/scrcpy/SurfaceEncoder.java index 5b494e78b0..52ddbdd097 100644 --- a/server/src/main/java/com/genymobile/scrcpy/SurfaceEncoder.java +++ b/server/src/main/java/com/genymobile/scrcpy/SurfaceEncoder.java @@ -30,7 +30,7 @@ public class SurfaceEncoder implements AsyncProcessor { private final int videoBitRate; private final int maxFps; private final boolean downsizeOnError; - + private boolean firstFrameSent; private int consecutiveErrors; From 858d19b7409d41f9cc98ec6a2e6e0f7fc2993054 Mon Sep 17 00:00:00 2001 From: RiggiG <44820045+RiggiG@users.noreply.github.com> Date: Thu, 29 Jun 2023 00:45:28 -0400 Subject: [PATCH 05/10] change: eliminate ambiguous description --- app/src/cli.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/cli.c b/app/src/cli.c index 37db8c6d08..4450c03a30 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -654,8 +654,7 @@ static const struct sc_option options[] = { { .longopt_id = OPT_ROOT, .longopt = "root", - .text = "Try to launch the server as root.\n" - "Disabled by default.", + .text = "Launch the server as root (disabled by default).", }, { .shortopt = 's', From 33bb8d51dc65cf1cb8f66c35172e9663bdbc0e12 Mon Sep 17 00:00:00 2001 From: RiggiG <44820045+RiggiG@users.noreply.github.com> Date: Thu, 29 Jun 2023 00:58:29 -0400 Subject: [PATCH 06/10] add: autocompletion for --root --- app/data/bash-completion/scrcpy | 1 + app/data/zsh-completion/_scrcpy | 2 ++ 2 files changed, 3 insertions(+) diff --git a/app/data/bash-completion/scrcpy b/app/data/bash-completion/scrcpy index 0c85431077..459353183e 100644 --- a/app/data/bash-completion/scrcpy +++ b/app/data/bash-completion/scrcpy @@ -66,6 +66,7 @@ _scrcpy() { --record-orientation= --render-driver= --require-audio + --root --rotation= -s --serial= -S --turn-screen-off diff --git a/app/data/zsh-completion/_scrcpy b/app/data/zsh-completion/_scrcpy index 3c7ca2178a..6107428dba 100644 --- a/app/data/zsh-completion/_scrcpy +++ b/app/data/zsh-completion/_scrcpy @@ -71,6 +71,8 @@ arguments=( '--record-orientation=[Set the record orientation]:orientation values:(0 90 180 270)' '--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]' + '--root[Launch the server as root]' + '--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,--turn-screen-off}'[Turn the device screen off immediately]' '--shortcut-mod=[\[key1,key2+key3,...\] Specify the modifiers to use for scrcpy shortcuts]:shortcut mod:(lctrl rctrl lalt ralt lsuper rsuper)' From 5c12cba825e4c3cce964a8ffb44b12864cbfe6eb Mon Sep 17 00:00:00 2001 From: RiggiG <44820045+RiggiG@users.noreply.github.com> Date: Thu, 29 Jun 2023 20:18:57 -0400 Subject: [PATCH 07/10] change: use getPackageName method rather than fixed constant for `--root` case --- .../com/genymobile/scrcpy/AudioCapture.java | 4 +-- .../com/genymobile/scrcpy/FakeContext.java | 17 +++++++++++ .../com/genymobile/scrcpy/Workarounds.java | 2 +- .../scrcpy/wrappers/ActivityManager.java | 2 +- .../scrcpy/wrappers/ClipboardManager.java | 28 +++++++++---------- .../scrcpy/wrappers/ContentProvider.java | 6 ++-- 6 files changed, 38 insertions(+), 21 deletions(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/AudioCapture.java b/server/src/main/java/com/genymobile/scrcpy/AudioCapture.java index e3de50e639..b20e8a87ad 100644 --- a/server/src/main/java/com/genymobile/scrcpy/AudioCapture.java +++ b/server/src/main/java/com/genymobile/scrcpy/AudioCapture.java @@ -78,12 +78,12 @@ private static void startWorkaroundAndroid11() { Intent intent = new Intent(Intent.ACTION_MAIN); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addCategory(Intent.CATEGORY_LAUNCHER); - intent.setComponent(new ComponentName(FakeContext.PACKAGE_NAME, "com.android.shell.HeapDumpActivity")); + intent.setComponent(new ComponentName(FakeContext.getPackageNameStatic(), "com.android.shell.HeapDumpActivity")); ServiceManager.getActivityManager().startActivityAsUserWithFeature(intent); } private static void stopWorkaroundAndroid11() { - ServiceManager.getActivityManager().forceStopPackage(FakeContext.PACKAGE_NAME); + ServiceManager.getActivityManager().forceStopPackage(FakeContext.getPackageNameStatic()); } private void tryStartRecording(int attempts, int delayMs) throws AudioCaptureForegroundException { diff --git a/server/src/main/java/com/genymobile/scrcpy/FakeContext.java b/server/src/main/java/com/genymobile/scrcpy/FakeContext.java index 2ea7bf4aff..b4f87aadfa 100644 --- a/server/src/main/java/com/genymobile/scrcpy/FakeContext.java +++ b/server/src/main/java/com/genymobile/scrcpy/FakeContext.java @@ -6,6 +6,7 @@ import android.content.ContextWrapper; import android.os.Build; import android.os.Process; +import android.system.Os; public final class FakeContext extends ContextWrapper { @@ -24,11 +25,24 @@ private FakeContext() { @Override public String getPackageName() { + if (Os.getuid() == 1000) { + return "android"; + } return PACKAGE_NAME; } @Override public String getOpPackageName() { + if (Os.getuid() == 1000) { + return "android"; + } + return PACKAGE_NAME; + } + + public static String getPackageNameStatic() { + if (Os.getuid() == 1000) { + return "android"; + } return PACKAGE_NAME; } @@ -37,6 +51,9 @@ public String getOpPackageName() { public AttributionSource getAttributionSource() { AttributionSource.Builder builder = new AttributionSource.Builder(Process.SHELL_UID); builder.setPackageName(PACKAGE_NAME); + if (Os.getuid() == 1000) { + builder.setPackageName("android"); + } return builder.build(); } diff --git a/server/src/main/java/com/genymobile/scrcpy/Workarounds.java b/server/src/main/java/com/genymobile/scrcpy/Workarounds.java index 448e7099fa..40ef8acb3d 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Workarounds.java +++ b/server/src/main/java/com/genymobile/scrcpy/Workarounds.java @@ -128,7 +128,7 @@ private static void fillAppInfo() { Object appBindData = appBindDataConstructor.newInstance(); ApplicationInfo applicationInfo = new ApplicationInfo(); - applicationInfo.packageName = FakeContext.PACKAGE_NAME; + applicationInfo.packageName = FakeContext.getPackageNameStatic(); // appBindData.appInfo = applicationInfo; Field appInfoField = appBindDataClass.getDeclaredField("appInfo"); diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/ActivityManager.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/ActivityManager.java index 75115618ba..591471f3d9 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/ActivityManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/ActivityManager.java @@ -113,7 +113,7 @@ public int startActivityAsUserWithFeature(Intent intent) { return (int) method.invoke( /* this */ manager, /* caller */ null, - /* callingPackage */ FakeContext.PACKAGE_NAME, + /* callingPackage */ FakeContext.getPackageNameStatic(), /* callingFeatureId */ null, /* intent */ intent, /* resolvedType */ null, diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/ClipboardManager.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/ClipboardManager.java index 0866d42dde..afc5ba85f5 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/ClipboardManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/ClipboardManager.java @@ -82,40 +82,40 @@ private Method getSetPrimaryClipMethod() throws NoSuchMethodException { private static ClipData getPrimaryClip(Method method, int methodVersion, IInterface manager) throws InvocationTargetException, IllegalAccessException { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { - return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME); + return (ClipData) method.invoke(manager, FakeContext.getPackageNameStatic()); } switch (methodVersion) { case 0: - return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, FakeContext.ROOT_UID); + return (ClipData) method.invoke(manager, FakeContext.getPackageNameStatic(), FakeContext.ROOT_UID); case 1: - return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID); + return (ClipData) method.invoke(manager, FakeContext.getPackageNameStatic(), null, FakeContext.ROOT_UID); case 2: - return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID, 0); + return (ClipData) method.invoke(manager, FakeContext.getPackageNameStatic(), null, FakeContext.ROOT_UID, 0); case 3: - return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, FakeContext.ROOT_UID, null); + return (ClipData) method.invoke(manager, FakeContext.getPackageNameStatic(), FakeContext.ROOT_UID, null); default: // The last boolean parameter is "userOperate" - return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID, 0, true); + return (ClipData) method.invoke(manager, FakeContext.getPackageNameStatic(), null, FakeContext.ROOT_UID, 0, true); } } private static void setPrimaryClip(Method method, int methodVersion, IInterface manager, ClipData clipData) throws InvocationTargetException, IllegalAccessException { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { - method.invoke(manager, clipData, FakeContext.PACKAGE_NAME); + method.invoke(manager, clipData, FakeContext.getPackageNameStatic()); return; } switch (methodVersion) { case 0: - method.invoke(manager, clipData, FakeContext.PACKAGE_NAME, FakeContext.ROOT_UID); + method.invoke(manager, clipData, FakeContext.getPackageNameStatic(), FakeContext.ROOT_UID); break; case 1: - method.invoke(manager, clipData, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID); + method.invoke(manager, clipData, FakeContext.getPackageNameStatic(), null, FakeContext.ROOT_UID); break; default: - method.invoke(manager, clipData, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID, 0); + method.invoke(manager, clipData, FakeContext.getPackageNameStatic(), null, FakeContext.ROOT_UID, 0); break; } } @@ -149,19 +149,19 @@ public boolean setText(CharSequence text) { private static void addPrimaryClipChangedListener(Method method, int methodVersion, IInterface manager, IOnPrimaryClipChangedListener listener) throws InvocationTargetException, IllegalAccessException { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { - method.invoke(manager, listener, FakeContext.PACKAGE_NAME); + method.invoke(manager, listener, FakeContext.getPackageNameStatic()); return; } switch (methodVersion) { case 0: - method.invoke(manager, listener, FakeContext.PACKAGE_NAME, FakeContext.ROOT_UID); + method.invoke(manager, listener, FakeContext.getPackageNameStatic(), FakeContext.ROOT_UID); break; case 1: - method.invoke(manager, listener, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID); + method.invoke(manager, listener, FakeContext.getPackageNameStatic(), null, FakeContext.ROOT_UID); break; default: - method.invoke(manager, listener, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID, 0); + method.invoke(manager, listener, FakeContext.getPackageNameStatic(), null, FakeContext.ROOT_UID, 0); break; } } diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/ContentProvider.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/ContentProvider.java index 8171988e90..93bb065b60 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/ContentProvider.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/ContentProvider.java @@ -88,13 +88,13 @@ private Bundle call(String callMethod, String arg, Bundle extras) } else { switch (callMethodVersion) { case 1: - args = new Object[]{FakeContext.PACKAGE_NAME, null, "settings", callMethod, arg, extras}; + args = new Object[]{FakeContext.getPackageNameStatic(), null, "settings", callMethod, arg, extras}; break; case 2: - args = new Object[]{FakeContext.PACKAGE_NAME, "settings", callMethod, arg, extras}; + args = new Object[]{FakeContext.getPackageNameStatic(), "settings", callMethod, arg, extras}; break; default: - args = new Object[]{FakeContext.PACKAGE_NAME, callMethod, arg, extras}; + args = new Object[]{FakeContext.getPackageNameStatic(), callMethod, arg, extras}; break; } } From aa0c71ccd4c9560eaa5afc5c002ec16a3e28911b Mon Sep 17 00:00:00 2001 From: RiggiG <44820045+RiggiG@users.noreply.github.com> Date: Thu, 29 Jun 2023 21:30:32 -0400 Subject: [PATCH 08/10] change: at least try audio when running as AID_SYSTEM on older versions --- app/src/server.c | 2 +- server/src/main/java/com/genymobile/scrcpy/AudioEncoder.java | 3 ++- .../src/main/java/com/genymobile/scrcpy/AudioRawRecorder.java | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/server.c b/app/src/server.c index 2473ec291c..2ba80c4c24 100644 --- a/app/src/server.c +++ b/app/src/server.c @@ -215,7 +215,7 @@ execute_server(struct sc_server *server, cmd[count++] = "-s"; cmd[count++] = serial; 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 diff --git a/server/src/main/java/com/genymobile/scrcpy/AudioEncoder.java b/server/src/main/java/com/genymobile/scrcpy/AudioEncoder.java index 0b59369b9e..f63b7310d8 100644 --- a/server/src/main/java/com/genymobile/scrcpy/AudioEncoder.java +++ b/server/src/main/java/com/genymobile/scrcpy/AudioEncoder.java @@ -7,6 +7,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; +import android.system.Os; import java.io.IOException; import java.nio.ByteBuffer; @@ -167,7 +168,7 @@ private synchronized void waitEnded() { @TargetApi(Build.VERSION_CODES.M) 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"); streamer.writeDisableStream(false); return; diff --git a/server/src/main/java/com/genymobile/scrcpy/AudioRawRecorder.java b/server/src/main/java/com/genymobile/scrcpy/AudioRawRecorder.java index 7e052f32d4..e2bdf09610 100644 --- a/server/src/main/java/com/genymobile/scrcpy/AudioRawRecorder.java +++ b/server/src/main/java/com/genymobile/scrcpy/AudioRawRecorder.java @@ -2,6 +2,7 @@ import android.media.MediaCodec; import android.os.Build; +import android.system.Os; import java.io.IOException; import java.nio.ByteBuffer; @@ -19,7 +20,7 @@ public AudioRawRecorder(AudioCapture capture, Streamer streamer) { } 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"); streamer.writeDisableStream(false); return; From 391d604712cb4bb5d04164048d992552ddba91cf Mon Sep 17 00:00:00 2001 From: RiggiG <44820045+RiggiG@users.noreply.github.com> Date: Thu, 29 Jun 2023 22:03:44 -0400 Subject: [PATCH 09/10] fixups: PACKAGE_SHELL constant where necessary, DRY --- .../java/com/genymobile/scrcpy/AudioCapture.java | 4 ++-- .../java/com/genymobile/scrcpy/FakeContext.java | 16 +++++----------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/AudioCapture.java b/server/src/main/java/com/genymobile/scrcpy/AudioCapture.java index b20e8a87ad..e2c4a2cc85 100644 --- a/server/src/main/java/com/genymobile/scrcpy/AudioCapture.java +++ b/server/src/main/java/com/genymobile/scrcpy/AudioCapture.java @@ -78,12 +78,12 @@ private static void startWorkaroundAndroid11() { Intent intent = new Intent(Intent.ACTION_MAIN); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addCategory(Intent.CATEGORY_LAUNCHER); - intent.setComponent(new ComponentName(FakeContext.getPackageNameStatic(), "com.android.shell.HeapDumpActivity")); + intent.setComponent(new ComponentName(FakeContext.PACKAGE_SHELL, "com.android.shell.HeapDumpActivity")); ServiceManager.getActivityManager().startActivityAsUserWithFeature(intent); } private static void stopWorkaroundAndroid11() { - ServiceManager.getActivityManager().forceStopPackage(FakeContext.getPackageNameStatic()); + ServiceManager.getActivityManager().forceStopPackage(FakeContext.PACKAGE_SHELL); } private void tryStartRecording(int attempts, int delayMs) throws AudioCaptureForegroundException { diff --git a/server/src/main/java/com/genymobile/scrcpy/FakeContext.java b/server/src/main/java/com/genymobile/scrcpy/FakeContext.java index b4f87aadfa..ba12917691 100644 --- a/server/src/main/java/com/genymobile/scrcpy/FakeContext.java +++ b/server/src/main/java/com/genymobile/scrcpy/FakeContext.java @@ -10,7 +10,7 @@ public final class FakeContext extends ContextWrapper { - public static final String PACKAGE_NAME = "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 private static final FakeContext INSTANCE = new FakeContext(); @@ -25,32 +25,26 @@ private FakeContext() { @Override public String getPackageName() { - if (Os.getuid() == 1000) { - return "android"; - } - return PACKAGE_NAME; + return getPackageNameStatic(); } @Override public String getOpPackageName() { - if (Os.getuid() == 1000) { - return "android"; - } - return PACKAGE_NAME; + return getPackageNameStatic(); } public static String getPackageNameStatic() { if (Os.getuid() == 1000) { return "android"; } - return PACKAGE_NAME; + return PACKAGE_SHELL; } @TargetApi(Build.VERSION_CODES.S) @Override public AttributionSource getAttributionSource() { AttributionSource.Builder builder = new AttributionSource.Builder(Process.SHELL_UID); - builder.setPackageName(PACKAGE_NAME); + builder.setPackageName(PACKAGE_SHELL); if (Os.getuid() == 1000) { builder.setPackageName("android"); } From dacdc2e487ba004bc91ea495569ddcf194756434 Mon Sep 17 00:00:00 2001 From: RiggiG <44820045+RiggiG@users.noreply.github.com> Date: Fri, 30 Jun 2023 18:21:15 -0400 Subject: [PATCH 10/10] cleanup: usage of PACKAGE_NAME + PACKAGE_SHELL --- .../com/genymobile/scrcpy/FakeContext.java | 16 +++-------- .../com/genymobile/scrcpy/Workarounds.java | 2 +- .../scrcpy/wrappers/ActivityManager.java | 2 +- .../scrcpy/wrappers/ClipboardManager.java | 28 +++++++++---------- .../scrcpy/wrappers/ContentProvider.java | 6 ++-- 5 files changed, 23 insertions(+), 31 deletions(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/FakeContext.java b/server/src/main/java/com/genymobile/scrcpy/FakeContext.java index ba12917691..aaed560ca6 100644 --- a/server/src/main/java/com/genymobile/scrcpy/FakeContext.java +++ b/server/src/main/java/com/genymobile/scrcpy/FakeContext.java @@ -10,6 +10,7 @@ public final class FakeContext extends ContextWrapper { + 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 @@ -25,29 +26,20 @@ private FakeContext() { @Override public String getPackageName() { - return getPackageNameStatic(); + return PACKAGE_NAME; } @Override public String getOpPackageName() { - return getPackageNameStatic(); + return PACKAGE_NAME; } - public static String getPackageNameStatic() { - if (Os.getuid() == 1000) { - return "android"; - } - return PACKAGE_SHELL; - } @TargetApi(Build.VERSION_CODES.S) @Override public AttributionSource getAttributionSource() { AttributionSource.Builder builder = new AttributionSource.Builder(Process.SHELL_UID); - builder.setPackageName(PACKAGE_SHELL); - if (Os.getuid() == 1000) { - builder.setPackageName("android"); - } + builder.setPackageName(PACKAGE_NAME); return builder.build(); } diff --git a/server/src/main/java/com/genymobile/scrcpy/Workarounds.java b/server/src/main/java/com/genymobile/scrcpy/Workarounds.java index 40ef8acb3d..448e7099fa 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Workarounds.java +++ b/server/src/main/java/com/genymobile/scrcpy/Workarounds.java @@ -128,7 +128,7 @@ private static void fillAppInfo() { Object appBindData = appBindDataConstructor.newInstance(); ApplicationInfo applicationInfo = new ApplicationInfo(); - applicationInfo.packageName = FakeContext.getPackageNameStatic(); + applicationInfo.packageName = FakeContext.PACKAGE_NAME; // appBindData.appInfo = applicationInfo; Field appInfoField = appBindDataClass.getDeclaredField("appInfo"); diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/ActivityManager.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/ActivityManager.java index 591471f3d9..75115618ba 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/ActivityManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/ActivityManager.java @@ -113,7 +113,7 @@ public int startActivityAsUserWithFeature(Intent intent) { return (int) method.invoke( /* this */ manager, /* caller */ null, - /* callingPackage */ FakeContext.getPackageNameStatic(), + /* callingPackage */ FakeContext.PACKAGE_NAME, /* callingFeatureId */ null, /* intent */ intent, /* resolvedType */ null, diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/ClipboardManager.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/ClipboardManager.java index afc5ba85f5..73eac6f54c 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/ClipboardManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/ClipboardManager.java @@ -82,40 +82,40 @@ private Method getSetPrimaryClipMethod() throws NoSuchMethodException { private static ClipData getPrimaryClip(Method method, int methodVersion, IInterface manager) throws InvocationTargetException, IllegalAccessException { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { - return (ClipData) method.invoke(manager, FakeContext.getPackageNameStatic()); + return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME); } switch (methodVersion) { case 0: - return (ClipData) method.invoke(manager, FakeContext.getPackageNameStatic(), FakeContext.ROOT_UID); + return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, FakeContext.ROOT_UID); case 1: - return (ClipData) method.invoke(manager, FakeContext.getPackageNameStatic(), null, FakeContext.ROOT_UID); + return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID); case 2: - return (ClipData) method.invoke(manager, FakeContext.getPackageNameStatic(), null, FakeContext.ROOT_UID, 0); + return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID, 0); case 3: - return (ClipData) method.invoke(manager, FakeContext.getPackageNameStatic(), FakeContext.ROOT_UID, null); + return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, FakeContext.ROOT_UID, null); default: // The last boolean parameter is "userOperate" - return (ClipData) method.invoke(manager, FakeContext.getPackageNameStatic(), null, FakeContext.ROOT_UID, 0, true); + return (ClipData) method.invoke(manager, FakeContext.FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID, 0, true); } } private static void setPrimaryClip(Method method, int methodVersion, IInterface manager, ClipData clipData) throws InvocationTargetException, IllegalAccessException { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { - method.invoke(manager, clipData, FakeContext.getPackageNameStatic()); + method.invoke(manager, clipData, FakeContext.PACKAGE_NAME); return; } switch (methodVersion) { case 0: - method.invoke(manager, clipData, FakeContext.getPackageNameStatic(), FakeContext.ROOT_UID); + method.invoke(manager, clipData, FakeContext.PACKAGE_NAME, FakeContext.ROOT_UID); break; case 1: - method.invoke(manager, clipData, FakeContext.getPackageNameStatic(), null, FakeContext.ROOT_UID); + method.invoke(manager, clipData, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID); break; default: - method.invoke(manager, clipData, FakeContext.getPackageNameStatic(), null, FakeContext.ROOT_UID, 0); + method.invoke(manager, clipData, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID, 0); break; } } @@ -149,19 +149,19 @@ public boolean setText(CharSequence text) { private static void addPrimaryClipChangedListener(Method method, int methodVersion, IInterface manager, IOnPrimaryClipChangedListener listener) throws InvocationTargetException, IllegalAccessException { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { - method.invoke(manager, listener, FakeContext.getPackageNameStatic()); + method.invoke(manager, listener, FakeContext.PACKAGE_NAME); return; } switch (methodVersion) { case 0: - method.invoke(manager, listener, FakeContext.getPackageNameStatic(), FakeContext.ROOT_UID); + method.invoke(manager, listener, FakeContext.PACKAGE_NAME, FakeContext.ROOT_UID); break; case 1: - method.invoke(manager, listener, FakeContext.getPackageNameStatic(), null, FakeContext.ROOT_UID); + method.invoke(manager, listener, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID); break; default: - method.invoke(manager, listener, FakeContext.getPackageNameStatic(), null, FakeContext.ROOT_UID, 0); + method.invoke(manager, listener, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID, 0); break; } } diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/ContentProvider.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/ContentProvider.java index 93bb065b60..8171988e90 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/ContentProvider.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/ContentProvider.java @@ -88,13 +88,13 @@ private Bundle call(String callMethod, String arg, Bundle extras) } else { switch (callMethodVersion) { case 1: - args = new Object[]{FakeContext.getPackageNameStatic(), null, "settings", callMethod, arg, extras}; + args = new Object[]{FakeContext.PACKAGE_NAME, null, "settings", callMethod, arg, extras}; break; case 2: - args = new Object[]{FakeContext.getPackageNameStatic(), "settings", callMethod, arg, extras}; + args = new Object[]{FakeContext.PACKAGE_NAME, "settings", callMethod, arg, extras}; break; default: - args = new Object[]{FakeContext.getPackageNameStatic(), callMethod, arg, extras}; + args = new Object[]{FakeContext.PACKAGE_NAME, callMethod, arg, extras}; break; } }