Mult proto + new app
This commit is contained in:
@@ -2,3 +2,5 @@
|
|||||||
35e7dbbb84bbb76b0eea052eff18d58e24c1fa1c43274aa6c1a5c686a1d38f40 *bin/dragontcp-hybrid-server-linux-arm64
|
35e7dbbb84bbb76b0eea052eff18d58e24c1fa1c43274aa6c1a5c686a1d38f40 *bin/dragontcp-hybrid-server-linux-arm64
|
||||||
2f820ce82a65c285684c875afd0311f1d62afe856f8b878eab682f9864252a19 *bin/dragontcp-hybrid-client-linux-amd64
|
2f820ce82a65c285684c875afd0311f1d62afe856f8b878eab682f9864252a19 *bin/dragontcp-hybrid-client-linux-amd64
|
||||||
1b5bcf4d3a446cec397206557fa6f1a4c229b11b8e871a7a1072ac3ae9f81e31 *android/lib/arm64-v8a/libdragontcp_client.so
|
1b5bcf4d3a446cec397206557fa6f1a4c229b11b8e871a7a1072ac3ae9f81e31 *android/lib/arm64-v8a/libdragontcp_client.so
|
||||||
|
8021680a9cff84b0dcdec3c6a1f38c5b6f9620aba2bed89766920e4b2f514afa *android/lib/armeabi-v7a/libdragontcp_client.so
|
||||||
|
baa7e4e304f96edd4d1fe8f22b9f11398e1d8ad00e09454afca8419ae718bdd2 *android/lib/x86_64/libdragontcp_client.so
|
||||||
|
|||||||
BIN
Binary file not shown.
Generated
+3
@@ -0,0 +1,3 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
Generated
+2289
File diff suppressed because it is too large
Load Diff
Generated
+10
@@ -0,0 +1,10 @@
|
|||||||
|
<component name="libraryTable">
|
||||||
|
<library name="build">
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$PROJECT_DIR$/build/jclasses.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/build/kclasses.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES />
|
||||||
|
</library>
|
||||||
|
</component>
|
||||||
Generated
+67
@@ -0,0 +1,67 @@
|
|||||||
|
<component name="libraryTable">
|
||||||
|
<library name="lib">
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/allopen-compiler-plugin.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-compiler.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-stdlib-jdk7-sources.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-daemon-client.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-metadata-jvm-sources.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-scripting-compiler-impl.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-annotation-processing.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-test.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-stdlib-jdk8-sources.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/scripting-compiler.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlinx-serialization-compiler-plugin.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-script-runtime-sources.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/mutability-annotations-compat.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-stdlib-js-sources.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-test-testng.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-runner.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-scripting-common.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/trove4j.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-stdlib-sources.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-daemon.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/lombok-compiler-plugin.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/compose-compiler-plugin.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-scripting-jvm.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-main-kts.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-reflect-sources.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-stdlib-jdk8.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-imports-dumper-compiler-plugin.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-test-testng-sources.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-test-junit5.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/annotations-13.0.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-script-runtime.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-test-sources.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-test-js-sources.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/parcelize-compiler.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/assignment-compiler-plugin.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-annotation-processing-compiler.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/sam-with-receiver-compiler-plugin.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-stdlib.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-scripting-compiler.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-test-junit-sources.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/android-extensions-compiler.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/power-assert-compiler-plugin.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-annotations-jvm.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-annotation-processing-cli.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-preloader.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/android-extensions-runtime.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/parcelize-runtime.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-test-junit5-sources.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-annotations-jvm-sources.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-stdlib-jdk7.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-test-junit.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-ant.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-metadata-jvm.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-reflect.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlinx-coroutines-core-jvm.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/noarg-compiler-plugin.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-annotation-processing-runtime.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/kotlin-serialization-compiler-plugin.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/.tools/kotlinc-2.1.21/kotlinc/lib/jvm-abi-gen.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES />
|
||||||
|
</library>
|
||||||
|
</component>
|
||||||
Generated
+5
@@ -0,0 +1,5 @@
|
|||||||
|
<project version="4">
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/android.iml" filepath="$PROJECT_DIR$/android.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -1,12 +1,23 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.dragontcp.client"
|
package="com.dragontcp.client"
|
||||||
android:versionCode="13"
|
android:versionCode="14"
|
||||||
android:versionName="13.0-hybrid">
|
android:versionName="14.0-hybrid">
|
||||||
|
|
||||||
|
<!-- minSdk 24 covers Android 7.0 and later. It was 29, which silently made
|
||||||
|
the APK uninstallable on every device older than Android 10. -->
|
||||||
|
<uses-sdk android:minSdkVersion="24" android:targetSdkVersion="36" />
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" />
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
<!-- Required from API 34 for a foreground service of type specialUse. -->
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
||||||
|
<!-- Required from API 33 for the ongoing VPN notification to be visible. -->
|
||||||
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
|
|
||||||
|
<!-- 64-bit ARM is the primary target; the other ABIs are shipped so 32-bit
|
||||||
|
and emulator devices can install too. -->
|
||||||
|
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
@@ -36,10 +47,14 @@
|
|||||||
<service
|
<service
|
||||||
android:name=".DragonService"
|
android:name=".DragonService"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
android:foregroundServiceType="specialUse"
|
||||||
android:permission="android.permission.BIND_VPN_SERVICE">
|
android:permission="android.permission.BIND_VPN_SERVICE">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.net.VpnService" />
|
<action android:name="android.net.VpnService" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<property
|
||||||
|
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
|
||||||
|
android:value="VPN tunnel transport kept alive while the device is connected" />
|
||||||
</service>
|
</service>
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
<orderEntry type="library" name="build" level="project" />
|
||||||
|
<orderEntry type="library" name="build" level="project" />
|
||||||
|
<orderEntry type="library" name="build" level="project" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
@@ -125,17 +125,14 @@ $platRoot = Join-Path $SdkRoot 'platforms'
|
|||||||
if ($Platform) {
|
if ($Platform) {
|
||||||
$AJ = Join-Path $platRoot "$Platform\android.jar"
|
$AJ = Join-Path $platRoot "$Platform\android.jar"
|
||||||
} else {
|
} else {
|
||||||
$preferred = Join-Path $platRoot 'android-35\android.jar'
|
# Compile against the newest installed platform: targetSdk 36 must be built
|
||||||
if (Test-Path $preferred) {
|
# against android-36 or aapt rejects the manifest.
|
||||||
$Platform = 'android-35'; $AJ = $preferred
|
|
||||||
} else {
|
|
||||||
$p = Get-ChildItem $platRoot -Directory -ErrorAction SilentlyContinue |
|
$p = Get-ChildItem $platRoot -Directory -ErrorAction SilentlyContinue |
|
||||||
Where-Object { Test-Path (Join-Path $_.FullName 'android.jar') } |
|
Where-Object { Test-Path (Join-Path $_.FullName 'android.jar') } |
|
||||||
Sort-Object { [int]($_.Name -replace '\D', '') } -Descending |
|
Sort-Object { [int]($_.Name -replace '\D', '') } -Descending |
|
||||||
Select-Object -First 1
|
Select-Object -First 1
|
||||||
if (-not $p) { Die "No android.jar found under $platRoot. Install a platform via the SDK Manager." }
|
if (-not $p) { Die "No android.jar found under $platRoot. Install a platform via the SDK Manager." }
|
||||||
$Platform = $p.Name; $AJ = Join-Path $p.FullName 'android.jar'
|
$Platform = $p.Name; $AJ = Join-Path $p.FullName 'android.jar'
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (-not (Test-Path $AJ)) { Die "Android platform jar not found: $AJ" }
|
if (-not (Test-Path $AJ)) { Die "Android platform jar not found: $AJ" }
|
||||||
|
|
||||||
@@ -232,7 +229,7 @@ if ($BuildCore -or -not (Test-Path $so)) {
|
|||||||
$coreScript = Join-Path $RepoRoot 'build_core.ps1'
|
$coreScript = Join-Path $RepoRoot 'build_core.ps1'
|
||||||
if (-not (Test-Path $coreScript)) { Die "$why but $coreScript was not found. Pass -RepoRoot." }
|
if (-not (Test-Path $coreScript)) { Die "$why but $coreScript was not found. Pass -RepoRoot." }
|
||||||
Step 'Native core (Go)...'
|
Step 'Native core (Go)...'
|
||||||
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $coreScript -ClientOnly -AndroidLibDir $libDir
|
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $coreScript -ClientOnly -AndroidLibRoot (Join-Path $Root 'lib')
|
||||||
if ($LASTEXITCODE -ne 0) { Die 'build_core.ps1 failed.' }
|
if ($LASTEXITCODE -ne 0) { Die 'build_core.ps1 failed.' }
|
||||||
}
|
}
|
||||||
if (-not (Test-Path $so)) { Die "Missing $so - build the Go core first." }
|
if (-not (Test-Path $so)) { Die "Missing $so - build the Go core first." }
|
||||||
@@ -257,6 +254,9 @@ Invoke-Tool (Join-Path $BT 'aapt.exe') @(
|
|||||||
'-S', (Join-Path $Root 'res'),
|
'-S', (Join-Path $Root 'res'),
|
||||||
'-A', (Join-Path $Root 'assets'),
|
'-A', (Join-Path $Root 'assets'),
|
||||||
'-I', $AJ,
|
'-I', $AJ,
|
||||||
|
# From targetSdk 30 Android refuses to install an APK whose resources.arsc
|
||||||
|
# is compressed, so tell aapt to store it instead.
|
||||||
|
'-0', 'arsc',
|
||||||
'-F', "$B\resources.ap_"
|
'-F', "$B\resources.ap_"
|
||||||
) 'aapt'
|
) 'aapt'
|
||||||
|
|
||||||
@@ -284,7 +284,7 @@ Invoke-Tool $jar @('cf', "$B\jclasses.jar", '-C', "$B\jclasses", '.') 'jar (java
|
|||||||
|
|
||||||
Step 'DEX...'
|
Step 'DEX...'
|
||||||
Invoke-Tool (Join-Path $BT 'd8.bat') (@(
|
Invoke-Tool (Join-Path $BT 'd8.bat') (@(
|
||||||
'--lib', $AJ, '--min-api', '29', '--output', "$B\dex",
|
'--lib', $AJ, '--min-api', '24', '--output', "$B\dex",
|
||||||
"$B\kclasses.jar", "$B\jclasses.jar", $coro
|
"$B\kclasses.jar", "$B\jclasses.jar", $coro
|
||||||
) + $stdlib) 'd8'
|
) + $stdlib) 'd8'
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -361,9 +361,10 @@ public class DragonService extends VpnService {
|
|||||||
if (process != null) {
|
if (process != null) {
|
||||||
try {
|
try {
|
||||||
process.destroy();
|
process.destroy();
|
||||||
if (!process.waitFor(1200, TimeUnit.MILLISECONDS)) {
|
// Process.waitFor(long, TimeUnit) is API 26; minSdk is 24.
|
||||||
|
if (!awaitExit(process, 1200)) {
|
||||||
process.destroyForcibly();
|
process.destroyForcibly();
|
||||||
process.waitFor(800, TimeUnit.MILLISECONDS);
|
awaitExit(process, 800);
|
||||||
}
|
}
|
||||||
} catch (Throwable ignored) {
|
} catch (Throwable ignored) {
|
||||||
try { process.destroyForcibly(); } catch (Throwable ignored2) {}
|
try { process.destroyForcibly(); } catch (Throwable ignored2) {}
|
||||||
@@ -371,6 +372,25 @@ public class DragonService extends VpnService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Waits up to timeoutMs for the process to exit, without needing API 26. */
|
||||||
|
private static boolean awaitExit(Process process, long timeoutMs) throws InterruptedException {
|
||||||
|
long deadline = System.currentTimeMillis() + timeoutMs;
|
||||||
|
while (System.currentTimeMillis() < deadline) {
|
||||||
|
try {
|
||||||
|
process.exitValue();
|
||||||
|
return true;
|
||||||
|
} catch (IllegalThreadStateException stillRunning) {
|
||||||
|
Thread.sleep(50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
process.exitValue();
|
||||||
|
return true;
|
||||||
|
} catch (IllegalThreadStateException stillRunning) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void stopEverythingLocked(String logMessage) {
|
private void stopEverythingLocked(String logMessage) {
|
||||||
if (stopping) return;
|
if (stopping) return;
|
||||||
stopping = true;
|
stopping = true;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import android.view.Gravity;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
|
import android.view.WindowInsets;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.ScrollView;
|
import android.widget.ScrollView;
|
||||||
@@ -127,6 +128,7 @@ public class LogActivity extends Activity {
|
|||||||
});
|
});
|
||||||
|
|
||||||
setContentView(root);
|
setContentView(root);
|
||||||
|
applyInsets(root, dp(16), dp(14), dp(16), dp(12));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void appendLogLine(String line) {
|
private void appendLogLine(String line) {
|
||||||
@@ -177,4 +179,27 @@ public class LogActivity extends Activity {
|
|||||||
private int dp(int value) {
|
private int dp(int value) {
|
||||||
return Math.round(value * getResources().getDisplayMetrics().density);
|
return Math.round(value * getResources().getDisplayMetrics().density);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pads the root by the system bar insets. Required from API 35, where the
|
||||||
|
* activity is laid out edge to edge and would otherwise draw under the
|
||||||
|
* status and navigation bars.
|
||||||
|
*/
|
||||||
|
private void applyInsets(final View root, final int l, final int t, final int r, final int b) {
|
||||||
|
root.setOnApplyWindowInsetsListener((v, insets) -> {
|
||||||
|
int top, bottom, left, right;
|
||||||
|
if (Build.VERSION.SDK_INT >= 30) {
|
||||||
|
android.graphics.Insets bars = insets.getInsets(WindowInsets.Type.systemBars());
|
||||||
|
top = bars.top; bottom = bars.bottom; left = bars.left; right = bars.right;
|
||||||
|
} else {
|
||||||
|
top = insets.getSystemWindowInsetTop();
|
||||||
|
bottom = insets.getSystemWindowInsetBottom();
|
||||||
|
left = insets.getSystemWindowInsetLeft();
|
||||||
|
right = insets.getSystemWindowInsetRight();
|
||||||
|
}
|
||||||
|
v.setPadding(l + left, t + top, r + right, b + bottom);
|
||||||
|
return insets;
|
||||||
|
});
|
||||||
|
root.requestApplyInsets();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import android.view.Gravity;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
|
import android.view.WindowInsets;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
@@ -28,6 +29,7 @@ import android.widget.Toast;
|
|||||||
|
|
||||||
public class MainActivity extends Activity {
|
public class MainActivity extends Activity {
|
||||||
private static final int VPN_REQUEST = 100;
|
private static final int VPN_REQUEST = 100;
|
||||||
|
private static final int NOTIF_REQUEST = 101;
|
||||||
private static final String PREFS = "dragontcp";
|
private static final String PREFS = "dragontcp";
|
||||||
|
|
||||||
private static final int BATCH_LIMIT = 256;
|
private static final int BATCH_LIMIT = 256;
|
||||||
@@ -86,6 +88,7 @@ public class MainActivity extends Activity {
|
|||||||
forceDarkSystemUi();
|
forceDarkSystemUi();
|
||||||
buildUi();
|
buildUi();
|
||||||
loadSettings();
|
loadSettings();
|
||||||
|
ensureNotificationPermission();
|
||||||
updateBatchHint();
|
updateBatchHint();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,7 +96,15 @@ public class MainActivity extends Activity {
|
|||||||
protected void onStart() {
|
protected void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
if (!receiverRegistered) {
|
if (!receiverRegistered) {
|
||||||
registerReceiver(stateReceiver, new IntentFilter(DragonService.ACTION_STATE));
|
// From API 33 the export flag is mandatory. This receiver only ever
|
||||||
|
// hears DragonService's own package-scoped broadcast, so it must be
|
||||||
|
// NOT_EXPORTED; omitting the flag throws SecurityException.
|
||||||
|
IntentFilter filter = new IntentFilter(DragonService.ACTION_STATE);
|
||||||
|
if (Build.VERSION.SDK_INT >= 33) {
|
||||||
|
registerReceiver(stateReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
|
||||||
|
} else {
|
||||||
|
registerReceiver(stateReceiver, filter);
|
||||||
|
}
|
||||||
receiverRegistered = true;
|
receiverRegistered = true;
|
||||||
}
|
}
|
||||||
updateConnectionUi(DragonService.isActive(), DragonService.currentStatus());
|
updateConnectionUi(DragonService.isActive(), DragonService.currentStatus());
|
||||||
@@ -110,15 +121,59 @@ public class MainActivity extends Activity {
|
|||||||
|
|
||||||
private void forceDarkSystemUi() {
|
private void forceDarkSystemUi() {
|
||||||
Window w = getWindow();
|
Window w = getWindow();
|
||||||
|
// setStatusBarColor / setNavigationBarColor are no-ops once the app
|
||||||
|
// targets API 35+, where every activity is laid out edge to edge. The
|
||||||
|
// window background supplies the colour instead and applyInsets() keeps
|
||||||
|
// content clear of the system bars.
|
||||||
|
if (Build.VERSION.SDK_INT < 35) {
|
||||||
w.setStatusBarColor(BG);
|
w.setStatusBarColor(BG);
|
||||||
w.setNavigationBarColor(Color.rgb(7, 10, 14));
|
w.setNavigationBarColor(Color.rgb(7, 10, 14));
|
||||||
|
}
|
||||||
if (Build.VERSION.SDK_INT >= 26) w.getDecorView().setSystemUiVisibility(0);
|
if (Build.VERSION.SDK_INT >= 26) w.getDecorView().setSystemUiVisibility(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pads the root view by the system bar insets. Required from API 35: the
|
||||||
|
* activity draws edge to edge, so without this the header sits under the
|
||||||
|
* status bar and the buttons under the navigation bar.
|
||||||
|
*/
|
||||||
|
private void applyInsets(final View root, final int l, final int t, final int r, final int b) {
|
||||||
|
root.setOnApplyWindowInsetsListener((v, insets) -> {
|
||||||
|
int top, bottom, left, right;
|
||||||
|
if (Build.VERSION.SDK_INT >= 30) {
|
||||||
|
android.graphics.Insets bars =
|
||||||
|
insets.getInsets(WindowInsets.Type.systemBars() | WindowInsets.Type.ime());
|
||||||
|
top = bars.top; bottom = bars.bottom; left = bars.left; right = bars.right;
|
||||||
|
} else {
|
||||||
|
top = insets.getSystemWindowInsetTop();
|
||||||
|
bottom = insets.getSystemWindowInsetBottom();
|
||||||
|
left = insets.getSystemWindowInsetLeft();
|
||||||
|
right = insets.getSystemWindowInsetRight();
|
||||||
|
}
|
||||||
|
v.setPadding(l + left, t + top, r + right, b + bottom);
|
||||||
|
return insets;
|
||||||
|
});
|
||||||
|
root.requestApplyInsets();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** From API 33 the ongoing VPN notification is hidden without this grant. */
|
||||||
|
private void ensureNotificationPermission() {
|
||||||
|
if (Build.VERSION.SDK_INT < 33) return;
|
||||||
|
if (checkSelfPermission(android.Manifest.permission.POST_NOTIFICATIONS)
|
||||||
|
== android.content.pm.PackageManager.PERMISSION_GRANTED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
requestPermissions(new String[]{ android.Manifest.permission.POST_NOTIFICATIONS }, NOTIF_REQUEST);
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void buildUi() {
|
private void buildUi() {
|
||||||
LinearLayout root = new LinearLayout(this);
|
LinearLayout root = new LinearLayout(this);
|
||||||
root.setOrientation(LinearLayout.VERTICAL);
|
root.setOrientation(LinearLayout.VERTICAL);
|
||||||
root.setBackgroundColor(BG);
|
root.setBackgroundColor(BG);
|
||||||
|
// Base padding; applyInsets() adds the system bar insets on top.
|
||||||
root.setPadding(dp(16), dp(14), dp(16), dp(12));
|
root.setPadding(dp(16), dp(14), dp(16), dp(12));
|
||||||
|
|
||||||
LinearLayout header = new LinearLayout(this);
|
LinearLayout header = new LinearLayout(this);
|
||||||
@@ -274,6 +329,7 @@ public class MainActivity extends Activity {
|
|||||||
logsButton.setOnClickListener(v -> startActivity(new Intent(this, LogActivity.class)));
|
logsButton.setOnClickListener(v -> startActivity(new Intent(this, LogActivity.class)));
|
||||||
|
|
||||||
setContentView(root);
|
setContentView(root);
|
||||||
|
applyInsets(root, dp(16), dp(14), dp(16), dp(12));
|
||||||
updateConnectionUi(false, "DISCONNECTED");
|
updateConnectionUi(false, "DISCONNECTED");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+94
-14
@@ -3,29 +3,37 @@
|
|||||||
Windows equivalent of build_core.sh - builds the Go core binaries.
|
Windows equivalent of build_core.sh - builds the Go core binaries.
|
||||||
|
|
||||||
.DESCRIPTION
|
.DESCRIPTION
|
||||||
Cross-compiles the Android ARM64 client (dropped into android\lib\arm64-v8a\
|
Builds the Android client for every shipped ABI and the Linux client/servers.
|
||||||
as libdragontcp_client.so) and the Linux AMD64/ARM64 servers.
|
|
||||||
CGO is disabled, so no C toolchain or NDK is required.
|
arm64-v8a builds with CGO disabled and needs no NDK. The other Android ABIs
|
||||||
|
do: `GOOS=android` requires external linking for arm, amd64 and 386, so an
|
||||||
|
NDK toolchain is located automatically. If none is found those ABIs are
|
||||||
|
skipped with a warning rather than failing the build - the APK is still
|
||||||
|
installable on 64-bit ARM, which is the overwhelming majority of devices.
|
||||||
|
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
.\build_core.ps1
|
.\build_core.ps1
|
||||||
.\build_core.ps1 -ClientOnly
|
.\build_core.ps1 -ClientOnly
|
||||||
|
.\build_core.ps1 -Abis arm64-v8a,armeabi-v7a
|
||||||
#>
|
#>
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param(
|
param(
|
||||||
# Only build the Android client .so (skip the Linux servers).
|
# Only build the Android client (skip the Linux client and servers).
|
||||||
[switch]$ClientOnly,
|
[switch]$ClientOnly,
|
||||||
# Where to drop libdragontcp_client.so. Defaults to android\lib\arm64-v8a
|
# Root of the APK's lib directory; per-ABI subdirectories are created here.
|
||||||
# under this script; build_apk.ps1 passes its own app directory explicitly,
|
[string]$AndroidLibRoot,
|
||||||
# which is what makes this work regardless of how the tree is nested.
|
# Which Android ABIs to build.
|
||||||
[string]$AndroidLibDir,
|
[string[]]$Abis = @('arm64-v8a', 'armeabi-v7a', 'x86_64'),
|
||||||
# Path to the go executable.
|
# NDK root; auto-detected from the SDK when omitted.
|
||||||
|
[string]$NdkRoot,
|
||||||
|
# Minimum Android API the native cores are built for.
|
||||||
|
[int]$MinApi = 24,
|
||||||
[string]$GoBin = $(if ($env:GO_BIN) { $env:GO_BIN } else { 'go' })
|
[string]$GoBin = $(if ($env:GO_BIN) { $env:GO_BIN } else { 'go' })
|
||||||
)
|
)
|
||||||
|
|
||||||
$ErrorActionPreference = 'Stop'
|
$ErrorActionPreference = 'Stop'
|
||||||
$Root = $PSScriptRoot
|
$Root = $PSScriptRoot
|
||||||
if (-not $AndroidLibDir) { $AndroidLibDir = Join-Path $Root 'android\lib\arm64-v8a' }
|
if (-not $AndroidLibRoot) { $AndroidLibRoot = Join-Path $Root 'android\lib' }
|
||||||
|
|
||||||
function Invoke-Go {
|
function Invoke-Go {
|
||||||
param([hashtable]$Env, [string[]]$GoArgs)
|
param([hashtable]$Env, [string[]]$GoArgs)
|
||||||
@@ -42,17 +50,89 @@ function Invoke-Go {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Locate an NDK llvm toolchain bin directory, newest first.
|
||||||
|
function Find-NdkBin {
|
||||||
|
param([string]$Explicit)
|
||||||
|
$roots = @()
|
||||||
|
if ($Explicit) { $roots += $Explicit }
|
||||||
|
if ($env:ANDROID_NDK_ROOT) { $roots += $env:ANDROID_NDK_ROOT }
|
||||||
|
if ($env:ANDROID_NDK_HOME) { $roots += $env:ANDROID_NDK_HOME }
|
||||||
|
foreach ($sdk in $env:ANDROID_SDK_ROOT, $env:ANDROID_HOME, "$env:LOCALAPPDATA\Android\Sdk") {
|
||||||
|
if ($sdk -and (Test-Path (Join-Path $sdk 'ndk'))) {
|
||||||
|
$roots += (Get-ChildItem (Join-Path $sdk 'ndk') -Directory |
|
||||||
|
Sort-Object { try { [version]$_.Name } catch { [version]'0.0.0' } } -Descending |
|
||||||
|
ForEach-Object { $_.FullName })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($r in $roots) {
|
||||||
|
if (-not $r) { continue }
|
||||||
|
$bin = Join-Path $r 'toolchains\llvm\prebuilt\windows-x86_64\bin'
|
||||||
|
if (Test-Path $bin) { return $bin }
|
||||||
|
}
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
|
||||||
|
# ABI -> (GOARCH, GOARM, clang triple prefix)
|
||||||
|
$abiSpec = @{
|
||||||
|
'arm64-v8a' = @{ arch = 'arm64'; arm = ''; cc = 'aarch64-linux-android' }
|
||||||
|
'armeabi-v7a' = @{ arch = 'arm'; arm = '7'; cc = 'armv7a-linux-androideabi' }
|
||||||
|
'x86_64' = @{ arch = 'amd64'; arm = ''; cc = 'x86_64-linux-android' }
|
||||||
|
'x86' = @{ arch = '386'; arm = ''; cc = 'i686-linux-android' }
|
||||||
|
}
|
||||||
|
|
||||||
$go = Get-Command $GoBin -ErrorAction SilentlyContinue
|
$go = Get-Command $GoBin -ErrorAction SilentlyContinue
|
||||||
if (-not $go) { throw "Go compiler not found. Install from https://go.dev/dl/ or set -GoBin." }
|
if (-not $go) { throw "Go compiler not found. Install from https://go.dev/dl/ or set -GoBin." }
|
||||||
|
|
||||||
New-Item -ItemType Directory -Force -Path "$Root\bin", $AndroidLibDir | Out-Null
|
$ndkBin = Find-NdkBin $NdkRoot
|
||||||
|
New-Item -ItemType Directory -Force -Path "$Root\bin" | Out-Null
|
||||||
Push-Location "$Root\core"
|
Push-Location "$Root\core"
|
||||||
try {
|
try {
|
||||||
$ldflags = '-ldflags=-s -w'
|
$ldflags = '-ldflags=-s -w'
|
||||||
|
$built = @()
|
||||||
|
|
||||||
Write-Host '[core] Android ARM64 client...' -ForegroundColor Cyan
|
foreach ($abi in $Abis) {
|
||||||
Invoke-Go @{ CGO_ENABLED = '0'; GOOS = 'android'; GOARCH = 'arm64' } `
|
if (-not $abiSpec.ContainsKey($abi)) { Write-Warning "unknown ABI $abi, skipping"; continue }
|
||||||
@('build', '-trimpath', $ldflags, '-o', (Join-Path $AndroidLibDir 'libdragontcp_client.so'), './cmd/dragontcp-client')
|
$spec = $abiSpec[$abi]
|
||||||
|
$outDir = Join-Path $AndroidLibRoot $abi
|
||||||
|
$out = Join-Path $outDir 'libdragontcp_client.so'
|
||||||
|
|
||||||
|
# arm64 links internally; everything else needs the NDK's clang.
|
||||||
|
$env = @{ GOOS = 'android'; GOARCH = $spec.arch }
|
||||||
|
if ($spec.arm) { $env['GOARM'] = $spec.arm }
|
||||||
|
if ($abi -eq 'arm64-v8a') {
|
||||||
|
$env['CGO_ENABLED'] = '0'
|
||||||
|
} else {
|
||||||
|
if (-not $ndkBin) {
|
||||||
|
Write-Warning "[core] $abi skipped: no NDK found (set -NdkRoot or ANDROID_NDK_ROOT)"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
$cc = Join-Path $ndkBin ("{0}{1}-clang.cmd" -f $spec.cc, $MinApi)
|
||||||
|
if (-not (Test-Path $cc)) {
|
||||||
|
Write-Warning "[core] $abi skipped: $cc not found"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
$env['CGO_ENABLED'] = '1'
|
||||||
|
$env['CC'] = $cc
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "[core] Android $abi client..." -ForegroundColor Cyan
|
||||||
|
New-Item -ItemType Directory -Force -Path $outDir | Out-Null
|
||||||
|
Invoke-Go $env @('build', '-trimpath', $ldflags, '-o', $out, './cmd/dragontcp-client')
|
||||||
|
$built += $abi
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $built) { throw 'No Android ABI was built.' }
|
||||||
|
Write-Host "[core] Android ABIs: $($built -join ', ')" -ForegroundColor Green
|
||||||
|
|
||||||
|
# Remove stale ABI directories so the APK never ships an out-of-date core.
|
||||||
|
if (Test-Path $AndroidLibRoot) {
|
||||||
|
foreach ($dir in Get-ChildItem $AndroidLibRoot -Directory) {
|
||||||
|
if ($built -notcontains $dir.Name) {
|
||||||
|
Write-Warning "[core] removing stale $($dir.Name) native library"
|
||||||
|
Remove-Item $dir.FullName -Recurse -Force
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (-not $ClientOnly) {
|
if (-not $ClientOnly) {
|
||||||
Write-Host '[core] Linux AMD64 client...' -ForegroundColor Cyan
|
Write-Host '[core] Linux AMD64 client...' -ForegroundColor Cyan
|
||||||
|
|||||||
Reference in New Issue
Block a user