There are a lot of "magic numbers" that we really don't want to extract as a constant. Until now, many @SuppressWarnings annotations were added, but it makes no sense to check for magic number if we silent the warnings everywhere.
119 lines
4.3 KiB
Java
119 lines
4.3 KiB
Java
package com.genymobile.scrcpy;
|
|
|
|
import android.net.LocalServerSocket;
|
|
import android.net.LocalSocket;
|
|
import android.net.LocalSocketAddress;
|
|
|
|
import java.io.Closeable;
|
|
import java.io.FileDescriptor;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.OutputStream;
|
|
import java.nio.charset.StandardCharsets;
|
|
|
|
public final class DesktopConnection implements Closeable {
|
|
|
|
private static final int DEVICE_NAME_FIELD_LENGTH = 64;
|
|
|
|
private static final String SOCKET_NAME = "scrcpy";
|
|
|
|
private final LocalSocket videoSocket;
|
|
private final FileDescriptor videoFd;
|
|
|
|
private final LocalSocket controlSocket;
|
|
private final InputStream controlInputStream;
|
|
private final OutputStream controlOutputStream;
|
|
|
|
private final ControlMessageReader reader = new ControlMessageReader();
|
|
private final DeviceMessageWriter writer = new DeviceMessageWriter();
|
|
|
|
private DesktopConnection(LocalSocket videoSocket, LocalSocket controlSocket) throws IOException {
|
|
this.videoSocket = videoSocket;
|
|
this.controlSocket = controlSocket;
|
|
controlInputStream = controlSocket.getInputStream();
|
|
controlOutputStream = controlSocket.getOutputStream();
|
|
videoFd = videoSocket.getFileDescriptor();
|
|
}
|
|
|
|
private static LocalSocket connect(String abstractName) throws IOException {
|
|
LocalSocket localSocket = new LocalSocket();
|
|
localSocket.connect(new LocalSocketAddress(abstractName));
|
|
return localSocket;
|
|
}
|
|
|
|
public static DesktopConnection open(Device device, boolean tunnelForward) throws IOException {
|
|
LocalSocket videoSocket;
|
|
LocalSocket controlSocket;
|
|
if (tunnelForward) {
|
|
LocalServerSocket localServerSocket = new LocalServerSocket(SOCKET_NAME);
|
|
try {
|
|
videoSocket = localServerSocket.accept();
|
|
// send one byte so the client may read() to detect a connection error
|
|
videoSocket.getOutputStream().write(0);
|
|
try {
|
|
controlSocket = localServerSocket.accept();
|
|
} catch (IOException | RuntimeException e) {
|
|
videoSocket.close();
|
|
throw e;
|
|
}
|
|
} finally {
|
|
localServerSocket.close();
|
|
}
|
|
} else {
|
|
videoSocket = connect(SOCKET_NAME);
|
|
try {
|
|
controlSocket = connect(SOCKET_NAME);
|
|
} catch (IOException | RuntimeException e) {
|
|
videoSocket.close();
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
DesktopConnection connection = new DesktopConnection(videoSocket, controlSocket);
|
|
Size videoSize = device.getScreenInfo().getVideoSize();
|
|
connection.send(Device.getDeviceName(), videoSize.getWidth(), videoSize.getHeight());
|
|
return connection;
|
|
}
|
|
|
|
public void close() throws IOException {
|
|
videoSocket.shutdownInput();
|
|
videoSocket.shutdownOutput();
|
|
videoSocket.close();
|
|
controlSocket.shutdownInput();
|
|
controlSocket.shutdownOutput();
|
|
controlSocket.close();
|
|
}
|
|
|
|
private void send(String deviceName, int width, int height) throws IOException {
|
|
byte[] buffer = new byte[DEVICE_NAME_FIELD_LENGTH + 4];
|
|
|
|
byte[] deviceNameBytes = deviceName.getBytes(StandardCharsets.UTF_8);
|
|
int len = StringUtils.getUtf8TruncationIndex(deviceNameBytes, DEVICE_NAME_FIELD_LENGTH - 1);
|
|
System.arraycopy(deviceNameBytes, 0, buffer, 0, len);
|
|
// byte[] are always 0-initialized in java, no need to set '\0' explicitly
|
|
|
|
buffer[DEVICE_NAME_FIELD_LENGTH] = (byte) (width >> 8);
|
|
buffer[DEVICE_NAME_FIELD_LENGTH + 1] = (byte) width;
|
|
buffer[DEVICE_NAME_FIELD_LENGTH + 2] = (byte) (height >> 8);
|
|
buffer[DEVICE_NAME_FIELD_LENGTH + 3] = (byte) height;
|
|
IO.writeFully(videoFd, buffer, 0, buffer.length);
|
|
}
|
|
|
|
public FileDescriptor getVideoFd() {
|
|
return videoFd;
|
|
}
|
|
|
|
public ControlMessage receiveControlMessage() throws IOException {
|
|
ControlMessage msg = reader.next();
|
|
while (msg == null) {
|
|
reader.readFrom(controlInputStream);
|
|
msg = reader.next();
|
|
}
|
|
return msg;
|
|
}
|
|
|
|
public void sendDeviceMessage(DeviceMessage msg) throws IOException {
|
|
writer.writeTo(msg, controlOutputStream);
|
|
}
|
|
}
|