Skip to content

Commit f264181

Browse files
committed
feat: update server
1 parent 5e1cc1b commit f264181

29 files changed

+1204
-288
lines changed

server/.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
*.iml
2+
.gradle
3+
/local.properties
4+
/.idea/
5+
.DS_Store
6+
/build
7+
/captures
8+
.externalNativeBuild

server/build.gradle

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
apply plugin: 'com.android.application'
22

33
android {
4-
compileSdkVersion 29
4+
compileSdkVersion 31
55
defaultConfig {
66
applicationId "com.genymobile.scrcpy"
77
minSdkVersion 21
8-
targetSdkVersion 29
9-
versionCode 16
10-
versionName "1.14"
8+
targetSdkVersion 31
9+
versionCode 12100
10+
versionName "1.21"
1111
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
1212
}
1313
buildTypes {
@@ -20,7 +20,7 @@ android {
2020

2121
dependencies {
2222
implementation fileTree(dir: 'libs', include: ['*.jar'])
23-
testImplementation 'junit:junit:4.12'
23+
testImplementation 'junit:junit:4.13.1'
2424
}
2525

2626
apply from: "$project.rootDir/config/android-checkstyle.gradle"

server/build_without_gradle.sh

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#!/usr/bin/env bash
2+
#
3+
# This script generates the scrcpy binary "manually" (without gradle).
4+
#
5+
# Adapt Android platform and build tools versions (via ANDROID_PLATFORM and
6+
# ANDROID_BUILD_TOOLS environment variables).
7+
#
8+
# Then execute:
9+
#
10+
# BUILD_DIR=my_build_dir ./build_without_gradle.sh
11+
12+
set -e
13+
14+
SCRCPY_DEBUG=false
15+
SCRCPY_VERSION_NAME=1.21
16+
17+
PLATFORM_VERSION=31
18+
PLATFORM=${ANDROID_PLATFORM:-$PLATFORM_VERSION}
19+
BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-31.0.0}
20+
21+
BUILD_DIR="$(realpath ${BUILD_DIR:-build_manual})"
22+
CLASSES_DIR="$BUILD_DIR/classes"
23+
SERVER_DIR=$(dirname "$0")
24+
SERVER_BINARY=scrcpy-server
25+
ANDROID_JAR="$ANDROID_HOME/platforms/android-$PLATFORM/android.jar"
26+
27+
echo "Platform: android-$PLATFORM"
28+
echo "Build-tools: $BUILD_TOOLS"
29+
echo "Build dir: $BUILD_DIR"
30+
31+
rm -rf "$CLASSES_DIR" "$BUILD_DIR/$SERVER_BINARY" classes.dex
32+
mkdir -p "$CLASSES_DIR/com/genymobile/scrcpy"
33+
34+
<< EOF cat > "$CLASSES_DIR/com/genymobile/scrcpy/BuildConfig.java"
35+
package com.genymobile.scrcpy;
36+
37+
public final class BuildConfig {
38+
public static final boolean DEBUG = $SCRCPY_DEBUG;
39+
public static final String VERSION_NAME = "$SCRCPY_VERSION_NAME";
40+
}
41+
EOF
42+
43+
echo "Generating java from aidl..."
44+
cd "$SERVER_DIR/src/main/aidl"
45+
"$ANDROID_HOME/build-tools/$BUILD_TOOLS/aidl" -o"$CLASSES_DIR" \
46+
android/view/IRotationWatcher.aidl
47+
"$ANDROID_HOME/build-tools/$BUILD_TOOLS/aidl" -o"$CLASSES_DIR" \
48+
android/content/IOnPrimaryClipChangedListener.aidl
49+
50+
echo "Compiling java sources..."
51+
cd ../java
52+
javac -bootclasspath "$ANDROID_JAR" -cp "$CLASSES_DIR" -d "$CLASSES_DIR" \
53+
-source 1.8 -target 1.8 \
54+
com/genymobile/scrcpy/*.java \
55+
com/genymobile/scrcpy/wrappers/*.java
56+
57+
echo "Dexing..."
58+
cd "$CLASSES_DIR"
59+
60+
if [[ $PLATFORM_VERSION -lt 31 ]]
61+
then
62+
# use dx
63+
"$ANDROID_HOME/build-tools/$BUILD_TOOLS/dx" --dex \
64+
--output "$BUILD_DIR/classes.dex" \
65+
android/view/*.class \
66+
android/content/*.class \
67+
com/genymobile/scrcpy/*.class \
68+
com/genymobile/scrcpy/wrappers/*.class
69+
70+
echo "Archiving..."
71+
cd "$BUILD_DIR"
72+
jar cvf "$SERVER_BINARY" classes.dex
73+
rm -rf classes.dex classes
74+
else
75+
# use d8
76+
"$ANDROID_HOME/build-tools/$BUILD_TOOLS/d8" --classpath "$ANDROID_JAR" \
77+
--output "$BUILD_DIR/classes.zip" \
78+
android/view/*.class \
79+
android/content/*.class \
80+
com/genymobile/scrcpy/*.class \
81+
com/genymobile/scrcpy/wrappers/*.class
82+
83+
cd "$BUILD_DIR"
84+
mv classes.zip "$SERVER_BINARY"
85+
rm -rf classes
86+
fi
87+
88+
echo "Server generated in $BUILD_DIR/$SERVER_BINARY"

server/meson.build

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# It may be useful to use a prebuilt server, so that no Android SDK is required
2+
# to build. If the 'prebuilt_server' option is set, just copy the file as is.
3+
prebuilt_server = get_option('prebuilt_server')
4+
if prebuilt_server == ''
5+
custom_target('scrcpy-server',
6+
# gradle is responsible for tracking source changes
7+
build_by_default: true,
8+
build_always_stale: true,
9+
output: 'scrcpy-server',
10+
command: [find_program('./scripts/build-wrapper.sh'), meson.current_source_dir(), '@OUTPUT@', get_option('buildtype')],
11+
console: true,
12+
install: true,
13+
install_dir: 'share/scrcpy')
14+
else
15+
if not prebuilt_server.startswith('/')
16+
# relative path needs some trick
17+
prebuilt_server = meson.source_root() + '/' + prebuilt_server
18+
endif
19+
custom_target('scrcpy-server-prebuilt',
20+
input: prebuilt_server,
21+
output: 'scrcpy-server',
22+
command: ['cp', '@INPUT@', '@OUTPUT@'],
23+
install: true,
24+
install_dir: 'share/scrcpy')
25+
endif

server/src/main/java/com/genymobile/scrcpy/CleanUp.java

Lines changed: 137 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package com.genymobile.scrcpy;
22

3-
import com.genymobile.scrcpy.wrappers.ContentProvider;
43
import com.genymobile.scrcpy.wrappers.ServiceManager;
54

5+
import android.os.Parcel;
6+
import android.os.Parcelable;
7+
import android.util.Base64;
8+
69
import java.io.File;
710
import java.io.IOException;
811

@@ -15,22 +18,123 @@ public final class CleanUp {
1518

1619
public static final String SERVER_PATH = "/data/local/tmp/scrcpy-server.jar";
1720

21+
// A simple struct to be passed from the main process to the cleanup process
22+
public static class Config implements Parcelable {
23+
24+
public static final Creator<Config> CREATOR = new Creator<Config>() {
25+
@Override
26+
public Config createFromParcel(Parcel in) {
27+
return new Config(in);
28+
}
29+
30+
@Override
31+
public Config[] newArray(int size) {
32+
return new Config[size];
33+
}
34+
};
35+
36+
private static final int FLAG_DISABLE_SHOW_TOUCHES = 1;
37+
private static final int FLAG_RESTORE_NORMAL_POWER_MODE = 2;
38+
private static final int FLAG_POWER_OFF_SCREEN = 4;
39+
40+
private int displayId;
41+
42+
// Restore the value (between 0 and 7), -1 to not restore
43+
// <https://developer.android.com/reference/android/provider/Settings.Global#STAY_ON_WHILE_PLUGGED_IN>
44+
private int restoreStayOn = -1;
45+
46+
private boolean disableShowTouches;
47+
private boolean restoreNormalPowerMode;
48+
private boolean powerOffScreen;
49+
50+
public Config() {
51+
// Default constructor, the fields are initialized by CleanUp.configure()
52+
}
53+
54+
protected Config(Parcel in) {
55+
displayId = in.readInt();
56+
restoreStayOn = in.readInt();
57+
byte options = in.readByte();
58+
disableShowTouches = (options & FLAG_DISABLE_SHOW_TOUCHES) != 0;
59+
restoreNormalPowerMode = (options & FLAG_RESTORE_NORMAL_POWER_MODE) != 0;
60+
powerOffScreen = (options & FLAG_POWER_OFF_SCREEN) != 0;
61+
}
62+
63+
@Override
64+
public void writeToParcel(Parcel dest, int flags) {
65+
dest.writeInt(displayId);
66+
dest.writeInt(restoreStayOn);
67+
byte options = 0;
68+
if (disableShowTouches) {
69+
options |= FLAG_DISABLE_SHOW_TOUCHES;
70+
}
71+
if (restoreNormalPowerMode) {
72+
options |= FLAG_RESTORE_NORMAL_POWER_MODE;
73+
}
74+
if (powerOffScreen) {
75+
options |= FLAG_POWER_OFF_SCREEN;
76+
}
77+
dest.writeByte(options);
78+
}
79+
80+
private boolean hasWork() {
81+
return disableShowTouches || restoreStayOn != -1 || restoreNormalPowerMode || powerOffScreen;
82+
}
83+
84+
@Override
85+
public int describeContents() {
86+
return 0;
87+
}
88+
89+
byte[] serialize() {
90+
Parcel parcel = Parcel.obtain();
91+
writeToParcel(parcel, 0);
92+
byte[] bytes = parcel.marshall();
93+
parcel.recycle();
94+
return bytes;
95+
}
96+
97+
static Config deserialize(byte[] bytes) {
98+
Parcel parcel = Parcel.obtain();
99+
parcel.unmarshall(bytes, 0, bytes.length);
100+
parcel.setDataPosition(0);
101+
return CREATOR.createFromParcel(parcel);
102+
}
103+
104+
static Config fromBase64(String base64) {
105+
byte[] bytes = Base64.decode(base64, Base64.NO_WRAP);
106+
return deserialize(bytes);
107+
}
108+
109+
String toBase64() {
110+
byte[] bytes = serialize();
111+
return Base64.encodeToString(bytes, Base64.NO_WRAP);
112+
}
113+
}
114+
18115
private CleanUp() {
19116
// not instantiable
20117
}
21118

22-
public static void configure(boolean disableShowTouches, int restoreStayOn) throws IOException {
23-
boolean needProcess = disableShowTouches || restoreStayOn != -1;
24-
if (needProcess) {
25-
startProcess(disableShowTouches, restoreStayOn);
119+
public static void configure(int displayId, int restoreStayOn, boolean disableShowTouches, boolean restoreNormalPowerMode, boolean powerOffScreen)
120+
throws IOException {
121+
Config config = new Config();
122+
config.displayId = displayId;
123+
config.disableShowTouches = disableShowTouches;
124+
config.restoreStayOn = restoreStayOn;
125+
config.restoreNormalPowerMode = restoreNormalPowerMode;
126+
config.powerOffScreen = powerOffScreen;
127+
128+
if (config.hasWork()) {
129+
startProcess(config);
26130
} else {
27131
// There is no additional clean up to do when scrcpy dies
28132
unlinkSelf();
29133
}
30134
}
31135

32-
private static void startProcess(boolean disableShowTouches, int restoreStayOn) throws IOException {
33-
String[] cmd = {"app_process", "/", CleanUp.class.getName(), String.valueOf(disableShowTouches), String.valueOf(restoreStayOn)};
136+
private static void startProcess(Config config) throws IOException {
137+
String[] cmd = {"app_process", "/", CleanUp.class.getName(), config.toBase64()};
34138

35139
ProcessBuilder builder = new ProcessBuilder(cmd);
36140
builder.environment().put("CLASSPATH", SERVER_PATH);
@@ -57,21 +161,37 @@ public static void main(String... args) {
57161

58162
Ln.i("Cleaning up");
59163

60-
boolean disableShowTouches = Boolean.parseBoolean(args[0]);
61-
int restoreStayOn = Integer.parseInt(args[1]);
164+
Config config = Config.fromBase64(args[0]);
62165

63-
if (disableShowTouches || restoreStayOn != -1) {
166+
if (config.disableShowTouches || config.restoreStayOn != -1) {
64167
ServiceManager serviceManager = new ServiceManager();
65-
try (ContentProvider settings = serviceManager.getActivityManager().createSettingsProvider()) {
66-
if (disableShowTouches) {
67-
Ln.i("Disabling \"show touches\"");
68-
settings.putValue(ContentProvider.TABLE_SYSTEM, "show_touches", "0");
168+
Settings settings = new Settings(serviceManager);
169+
if (config.disableShowTouches) {
170+
Ln.i("Disabling \"show touches\"");
171+
try {
172+
settings.putValue(Settings.TABLE_SYSTEM, "show_touches", "0");
173+
} catch (SettingsException e) {
174+
Ln.e("Could not restore \"show_touches\"", e);
69175
}
70-
if (restoreStayOn != -1) {
71-
Ln.i("Restoring \"stay awake\"");
72-
settings.putValue(ContentProvider.TABLE_GLOBAL, "stay_on_while_plugged_in", String.valueOf(restoreStayOn));
176+
}
177+
if (config.restoreStayOn != -1) {
178+
Ln.i("Restoring \"stay awake\"");
179+
try {
180+
settings.putValue(Settings.TABLE_GLOBAL, "stay_on_while_plugged_in", String.valueOf(config.restoreStayOn));
181+
} catch (SettingsException e) {
182+
Ln.e("Could not restore \"stay_on_while_plugged_in\"", e);
73183
}
74184
}
75185
}
186+
187+
if (Device.isScreenOn()) {
188+
if (config.powerOffScreen) {
189+
Ln.i("Power off screen");
190+
Device.powerOffScreen(config.displayId);
191+
} else if (config.restoreNormalPowerMode) {
192+
Ln.i("Restoring normal power mode");
193+
Device.setScreenPowerMode(Device.POWER_MODE_NORMAL);
194+
}
195+
}
76196
}
77197
}

server/src/main/java/com/genymobile/scrcpy/CodecOption.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public Object getValue() {
2121
}
2222

2323
public static List<CodecOption> parse(String codecOptions) {
24-
if ("-".equals(codecOptions)) {
24+
if (codecOptions.isEmpty()) {
2525
return null;
2626
}
2727

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.genymobile.scrcpy;
2+
3+
import java.io.IOException;
4+
import java.util.Arrays;
5+
import java.util.Scanner;
6+
7+
public final class Command {
8+
private Command() {
9+
// not instantiable
10+
}
11+
12+
public static void exec(String... cmd) throws IOException, InterruptedException {
13+
Process process = Runtime.getRuntime().exec(cmd);
14+
int exitCode = process.waitFor();
15+
if (exitCode != 0) {
16+
throw new IOException("Command " + Arrays.toString(cmd) + " returned with value " + exitCode);
17+
}
18+
}
19+
20+
public static String execReadLine(String... cmd) throws IOException, InterruptedException {
21+
String result = null;
22+
Process process = Runtime.getRuntime().exec(cmd);
23+
Scanner scanner = new Scanner(process.getInputStream());
24+
if (scanner.hasNextLine()) {
25+
result = scanner.nextLine();
26+
}
27+
int exitCode = process.waitFor();
28+
if (exitCode != 0) {
29+
throw new IOException("Command " + Arrays.toString(cmd) + " returned with value " + exitCode);
30+
}
31+
return result;
32+
}
33+
}

0 commit comments

Comments
 (0)