Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
24 changes: 24 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 option instructs it to "
"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,20 @@ 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;
opts->clipboard_autosync = false;
LOGI("Clipboard autosync breaks when root is used to create a"
" secure display.\n"
"The shell user has direct Binder bypasses such that it"
" is allowed access to the\n"
"clipboard unrestricted. However, the system user does not"
" have this bypass.\n"
"It can't read its contents or set up listeners, but it"
" has write permissions.\n"
"Until further notice, clipboard autosync will be forcibly"
" disabled with --root.");
break;
case OPT_AUDIO_BUFFER:
if (!parse_buffering_time(optarg, &opts->audio_buffer)) {
return false;
Expand Down
1 change: 1 addition & 0 deletions app/src/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ const struct scrcpy_options scrcpy_options_default = {
.angle = NULL,
.vd_destroy_content = true,
.vd_system_decorations = true,
.root = false,
};

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

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) {
cmd[count++] = "su";
cmd[count++] = "1000";
cmd[count++] = "-c";
}
cmd[count++] = "CLASSPATH=" SC_DEVICE_SERVER_PATH;
cmd[count++] = "app_process";

Expand Down
1 change: 1 addition & 0 deletions app/src/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ struct sc_server_params {
bool camera_high_speed;
bool vd_destroy_content;
bool vd_system_decorations;
bool root;
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 };

}
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;
return SurfaceControl.createDisplay("scrcpy", secure);
}

Expand Down