Skip to content

Commit ec7ea75

Browse files
committed
feat: Move to seperate project IPC handler
1 parent 686f0f2 commit ec7ea75

File tree

6 files changed

+51
-543
lines changed

6 files changed

+51
-543
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
## Overview
22

33
This is a java interop library for [pymobiledevice3](https://github.com/doronz88/pymobiledevice3).
4-
It uses an IPC protocol, to communicate with a python3 daemon running in the background.
4+
It uses an [IPC protocol](https://github.com/multi-os-engine/IPCPyMobileDevice3), to communicate with a python3 daemon running in the background.
55
All request/responses are implemented asyncronous.
66

77
## How to use

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,7 @@ public static void startDaemon(PyInstallation installation) throws IOException {
9090
File portFile = getPortFile();
9191
Files.deleteIfExists(portFile.toPath());
9292
ProcessBuilder pb = new ProcessBuilder()
93-
.command(installation.getPythonExecutable().getAbsolutePath() , "-u", installation.getHandler().getAbsolutePath(), String.valueOf(
94-
PyMobileDevice3IPC.PROTOCOL_VERSION), portFile.getAbsolutePath());
93+
.command(installation.getPythonExecutable().getAbsolutePath() , "-u", installation.getHandler().getAbsolutePath(), portFile.getAbsolutePath());
9594

9695
pb.redirectErrorStream(true);
9796
pb.redirectOutput(getLogFile());

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

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,6 @@
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;
2621
import java.util.concurrent.ArrayBlockingQueue;
2722
import java.util.concurrent.CompletableFuture;
2823
import java.util.concurrent.ConcurrentHashMap;
@@ -35,7 +30,7 @@ public class PyMobileDevice3IPC implements Closeable {
3530

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

38-
public static final int PROTOCOL_VERSION = 2;
33+
public static final int PROTOCOL_VERSION = 3;
3934

4035
private final int daemonProtocolVersion;
4136
private final Socket socket;
@@ -214,7 +209,7 @@ public CompletableFuture<DeviceInfo> getDevice(String uuid) {
214209
object.put("device_id", uuid);
215210

216211
return createRequest(object, (future, jsonObject) -> {
217-
if (jsonObject.get("state").equals("failed_expected")) {
212+
if (jsonObject.get("state").equals("failed_no_device")) {
218213
future.complete(null);
219214
} else {
220215
DeviceInfo deviceInfo = DeviceInfo.fromJson(jsonObject.getJSONObject("result"));
@@ -255,7 +250,7 @@ public CompletableFuture<String> getInstalledPath(DeviceInfo info, String bundle
255250
object.put("bundle_identifier", bundleIdentifier);
256251

257252
return createRequest(object, (future, jsonObject) -> {
258-
if (jsonObject.get("state").equals("not_installed")) {
253+
if (jsonObject.get("state").equals("failed_not_installed")) {
259254
future.complete(null);
260255
} else {
261256
future.complete(jsonObject.getString("result"));
@@ -337,7 +332,7 @@ public CompletableFuture<DebugServerConnection> debugServerConnect(DeviceInfo in
337332
object.put("device_id", info.getUniqueDeviceId());
338333
object.put("port", port);
339334
return createRequest(object, (future, jsonObject) -> {
340-
if (jsonObject.getString("state").equals("failed_tunneld")) {
335+
if (jsonObject.getString("state").equals("failed_no_tunneld")) {
341336
future.completeExceptionally(new PyMobileDevice3Error("No tunneld instance for device " + info.getUniqueDeviceId() + " found."));
342337
return;
343338
}

src/main/java/io/github/berstanio/pymobiledevice3/venv/PyInstallationHandler.java

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
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

67
import java.io.File;
78
import java.io.IOException;
89
import java.io.InputStream;
10+
import java.net.MalformedURLException;
11+
import java.net.URL;
912
import java.nio.charset.StandardCharsets;
1013
import java.nio.file.Files;
1114
import java.nio.file.Path;
@@ -19,16 +22,56 @@ public class PyInstallationHandler {
1922
private static final String HANDLER_NAME = "handler.py";
2023
private static final String REQUIREMENTS_NAME = "requirements.txt";
2124
private static final String PYTHON_PATH = "bin/python3";
25+
private static final String BASE_URL = "https://raw.githubusercontent.com/multi-os-engine/IPCPyMobileDevice3/refs/tags/v" + PyMobileDevice3IPC.PROTOCOL_VERSION;
26+
private static final String HANDLER_DEFAULT_URL = BASE_URL + "/" + HANDLER_NAME;
27+
private static final String REQUIREMENTS_DEFAULT_URL = BASE_URL + "/" + REQUIREMENTS_NAME;
2228

2329
public static PyInstallation install(File directory) {
30+
try {
31+
return PyInstallationHandler.install(directory, new URL(HANDLER_DEFAULT_URL), new URL(REQUIREMENTS_DEFAULT_URL));
32+
} catch (MalformedURLException e) {
33+
throw new RuntimeException("Failed to resolve IPC URL", e);
34+
}
35+
}
36+
37+
public static PyInstallation install(File directory, File handlerFile, File requirementsFile) {
38+
directory.mkdirs();
39+
if (!directory.exists() || !directory.isDirectory())
40+
throw new IllegalArgumentException(directory.getAbsolutePath() + " does not exist or is not a directory");
41+
42+
try {
43+
Files.copy(handlerFile.toPath(), directory.toPath().resolve(HANDLER_NAME), StandardCopyOption.REPLACE_EXISTING);
44+
Files.copy(requirementsFile.toPath(), directory.toPath().resolve(REQUIREMENTS_NAME), StandardCopyOption.REPLACE_EXISTING);
45+
} catch (IOException e) {
46+
throw new RuntimeException("Failed to copy files", e);
47+
}
48+
49+
return installInternal(directory);
50+
}
51+
52+
public static PyInstallation install(File directory, URL handlerFile, URL requirementsFile) {
2453
directory.mkdirs();
2554
if (!directory.exists() || !directory.isDirectory())
2655
throw new IllegalArgumentException(directory.getAbsolutePath() + " does not exist or is not a directory");
56+
57+
try {
58+
try (InputStream in = handlerFile.openStream()) {
59+
Files.copy(in, directory.toPath().resolve(HANDLER_NAME), StandardCopyOption.REPLACE_EXISTING);
60+
}
61+
try (InputStream in = requirementsFile.openStream()) {
62+
Files.copy(in, directory.toPath().resolve(REQUIREMENTS_NAME), StandardCopyOption.REPLACE_EXISTING);
63+
}
64+
} catch (IOException e) {
65+
throw new RuntimeException("Failed to download files", e);
66+
}
67+
68+
return installInternal(directory);
69+
}
70+
71+
private static PyInstallation installInternal(File directory) {
2772
File venv = new File(directory, VENV_NAME);
2873
if (venv.exists()) {
2974
if (checkVEnv(venv)) {
30-
// Env is valid. Copy files again, just in case
31-
copyFiles(directory);
3275
installRequirements(directory);
3376

3477
return finalizeInstallation(directory);
@@ -69,7 +112,6 @@ public static PyInstallation install(File directory) {
69112
throw new IllegalStateException("Installed venv at " + venv.getAbsolutePath() + ", but it's invalid?");
70113
}
71114

72-
copyFiles(directory);
73115
installRequirements(directory);
74116

75117
return finalizeInstallation(directory);
@@ -106,24 +148,6 @@ private static File copyToTempDir(File directory) {
106148
}
107149
}
108150

109-
private static void copyFiles(File directory) {
110-
try {
111-
try (InputStream in = PyInstallationHandler.class.getResourceAsStream("/" + HANDLER_NAME)) {
112-
if (in == null)
113-
throw new IllegalStateException(HANDLER_NAME + " not found in resources");
114-
Files.copy(in, directory.toPath().resolve(HANDLER_NAME), StandardCopyOption.REPLACE_EXISTING);
115-
}
116-
117-
try (InputStream in = PyInstallationHandler.class.getResourceAsStream("/" + REQUIREMENTS_NAME)) {
118-
if (in == null)
119-
throw new IllegalStateException(REQUIREMENTS_NAME + " not found in resources");
120-
Files.copy(in, directory.toPath().resolve(REQUIREMENTS_NAME), StandardCopyOption.REPLACE_EXISTING);
121-
}
122-
} catch (IOException e) {
123-
throw new RuntimeException(e);
124-
}
125-
}
126-
127151
private static boolean checkVEnv(File venv) {
128152
File pythonExecutable = new File(venv, PYTHON_PATH);
129153
if (!pythonExecutable.exists()) {

0 commit comments

Comments
 (0)