Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions app/src/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ enum {
OPT_NO_VD_SYSTEM_DECORATIONS,
OPT_NO_VD_DESTROY_CONTENT,
OPT_DISPLAY_IME_POLICY,
OPT_ROOT,
};

struct sc_option {
Expand Down Expand Up @@ -841,6 +842,15 @@ static const struct sc_option options[] = {
"fails on the device. This option makes scrcpy fail if audio "
"is enabled but does not work."
},
{
.longopt_id = OPT_ROOT,
.longopt = "root",
.text = "By default, scrcpy simply uses the ADB shell user context to "
"execute the server on the device. This will instruct scrcpy "
"use the `su` command to run the server as the system user in "
"order to retain the ability to create a secure display. This "
"allows apps that use `FLAG_SECURE` to be mirrored to your PC."
},
{
// deprecated
.longopt_id = OPT_ROTATION,
Expand Down Expand Up @@ -2724,6 +2734,43 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
case OPT_REQUIRE_AUDIO:
opts->require_audio = true;
break;
case OPT_ROOT:
opts->root = true;

// 1. Check if 'su 1000 -c true' works
int su_status = system("adb shell su 1000 -c true >/dev/null 2>&1");
bool can_use_root = (su_status == 0);

// 2. Check if secure display is possible under shell
int sdk_version = 0;
char codename[16] = {0};

FILE *fp = popen("adb shell getprop ro.build.version.sdk", "r");
if (fp) {
fscanf(fp, "%d", &sdk_version);
pclose(fp);
}

fp = popen("adb shell getprop ro.build.version.codename", "r");
if (fp) {
fscanf(fp, "%15s", codename);
pclose(fp);
}

// 3. Determine secure display capability as upstream JAR does
bool shell_can_create_secure_display = (sdk_version < 30 || (sdk_version == 30 && strcmp(codename, "S") != 0));

// 4. Compute whether root should actually be enabled
bool needs_root_for_secure_display = !shell_can_create_secure_display;
opts->root_enabled = opts->root && can_use_root && needs_root_for_secure_display;

// 5. Provide user feedback
if (opts->root && !can_use_root && needs_root_for_secure_display) {
fprintf(stderr,
"WARNING: --root requested but 'su 1000' failed and secure display is unavailable on Android 12+.\n"
"Secure content will not be mirrored.\n");
}
break;
Comment thread
Giantvince1 marked this conversation as resolved.
case OPT_AUDIO_BUFFER:
if (!parse_buffering_time(optarg, &opts->audio_buffer)) {
return false;
Expand Down
2 changes: 2 additions & 0 deletions app/src/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ const struct scrcpy_options scrcpy_options_default = {
.angle = NULL,
.vd_destroy_content = true,
.vd_system_decorations = true,
.root = false,
.root_enabled = false,
};

enum sc_orientation
Expand Down
2 changes: 2 additions & 0 deletions app/src/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,8 @@ struct scrcpy_options {
const char *start_app;
bool vd_destroy_content;
bool vd_system_decorations;
bool root;
bool root_enabled;
};

extern const struct scrcpy_options scrcpy_options_default;
Expand Down
1 change: 1 addition & 0 deletions app/src/scrcpy.c
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,7 @@ scrcpy(struct scrcpy_options *options) {
.camera_high_speed = options->camera_high_speed,
.vd_destroy_content = options->vd_destroy_content,
.vd_system_decorations = options->vd_system_decorations,
.root = options->root,
.list = options->list,
};

Expand Down
5 changes: 5 additions & 0 deletions app/src/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,11 @@ execute_server(struct sc_server *server,
cmd[count++] = "-s";
cmd[count++] = serial;
cmd[count++] = "shell";
if (params->root_enabled) {
cmd[count++] = "su";
cmd[count++] = "1000";
cmd[count++] = "-c";
}
cmd[count++] = "CLASSPATH=" SC_DEVICE_SERVER_PATH;
cmd[count++] = "app_process";

Expand Down
2 changes: 2 additions & 0 deletions app/src/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ struct sc_server_params {
bool camera_high_speed;
bool vd_destroy_content;
bool vd_system_decorations;
bool root;
bool root_enabled;
uint8_t list;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

public final class FakeContext extends ContextWrapper {

public static final String PACKAGE_NAME = "com.android.shell";
public static final String PACKAGE_NAME = Process.myUid() == 1000 ? "android" : "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();
Expand Down
40 changes: 31 additions & 9 deletions server/src/main/java/com/genymobile/scrcpy/util/Settings.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import com.genymobile.scrcpy.wrappers.ContentProvider;
import com.genymobile.scrcpy.wrappers.ServiceManager;

import android.os.Process;

public final class Settings {

public static final String TABLE_SYSTEM = ContentProvider.TABLE_SYSTEM;
Expand All @@ -16,23 +18,43 @@ private Settings() {
public static String getValue(String table, String key) throws SettingsException {
try (ContentProvider provider = ServiceManager.getActivityManager().createSettingsProvider()) {
return provider.getValue(table, key);
} catch (Exception e) {
throw new SettingsException("getValue", table, key, null, e);
}
}

public static void putValue(String table, String key, String value) throws SettingsException {
try (ContentProvider provider = ServiceManager.getActivityManager().createSettingsProvider()) {
provider.putValue(table, key, value);
}
if (Process.myUid() == 1000) {
// UID_SYSTEM path: fallback to 'su -c settings put ...'
try {
String cmd = "settings put " + table + " " + key + " " + value;
String[] command = { "su", "-c", cmd };
Comment thread
Giantvince1 marked this conversation as resolved.

}
java.lang.Process proc = Runtime.getRuntime().exec(command);
int exit = proc.waitFor();

public static String getAndPutValue(String table, String key, String value) throws SettingsException {
try (ContentProvider provider = ServiceManager.getActivityManager().createSettingsProvider()) {
String oldValue = provider.getValue(table, key);
if (!value.equals(oldValue)) {
if (exit != 0) {
throw new SettingsException("putValue", table, key, value, null);
}

} catch (Exception e) {
throw new SettingsException("putValue", table, key, value, e);
}
} else {
// AID_SHELL path: use standard provider
try (ContentProvider provider = ServiceManager.getActivityManager().createSettingsProvider()) {
provider.putValue(table, key, value);
} catch (Exception e) {
throw new SettingsException("putValue", table, key, value, e);
}
return oldValue;
}
}

public static String getAndPutValue(String table, String key, String value) throws SettingsException {
String oldValue = getValue(table, key);
if (!value.equals(oldValue)) {
putValue(table, key, value);
}
return oldValue;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import android.hardware.display.VirtualDisplay;
import android.os.Build;
import android.os.IBinder;
import android.os.Process;
import android.view.Surface;

import java.io.IOException;
Expand Down Expand Up @@ -196,8 +197,8 @@ public boolean setMaxSize(int newMaxSize) {
private static IBinder createDisplay() throws Exception {
// 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 < AndroidVersions.API_30_ANDROID_11 || (Build.VERSION.SDK_INT == AndroidVersions.API_30_ANDROID_11
&& !"S".equals(Build.VERSION.CODENAME));
boolean secure = (Build.VERSION.SDK_INT < AndroidVersions.API_30_ANDROID_11 || (Build.VERSION.SDK_INT == AndroidVersions.API_30_ANDROID_11
&& !"S".equals(Build.VERSION.CODENAME))) || android.os.Process.myUid() == 1000;
Comment thread
Giantvince1 marked this conversation as resolved.
return SurfaceControl.createDisplay("scrcpy", secure);
}

Expand Down