mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-20 03:23:07 +00:00
Use a socket for communication from EvdevReader to Moonlight rather than stdin/stdout. On some devices, fwrite(stdout) hangs for unknown reasons.
This commit is contained in:
parent
7da5d5322b
commit
22977a4c5b
@ -2,10 +2,15 @@ package com.limelight.binding.input.evdev;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.limelight.LimeLog;
|
||||||
|
|
||||||
|
import java.io.DataOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
public class EvdevHandler {
|
public class EvdevHandler {
|
||||||
|
|
||||||
@ -15,7 +20,9 @@ public class EvdevHandler {
|
|||||||
private boolean shutdown = false;
|
private boolean shutdown = false;
|
||||||
private InputStream evdevIn;
|
private InputStream evdevIn;
|
||||||
private OutputStream evdevOut;
|
private OutputStream evdevOut;
|
||||||
private Process reader;
|
private Process su;
|
||||||
|
private ServerSocket servSock;
|
||||||
|
private Socket evdevSock;
|
||||||
|
|
||||||
private static final byte UNGRAB_REQUEST = 1;
|
private static final byte UNGRAB_REQUEST = 1;
|
||||||
private static final byte REGRAB_REQUEST = 2;
|
private static final byte REGRAB_REQUEST = 2;
|
||||||
@ -27,19 +34,45 @@ public class EvdevHandler {
|
|||||||
int deltaY = 0;
|
int deltaY = 0;
|
||||||
byte deltaScroll = 0;
|
byte deltaScroll = 0;
|
||||||
|
|
||||||
// Launch the evdev reader shell
|
// Bind a local listening socket for evdevreader to connect to
|
||||||
ProcessBuilder builder = new ProcessBuilder("su", "-c", libraryPath+File.separatorChar+"libevdev_reader.so");
|
|
||||||
builder.redirectErrorStream(false);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
reader = builder.start();
|
servSock = new ServerSocket(0, 1);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
evdevIn = reader.getInputStream();
|
// Launch a su shell
|
||||||
evdevOut = reader.getOutputStream();
|
ProcessBuilder builder = new ProcessBuilder("su");
|
||||||
|
builder.redirectErrorStream(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
su = builder.start();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start evdevreader
|
||||||
|
DataOutputStream suOut = new DataOutputStream(su.getOutputStream());
|
||||||
|
try {
|
||||||
|
suOut.writeChars(libraryPath+File.separatorChar+"libevdev_reader.so "+servSock.getLocalPort()+"\n");
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for evdevreader's connection
|
||||||
|
LimeLog.info("Waiting for EvdevReader connection to port "+servSock.getLocalPort());
|
||||||
|
try {
|
||||||
|
evdevSock = servSock.accept();
|
||||||
|
evdevIn = evdevSock.getInputStream();
|
||||||
|
evdevOut = evdevSock.getOutputStream();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LimeLog.info("EvdevReader connected from port "+evdevSock.getPort());
|
||||||
|
|
||||||
while (!isInterrupted() && !shutdown) {
|
while (!isInterrupted() && !shutdown) {
|
||||||
EvdevEvent event;
|
EvdevEvent event;
|
||||||
@ -159,6 +192,22 @@ public class EvdevHandler {
|
|||||||
shutdown = true;
|
shutdown = true;
|
||||||
handlerThread.interrupt();
|
handlerThread.interrupt();
|
||||||
|
|
||||||
|
if (servSock != null) {
|
||||||
|
try {
|
||||||
|
servSock.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (evdevSock != null) {
|
||||||
|
try {
|
||||||
|
evdevSock.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (evdevIn != null) {
|
if (evdevIn != null) {
|
||||||
try {
|
try {
|
||||||
evdevIn.close();
|
evdevIn.close();
|
||||||
@ -175,8 +224,8 @@ public class EvdevHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reader != null) {
|
if (su != null) {
|
||||||
reader.destroy();
|
su.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
@ -11,6 +12,8 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
|
||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
|
|
||||||
@ -32,9 +35,11 @@ struct DeviceEntry {
|
|||||||
static struct DeviceEntry *DeviceListHead;
|
static struct DeviceEntry *DeviceListHead;
|
||||||
static int grabbing = 1;
|
static int grabbing = 1;
|
||||||
static pthread_mutex_t DeviceListLock = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t DeviceListLock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
static pthread_mutex_t SocketSendLock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
static int sock;
|
||||||
|
|
||||||
// This is a small executable that runs in a root shell. It reads input
|
// This is a small executable that runs in a root shell. It reads input
|
||||||
// devices and writes the evdev output packets to stdout. This allows
|
// devices and writes the evdev output packets to a socket. This allows
|
||||||
// Moonlight to read input devices without having to muck with changing
|
// Moonlight to read input devices without having to muck with changing
|
||||||
// device permissions or modifying SELinux policy (which is prevented in
|
// device permissions or modifying SELinux policy (which is prevented in
|
||||||
// Marshmallow anyway).
|
// Marshmallow anyway).
|
||||||
@ -58,13 +63,16 @@ static int hasKey(int fd, short key) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void outputEvdevData(char *data, int dataSize) {
|
static void outputEvdevData(char *data, int dataSize) {
|
||||||
// We need to lock stdout before writing to prevent
|
char packetBuffer[EVDEV_MAX_EVENT_SIZE + sizeof(dataSize)];
|
||||||
// interleaving of data between threads.
|
|
||||||
flockfile(stdout);
|
// Copy the full packet into our buffer
|
||||||
fwrite(&dataSize, sizeof(dataSize), 1, stdout);
|
memcpy(packetBuffer, &dataSize, sizeof(dataSize));
|
||||||
fwrite(data, dataSize, 1, stdout);
|
memcpy(&packetBuffer[sizeof(dataSize)], data, dataSize);
|
||||||
fflush(stdout);
|
|
||||||
funlockfile(stdout);
|
// Lock to prevent other threads from sending at the same time
|
||||||
|
pthread_mutex_lock(&SocketSendLock);
|
||||||
|
send(sock, packetBuffer, dataSize + sizeof(dataSize), 0);
|
||||||
|
pthread_mutex_unlock(&SocketSendLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* pollThreadFunc(void* context) {
|
void* pollThreadFunc(void* context) {
|
||||||
@ -274,6 +282,39 @@ static int enumerateDevices(void) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int connectSocket(int port) {
|
||||||
|
struct sockaddr_in saddr;
|
||||||
|
int ret;
|
||||||
|
int val;
|
||||||
|
|
||||||
|
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||||
|
if (sock < 0) {
|
||||||
|
__android_log_print(ANDROID_LOG_ERROR, "EvdevReader", "socket() failed: %d", errno);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&saddr, 0, sizeof(saddr));
|
||||||
|
saddr.sin_family = AF_INET;
|
||||||
|
saddr.sin_port = htons(port);
|
||||||
|
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
|
||||||
|
ret = connect(sock, (struct sockaddr*)&saddr, sizeof(saddr));
|
||||||
|
if (ret < 0) {
|
||||||
|
__android_log_print(ANDROID_LOG_ERROR, "EvdevReader", "connect() failed: %d", errno);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = 1;
|
||||||
|
ret = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&val, sizeof(val));
|
||||||
|
if (ret < 0) {
|
||||||
|
__android_log_print(ANDROID_LOG_ERROR, "EvdevReader", "setsockopt() failed: %d", errno);
|
||||||
|
// We can continue anyways
|
||||||
|
}
|
||||||
|
|
||||||
|
__android_log_print(ANDROID_LOG_INFO, "EvdevReader", "Connection established to port %d", port);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#define UNGRAB_REQ 1
|
#define UNGRAB_REQ 1
|
||||||
#define REGRAB_REQ 2
|
#define REGRAB_REQ 2
|
||||||
|
|
||||||
@ -281,9 +322,19 @@ int main(int argc, char* argv[]) {
|
|||||||
int ret;
|
int ret;
|
||||||
int pollres;
|
int pollres;
|
||||||
struct pollfd pollinfo;
|
struct pollfd pollinfo;
|
||||||
|
int port;
|
||||||
|
|
||||||
__android_log_print(ANDROID_LOG_INFO, "EvdevReader", "Entered main()");
|
__android_log_print(ANDROID_LOG_INFO, "EvdevReader", "Entered main()");
|
||||||
|
|
||||||
|
port = atoi(argv[1]);
|
||||||
|
__android_log_print(ANDROID_LOG_INFO, "EvdevReader", "Requested port number: %d", port);
|
||||||
|
|
||||||
|
// Connect to the app's socket
|
||||||
|
ret = connectSocket(port);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
// Perform initial enumeration
|
// Perform initial enumeration
|
||||||
ret = enumerateDevices();
|
ret = enumerateDevices();
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@ -297,7 +348,7 @@ int main(int argc, char* argv[]) {
|
|||||||
do {
|
do {
|
||||||
// Every second we poll again for new devices if
|
// Every second we poll again for new devices if
|
||||||
// we haven't received any new events
|
// we haven't received any new events
|
||||||
pollinfo.fd = STDIN_FILENO;
|
pollinfo.fd = sock;
|
||||||
pollinfo.events = POLLIN;
|
pollinfo.events = POLLIN;
|
||||||
pollinfo.revents = 0;
|
pollinfo.revents = 0;
|
||||||
pollres = poll(&pollinfo, 1, 1000);
|
pollres = poll(&pollinfo, 1, 1000);
|
||||||
@ -310,9 +361,9 @@ int main(int argc, char* argv[]) {
|
|||||||
|
|
||||||
if (pollres > 0 && (pollinfo.revents & POLLIN)) {
|
if (pollres > 0 && (pollinfo.revents & POLLIN)) {
|
||||||
// We'll have data available now
|
// We'll have data available now
|
||||||
ret = fread(&requestId, sizeof(requestId), 1, stdin);
|
ret = recv(sock, &requestId, sizeof(requestId), 0);
|
||||||
if (ret < sizeof(requestId)) {
|
if (ret < sizeof(requestId)) {
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "EvdevReader", "Short read on input");
|
__android_log_print(ANDROID_LOG_ERROR, "EvdevReader", "Short read on socket");
|
||||||
return errno;
|
return errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -337,17 +388,20 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_unlock(&DeviceListLock);
|
pthread_mutex_unlock(&DeviceListLock);
|
||||||
|
|
||||||
|
__android_log_print(ANDROID_LOG_INFO, "EvdevReader", "New grab status is: %s",
|
||||||
|
grabbing ? "enabled" : "disabled");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Terminate this thread
|
// Terminate this thread
|
||||||
if (pollres < 0) {
|
if (pollres < 0) {
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "EvdevReader",
|
__android_log_print(ANDROID_LOG_ERROR, "EvdevReader",
|
||||||
"Stdin poll() failed: %d", errno);
|
"Socket recv poll() failed: %d", errno);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "EvdevReader",
|
__android_log_print(ANDROID_LOG_ERROR, "EvdevReader",
|
||||||
"Stdin unexpected revents: %d", pollinfo.revents);
|
"Socket poll unexpected revents: %d", pollinfo.revents);
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user