Skip to content

Commit 8e63388

Browse files
committed
feat: Add version exchange on startup
1 parent f91357c commit 8e63388

File tree

3 files changed

+58
-7
lines changed

3 files changed

+58
-7
lines changed

src/main/java/io/github/berstanio/pymobiledevice3/daemon/DaemonHandler.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.badlogic.gdx.jnigen.commons.HostDetection;
44
import com.badlogic.gdx.jnigen.commons.Os;
5+
import io.github.berstanio.pymobiledevice3.ipc.PyMobileDevice3IPC;
56
import io.github.berstanio.pymobiledevice3.venv.PyInstallation;
67

78
import java.io.BufferedReader;
@@ -89,7 +90,8 @@ public static void startDaemon(PyInstallation installation) throws IOException {
8990
File portFile = getPortFile();
9091
Files.deleteIfExists(portFile.toPath());
9192
ProcessBuilder pb = new ProcessBuilder()
92-
.command(installation.getPythonExecutable().getAbsolutePath() , "-u", installation.getHandler().getAbsolutePath(), portFile.getAbsolutePath());
93+
.command(installation.getPythonExecutable().getAbsolutePath() , "-u", installation.getHandler().getAbsolutePath(), String.valueOf(
94+
PyMobileDevice3IPC.PROTOCOL_VERSION), portFile.getAbsolutePath());
9395

9496
pb.redirectErrorStream(true);
9597
pb.redirectOutput(getLogFile());

src/main/java/io/github/berstanio/pymobiledevice3/ipc/PyMobileDevice3IPC.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818
import java.io.InputStreamReader;
1919
import java.io.PrintWriter;
2020
import java.net.Socket;
21+
import java.net.SocketTimeoutException;
22+
import java.nio.ByteBuffer;
23+
import java.nio.channels.SelectionKey;
24+
import java.nio.channels.Selector;
25+
import java.nio.channels.SocketChannel;
2126
import java.util.concurrent.ArrayBlockingQueue;
2227
import java.util.concurrent.CompletableFuture;
2328
import java.util.concurrent.ConcurrentHashMap;
@@ -30,6 +35,9 @@ public class PyMobileDevice3IPC implements Closeable {
3035

3136
private static final boolean DEBUG = System.getProperty("java.pymobiledevice3.debug") != null;
3237

38+
public static final int PROTOCOL_VERSION = 1;
39+
40+
private final int daemonProtocolVersion;
3341
private final Socket socket;
3442
private final BufferedReader reader;
3543
private final PrintWriter writer;
@@ -49,6 +57,12 @@ public PyMobileDevice3IPC() throws IOException {
4957
throw new IllegalStateException("Daemon is not running");
5058

5159
socket = daemonSocket;
60+
61+
socket.getOutputStream().write(PROTOCOL_VERSION);
62+
socket.getOutputStream().flush();
63+
64+
daemonProtocolVersion = readVersion();
65+
5266
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
5367
writer = new PrintWriter(socket.getOutputStream(), true);
5468

@@ -101,6 +115,15 @@ public PyMobileDevice3IPC() throws IOException {
101115
readThread.start();
102116
}
103117

118+
private int readVersion() throws IOException {
119+
try {
120+
socket.setSoTimeout(2000);
121+
return socket.getInputStream().read();
122+
} finally {
123+
socket.setSoTimeout(0);
124+
}
125+
}
126+
104127
private <U> CompletableFuture<U> createRequest(JSONObject request, BiConsumer<CompletableFuture<U>, JSONObject> handler) {
105128
if (destroyed)
106129
return CompletableFuture.failedFuture(new PyMobileDevice3Error("Python process has been destroyed"));
@@ -306,6 +329,10 @@ public CompletableFuture<Void> ensureTunneldRunning() {
306329
});
307330
}
308331

332+
public int getDaemonProtocolVersion() {
333+
return daemonProtocolVersion;
334+
}
335+
309336
public static void main(String[] args) throws IOException {
310337
if (!DaemonHandler.isDaemonRunning()) {
311338
PyInstallation installation = PyInstallationHandler.install(new File("build/pyenv/"));

src/main/resources/handler.py

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,15 @@
2929
from pymobiledevice3.lockdown import create_using_usbmux
3030
import plistlib
3131

32+
VERSION: int
33+
3234
class IPCClient:
33-
def __init__(self, sock, address):
35+
def __init__(self, sock, address, version):
3436
self.sock = sock
3537
self.read_file = sock.makefile('r')
3638
self.write_file = sock.makefile('w')
3739
self.address = address
40+
self.version = version
3841

3942
def close(self):
4043
self.sock.close()
@@ -123,7 +126,7 @@ def remove_client(self, ipc_client):
123126
def add_client(self, ipc_client):
124127
self.clients[ipc_client.sock] = ipc_client
125128
self.socket_list.append(ipc_client.sock)
126-
print(f"Connected {ipc_client.address}")
129+
print(f"Connected {ipc_client.address} with version {ipc_client.version}")
127130

128131

129132
def shutdown(self):
@@ -350,6 +353,9 @@ def usbmux_forward_close(id, local_port, ipc_client):
350353
reply = {"id": id, "state": "completed"}
351354
write_dispatcher.write_reply(ipc_client, reply)
352355

356+
def get_version(id, ipc_client):
357+
reply = {"id": id, "state": "completed", "result": VERSION}
358+
write_dispatcher.write_reply(ipc_client, reply)
353359

354360
def handle_command(command, ipc_client):
355361
try:
@@ -393,6 +399,9 @@ def handle_command(command, ipc_client):
393399
reply = {"id": id, "state": "completed", "result": res}
394400
write_dispatcher.write_reply(ipc_client, reply)
395401
return
402+
elif command_type == "get_version":
403+
get_version(id, ipc_client)
404+
return
396405

397406
# Now come the device targetted functions
398407
device_id = res['device_id']
@@ -414,16 +423,19 @@ def handle_command(command, ipc_client):
414423

415424

416425
def main():
417-
if len(sys.argv) < 2:
418-
print("Usage: python handler.py <port_file>")
426+
if len(sys.argv) < 3:
427+
print("Usage: python handler.py <protocol_version> <port_file>")
419428
sys.exit(1)
420429

430+
global VERSION
431+
VERSION = int(sys.argv[1])
432+
421433
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
422434
server.bind(('localhost', 0))
423435
server.listen(5)
424436

425437
port = server.getsockname()[1]
426-
path = sys.argv[1]
438+
path = sys.argv[2]
427439

428440
with open(path, "w") as f:
429441
f.write(str(port))
@@ -435,6 +447,16 @@ def main():
435447
print(f"Start listening on port {port}")
436448
while True:
437449
client_socket, client_address = server.accept()
438-
read_dispatcher.add_client(IPCClient(client_socket, client_address))
450+
try:
451+
reads, _, _ = select.select([client_socket], [], [], 2)
452+
if client_socket not in reads:
453+
raise RuntimeError()
454+
java_protocol_version = client_socket.recv(1)[0]
455+
client_socket.send(bytes([VERSION]))
456+
457+
read_dispatcher.add_client(IPCClient(client_socket, client_address, java_protocol_version))
458+
except Exception:
459+
print(f"{client_address}: Failed to send version")
460+
439461

440462
main()

0 commit comments

Comments
 (0)