Merge pull request #152 from amnezia-vpn/feature/android_qt6_moving
Moving to Qt 6 and cmake on Android client
This commit is contained in:
commit
18d89e9cad
39 changed files with 1267 additions and 573 deletions
14
.github/workflows/deploy.yml
vendored
14
.github/workflows/deploy.yml
vendored
|
@ -255,7 +255,7 @@ jobs:
|
||||||
host: 'linux'
|
host: 'linux'
|
||||||
target: 'desktop'
|
target: 'desktop'
|
||||||
arch: 'gcc_64'
|
arch: 'gcc_64'
|
||||||
modules: 'qtremoteobjects qt5compat'
|
modules: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
|
||||||
dir: ${{ runner.temp }}
|
dir: ${{ runner.temp }}
|
||||||
setup-python: 'true'
|
setup-python: 'true'
|
||||||
set-env: 'true'
|
set-env: 'true'
|
||||||
|
@ -268,7 +268,7 @@ jobs:
|
||||||
host: 'linux'
|
host: 'linux'
|
||||||
target: 'android'
|
target: 'android'
|
||||||
arch: ${{ matrix.arch }}
|
arch: ${{ matrix.arch }}
|
||||||
modules: 'qtremoteobjects qt5compat'
|
modules: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
|
||||||
dir: ${{ runner.temp }}
|
dir: ${{ runner.temp }}
|
||||||
setup-python: 'true'
|
setup-python: 'true'
|
||||||
set-env: 'true'
|
set-env: 'true'
|
||||||
|
@ -287,17 +287,19 @@ jobs:
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: '8'
|
java-version: '11'
|
||||||
|
|
||||||
- name: 'Build project'
|
- name: 'Build project'
|
||||||
run: |
|
run: |
|
||||||
export NDK_VERSION=21d
|
export QT_HOST_PATH="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/gcc_64"
|
||||||
export ANDROID_NDK_PLATFORM=android-21
|
export NDK_VERSION=23c
|
||||||
|
export ANDROID_NDK_PLATFORM=android-23
|
||||||
export ANDROID_NDK_HOME=${{ runner.temp }}/android-ndk-r${NDK_VERSION}
|
export ANDROID_NDK_HOME=${{ runner.temp }}/android-ndk-r${NDK_VERSION}
|
||||||
export ANDROID_NDK_ROOT=$ANDROID_NDK_HOME
|
export ANDROID_NDK_ROOT=$ANDROID_NDK_HOME
|
||||||
|
export ANDROID_CURRENT_ARCH=${{ matrix.arch }}
|
||||||
|
|
||||||
if [ ! -f $ANDROID_NDK_ROOT/ndk-build ]; then
|
if [ ! -f $ANDROID_NDK_ROOT/ndk-build ]; then
|
||||||
wget https://dl.google.com/android/repository/android-ndk-r${NDK_VERSION}-linux-x86_64.zip -qO ${{ runner.temp }}/ndk.zip &&
|
wget https://dl.google.com/android/repository/android-ndk-r${NDK_VERSION}-linux.zip -qO ${{ runner.temp }}/ndk.zip &&
|
||||||
unzip -q -d ${{ runner.temp }} ${{ runner.temp }}/ndk.zip ;
|
unzip -q -d ${{ runner.temp }} ${{ runner.temp }}/ndk.zip ;
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
142
.github/workflows/tag-deploy.yml
vendored
Normal file
142
.github/workflows/tag-deploy.yml
vendored
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
name: 'Release deploy workflow'
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
# push:
|
||||||
|
# tags:
|
||||||
|
# - **
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
Build-Android-Release:
|
||||||
|
name: 'Build-Android-Release'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
env:
|
||||||
|
QT_VERSION: 6.4.1
|
||||||
|
QIF_VERSION: 4.5
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: 'Install desktop Qt'
|
||||||
|
uses: jurplel/install-qt-action@v3
|
||||||
|
with:
|
||||||
|
version: ${{ env.QT_VERSION }}
|
||||||
|
host: 'linux'
|
||||||
|
target: 'desktop'
|
||||||
|
arch: 'gcc_64'
|
||||||
|
modules: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
|
||||||
|
dir: ${{ runner.temp }}
|
||||||
|
setup-python: 'true'
|
||||||
|
set-env: 'true'
|
||||||
|
extra: '--external 7z'
|
||||||
|
|
||||||
|
- name: 'Install android Qt x86_64'
|
||||||
|
uses: jurplel/install-qt-action@v3
|
||||||
|
with:
|
||||||
|
version: ${{ env.QT_VERSION }}
|
||||||
|
host: 'linux'
|
||||||
|
target: 'android'
|
||||||
|
arch: 'android_x86_64'
|
||||||
|
modules: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
|
||||||
|
dir: ${{ runner.temp }}
|
||||||
|
setup-python: 'true'
|
||||||
|
set-env: 'true'
|
||||||
|
extra: '--external 7z'
|
||||||
|
|
||||||
|
- name: 'Install android Qt x86'
|
||||||
|
uses: jurplel/install-qt-action@v3
|
||||||
|
with:
|
||||||
|
version: ${{ env.QT_VERSION }}
|
||||||
|
host: 'linux'
|
||||||
|
target: 'android'
|
||||||
|
arch: 'android_x86'
|
||||||
|
modules: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
|
||||||
|
dir: ${{ runner.temp }}
|
||||||
|
setup-python: 'true'
|
||||||
|
set-env: 'true'
|
||||||
|
extra: '--external 7z'
|
||||||
|
|
||||||
|
- name: 'Install android Qt arm_v7'
|
||||||
|
uses: jurplel/install-qt-action@v3
|
||||||
|
with:
|
||||||
|
version: ${{ env.QT_VERSION }}
|
||||||
|
host: 'linux'
|
||||||
|
target: 'android'
|
||||||
|
arch: 'android_armv7'
|
||||||
|
modules: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
|
||||||
|
dir: ${{ runner.temp }}
|
||||||
|
setup-python: 'true'
|
||||||
|
set-env: 'true'
|
||||||
|
extra: '--external 7z'
|
||||||
|
|
||||||
|
- name: 'Install android Qt arm_v8'
|
||||||
|
uses: jurplel/install-qt-action@v3
|
||||||
|
with:
|
||||||
|
version: ${{ env.QT_VERSION }}
|
||||||
|
host: 'linux'
|
||||||
|
target: 'android'
|
||||||
|
arch: 'android_arm64_v8a'
|
||||||
|
modules: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
|
||||||
|
dir: ${{ runner.temp }}
|
||||||
|
setup-python: 'true'
|
||||||
|
set-env: 'true'
|
||||||
|
extra: '--external 7z'
|
||||||
|
|
||||||
|
- name: 'Get sources'
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
path: main
|
||||||
|
submodules: 'true'
|
||||||
|
fetch-depth: 10
|
||||||
|
|
||||||
|
- name: 'Preparations before keystore fetching'
|
||||||
|
run: |
|
||||||
|
mkdir keystore
|
||||||
|
|
||||||
|
- name: 'Getting keystore'
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
repository: amnezia-vpn/amnezia-android-certificates
|
||||||
|
ssh-key: ${{ secrets.ANDROID_CERTS_SSH_PRIVATE_KEY }}
|
||||||
|
path: keystore
|
||||||
|
|
||||||
|
- name: 'Setup ccache'
|
||||||
|
uses: hendrikmuhs/ccache-action@v1.2
|
||||||
|
|
||||||
|
- name: 'Setup Java'
|
||||||
|
uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
distribution: 'temurin'
|
||||||
|
java-version: '11'
|
||||||
|
|
||||||
|
- name: 'Build project'
|
||||||
|
run: |
|
||||||
|
export QT_HOST_PATH="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/gcc_64"
|
||||||
|
export NDK_VERSION=23c
|
||||||
|
export ANDROID_NDK_PLATFORM=android-23
|
||||||
|
export ANDROID_NDK_HOME=${{ runner.temp }}/android-ndk-r${NDK_VERSION}
|
||||||
|
export ANDROID_NDK_ROOT=$ANDROID_NDK_HOME
|
||||||
|
|
||||||
|
if [ ! -f $ANDROID_NDK_ROOT/ndk-build ]; then
|
||||||
|
wget https://dl.google.com/android/repository/android-ndk-r${NDK_VERSION}-linux.zip -qO ${{ runner.temp }}/ndk.zip &&
|
||||||
|
unzip -q -d ${{ runner.temp }} ${{ runner.temp }}/ndk.zip ;
|
||||||
|
fi
|
||||||
|
|
||||||
|
export QT_BIN_DIR=${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/android_arm64_v8a/bin
|
||||||
|
cd main
|
||||||
|
bash deploy/build_android.sh
|
||||||
|
|
||||||
|
- name: 'Signing APK'
|
||||||
|
run: |
|
||||||
|
pwd
|
||||||
|
|
||||||
|
ANDROID_BUILD_TOOLS_VERSION=30.0.3
|
||||||
|
|
||||||
|
${ANDROID_HOME}/build-tools/${ANDROID_BUILD_TOOLS_VERSION}/zipalign -f -v 4 AmneziaVPN-release-unsigned.apk AmneziaVPN-release-aligned.apk
|
||||||
|
${ANDROID_HOME}/build-tools/${ANDROID_BUILD_TOOLS_VERSION}/apksigner sign --out AmneziaVPN-release-signed.apk --ks keystore/debug.keystore --ks-key-alias ${{ secrets.DEBUG_ANDROID_KEYSTORE_KEY_ALIAS }} --ks-pass pass:${{secrets.DEBUG_ANDROID_KEYSTOTE_KEY_PASS }} AmneziaVPN-release-aligned.apk
|
||||||
|
|
||||||
|
- name: 'Upload'
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: Release APK
|
||||||
|
path: ${{ runner.temp }}/main/AmneziaVPN-release-signed.apk
|
|
@ -3,6 +3,10 @@ cmake_minimum_required(VERSION 3.23.0 FATAL_ERROR)
|
||||||
set(PROJECT AmneziaVPN)
|
set(PROJECT AmneziaVPN)
|
||||||
project(${PROJECT})
|
project(${PROJECT})
|
||||||
|
|
||||||
|
if(ANDROID)
|
||||||
|
set(QT_ANDROID_BUILD_ALL_ABIS ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_subdirectory(client)
|
add_subdirectory(client)
|
||||||
|
|
||||||
if(NOT IOS AND NOT ANDROID)
|
if(NOT IOS AND NOT ANDROID)
|
||||||
|
|
|
@ -38,11 +38,13 @@ if(ANDROID)
|
||||||
# We need to include qtprivate api's
|
# We need to include qtprivate api's
|
||||||
# As QAndroidBinder is not yet implemented with a public api
|
# As QAndroidBinder is not yet implemented with a public api
|
||||||
set(LIBS ${LIBS} Qt6::CorePrivate)
|
set(LIBS ${LIBS} Qt6::CorePrivate)
|
||||||
set(ANDROID_ABIS ANDROID_TARGET_ARCH)
|
|
||||||
|
|
||||||
link_directories(${CMAKE_CURRENT_LIST_DIR}/android/${ANDROID_TARGET_ARCH})
|
set(abi ${CMAKE_ANDROID_ARCH_ABI})
|
||||||
set(HEADERS ${HEADERS} ${CMAKE_CURRENT_LIST_DIR}/android/${ANDROID_TARGET_ARCH}/botan_all.h)
|
|
||||||
set(SOURCES ${SOURCES} ${CMAKE_CURRENT_LIST_DIR}/android/${ANDROID_TARGET_ARCH}/botan_all.cpp)
|
include_directories(${CMAKE_CURRENT_LIST_DIR}/android/${abi})
|
||||||
|
link_directories(${CMAKE_CURRENT_LIST_DIR}/android/${abi})
|
||||||
|
set(HEADERS ${HEADERS} ${CMAKE_CURRENT_LIST_DIR}/android/${abi}/botan_all.h)
|
||||||
|
set(SOURCES ${SOURCES} ${CMAKE_CURRENT_LIST_DIR}/android/${abi}/botan_all.cpp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(IOS)
|
if(IOS)
|
||||||
|
@ -66,8 +68,4 @@ if(IOS)
|
||||||
include_directories(${CMAKE_CURRENT_LIST_DIR}/ios/iphone)
|
include_directories(${CMAKE_CURRENT_LIST_DIR}/ios/iphone)
|
||||||
set(HEADERS ${HEADERS} ${CMAKE_CURRENT_LIST_DIR}/ios/iphone/botan_all.h)
|
set(HEADERS ${HEADERS} ${CMAKE_CURRENT_LIST_DIR}/ios/iphone/botan_all.h)
|
||||||
set(SOURCES ${SOURCES} ${CMAKE_CURRENT_LIST_DIR}/ios/iphone/botan_all.cpp)
|
set(SOURCES ${SOURCES} ${CMAKE_CURRENT_LIST_DIR}/ios/iphone/botan_all.cpp)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit f197cdb935b0cfd9881fdc6860874cb8379d1238
|
Subproject commit c6f0b66318f8da6917fb4681103f7303b1836194
|
|
@ -266,7 +266,6 @@ if(ANDROID)
|
||||||
# We need to include qtprivate api's
|
# We need to include qtprivate api's
|
||||||
# As QAndroidBinder is not yet implemented with a public api
|
# As QAndroidBinder is not yet implemented with a public api
|
||||||
set(LIBS ${LIBS} Qt6::CorePrivate)
|
set(LIBS ${LIBS} Qt6::CorePrivate)
|
||||||
set(ANDROID_ABIS ANDROID_TARGET_ARCH)
|
|
||||||
|
|
||||||
add_compile_definitions(MVPN_ANDROID)
|
add_compile_definitions(MVPN_ANDROID)
|
||||||
|
|
||||||
|
@ -275,12 +274,16 @@ if(ANDROID)
|
||||||
set(HEADERS ${HEADERS}
|
set(HEADERS ${HEADERS}
|
||||||
${CMAKE_CURRENT_LIST_DIR}/platforms/android/android_controller.h
|
${CMAKE_CURRENT_LIST_DIR}/platforms/android/android_controller.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/platforms/android/android_notificationhandler.h
|
${CMAKE_CURRENT_LIST_DIR}/platforms/android/android_notificationhandler.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/platforms/android/androidutils.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/platforms/android/androidvpnactivity.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/protocols/android_vpnprotocol.h
|
${CMAKE_CURRENT_LIST_DIR}/protocols/android_vpnprotocol.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(SOURCES ${SOURCES}
|
set(SOURCES ${SOURCES}
|
||||||
${CMAKE_CURRENT_LIST_DIR}/platforms/android/android_controller.cp
|
${CMAKE_CURRENT_LIST_DIR}/platforms/android/android_controller.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/platforms/android/android_notificationhandler.cpp
|
${CMAKE_CURRENT_LIST_DIR}/platforms/android/android_notificationhandler.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/platforms/android/androidutils.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/platforms/android/androidvpnactivity.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/protocols/android_vpnprotocol.cpp
|
${CMAKE_CURRENT_LIST_DIR}/protocols/android_vpnprotocol.cpp
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
@ -482,25 +485,26 @@ if(ANDROID)
|
||||||
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/NotificationUtil.kt
|
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/NotificationUtil.kt
|
||||||
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/OpenVPNThreadv3.kt
|
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/OpenVPNThreadv3.kt
|
||||||
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/Prefs.kt
|
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/Prefs.kt
|
||||||
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/VpnLogger.kt
|
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/VPNLogger.kt
|
||||||
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/VpnService.kt
|
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/VPNService.kt
|
||||||
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/VpnServiceBinder.kt
|
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/VPNServiceBinder.kt
|
||||||
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/qt/AmneziaApp.kt
|
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/qt/AmneziaApp.kt
|
||||||
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/qt/PackageManagerHelper.java
|
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/qt/PackageManagerHelper.java
|
||||||
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/qt/VPNActivity.kt
|
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/qt/VPNActivity.kt
|
||||||
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/qt/VPNApplication.java
|
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/qt/VPNApplication.java
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/qt/VPNClientBinder.kt
|
||||||
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/qt/VPNPermissionHelper.kt
|
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/qt/VPNPermissionHelper.kt
|
||||||
${CMAKE_CURRENT_BINARY_DIR}
|
${CMAKE_CURRENT_BINARY_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
set_property(TARGET ${PROJECT}
|
set_property(TARGET ${PROJECT} PROPERTY
|
||||||
QT_ANDROID_PACKAGE_SOURCE_DIR
|
QT_ANDROID_PACKAGE_SOURCE_DIR
|
||||||
${CMAKE_CURRENT_LIST_DIR}/android
|
${CMAKE_CURRENT_LIST_DIR}/android
|
||||||
)
|
)
|
||||||
|
|
||||||
foreach(abi IN ANDROID_ABIS)
|
foreach(abi IN ITEMS ${QT_ANDROID_ABIS})
|
||||||
if(ANDROID_TARGET_ARCH EQUAL ${abi})
|
if(CMAKE_ANDROID_ARCH_ABI STREQUAL ${abi})
|
||||||
set(LIBS ${LIBS}
|
set(LIBS ${LIBS}
|
||||||
${CMAKE_CURRENT_LIST_DIR}/3rd/OpenSSL/lib/android/${abi}/libcrypto.a
|
${CMAKE_CURRENT_LIST_DIR}/3rd/OpenSSL/lib/android/${abi}/libcrypto.a
|
||||||
${CMAKE_CURRENT_LIST_DIR}/3rd/OpenSSL/lib/android/${abi}/libssl.a
|
${CMAKE_CURRENT_LIST_DIR}/3rd/OpenSSL/lib/android/${abi}/libssl.a
|
||||||
)
|
)
|
||||||
|
@ -510,7 +514,7 @@ if(ANDROID)
|
||||||
${CMAKE_CURRENT_LIST_DIR}/android/lib/wireguard/${abi}/libwg.so
|
${CMAKE_CURRENT_LIST_DIR}/android/lib/wireguard/${abi}/libwg.so
|
||||||
${CMAKE_CURRENT_LIST_DIR}/android/lib/wireguard/${abi}/libwg-go.so
|
${CMAKE_CURRENT_LIST_DIR}/android/lib/wireguard/${abi}/libwg-go.so
|
||||||
${CMAKE_CURRENT_LIST_DIR}/android/lib/wireguard/${abi}/libwg-quick.so
|
${CMAKE_CURRENT_LIST_DIR}/android/lib/wireguard/${abi}/libwg-quick.so
|
||||||
|
|
||||||
${CMAKE_CURRENT_LIST_DIR}/android/lib/openvpn/${abi}/libjbcrypto.so
|
${CMAKE_CURRENT_LIST_DIR}/android/lib/openvpn/${abi}/libjbcrypto.so
|
||||||
${CMAKE_CURRENT_LIST_DIR}/android/lib/openvpn/${abi}/libopenvpn.so
|
${CMAKE_CURRENT_LIST_DIR}/android/lib/openvpn/${abi}/libopenvpn.so
|
||||||
${CMAKE_CURRENT_LIST_DIR}/android/lib/openvpn/${abi}/libopvpnutil.so
|
${CMAKE_CURRENT_LIST_DIR}/android/lib/openvpn/${abi}/libopvpnutil.so
|
||||||
|
@ -518,6 +522,7 @@ if(ANDROID)
|
||||||
${CMAKE_CURRENT_LIST_DIR}/android/lib/openvpn/${abi}/libovpnexec.so
|
${CMAKE_CURRENT_LIST_DIR}/android/lib/openvpn/${abi}/libovpnexec.so
|
||||||
)
|
)
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(${PROJECT} PRIVATE ${LIBS})
|
target_link_libraries(${PROJECT} PRIVATE ${LIBS})
|
||||||
|
|
|
@ -21,13 +21,19 @@
|
||||||
Remove the comment if you do not require these default features. -->
|
Remove the comment if you do not require these default features. -->
|
||||||
<!-- %%INSERT_FEATURES -->
|
<!-- %%INSERT_FEATURES -->
|
||||||
|
|
||||||
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
|
<supports-screens
|
||||||
|
android:largeScreens="true"
|
||||||
|
android:normalScreens="true"
|
||||||
|
android:anyDensity="true"
|
||||||
|
android:smallScreens="true"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".qt.AmneziaApp"
|
android:name=".qt.AmneziaApp"
|
||||||
android:hardwareAccelerated="true"
|
android:hardwareAccelerated="true"
|
||||||
android:label="-- %%INSERT_APP_NAME%% --"
|
android:label="-- %%INSERT_APP_NAME%% --"
|
||||||
android:extractNativeLibs="true"
|
android:extractNativeLibs="true"
|
||||||
|
android:requestLegacyExternalStorage="true"
|
||||||
|
android:allowNativeHeapPointerTagging="false"
|
||||||
android:icon="@drawable/icon">
|
android:icon="@drawable/icon">
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
|
@ -36,21 +42,22 @@
|
||||||
android:label="-- %%INSERT_APP_NAME%% --"
|
android:label="-- %%INSERT_APP_NAME%% --"
|
||||||
android:screenOrientation="unspecified"
|
android:screenOrientation="unspecified"
|
||||||
android:launchMode="singleInstance"
|
android:launchMode="singleInstance"
|
||||||
android:taskAffinity=""
|
android:exported="true">
|
||||||
android:theme="@style/splashScreenTheme">
|
|
||||||
|
<!-- android:theme="@style/splashScreenTheme"-->
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<intent-filter android:label="AmneziaVPN">
|
<intent-filter android:label="AmneziaVPN">
|
||||||
<action android:name="android.intent.action.SEND" />
|
<action android:name="android.intent.action.SEND"/>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
<data android:scheme="file"/>
|
<data android:scheme="file"/>
|
||||||
<data android:scheme="content"/>
|
<data android:scheme="content"/>
|
||||||
<data android:mimeType= "*/*"/>
|
<data android:mimeType="*/*"/>
|
||||||
<data android:host="*"/>
|
<data android:host="*"/>
|
||||||
<data android:pathPattern=".*\\.vpn"/>
|
<data android:pathPattern=".*\\.vpn"/>
|
||||||
<data android:pathPattern=".*\\..*\\.vpn"/>
|
<data android:pathPattern=".*\\..*\\.vpn"/>
|
||||||
|
@ -58,14 +65,14 @@
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\.vpn"/>
|
<data android:pathPattern=".*\\..*\\..*\\..*\\.vpn"/>
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.vpn"/>
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.vpn"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<intent-filter android:label="AmneziaVPN">
|
<intent-filter android:label="AmneziaVPN">
|
||||||
<action android:name="android.intent.action.SEND" />
|
<action android:name="android.intent.action.SEND"/>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
<data android:scheme="file"/>
|
<data android:scheme="file"/>
|
||||||
<data android:scheme="content"/>
|
<data android:scheme="content"/>
|
||||||
<data android:mimeType= "*/*"/>
|
<data android:mimeType="*/*"/>
|
||||||
<data android:host="*"/>
|
<data android:host="*"/>
|
||||||
<data android:pathPattern=".*\\.cfg"/>
|
<data android:pathPattern=".*\\.cfg"/>
|
||||||
<data android:pathPattern=".*\\..*\\.cfg"/>
|
<data android:pathPattern=".*\\..*\\.cfg"/>
|
||||||
|
@ -73,14 +80,14 @@
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\.cfg"/>
|
<data android:pathPattern=".*\\..*\\..*\\..*\\.cfg"/>
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.cfg"/>
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.cfg"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<intent-filter android:label="AmneziaVPN">
|
<intent-filter android:label="AmneziaVPN">
|
||||||
<action android:name="android.intent.action.SEND" />
|
<action android:name="android.intent.action.SEND"/>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
<data android:scheme="file"/>
|
<data android:scheme="file"/>
|
||||||
<data android:scheme="content"/>
|
<data android:scheme="content"/>
|
||||||
<data android:mimeType= "*/*"/>
|
<data android:mimeType="*/*"/>
|
||||||
<data android:host="*"/>
|
<data android:host="*"/>
|
||||||
<data android:pathPattern=".*\\.conf"/>
|
<data android:pathPattern=".*\\.conf"/>
|
||||||
<data android:pathPattern=".*\\..*\\.conf"/>
|
<data android:pathPattern=".*\\..*\\.conf"/>
|
||||||
|
@ -88,102 +95,51 @@
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\.conf"/>
|
<data android:pathPattern=".*\\..*\\..*\\..*\\.conf"/>
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.conf"/>
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.conf"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.app.lib_name"
|
||||||
|
android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
|
||||||
|
|
||||||
<!-- Application arguments -->
|
<meta-data
|
||||||
<!-- meta-data android:name="android.app.arguments" android:value="arg1 arg2 arg3"/ -->
|
android:name="android.app.extract_android_style"
|
||||||
<!-- Application arguments -->
|
android:value="minimal" />
|
||||||
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
|
|
||||||
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
|
<meta-data
|
||||||
<meta-data android:name="android.app.repository" android:value="default"/>
|
android:name="android.app.background_running"
|
||||||
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
|
android:value="false"/>
|
||||||
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
|
|
||||||
<!-- Deploy Qt libs as part of package -->
|
<meta-data
|
||||||
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
|
android:name="android.app.arguments"
|
||||||
<!-- Run with local libs -->
|
android:value="-- %%INSERT_APP_ARGUMENTS%% --" />
|
||||||
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
|
|
||||||
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
|
|
||||||
<meta-data android:name="android.app.load_local_libs_resource_id" android:resource="@array/load_local_libs"/>
|
|
||||||
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
|
|
||||||
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
|
|
||||||
<!-- Used to specify custom system library path to run with local system libs -->
|
|
||||||
<!-- <meta-data android:name="android.app.system_libs_prefix" android:value="/system/lib/"/> -->
|
|
||||||
<!-- Messages maps -->
|
|
||||||
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
|
|
||||||
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
|
|
||||||
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
|
|
||||||
<meta-data android:value="@string/unsupported_android_version" android:name="android.app.unsupported_android_version"/>
|
|
||||||
<!-- Messages maps -->
|
|
||||||
<!-- Splash screen -->
|
|
||||||
<!-- Orientation-specific (portrait/landscape) data is checked first. If not available for current orientation,
|
|
||||||
then android.app.splash_screen_drawable. For best results, use together with splash_screen_sticky and
|
|
||||||
use hideSplashScreen() with a fade-out animation from Qt Android Extras to hide the splash screen when you
|
|
||||||
are done populating your window with content. -->
|
|
||||||
<!-- meta-data android:name="android.app.splash_screen_drawable_portrait" android:resource="@drawable/logo_portrait" / -->
|
|
||||||
<!-- meta-data android:name="android.app.splash_screen_drawable_landscape" android:resource="@drawable/logo_landscape" / -->
|
|
||||||
<!-- meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/ -->
|
|
||||||
<!-- meta-data android:name="android.app.splash_screen_sticky" android:value="true"/ -->
|
|
||||||
<!-- Splash screen -->
|
|
||||||
<!-- Background running -->
|
|
||||||
<!-- Warning: changing this value to true may cause unexpected crashes if the
|
|
||||||
application still try to draw after
|
|
||||||
"applicationStateChanged(Qt::ApplicationSuspended)"
|
|
||||||
signal is sent! -->
|
|
||||||
<meta-data android:name="android.app.background_running" android:value="false"/>
|
|
||||||
<!-- Background running -->
|
|
||||||
<!-- auto screen scale factor -->
|
|
||||||
<meta-data android:name="android.app.auto_screen_scale_factor" android:value="false"/>
|
|
||||||
<!-- auto screen scale factor -->
|
|
||||||
<!-- extract android style -->
|
|
||||||
<!-- available android:values :
|
|
||||||
* default - In most cases this will be the same as "full", but it can also be something else if needed, e.g., for compatibility reasons
|
|
||||||
* full - useful QWidget & Quick Controls 1 apps
|
|
||||||
* minimal - useful for Quick Controls 2 apps, it is much faster than "full"
|
|
||||||
* none - useful for apps that don't use any of the above Qt modules
|
|
||||||
-->
|
|
||||||
<meta-data android:name="android.app.extract_android_style" android:value="default"/>
|
|
||||||
<!-- extract android style -->
|
|
||||||
<meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/splashscreen"/>
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".VPNService"
|
android:name=".VPNService"
|
||||||
android:permission="android.permission.BIND_VPN_SERVICE"
|
|
||||||
android:process=":QtOnlyProcess"
|
android:process=":QtOnlyProcess"
|
||||||
|
android:permission="android.permission.BIND_VPN_SERVICE"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
|
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.net.VpnService"/>
|
<intent-filter>
|
||||||
</intent-filter>
|
<action android:name="android.net.VpnService"/>
|
||||||
|
</intent-filter>
|
||||||
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
|
|
||||||
<meta-data android:name="android.app.repository" android:value="default"/>
|
|
||||||
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
|
|
||||||
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
|
|
||||||
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
|
|
||||||
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
|
|
||||||
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
|
|
||||||
<meta-data android:name="android.app.load_local_libs_resource_id" android:resource="@array/load_local_libs"/>
|
|
||||||
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
|
|
||||||
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
|
|
||||||
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
|
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".qt.VPNPermissionHelper"
|
android:name=".qt.VPNPermissionHelper"
|
||||||
android:permission="android.permission.BIND_VPN_SERVICE"
|
android:permission="android.permission.BIND_VPN_SERVICE"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
|
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
|
||||||
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
|
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
|
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:authorities="org.amnezia.vpn.fileprovider"
|
android:authorities="org.amnezia.vpn.fileprovider"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:grantUriPermissions="true">
|
android:grantUriPermissions="true">
|
||||||
|
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/fileprovider"/>
|
||||||
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/fileprovider"/>
|
|
||||||
</provider>
|
</provider>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ buildscript {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:4.0.0'
|
classpath 'com.android.tools.build:gradle:7.2.1'
|
||||||
classpath 'com.github.ben-manes:gradle-versions-plugin:0.21.0'
|
classpath 'com.github.ben-manes:gradle-versions-plugin:0.21.0'
|
||||||
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.8.0'
|
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.8.0'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
@ -72,7 +72,11 @@ android {
|
||||||
|
|
||||||
compileSdkVersion androidCompileSdkVersion.toInteger()
|
compileSdkVersion androidCompileSdkVersion.toInteger()
|
||||||
|
|
||||||
//buildToolsVersion '28.0.3'
|
buildToolsVersion androidBuildToolsVersion
|
||||||
|
ndkVersion androidNdkVersion
|
||||||
|
|
||||||
|
// Extract native libraries from the APK
|
||||||
|
packagingOptions.jniLibs.useLegacyPackaging true
|
||||||
|
|
||||||
dexOptions {
|
dexOptions {
|
||||||
javaMaxHeapSize "3g"
|
javaMaxHeapSize "3g"
|
||||||
|
@ -81,9 +85,9 @@ android {
|
||||||
sourceSets {
|
sourceSets {
|
||||||
main {
|
main {
|
||||||
manifest.srcFile 'AndroidManifest.xml'
|
manifest.srcFile 'AndroidManifest.xml'
|
||||||
java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java']
|
java.srcDirs = [qtAndroidDir + '/src', 'src', 'java']
|
||||||
aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl']
|
aidl.srcDirs = [qtAndroidDir + '/src', 'src', 'aidl']
|
||||||
res.srcDirs = [qt5AndroidDir + '/res', 'res']
|
res.srcDirs = [qtAndroidDir + '/res', 'res']
|
||||||
resources.srcDirs = ['resources']
|
resources.srcDirs = ['resources']
|
||||||
renderscript.srcDirs = ['src']
|
renderscript.srcDirs = ['src']
|
||||||
assets.srcDirs = ['assets']
|
assets.srcDirs = ['assets']
|
||||||
|
@ -139,6 +143,7 @@ android {
|
||||||
debug {
|
debug {
|
||||||
//applicationIdSuffix ".debug"
|
//applicationIdSuffix ".debug"
|
||||||
//versionNameSuffix "-debug"
|
//versionNameSuffix "-debug"
|
||||||
|
minifyEnabled false
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
arguments "-DANDROID_PACKAGE_NAME=${groupName}", "-DGRADLE_USER_HOME=${project.gradle.gradleUserHomeDir}"
|
arguments "-DANDROID_PACKAGE_NAME=${groupName}", "-DGRADLE_USER_HOME=${project.gradle.gradleUserHomeDir}"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<resources>
|
<resources>
|
||||||
<array name="qt_sources">
|
<!-- DO NOT EDIT THIS: This file is populated automatically by the deployment tool. -->
|
||||||
<item>https://download.qt.io/ministro/android/qt5/qt-5.14</item>
|
|
||||||
</array>
|
|
||||||
|
|
||||||
<!-- The following is handled automatically by the deployment tool. It should
|
|
||||||
not be edited manually. -->
|
|
||||||
|
|
||||||
<array name="bundled_libs">
|
<array name="bundled_libs">
|
||||||
<!-- %%INSERT_EXTRA_LIBS%% -->
|
<!-- %%INSERT_EXTRA_LIBS%% -->
|
||||||
|
@ -19,4 +14,8 @@
|
||||||
<!-- %%INSERT_LOCAL_LIBS%% -->
|
<!-- %%INSERT_LOCAL_LIBS%% -->
|
||||||
</array>
|
</array>
|
||||||
|
|
||||||
|
<string name="static_init_classes"><!-- %%INSERT_INIT_CLASSES%% --></string>
|
||||||
|
<string name="use_local_qt_libs"><!-- %%USE_LOCAL_QT_LIBS%% --></string>
|
||||||
|
<string name="bundle_local_qt_libs"><!-- %%BUNDLE_LOCAL_QT_LIBS%% --></string>
|
||||||
|
<string name="system_libs_prefix"><!-- %%SYSTEM_LIBS_PREFIX%% --></string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -3,7 +3,7 @@ package org.amnezia.vpn;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.app.KeyguardManager;
|
import android.app.KeyguardManager;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import org.qtproject.qt5.android.bindings.QtActivity;
|
import org.qtproject.qt.android.bindings.QtActivity;
|
||||||
|
|
||||||
|
|
||||||
import static android.content.Context.KEYGUARD_SERVICE;
|
import static android.content.Context.KEYGUARD_SERVICE;
|
||||||
|
|
|
@ -31,6 +31,31 @@ class OpenVPNThreadv3(var service: VPNService): ClientAPI_OpenVPNClient(), Runna
|
||||||
private var mAlreadyInitialised = false
|
private var mAlreadyInitialised = false
|
||||||
private var mService: VPNService = service
|
private var mService: VPNService = service
|
||||||
|
|
||||||
|
private var bytesInIndex = -1
|
||||||
|
private var bytesOutIndex = -1
|
||||||
|
|
||||||
|
init {
|
||||||
|
findConfigIndicies()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findConfigIndicies() {
|
||||||
|
val n: Int = stats_n()
|
||||||
|
|
||||||
|
for (i in 0 until n) {
|
||||||
|
val name: String = stats_name(i)
|
||||||
|
if (name == "BYTES_IN") bytesInIndex = i
|
||||||
|
if (name == "BYTES_OUT") bytesOutIndex = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTotalRxBytes(): Long {
|
||||||
|
return stats_value(bytesInIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTotalTxBytes(): Long {
|
||||||
|
return stats_value(bytesOutIndex)
|
||||||
|
}
|
||||||
|
|
||||||
override fun run() {
|
override fun run() {
|
||||||
|
|
||||||
val config: ClientAPI_Config = ClientAPI_Config()
|
val config: ClientAPI_Config = ClientAPI_Config()
|
||||||
|
|
|
@ -41,6 +41,7 @@ import java.io.Closeable
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileDescriptor
|
import java.io.FileDescriptor
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import java.lang.Exception
|
||||||
import android.net.VpnService as BaseVpnService
|
import android.net.VpnService as BaseVpnService
|
||||||
|
|
||||||
|
|
||||||
|
@ -153,31 +154,6 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
|
||||||
private var flags = 0
|
private var flags = 0
|
||||||
private var startId = 0
|
private var startId = 0
|
||||||
|
|
||||||
private lateinit var mMessenger: Messenger
|
|
||||||
|
|
||||||
internal class ExternalConfigImportHandler(
|
|
||||||
context: Context,
|
|
||||||
private val serviceBinder: VPNServiceBinder,
|
|
||||||
private val applicationContext: Context = context.applicationContext
|
|
||||||
) : Handler() {
|
|
||||||
|
|
||||||
override fun handleMessage(msg: Message) {
|
|
||||||
when (msg.what) {
|
|
||||||
IMPORT_COMMAND_CODE -> {
|
|
||||||
val data = msg.data.getString(IMPORT_CONFIG_KEY)
|
|
||||||
|
|
||||||
if (data != null) {
|
|
||||||
serviceBinder.importConfig(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
super.handleMessage(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun init() {
|
fun init() {
|
||||||
if (mAlreadyInitialised) {
|
if (mAlreadyInitialised) {
|
||||||
return
|
return
|
||||||
|
@ -216,13 +192,6 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
|
||||||
override fun onBind(intent: Intent): IBinder {
|
override fun onBind(intent: Intent): IBinder {
|
||||||
Log.v(tag, "Aman: onBind....................")
|
Log.v(tag, "Aman: onBind....................")
|
||||||
|
|
||||||
if (intent.action != null && intent.action == IMPORT_ACTION_CODE) {
|
|
||||||
Log.v(tag, "Service bind for import of config")
|
|
||||||
mMessenger = Messenger(ExternalConfigImportHandler(this, mBinder))
|
|
||||||
return mMessenger.binder
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.v(tag, "Regular service bind")
|
|
||||||
when (mProtocol) {
|
when (mProtocol) {
|
||||||
"shadowsocks" -> {
|
"shadowsocks" -> {
|
||||||
when (intent.action) {
|
when (intent.action) {
|
||||||
|
@ -306,9 +275,16 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
|
||||||
return mConnectionTime
|
return mConnectionTime
|
||||||
}
|
}
|
||||||
|
|
||||||
var isUp: Boolean
|
var isUp: Boolean = false
|
||||||
get() {
|
get() {
|
||||||
return currentTunnelHandle >= 0
|
return when (mProtocol) {
|
||||||
|
"openvpn" -> {
|
||||||
|
field
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
currentTunnelHandle >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
set(value) {
|
set(value) {
|
||||||
if (value) {
|
if (value) {
|
||||||
|
@ -319,17 +295,52 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
|
||||||
mBinder.dispatchEvent(VPNServiceBinder.EVENTS.disconnected, "")
|
mBinder.dispatchEvent(VPNServiceBinder.EVENTS.disconnected, "")
|
||||||
mConnectionTime = 0
|
mConnectionTime = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
val status: JSONObject
|
val status: JSONObject
|
||||||
get() {
|
get() {
|
||||||
val deviceIpv4: String = ""
|
val deviceIpv4: String = ""
|
||||||
|
|
||||||
|
val status = when (mProtocol) {
|
||||||
|
"openvpn" -> {
|
||||||
|
if (mOpenVPNThreadv3 == null) {
|
||||||
|
Status(null, null, null, null)
|
||||||
|
} else {
|
||||||
|
val rx = mOpenVPNThreadv3?.getTotalRxBytes() ?: ""
|
||||||
|
val tx = mOpenVPNThreadv3?.getTotalTxBytes() ?: ""
|
||||||
|
|
||||||
|
Status(
|
||||||
|
rx.toString(),
|
||||||
|
tx.toString(),
|
||||||
|
if (mConfig!!.has("server")) { mConfig?.getJSONObject("server")?.getString("ipv4Gateway") } else {""},
|
||||||
|
if (mConfig!!.has("device")) { mConfig?.getJSONObject("device")?.getString("ipv4Address") } else {""}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Status(
|
||||||
|
getConfigValue("rx_bytes"),
|
||||||
|
getConfigValue("tx_bytes"),
|
||||||
|
if (mConfig!!.has("server")) { mConfig?.getJSONObject("server")?.getString("ipv4Gateway") } else {""},
|
||||||
|
if (mConfig!!.has("server")) {mConfig?.getJSONObject("device")?.getString("ipv4Address") } else {""}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return JSONObject().apply {
|
return JSONObject().apply {
|
||||||
putOpt("rx_bytes", getConfigValue("rx_bytes"))
|
putOpt("rx_bytes", status.rxBytes)
|
||||||
putOpt("tx_bytes", getConfigValue("tx_bytes"))
|
putOpt("tx_bytes", status.txBytes)
|
||||||
putOpt("endpoint", mConfig?.getJSONObject("server")?.getString("ipv4Gateway"))
|
putOpt("endpoint", status.endpoint)
|
||||||
putOpt("deviceIpv4", mConfig?.getJSONObject("device")?.getString("ipv4Address"))
|
putOpt("deviceIpv4", status.device)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class Status(
|
||||||
|
var rxBytes: String?,
|
||||||
|
var txBytes: String?,
|
||||||
|
var endpoint: String?,
|
||||||
|
var device: String?
|
||||||
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Checks if the VPN Permission is given.
|
* Checks if the VPN Permission is given.
|
||||||
* If the permission is given, returns true
|
* If the permission is given, returns true
|
||||||
|
@ -677,6 +688,7 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
|
||||||
|
|
||||||
private fun startOpenVpn() {
|
private fun startOpenVpn() {
|
||||||
mOpenVPNThreadv3 = OpenVPNThreadv3(this)
|
mOpenVPNThreadv3 = OpenVPNThreadv3(this)
|
||||||
|
|
||||||
Thread({
|
Thread({
|
||||||
mOpenVPNThreadv3?.run()
|
mOpenVPNThreadv3?.run()
|
||||||
}).start()
|
}).start()
|
||||||
|
|
|
@ -33,6 +33,7 @@ class VPNServiceBinder(service: VPNService) : Binder() {
|
||||||
const val setNotificationText = 8
|
const val setNotificationText = 8
|
||||||
const val setFallBackNotification = 9
|
const val setFallBackNotification = 9
|
||||||
const val shareConfig = 10
|
const val shareConfig = 10
|
||||||
|
const val importConfig = 11
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -75,13 +76,14 @@ class VPNServiceBinder(service: VPNService) : Binder() {
|
||||||
ACTIONS.resumeActivate -> {
|
ACTIONS.resumeActivate -> {
|
||||||
// [data] is empty
|
// [data] is empty
|
||||||
// Activate the current tunnel
|
// Activate the current tunnel
|
||||||
try {
|
Log.i(tag, "resume activate")
|
||||||
mResumeConfig?.let { this.mService.turnOn(it) }
|
try {
|
||||||
|
mResumeConfig?.let { this.mService.turnOn(it) }
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(tag, "An Error occurred while enabling the VPN: ${e.localizedMessage}")
|
Log.e(tag, "An Error occurred while enabling the VPN: ${e.localizedMessage}")
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
ACTIONS.deactivate -> {
|
ACTIONS.deactivate -> {
|
||||||
// [data] here is empty
|
// [data] here is empty
|
||||||
|
@ -90,6 +92,7 @@ class VPNServiceBinder(service: VPNService) : Binder() {
|
||||||
}
|
}
|
||||||
|
|
||||||
ACTIONS.registerEventListener -> {
|
ACTIONS.registerEventListener -> {
|
||||||
|
Log.i(tag, "register: start")
|
||||||
// [data] contains the Binder that we need to dispatch the Events
|
// [data] contains the Binder that we need to dispatch the Events
|
||||||
val binder = data.readStrongBinder()
|
val binder = data.readStrongBinder()
|
||||||
mListener = binder
|
mListener = binder
|
||||||
|
@ -150,6 +153,23 @@ class VPNServiceBinder(service: VPNService) : Binder() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ACTIONS.importConfig -> {
|
||||||
|
val buffer = data.readString()
|
||||||
|
|
||||||
|
val obj = JSONObject()
|
||||||
|
obj.put("config", buffer)
|
||||||
|
|
||||||
|
val resultString = obj.toString()
|
||||||
|
|
||||||
|
Log.i(tag, "Transact import config request")
|
||||||
|
|
||||||
|
if (mListener != null) {
|
||||||
|
dispatchEvent(EVENTS.configImport, resultString)
|
||||||
|
} else {
|
||||||
|
mImportedConfig = resultString
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
IBinder.LAST_CALL_TRANSACTION -> {
|
IBinder.LAST_CALL_TRANSACTION -> {
|
||||||
Log.e(tag, "The OS Requested to shut down the VPN")
|
Log.e(tag, "The OS Requested to shut down the VPN")
|
||||||
this.mService.turnOff()
|
this.mService.turnOff()
|
||||||
|
@ -176,9 +196,12 @@ class VPNServiceBinder(service: VPNService) : Binder() {
|
||||||
try {
|
try {
|
||||||
mListener?.let {
|
mListener?.let {
|
||||||
if (it.isBinderAlive) {
|
if (it.isBinderAlive) {
|
||||||
|
Log.i(tag, "Dispatching event: binder alive")
|
||||||
val data = Parcel.obtain()
|
val data = Parcel.obtain()
|
||||||
data.writeByteArray(payload?.toByteArray(charset("UTF-8")))
|
data.writeByteArray(payload?.toByteArray(charset("UTF-8")))
|
||||||
it.transact(code, data, Parcel.obtain(), 0)
|
it.transact(code, data, Parcel.obtain(), 0)
|
||||||
|
} else {
|
||||||
|
Log.i(tag, "Dispatching event: binder NOT alive")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: DeadObjectException) {
|
} catch (e: DeadObjectException) {
|
||||||
|
@ -197,23 +220,7 @@ class VPNServiceBinder(service: VPNService) : Binder() {
|
||||||
const val statisticUpdate = 3
|
const val statisticUpdate = 3
|
||||||
const val backendLogs = 4
|
const val backendLogs = 4
|
||||||
const val activationError = 5
|
const val activationError = 5
|
||||||
const val configImport = 6
|
const val permissionRequired = 6
|
||||||
}
|
const val configImport = 7
|
||||||
|
|
||||||
fun importConfig(config: String) {
|
|
||||||
val obj = JSONObject()
|
|
||||||
obj.put("config", config)
|
|
||||||
|
|
||||||
val resultString = obj.toString()
|
|
||||||
|
|
||||||
Log.i(tag, "Transact import config request")
|
|
||||||
|
|
||||||
if (mListener != null) {
|
|
||||||
Log.i(tag, "binder alive")
|
|
||||||
dispatchEvent(EVENTS.configImport, resultString)
|
|
||||||
} else {
|
|
||||||
Log.i(tag, "binder NOT alive")
|
|
||||||
mImportedConfig = resultString
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,8 @@ package org.amnezia.vpn.qt
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import org.amnezia.vpn.shadowsocks.core.Core
|
import org.amnezia.vpn.shadowsocks.core.Core
|
||||||
import org.amnezia.vpn.shadowsocks.core.VpnManager
|
import org.amnezia.vpn.shadowsocks.core.VpnManager
|
||||||
import org.qtproject.qt5.android.bindings.QtActivity
|
import org.qtproject.qt.android.bindings.QtActivity
|
||||||
import org.qtproject.qt5.android.bindings.QtApplication
|
import org.qtproject.qt.android.bindings.QtApplication
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
|
||||||
class AmneziaApp: Application() {
|
class AmneziaApp: Application() {
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
package org.amnezia.vpn.qt;
|
package org.amnezia.vpn.qt;
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
|
@ -20,18 +24,33 @@ import org.amnezia.vpn.VPNServiceBinder
|
||||||
import org.amnezia.vpn.IMPORT_COMMAND_CODE
|
import org.amnezia.vpn.IMPORT_COMMAND_CODE
|
||||||
import org.amnezia.vpn.IMPORT_ACTION_CODE
|
import org.amnezia.vpn.IMPORT_ACTION_CODE
|
||||||
import org.amnezia.vpn.IMPORT_CONFIG_KEY
|
import org.amnezia.vpn.IMPORT_CONFIG_KEY
|
||||||
import org.qtproject.qt5.android.bindings.QtActivity
|
import org.qtproject.qt.android.bindings.QtActivity
|
||||||
import java.io.*
|
import java.io.*
|
||||||
|
|
||||||
class VPNActivity : org.qtproject.qt5.android.bindings.QtActivity() {
|
class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() {
|
||||||
|
|
||||||
private var configString: String? = null
|
private var configString: String? = null
|
||||||
private var vpnServiceBinder: Messenger? = null
|
private var vpnServiceBinder: IBinder? = null
|
||||||
private var isBound = false
|
private var isBound = false
|
||||||
|
|
||||||
private val TAG = "VPNActivity"
|
private val TAG = "VPNActivity"
|
||||||
private val STORAGE_PERMISSION_CODE = 42
|
private val STORAGE_PERMISSION_CODE = 42
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private lateinit var instance: VPNActivity
|
||||||
|
|
||||||
|
@JvmStatic fun getInstance(): VPNActivity {
|
||||||
|
return instance
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic fun connectService() {
|
||||||
|
VPNActivity.getInstance().initServiceConnection()
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic fun sendToService(actionCode: Int, body: String) {
|
||||||
|
VPNActivity.getInstance().dispatchParcel(actionCode, body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
val newIntent = intent
|
val newIntent = intent
|
||||||
|
@ -42,6 +61,48 @@ class VPNActivity : org.qtproject.qt5.android.bindings.QtActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSystemService(name: String): Any? {
|
||||||
|
return if (Build.VERSION.SDK_INT >= 29 && name == "clipboard") {
|
||||||
|
// QT will always attempt to read the clipboard if content is there.
|
||||||
|
// since we have no use of the clipboard in android 10+
|
||||||
|
// we _can_ return null
|
||||||
|
// And we defnitly should since android 12 displays clipboard access.
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
super.getSystemService(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
external fun handleBackButton(): Boolean
|
||||||
|
|
||||||
|
external fun onServiceMessage(actionCode: Int, body: String?)
|
||||||
|
external fun qtOnServiceConnected()
|
||||||
|
external fun qtOnServiceDisconnected()
|
||||||
|
|
||||||
|
private fun dispatchParcel(actionCode: Int, body: String) {
|
||||||
|
if (!isBound) {
|
||||||
|
Log.d(TAG, "dispatchParcel: not bound")
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "dispatchParcel: bound")
|
||||||
|
}
|
||||||
|
|
||||||
|
val out: Parcel = Parcel.obtain()
|
||||||
|
out.writeByteArray(body.toByteArray())
|
||||||
|
|
||||||
|
try {
|
||||||
|
vpnServiceBinder?.transact(actionCode, out, Parcel.obtain(), 0)
|
||||||
|
} catch (e: DeadObjectException) {
|
||||||
|
isBound = false
|
||||||
|
vpnServiceBinder = null
|
||||||
|
qtOnServiceDisconnected()
|
||||||
|
} catch (e: RemoteException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNewIntent(newIntent: Intent) {
|
override fun onNewIntent(newIntent: Intent) {
|
||||||
|
@ -63,19 +124,11 @@ class VPNActivity : org.qtproject.qt5.android.bindings.QtActivity() {
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
|
||||||
if (configString != null && !isBound) {
|
if (configString != null && isBound) {
|
||||||
bindVpnService()
|
sendImportConfigCommand()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
|
||||||
if (vpnServiceBinder != null && isBound) {
|
|
||||||
unbindService(connection)
|
|
||||||
isBound = false
|
|
||||||
}
|
|
||||||
super.onPause()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isReadStorageAllowed(): Boolean {
|
private fun isReadStorageAllowed(): Boolean {
|
||||||
val permissionStatus = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
|
val permissionStatus = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||||
return permissionStatus == PackageManager.PERMISSION_GRANTED
|
return permissionStatus == PackageManager.PERMISSION_GRANTED
|
||||||
|
@ -90,8 +143,15 @@ class VPNActivity : org.qtproject.qt5.android.bindings.QtActivity() {
|
||||||
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
Log.d(TAG, "Storage read permission granted")
|
Log.d(TAG, "Storage read permission granted")
|
||||||
|
|
||||||
|
if (configString == null) {
|
||||||
|
configString = processIntent(intent, intent.action!!)
|
||||||
|
}
|
||||||
|
|
||||||
if (configString != null) {
|
if (configString != null) {
|
||||||
bindVpnService()
|
Log.d(TAG, "not empty")
|
||||||
|
sendImportConfigCommand()
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "empty")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(this, "Oops you just denied the permission", Toast.LENGTH_LONG).show()
|
Toast.makeText(this, "Oops you just denied the permission", Toast.LENGTH_LONG).show()
|
||||||
|
@ -99,17 +159,6 @@ class VPNActivity : org.qtproject.qt5.android.bindings.QtActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindVpnService() {
|
|
||||||
try {
|
|
||||||
val intent = Intent(this, VPNService::class.java)
|
|
||||||
intent.action = IMPORT_ACTION_CODE
|
|
||||||
|
|
||||||
bindService(intent, connection, Context.BIND_AUTO_CREATE)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun processIntent(intent: Intent, action: String): String? {
|
private fun processIntent(intent: Intent, action: String): String? {
|
||||||
val scheme = intent.scheme
|
val scheme = intent.scheme
|
||||||
|
|
||||||
|
@ -158,23 +207,35 @@ class VPNActivity : org.qtproject.qt5.android.bindings.QtActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun sendImportConfigCommand() {
|
||||||
|
if (configString != null) {
|
||||||
|
val msg: Parcel = Parcel.obtain()
|
||||||
|
msg.writeString(configString!!)
|
||||||
|
|
||||||
|
try {
|
||||||
|
vpnServiceBinder?.transact(ACTION_IMPORT_CONFIG, msg, Parcel.obtain(), 0)
|
||||||
|
} catch (e: RemoteException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
configString = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private var connection: ServiceConnection = object : ServiceConnection {
|
private var connection: ServiceConnection = object : ServiceConnection {
|
||||||
override fun onServiceConnected(className: ComponentName, binder: IBinder) {
|
override fun onServiceConnected(className: ComponentName, binder: IBinder) {
|
||||||
vpnServiceBinder = Messenger(binder)
|
vpnServiceBinder = binder
|
||||||
|
|
||||||
if (configString != null) {
|
// This is called when the connection with the service has been
|
||||||
val msg: Message = Message.obtain(null, IMPORT_COMMAND_CODE, 0, 0)
|
// established, giving us the object we can use to
|
||||||
val bundle = Bundle()
|
// interact with the service. We are communicating with the
|
||||||
bundle.putString(IMPORT_CONFIG_KEY, configString!!)
|
// service using a Messenger, so here we get a client-side
|
||||||
msg.data = bundle
|
// representation of that from the raw IBinder object.
|
||||||
|
if (registerBinder()){
|
||||||
try {
|
qtOnServiceConnected();
|
||||||
vpnServiceBinder?.send(msg)
|
} else {
|
||||||
} catch (e: RemoteException) {
|
qtOnServiceDisconnected();
|
||||||
e.printStackTrace()
|
return
|
||||||
}
|
|
||||||
|
|
||||||
configString = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isBound = true
|
isBound = true
|
||||||
|
@ -183,9 +244,75 @@ class VPNActivity : org.qtproject.qt5.android.bindings.QtActivity() {
|
||||||
override fun onServiceDisconnected(className: ComponentName) {
|
override fun onServiceDisconnected(className: ComponentName) {
|
||||||
vpnServiceBinder = null
|
vpnServiceBinder = null
|
||||||
isBound = false
|
isBound = false
|
||||||
|
qtOnServiceDisconnected();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun registerBinder(): Boolean {
|
||||||
|
val binder = VPNClientBinder()
|
||||||
|
val out: Parcel = Parcel.obtain()
|
||||||
|
out.writeStrongBinder(binder)
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Register our IBinder Listener
|
||||||
|
vpnServiceBinder?.transact(ACTION_REGISTER_LISTENER, out, Parcel.obtain(), 0)
|
||||||
|
return true
|
||||||
|
} catch (e: DeadObjectException) {
|
||||||
|
isBound = false
|
||||||
|
vpnServiceBinder = null
|
||||||
|
} catch (e: RemoteException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initServiceConnection() {
|
||||||
|
// We already have a connection to the service,
|
||||||
|
// just need to re-register the binder
|
||||||
|
if (isBound && vpnServiceBinder!!.isBinderAlive() && registerBinder()) {
|
||||||
|
qtOnServiceConnected()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
bindService(Intent(this, VPNService::class.java), connection, Context.BIND_AUTO_CREATE)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Move all ipc codes into a shared lib.
|
||||||
|
// this is getting out of hand.
|
||||||
|
private val PERMISSION_TRANSACTION = 1337
|
||||||
|
private val ACTION_REGISTER_LISTENER = 3
|
||||||
|
private val ACTION_RESUME_ACTIVATE = 7
|
||||||
|
private val ACTION_IMPORT_CONFIG = 11
|
||||||
|
private val EVENT_PERMISSION_REQURED = 6
|
||||||
|
private val EVENT_DISCONNECTED = 2
|
||||||
|
|
||||||
|
|
||||||
|
fun onPermissionRequest(code: Int, data: Parcel?) {
|
||||||
|
if (code != EVENT_PERMISSION_REQURED) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val x = Intent()
|
||||||
|
x.readFromParcel(data)
|
||||||
|
|
||||||
|
startActivityForResult(x, PERMISSION_TRANSACTION)
|
||||||
|
}
|
||||||
|
|
||||||
|
override protected fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
if (requestCode == PERMISSION_TRANSACTION) {
|
||||||
|
// THATS US!
|
||||||
|
if (resultCode == RESULT_OK) {
|
||||||
|
// Prompt accepted, tell service to retry.
|
||||||
|
dispatchParcel(ACTION_RESUME_ACTIVATE, "")
|
||||||
|
} else {
|
||||||
|
// Tell the Client we've disconnected
|
||||||
|
onServiceMessage(EVENT_DISCONNECTED, "")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
|
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
|
||||||
if (keyCode == KeyEvent.KEYCODE_BACK && event.repeatCount == 0) {
|
if (keyCode == KeyEvent.KEYCODE_BACK && event.repeatCount == 0) {
|
||||||
onBackPressed()
|
onBackPressed()
|
||||||
|
|
|
@ -5,7 +5,7 @@ import androidx.annotation.NonNull;
|
||||||
import org.amnezia.vpn.shadowsocks.core.Core;
|
import org.amnezia.vpn.shadowsocks.core.Core;
|
||||||
import org.amnezia.vpn.shadowsocks.core.VpnManager;
|
import org.amnezia.vpn.shadowsocks.core.VpnManager;
|
||||||
|
|
||||||
public class VPNApplication extends org.qtproject.qt5.android.bindings.QtApplication {
|
public class VPNApplication extends org.qtproject.qt.android.bindings.QtApplication {
|
||||||
private static VPNApplication instance;
|
private static VPNApplication instance;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
27
client/android/src/org/amnezia/vpn/qt/VPNClientBinder.kt
Normal file
27
client/android/src/org/amnezia/vpn/qt/VPNClientBinder.kt
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.amnezia.vpn.qt
|
||||||
|
|
||||||
|
import android.os.Binder
|
||||||
|
import android.os.Parcel
|
||||||
|
import android.util.Log
|
||||||
|
|
||||||
|
const val permissionRequired = 6
|
||||||
|
|
||||||
|
class VPNClientBinder() : Binder() {
|
||||||
|
|
||||||
|
override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean {
|
||||||
|
if (code == permissionRequired) {
|
||||||
|
VPNActivity.getInstance().onPermissionRequest(code, data)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
val buffer = data.createByteArray()
|
||||||
|
val stringData = buffer?.let { String(it) }
|
||||||
|
VPNActivity.getInstance().onServiceMessage(code, stringData)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@ package org.ftylitak.qzxing;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import org.qtproject.qt5.android.bindings.QtActivity;
|
import org.qtproject.qt.android.bindings.QtActivity;
|
||||||
import static org.ftylitak.qzxing.Utilities.REQUEST_CAMERA;
|
import static org.ftylitak.qzxing.Utilities.REQUEST_CAMERA;
|
||||||
|
|
||||||
public class QZXingLiveActivity extends QtActivity {
|
public class QZXingLiveActivity extends QtActivity {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
QT += widgets core gui network xml remoteobjects quick svg quickcontrols2 core5compat
|
QT += widgets core gui network xml remoteobjects quick svg quickcontrols2
|
||||||
equals(QT_MAJOR_VERSION, 6): QT += core5compat
|
equals(QT_MAJOR_VERSION, 6): QT += core5compat
|
||||||
|
|
||||||
TARGET = AmneziaVPN
|
TARGET = AmneziaVPN
|
||||||
|
@ -241,14 +241,8 @@ android {
|
||||||
versionAtLeast(QT_VERSION, 6.0.0) {
|
versionAtLeast(QT_VERSION, 6.0.0) {
|
||||||
# We need to include qtprivate api's
|
# We need to include qtprivate api's
|
||||||
# As QAndroidBinder is not yet implemented with a public api
|
# As QAndroidBinder is not yet implemented with a public api
|
||||||
QT+=core-private
|
QT += core-private
|
||||||
ANDROID_ABIS=ANDROID_TARGET_ARCH
|
ANDROID_ABIS = $$ANDROID_TARGET_ARCH
|
||||||
|
|
||||||
# for not changing qtkeychain sources for qt6
|
|
||||||
QT -= androidextras
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
QT += androidextras
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFINES += MVPN_ANDROID
|
DEFINES += MVPN_ANDROID
|
||||||
|
@ -258,13 +252,16 @@ android {
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
platforms/android/android_controller.h \
|
platforms/android/android_controller.h \
|
||||||
platforms/android/android_notificationhandler.h \
|
platforms/android/android_notificationhandler.h \
|
||||||
protocols/android_vpnprotocol.h
|
protocols/android_vpnprotocol.h \
|
||||||
|
platforms/android/androidutils.h \
|
||||||
|
platforms/android/androidvpnactivity.h
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
platforms/android/android_controller.cpp \
|
platforms/android/android_controller.cpp \
|
||||||
platforms/android/android_notificationhandler.cpp \
|
platforms/android/android_notificationhandler.cpp \
|
||||||
protocols/android_vpnprotocol.cpp
|
protocols/android_vpnprotocol.cpp \
|
||||||
|
platforms/android/androidutils.cpp \
|
||||||
|
platforms/android/androidvpnactivity.cpp
|
||||||
|
|
||||||
DISTFILES += \
|
DISTFILES += \
|
||||||
android/AndroidManifest.xml \
|
android/AndroidManifest.xml \
|
||||||
|
@ -293,6 +290,7 @@ android {
|
||||||
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android
|
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android
|
||||||
|
|
||||||
for (abi, ANDROID_ABIS): {
|
for (abi, ANDROID_ABIS): {
|
||||||
|
|
||||||
equals(ANDROID_TARGET_ARCH,$$abi) {
|
equals(ANDROID_TARGET_ARCH,$$abi) {
|
||||||
LIBS += $$PWD/3rd/OpenSSL/lib/android/$${abi}/libcrypto.a
|
LIBS += $$PWD/3rd/OpenSSL/lib/android/$${abi}/libcrypto.a
|
||||||
LIBS += $$PWD/3rd/OpenSSL/lib/android/$${abi}/libssl.a
|
LIBS += $$PWD/3rd/OpenSSL/lib/android/$${abi}/libssl.a
|
||||||
|
|
|
@ -19,8 +19,8 @@ add_subdirectory(${CLIENT_ROOT_DIR}/3rd/qtkeychain)
|
||||||
set(LIBS ${LIBS} qt6keychain)
|
set(LIBS ${LIBS} qt6keychain)
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
${CLIENT_ROOT_DIR}/3rd/QSimpleCrypto/include
|
|
||||||
${CLIENT_ROOT_DIR}/3rd/OpenSSL/include
|
${CLIENT_ROOT_DIR}/3rd/OpenSSL/include
|
||||||
|
${CLIENT_ROOT_DIR}/3rd/QSimpleCrypto/include
|
||||||
${CLIENT_ROOT_DIR}/3rd/qtkeychain
|
${CLIENT_ROOT_DIR}/3rd/qtkeychain
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/3rd/qtkeychain
|
${CMAKE_CURRENT_BINARY_DIR}/3rd/qtkeychain
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QHostAddress>
|
#include <QHostAddress>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
|
@ -7,51 +11,131 @@
|
||||||
#include <QTextCodec>
|
#include <QTextCodec>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
#include <QtCore/private/qandroidextras_p.h>
|
|
||||||
|
|
||||||
#include "android_controller.h"
|
#include "android_controller.h"
|
||||||
#include "core/errorstrings.h"
|
#include "private/qandroidextras_p.h"
|
||||||
#include "ui/pages_logic/StartPageLogic.h"
|
#include "ui/pages_logic/StartPageLogic.h"
|
||||||
|
|
||||||
// Binder Codes for VPNServiceBinder
|
#include "androidvpnactivity.h"
|
||||||
// See also - VPNServiceBinder.kt
|
#include "androidutils.h"
|
||||||
// Actions that are Requestable
|
|
||||||
const int ACTION_ACTIVATE = 1;
|
|
||||||
const int ACTION_DEACTIVATE = 2;
|
|
||||||
const int ACTION_REGISTER_LISTENER = 3;
|
|
||||||
const int ACTION_REQUEST_STATISTIC = 4;
|
|
||||||
const int ACTION_REQUEST_GET_LOG = 5;
|
|
||||||
const int ACTION_REQUEST_CLEANUP_LOG = 6;
|
|
||||||
const int ACTION_RESUME_ACTIVATE = 7;
|
|
||||||
const int ACTION_SET_NOTIFICATION_TEXT = 8;
|
|
||||||
const int ACTION_SET_NOTIFICATION_FALLBACK = 9;
|
|
||||||
const int ACTION_SHARE_CONFIG = 10;
|
|
||||||
|
|
||||||
// Event Types that will be Dispatched after registration
|
|
||||||
const int EVENT_INIT = 0;
|
|
||||||
const int EVENT_CONNECTED = 1;
|
|
||||||
const int EVENT_DISCONNECTED = 2;
|
|
||||||
const int EVENT_STATISTIC_UPDATE = 3;
|
|
||||||
const int EVENT_BACKEND_LOGS = 4;
|
|
||||||
const int EVENT_ACTIVATION_ERROR = 5;
|
|
||||||
const int EVENT_CONFIG_IMPORT = 6;
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
AndroidController* s_instance = nullptr;
|
AndroidController* s_instance = nullptr;
|
||||||
|
|
||||||
constexpr auto PERMISSIONHELPER_CLASS =
|
constexpr auto PERMISSIONHELPER_CLASS =
|
||||||
"org/amnezia/vpn/qt/VPNPermissionHelper";
|
"org/amnezia/vpn/qt/VPNPermissionHelper";
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
AndroidController::AndroidController():
|
AndroidController::AndroidController() : QObject()
|
||||||
m_binder(this)
|
|
||||||
{
|
{
|
||||||
|
connect(this, &AndroidController::scheduleStatusCheckSignal, this, &AndroidController::scheduleStatusCheckSlot);
|
||||||
|
|
||||||
|
s_instance = this;
|
||||||
|
|
||||||
|
auto activity = AndroidVPNActivity::instance();
|
||||||
|
|
||||||
|
connect(activity, &AndroidVPNActivity::serviceConnected, this, []() {
|
||||||
|
qDebug() << "Transact: service connected";
|
||||||
|
AndroidVPNActivity::sendToService(ServiceAction::ACTION_REQUEST_STATISTIC, "");
|
||||||
|
}, Qt::QueuedConnection);
|
||||||
|
|
||||||
|
connect(activity, &AndroidVPNActivity::eventInitialized, this,
|
||||||
|
[this](const QString& parcelBody) {
|
||||||
|
// We might get multiple Init events as widgets, or fragments
|
||||||
|
// might query this.
|
||||||
|
if (m_init) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Transact: init";
|
||||||
|
|
||||||
|
m_init = true;
|
||||||
|
|
||||||
|
auto doc = QJsonDocument::fromJson(parcelBody.toUtf8());
|
||||||
|
qlonglong time = doc.object()["time"].toVariant().toLongLong();
|
||||||
|
|
||||||
|
isConnected = doc.object()["connected"].toBool();
|
||||||
|
|
||||||
|
emit initialized(
|
||||||
|
true, isConnected,
|
||||||
|
time > 0 ? QDateTime::fromMSecsSinceEpoch(time) : QDateTime());
|
||||||
|
|
||||||
|
setFallbackConnectedNotification();
|
||||||
|
}, Qt::QueuedConnection);
|
||||||
|
|
||||||
|
connect(activity, &AndroidVPNActivity::eventConnected, this,
|
||||||
|
[this](const QString& parcelBody) {
|
||||||
|
Q_UNUSED(parcelBody);
|
||||||
|
qDebug() << "Transact: connected";
|
||||||
|
|
||||||
|
isConnected = true;
|
||||||
|
|
||||||
|
emit scheduleStatusCheckSignal();
|
||||||
|
|
||||||
|
emit connectionStateChanged(VpnProtocol::Connected);
|
||||||
|
}, Qt::QueuedConnection);
|
||||||
|
|
||||||
|
connect(activity, &AndroidVPNActivity::eventDisconnected, this,
|
||||||
|
[this]() {
|
||||||
|
qDebug() << "Transact: disconnected";
|
||||||
|
|
||||||
|
isConnected = false;
|
||||||
|
|
||||||
|
emit connectionStateChanged(VpnProtocol::Disconnected);
|
||||||
|
}, Qt::QueuedConnection);
|
||||||
|
|
||||||
|
connect(activity, &AndroidVPNActivity::eventStatisticUpdate, this,
|
||||||
|
[this](const QString& parcelBody) {
|
||||||
|
qDebug() << "Transact: update";
|
||||||
|
|
||||||
|
auto doc = QJsonDocument::fromJson(parcelBody.toUtf8());
|
||||||
|
|
||||||
|
QString rx = doc.object()["rx_bytes"].toString();
|
||||||
|
QString tx = doc.object()["tx_bytes"].toString();
|
||||||
|
QString endpoint = doc.object()["endpoint"].toString();
|
||||||
|
QString deviceIPv4 = doc.object()["deviceIpv4"].toString();
|
||||||
|
|
||||||
|
emit statusUpdated(rx, tx, endpoint, deviceIPv4);
|
||||||
|
}, Qt::QueuedConnection);
|
||||||
|
|
||||||
|
connect(activity, &AndroidVPNActivity::eventBackendLogs, this,
|
||||||
|
[this](const QString& parcelBody) {
|
||||||
|
qDebug() << "Transact: backend logs";
|
||||||
|
|
||||||
|
QString buffer = parcelBody.toUtf8();
|
||||||
|
if (m_logCallback) {
|
||||||
|
m_logCallback(buffer);
|
||||||
|
}
|
||||||
|
}, Qt::QueuedConnection);
|
||||||
|
|
||||||
|
connect(activity, &AndroidVPNActivity::eventActivationError, this,
|
||||||
|
[this](const QString& parcelBody) {
|
||||||
|
Q_UNUSED(parcelBody)
|
||||||
|
qDebug() << "Transact: error";
|
||||||
|
emit connectionStateChanged(VpnProtocol::Error);
|
||||||
|
}, Qt::QueuedConnection);
|
||||||
|
|
||||||
|
connect(activity, &AndroidVPNActivity::eventConfigImport, this,
|
||||||
|
[this](const QString& parcelBody) {
|
||||||
|
qDebug() << "Transact: config import";
|
||||||
|
auto doc = QJsonDocument::fromJson(parcelBody.toUtf8());
|
||||||
|
|
||||||
|
QString buffer = doc.object()["config"].toString();
|
||||||
|
qDebug() << "Transact: config string" << buffer;
|
||||||
|
importConfig(buffer);
|
||||||
|
}, Qt::QueuedConnection);
|
||||||
|
|
||||||
|
connect(activity, &AndroidVPNActivity::serviceDisconnected, this,
|
||||||
|
[this]() {
|
||||||
|
qDebug() << "Transact: service disconnected";
|
||||||
|
m_serviceConnected = false;
|
||||||
|
}, Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
AndroidController* AndroidController::instance() {
|
AndroidController* AndroidController::instance() {
|
||||||
if (!s_instance) s_instance = new AndroidController();
|
if (!s_instance) {
|
||||||
|
s_instance = new AndroidController();
|
||||||
|
}
|
||||||
|
|
||||||
return s_instance;
|
return s_instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,71 +149,43 @@ bool AndroidController::initialize(StartPageLogic *startPageLogic)
|
||||||
JNINativeMethod methods[]{{"startActivityForResult",
|
JNINativeMethod methods[]{{"startActivityForResult",
|
||||||
"(Landroid/content/Intent;)V",
|
"(Landroid/content/Intent;)V",
|
||||||
reinterpret_cast<void*>(startActivityForResult)}};
|
reinterpret_cast<void*>(startActivityForResult)}};
|
||||||
QAndroidJniObject javaClass(PERMISSIONHELPER_CLASS);
|
QJniObject javaClass(PERMISSIONHELPER_CLASS);
|
||||||
QAndroidJniEnvironment env;
|
QJniEnvironment env;
|
||||||
jclass objectClass = env->GetObjectClass(javaClass.object<jobject>());
|
jclass objectClass = env->GetObjectClass(javaClass.object<jobject>());
|
||||||
env->RegisterNatives(objectClass, methods,
|
env->RegisterNatives(objectClass, methods, sizeof(methods) / sizeof(methods[0]));
|
||||||
sizeof(methods) / sizeof(methods[0]));
|
|
||||||
env->DeleteLocalRef(objectClass);
|
env->DeleteLocalRef(objectClass);
|
||||||
|
|
||||||
auto appContext = QtAndroid::androidActivity().callObjectMethod(
|
AndroidVPNActivity::connectService();
|
||||||
"getApplicationContext", "()Landroid/content/Context;");
|
|
||||||
|
|
||||||
QAndroidJniObject::callStaticMethod<void>(
|
return true;
|
||||||
"org/amnezia/vpn/VPNService", "startService",
|
|
||||||
"(Landroid/content/Context;)V", appContext.object());
|
|
||||||
|
|
||||||
// Start the VPN Service (if not yet) and Bind to it
|
|
||||||
const bool bindResult = QtAndroid::bindService(
|
|
||||||
QAndroidIntent(appContext.object(), "org.amnezia.vpn.VPNService"),
|
|
||||||
*this, QtAndroid::BindFlag::AutoCreate);
|
|
||||||
qDebug() << "Binding to the service..." << bindResult;
|
|
||||||
|
|
||||||
return bindResult;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode AndroidController::start()
|
ErrorCode AndroidController::start()
|
||||||
{
|
{
|
||||||
|
|
||||||
//qDebug().noquote() << "AndroidController::start" << QJsonDocument(m_rawConfig).toJson();
|
|
||||||
qDebug() << "Prompting for VPN permission";
|
qDebug() << "Prompting for VPN permission";
|
||||||
auto appContext = QtAndroid::androidActivity().callObjectMethod(
|
QJniObject activity = AndroidUtils::getActivity();
|
||||||
|
auto appContext = activity.callObjectMethod(
|
||||||
"getApplicationContext", "()Landroid/content/Context;");
|
"getApplicationContext", "()Landroid/content/Context;");
|
||||||
QAndroidJniObject::callStaticMethod<void>(
|
QJniObject::callStaticMethod<void>(
|
||||||
PERMISSIONHELPER_CLASS, "startService", "(Landroid/content/Context;)V",
|
PERMISSIONHELPER_CLASS, "startService", "(Landroid/content/Context;)V",
|
||||||
appContext.object());
|
appContext.object());
|
||||||
|
|
||||||
QAndroidParcel sendData;
|
QJsonDocument doc(m_vpnConfig);
|
||||||
sendData.writeData(QJsonDocument(m_vpnConfig).toJson());
|
AndroidVPNActivity::sendToService(ServiceAction::ACTION_ACTIVATE, doc.toJson());
|
||||||
bool activateResult = false;
|
|
||||||
while (!activateResult){
|
|
||||||
activateResult = m_serviceBinder.transact(ACTION_ACTIVATE, sendData, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
return activateResult ? NoError : UnknownError;
|
return NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidController::stop() {
|
void AndroidController::stop() {
|
||||||
qDebug() << "AndroidController::stop";
|
qDebug() << "AndroidController::stop";
|
||||||
|
|
||||||
// if (reason != ReasonNone) {
|
AndroidVPNActivity::sendToService(ServiceAction::ACTION_DEACTIVATE, QString());
|
||||||
// // Just show that we're disconnected
|
|
||||||
// // we're doing the actual disconnect once
|
|
||||||
// // the vpn-service has the new server ready in Action->Activate
|
|
||||||
// emit disconnected();
|
|
||||||
// qCritical() << "deactivation skipped for Switching";
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
QAndroidParcel nullData;
|
|
||||||
m_serviceBinder.transact(ACTION_DEACTIVATE, nullData, nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Activates the tunnel that is currently set
|
// Activates the tunnel that is currently set
|
||||||
// in the VPN Service
|
// in the VPN Service
|
||||||
void AndroidController::resumeStart() {
|
void AndroidController::resumeStart() {
|
||||||
QAndroidParcel nullData;
|
AndroidVPNActivity::sendToService(ServiceAction::ACTION_RESUME_ACTIVATE, QString());
|
||||||
m_serviceBinder.transact(ACTION_RESUME_ACTIVATE, nullData, nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -138,14 +194,13 @@ void AndroidController::resumeStart() {
|
||||||
void AndroidController::setNotificationText(const QString& title,
|
void AndroidController::setNotificationText(const QString& title,
|
||||||
const QString& message,
|
const QString& message,
|
||||||
int timerSec) {
|
int timerSec) {
|
||||||
QJsonObject args;
|
QJsonObject args;
|
||||||
args["title"] = title;
|
args["title"] = title;
|
||||||
args["message"] = message;
|
args["message"] = message;
|
||||||
args["sec"] = timerSec;
|
args["sec"] = timerSec;
|
||||||
QJsonDocument doc(args);
|
QJsonDocument doc(args);
|
||||||
QAndroidParcel data;
|
|
||||||
data.writeData(doc.toJson());
|
AndroidVPNActivity::sendToService(ServiceAction::ACTION_SET_NOTIFICATION_TEXT, doc.toJson());
|
||||||
m_serviceBinder.transact(ACTION_SET_NOTIFICATION_TEXT, data, nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidController::shareConfig(const QString& configContent, const QString& suggestedName) {
|
void AndroidController::shareConfig(const QString& configContent, const QString& suggestedName) {
|
||||||
|
@ -153,9 +208,8 @@ void AndroidController::shareConfig(const QString& configContent, const QString&
|
||||||
rootObject["data"] = configContent;
|
rootObject["data"] = configContent;
|
||||||
rootObject["suggestedName"] = suggestedName;
|
rootObject["suggestedName"] = suggestedName;
|
||||||
QJsonDocument doc(rootObject);
|
QJsonDocument doc(rootObject);
|
||||||
QAndroidParcel parcel;
|
|
||||||
parcel.writeData(doc.toJson());
|
AndroidVPNActivity::sendToService(ServiceAction::ACTION_SHARE_CONFIG, doc.toJson());
|
||||||
m_serviceBinder.transact(ACTION_SHARE_CONFIG, parcel, nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -164,64 +218,40 @@ void AndroidController::shareConfig(const QString& configContent, const QString&
|
||||||
* e.g via always-on vpn
|
* e.g via always-on vpn
|
||||||
*/
|
*/
|
||||||
void AndroidController::setFallbackConnectedNotification() {
|
void AndroidController::setFallbackConnectedNotification() {
|
||||||
QJsonObject args;
|
QJsonObject args;
|
||||||
args["title"] = tr("AmneziaVPN");
|
args["title"] = tr("AmneziaVPN");
|
||||||
//% "Ready for you to connect"
|
//% "Ready for you to connect"
|
||||||
//: Refers to the app - which is currently running the background and waiting
|
//: Refers to the app - which is currently running the background and waiting
|
||||||
args["message"] = tr("VPN Connected");
|
args["message"] = tr("VPN Connected");
|
||||||
QJsonDocument doc(args);
|
QJsonDocument doc(args);
|
||||||
QAndroidParcel data;
|
|
||||||
data.writeData(doc.toJson());
|
AndroidVPNActivity::sendToService(ServiceAction::ACTION_SET_NOTIFICATION_FALLBACK, doc.toJson());
|
||||||
m_serviceBinder.transact(ACTION_SET_NOTIFICATION_FALLBACK, data, nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidController::checkStatus() {
|
void AndroidController::checkStatus() {
|
||||||
qDebug() << "check status";
|
qDebug() << "check status";
|
||||||
|
|
||||||
QAndroidParcel nullParcel;
|
AndroidVPNActivity::sendToService(ServiceAction::ACTION_REQUEST_STATISTIC, QString());
|
||||||
m_serviceBinder.transact(ACTION_REQUEST_STATISTIC, nullParcel, nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidController::getBackendLogs(std::function<void(const QString&)>&& a_callback) {
|
void AndroidController::getBackendLogs(std::function<void(const QString&)>&& a_callback) {
|
||||||
qDebug() << "get logs";
|
qDebug() << "get logs";
|
||||||
|
|
||||||
m_logCallback = std::move(a_callback);
|
m_logCallback = std::move(a_callback);
|
||||||
QAndroidParcel nullData, replyData;
|
|
||||||
m_serviceBinder.transact(ACTION_REQUEST_GET_LOG, nullData, &replyData);
|
AndroidVPNActivity::sendToService(ServiceAction::ACTION_REQUEST_GET_LOG, QString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidController::cleanupBackendLogs() {
|
void AndroidController::cleanupBackendLogs() {
|
||||||
qDebug() << "cleanup logs";
|
qDebug() << "cleanup logs";
|
||||||
|
|
||||||
QAndroidParcel nullParcel;
|
AndroidVPNActivity::sendToService(ServiceAction::ACTION_REQUEST_CLEANUP_LOG, QString());
|
||||||
m_serviceBinder.transact(ACTION_REQUEST_CLEANUP_LOG, nullParcel, nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidController::importConfig(const QString& data){
|
void AndroidController::importConfig(const QString& data){
|
||||||
m_startPageLogic->importConnectionFromCode(data);
|
m_startPageLogic->importConnectionFromCode(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidController::onServiceConnected(
|
|
||||||
const QString& name, const QAndroidBinder& serviceBinder) {
|
|
||||||
qDebug() << "Server " + name + " connected";
|
|
||||||
|
|
||||||
Q_UNUSED(name);
|
|
||||||
|
|
||||||
m_serviceBinder = serviceBinder;
|
|
||||||
|
|
||||||
// Send the Service our Binder to recive incoming Events
|
|
||||||
QAndroidParcel binderParcel;
|
|
||||||
binderParcel.writeBinder(m_binder);
|
|
||||||
m_serviceBinder.transact(ACTION_REGISTER_LISTENER, binderParcel, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AndroidController::onServiceDisconnected(const QString& name) {
|
|
||||||
qDebug() << "Server disconnected";
|
|
||||||
m_serviceConnected = false;
|
|
||||||
Q_UNUSED(name);
|
|
||||||
// TODO: Maybe restart? Or crash?
|
|
||||||
}
|
|
||||||
|
|
||||||
const QJsonObject &AndroidController::vpnConfig() const
|
const QJsonObject &AndroidController::vpnConfig() const
|
||||||
{
|
{
|
||||||
return m_vpnConfig;
|
return m_vpnConfig;
|
||||||
|
@ -232,86 +262,14 @@ void AndroidController::setVpnConfig(const QJsonObject &newVpnConfig)
|
||||||
m_vpnConfig = newVpnConfig;
|
m_vpnConfig = newVpnConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AndroidController::scheduleStatusCheckSlot()
|
||||||
/**
|
{
|
||||||
* @brief AndroidController::VPNBinder::onTransact
|
QTimer::singleShot(1000, [this]() {
|
||||||
* @param code the Event-Type we get From the VPNService See
|
if (isConnected) {
|
||||||
* @param data - Might contain UTF-8 JSON in case the Event has a payload
|
checkStatus();
|
||||||
* @param reply - always null
|
emit scheduleStatusCheckSignal();
|
||||||
* @param flags - unused
|
}
|
||||||
* @return Returns true is the code was a valid Event Code
|
});
|
||||||
*/
|
|
||||||
bool AndroidController::VPNBinder::onTransact(int code,
|
|
||||||
const QAndroidParcel& data,
|
|
||||||
const QAndroidParcel& reply,
|
|
||||||
QAndroidBinder::CallType flags) {
|
|
||||||
Q_UNUSED(data);
|
|
||||||
Q_UNUSED(reply);
|
|
||||||
Q_UNUSED(flags);
|
|
||||||
|
|
||||||
QJsonDocument doc;
|
|
||||||
QString buffer;
|
|
||||||
switch (code) {
|
|
||||||
case EVENT_INIT:
|
|
||||||
qDebug() << "Transact: init";
|
|
||||||
doc = QJsonDocument::fromJson(data.readData());
|
|
||||||
emit m_controller->initialized(
|
|
||||||
true, doc.object()["connected"].toBool(),
|
|
||||||
QDateTime::fromMSecsSinceEpoch(
|
|
||||||
doc.object()["time"].toVariant().toLongLong()));
|
|
||||||
// Pass a localised version of the Fallback string for the Notification
|
|
||||||
m_controller->setFallbackConnectedNotification();
|
|
||||||
|
|
||||||
break;
|
|
||||||
case EVENT_CONNECTED:
|
|
||||||
qDebug() << "Transact: connected";
|
|
||||||
emit m_controller->connectionStateChanged(VpnProtocol::Connected);
|
|
||||||
break;
|
|
||||||
case EVENT_DISCONNECTED:
|
|
||||||
qDebug() << "Transact: disconnected";
|
|
||||||
emit m_controller->connectionStateChanged(VpnProtocol::Disconnected);
|
|
||||||
break;
|
|
||||||
case EVENT_STATISTIC_UPDATE:
|
|
||||||
qDebug() << "Transact:: update";
|
|
||||||
|
|
||||||
// Data is here a JSON String
|
|
||||||
doc = QJsonDocument::fromJson(data.readData());
|
|
||||||
// TODO update counters
|
|
||||||
// emit m_controller->statusUpdated(doc.object()["endpoint"].toString(),
|
|
||||||
// doc.object()["deviceIpv4"].toString(),
|
|
||||||
// doc.object()["totalTX"].toInt(),
|
|
||||||
// doc.object()["totalRX"].toInt());
|
|
||||||
break;
|
|
||||||
case EVENT_BACKEND_LOGS:
|
|
||||||
qDebug() << "Transact: backend logs";
|
|
||||||
|
|
||||||
buffer = readUTF8Parcel(data);
|
|
||||||
if (m_controller->m_logCallback) {
|
|
||||||
m_controller->m_logCallback(buffer);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case EVENT_ACTIVATION_ERROR:
|
|
||||||
qDebug() << "Transact: error";
|
|
||||||
emit m_controller->connectionStateChanged(VpnProtocol::Error);
|
|
||||||
break;
|
|
||||||
case EVENT_CONFIG_IMPORT:
|
|
||||||
qDebug() << "Transact: config import";
|
|
||||||
doc = QJsonDocument::fromJson(data.readData());
|
|
||||||
buffer = doc.object()["config"].toString();
|
|
||||||
qDebug() << "Transact: config string" << buffer;
|
|
||||||
m_controller->importConfig(buffer);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
qWarning() << "Transact: Invalid!";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString AndroidController::VPNBinder::readUTF8Parcel(QAndroidParcel data) {
|
|
||||||
// 106 is the Code for UTF-8
|
|
||||||
return QTextCodec::codecForMib(106)->toUnicode(data.readData());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const int ACTIVITY_RESULT_OK = 0xffffffff;
|
const int ACTIVITY_RESULT_OK = 0xffffffff;
|
||||||
|
@ -322,34 +280,34 @@ const int ACTIVITY_RESULT_OK = 0xffffffff;
|
||||||
*/
|
*/
|
||||||
void AndroidController::startActivityForResult(JNIEnv *env, jobject, jobject intent)
|
void AndroidController::startActivityForResult(JNIEnv *env, jobject, jobject intent)
|
||||||
{
|
{
|
||||||
qDebug() << "start activity";
|
qDebug() << "start vpnPermissionHelper";
|
||||||
Q_UNUSED(env);
|
Q_UNUSED(env);
|
||||||
QtAndroid::startActivity(intent, 1337,
|
|
||||||
[](int receiverRequestCode, int resultCode,
|
|
||||||
const QAndroidJniObject& data) {
|
|
||||||
// Currently this function just used in
|
|
||||||
// VPNService.kt::checkPersmissions. So the result
|
|
||||||
// we're getting is if the User gave us the
|
|
||||||
// Vpn.bind permission. In case of NO we should
|
|
||||||
// abort.
|
|
||||||
Q_UNUSED(receiverRequestCode);
|
|
||||||
Q_UNUSED(data);
|
|
||||||
|
|
||||||
AndroidController* controller =
|
QtAndroidPrivate::startActivity(intent, 1337,
|
||||||
AndroidController::instance();
|
[](int receiverRequestCode, int resultCode,
|
||||||
if (!controller) {
|
const QJniObject& data) {
|
||||||
return;
|
// Currently this function just used in
|
||||||
}
|
// VPNService.kt::checkPersmissions. So the result
|
||||||
|
// we're getting is if the User gave us the
|
||||||
|
// Vpn.bind permission. In case of NO we should
|
||||||
|
// abort.
|
||||||
|
Q_UNUSED(receiverRequestCode);
|
||||||
|
Q_UNUSED(data);
|
||||||
|
|
||||||
if (resultCode == ACTIVITY_RESULT_OK) {
|
AndroidController* controller = AndroidController::instance();
|
||||||
qDebug() << "VPN PROMPT RESULT - Accepted";
|
if (!controller) {
|
||||||
controller->resumeStart();
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
// If the request got rejected abort the current
|
if (resultCode == ACTIVITY_RESULT_OK) {
|
||||||
// connection.
|
qDebug() << "VPN PROMPT RESULT - Accepted";
|
||||||
qWarning() << "VPN PROMPT RESULT - Rejected";
|
controller->resumeStart();
|
||||||
emit controller->connectionStateChanged(VpnProtocol::Disconnected);
|
return;
|
||||||
});
|
}
|
||||||
|
// If the request got rejected abort the current
|
||||||
|
// connection.
|
||||||
|
qWarning() << "VPN PROMPT RESULT - Rejected";
|
||||||
|
emit controller->connectionStateChanged(VpnProtocol::Disconnected);
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,21 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#ifndef ANDROID_CONTROLLER_H
|
#ifndef ANDROID_CONTROLLER_H
|
||||||
#define ANDROID_CONTROLLER_H
|
#define ANDROID_CONTROLLER_H
|
||||||
|
|
||||||
#include <QtCore/private/qandroidextras_p.h>
|
#include <QJniEnvironment>
|
||||||
|
#include <QJniObject>
|
||||||
|
|
||||||
#include "ui/uilogic.h"
|
|
||||||
#include "ui/pages_logic/StartPageLogic.h"
|
#include "ui/pages_logic/StartPageLogic.h"
|
||||||
|
|
||||||
#include "protocols/vpnprotocol.h"
|
#include "protocols/vpnprotocol.h"
|
||||||
|
|
||||||
using namespace amnezia;
|
using namespace amnezia;
|
||||||
|
|
||||||
|
|
||||||
class AndroidController : public QObject, public QAndroidServiceConnection
|
class AndroidController : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
@ -34,10 +39,6 @@ public:
|
||||||
void cleanupBackendLogs();
|
void cleanupBackendLogs();
|
||||||
void importConfig(const QString& data);
|
void importConfig(const QString& data);
|
||||||
|
|
||||||
// from QAndroidServiceConnection
|
|
||||||
void onServiceConnected(const QString& name, const QAndroidBinder& serviceBinder) override;
|
|
||||||
void onServiceDisconnected(const QString& name) override;
|
|
||||||
|
|
||||||
const QJsonObject &vpnConfig() const;
|
const QJsonObject &vpnConfig() const;
|
||||||
void setVpnConfig(const QJsonObject &newVpnConfig);
|
void setVpnConfig(const QJsonObject &newVpnConfig);
|
||||||
|
|
||||||
|
@ -52,13 +53,18 @@ signals:
|
||||||
void initialized(bool status, bool connected,
|
void initialized(bool status, bool connected,
|
||||||
const QDateTime& connectionDate);
|
const QDateTime& connectionDate);
|
||||||
|
|
||||||
|
void statusUpdated(QString totalRx, QString totalTx, QString endpoint, QString deviceIPv4);
|
||||||
|
void scheduleStatusCheckSignal();
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
|
void scheduleStatusCheckSlot();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//Protocol m_protocol;
|
bool m_init = false;
|
||||||
|
|
||||||
QJsonObject m_vpnConfig;
|
QJsonObject m_vpnConfig;
|
||||||
|
|
||||||
StartPageLogic *m_startPageLogic;
|
StartPageLogic *m_startPageLogic;
|
||||||
|
@ -66,24 +72,11 @@ private:
|
||||||
bool m_serviceConnected = false;
|
bool m_serviceConnected = false;
|
||||||
std::function<void(const QString&)> m_logCallback;
|
std::function<void(const QString&)> m_logCallback;
|
||||||
|
|
||||||
QAndroidBinder m_serviceBinder;
|
|
||||||
class VPNBinder : public QAndroidBinder {
|
|
||||||
public:
|
|
||||||
VPNBinder(AndroidController* controller) : m_controller(controller) {}
|
|
||||||
|
|
||||||
bool onTransact(int code, const QAndroidParcel& data,
|
|
||||||
const QAndroidParcel& reply,
|
|
||||||
QAndroidBinder::CallType flags) override;
|
|
||||||
|
|
||||||
QString readUTF8Parcel(QAndroidParcel data);
|
|
||||||
|
|
||||||
private:
|
|
||||||
AndroidController* m_controller = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
VPNBinder m_binder;
|
|
||||||
|
|
||||||
static void startActivityForResult(JNIEnv* env, jobject /*thiz*/, jobject intent);
|
static void startActivityForResult(JNIEnv* env, jobject /*thiz*/, jobject intent);
|
||||||
|
|
||||||
|
bool isConnected = false;
|
||||||
|
|
||||||
|
void scheduleStatusCheck();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ANDROID_CONTROLLER_H
|
#endif // ANDROID_CONTROLLER_H
|
||||||
|
|
174
client/platforms/android/androidutils.cpp
Normal file
174
client/platforms/android/androidutils.cpp
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include "androidutils.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QJniEnvironment>
|
||||||
|
#include <QJniObject>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QNetworkCookieJar>
|
||||||
|
#include <QUrlQuery>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include "jni.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
AndroidUtils* s_instance = nullptr;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// static
|
||||||
|
QString AndroidUtils::GetDeviceName() {
|
||||||
|
QJniEnvironment env;
|
||||||
|
jclass BUILD = env->FindClass("android/os/Build");
|
||||||
|
jfieldID model = env->GetStaticFieldID(BUILD, "MODEL", "Ljava/lang/String;");
|
||||||
|
jstring value = (jstring)env->GetStaticObjectField(BUILD, model);
|
||||||
|
|
||||||
|
if (!value) {
|
||||||
|
return QString("Android Device");
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* buffer = env->GetStringUTFChars(value, nullptr);
|
||||||
|
if (!buffer) {
|
||||||
|
return QString("Android Device");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString res(buffer);
|
||||||
|
env->ReleaseStringUTFChars(value, buffer);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
// static
|
||||||
|
AndroidUtils* AndroidUtils::instance() {
|
||||||
|
if (!s_instance) {
|
||||||
|
Q_ASSERT(qApp);
|
||||||
|
s_instance = new AndroidUtils(qApp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return s_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
AndroidUtils::AndroidUtils(QObject* parent) : QObject(parent) {
|
||||||
|
Q_ASSERT(!s_instance);
|
||||||
|
s_instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
AndroidUtils::~AndroidUtils() {
|
||||||
|
Q_ASSERT(s_instance == this);
|
||||||
|
s_instance = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
void AndroidUtils::dispatchToMainThread(std::function<void()> callback) {
|
||||||
|
QTimer* timer = new QTimer();
|
||||||
|
timer->moveToThread(qApp->thread());
|
||||||
|
timer->setSingleShot(true);
|
||||||
|
QObject::connect(timer, &QTimer::timeout, [=]() {
|
||||||
|
callback();
|
||||||
|
timer->deleteLater();
|
||||||
|
});
|
||||||
|
QMetaObject::invokeMethod(timer, "start", Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
QByteArray AndroidUtils::getQByteArrayFromJString(JNIEnv* env, jstring data) {
|
||||||
|
const char* buffer = env->GetStringUTFChars(data, nullptr);
|
||||||
|
if (!buffer) {
|
||||||
|
qDebug() << "getQByteArrayFromJString - failed to parse data.";
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray out(buffer);
|
||||||
|
env->ReleaseStringUTFChars(data, buffer);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
QString AndroidUtils::getQStringFromJString(JNIEnv* env, jstring data) {
|
||||||
|
const char* buffer = env->GetStringUTFChars(data, nullptr);
|
||||||
|
if (!buffer) {
|
||||||
|
qDebug() << "getQStringFromJString - failed to parse data.";
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString out(buffer);
|
||||||
|
env->ReleaseStringUTFChars(data, buffer);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
QJsonObject AndroidUtils::getQJsonObjectFromJString(JNIEnv* env, jstring data) {
|
||||||
|
QByteArray raw(getQByteArrayFromJString(env, data));
|
||||||
|
QJsonParseError jsonError;
|
||||||
|
QJsonDocument json = QJsonDocument::fromJson(raw, &jsonError);
|
||||||
|
if (QJsonParseError::NoError != jsonError.error) {
|
||||||
|
qDebug() << "getQJsonObjectFromJstring - error parsing json. Code: "
|
||||||
|
<< jsonError.error << "Offset: " << jsonError.offset
|
||||||
|
<< "Message: " << jsonError.errorString()
|
||||||
|
<< "Data: " << raw;
|
||||||
|
return QJsonObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!json.isObject()) {
|
||||||
|
qDebug() << "getQJsonObjectFromJString - object expected.";
|
||||||
|
return QJsonObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.object();
|
||||||
|
}
|
||||||
|
|
||||||
|
QJniObject AndroidUtils::getActivity() {
|
||||||
|
return QNativeInterface::QAndroidApplication::context();
|
||||||
|
}
|
||||||
|
|
||||||
|
int AndroidUtils::GetSDKVersion() {
|
||||||
|
QJniEnvironment env;
|
||||||
|
jclass versionClass = env->FindClass("android/os/Build$VERSION");
|
||||||
|
jfieldID sdkIntFieldID = env->GetStaticFieldID(versionClass, "SDK_INT", "I");
|
||||||
|
int sdk = env->GetStaticIntField(versionClass, sdkIntFieldID);
|
||||||
|
|
||||||
|
return sdk;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString AndroidUtils::GetManufacturer() {
|
||||||
|
QJniEnvironment env;
|
||||||
|
jclass buildClass = env->FindClass("android/os/Build");
|
||||||
|
jfieldID manuFacturerField =
|
||||||
|
env->GetStaticFieldID(buildClass, "MANUFACTURER", "Ljava/lang/String;");
|
||||||
|
jstring value =
|
||||||
|
(jstring)env->GetStaticObjectField(buildClass, manuFacturerField);
|
||||||
|
|
||||||
|
const char* buffer = env->GetStringUTFChars(value, nullptr);
|
||||||
|
|
||||||
|
if (!buffer) {
|
||||||
|
qDebug() << "Failed to fetch MANUFACTURER";
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString res(buffer);
|
||||||
|
qDebug() << "MANUFACTURER: " << res;
|
||||||
|
env->ReleaseStringUTFChars(value, buffer);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidUtils::runOnAndroidThreadSync(const std::function<void()> runnable) {
|
||||||
|
QNativeInterface::QAndroidApplication::runOnAndroidMainThread(runnable)
|
||||||
|
.waitForFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidUtils::runOnAndroidThreadAsync(const std::function<void()> runnable) {
|
||||||
|
QNativeInterface::QAndroidApplication::runOnAndroidMainThread(runnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Static
|
||||||
|
// Creates a copy of the passed QByteArray in the JVM and passes back a ref
|
||||||
|
jbyteArray AndroidUtils::tojByteArray(const QByteArray& data) {
|
||||||
|
QJniEnvironment env;
|
||||||
|
jbyteArray out = env->NewByteArray(data.size());
|
||||||
|
env->SetByteArrayRegion(out, 0, data.size(),
|
||||||
|
reinterpret_cast<const jbyte*>(data.constData()));
|
||||||
|
return out;
|
||||||
|
}
|
49
client/platforms/android/androidutils.h
Normal file
49
client/platforms/android/androidutils.h
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef ANDROIDUTILS_H
|
||||||
|
#define ANDROIDUTILS_H
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
|
||||||
|
#include <QJniEnvironment>
|
||||||
|
#include <QJniObject>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
class AndroidUtils final : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY_MOVE(AndroidUtils)
|
||||||
|
|
||||||
|
public:
|
||||||
|
static QString GetDeviceName();
|
||||||
|
|
||||||
|
static int GetSDKVersion();
|
||||||
|
static QString GetManufacturer();
|
||||||
|
|
||||||
|
static AndroidUtils* instance();
|
||||||
|
|
||||||
|
static void dispatchToMainThread(std::function<void()> callback);
|
||||||
|
|
||||||
|
static QByteArray getQByteArrayFromJString(JNIEnv* env, jstring data);
|
||||||
|
|
||||||
|
static jbyteArray tojByteArray(const QByteArray& data);
|
||||||
|
|
||||||
|
static QString getQStringFromJString(JNIEnv* env, jstring data);
|
||||||
|
|
||||||
|
static QJsonObject getQJsonObjectFromJString(JNIEnv* env, jstring data);
|
||||||
|
|
||||||
|
static QJniObject getActivity();
|
||||||
|
|
||||||
|
static void runOnAndroidThreadSync(const std::function<void()> runnable);
|
||||||
|
static void runOnAndroidThreadAsync(const std::function<void()> runnable);
|
||||||
|
|
||||||
|
private:
|
||||||
|
AndroidUtils(QObject* parent);
|
||||||
|
~AndroidUtils();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ANDROIDUTILS_H
|
136
client/platforms/android/androidvpnactivity.cpp
Normal file
136
client/platforms/android/androidvpnactivity.cpp
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include "androidvpnactivity.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QJniEnvironment>
|
||||||
|
#include <QJniObject>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
#include "androidutils.h"
|
||||||
|
#include "jni.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
AndroidVPNActivity* s_instance = nullptr;
|
||||||
|
constexpr auto CLASSNAME = "org.amnezia.vpn.qt.VPNActivity";
|
||||||
|
}
|
||||||
|
|
||||||
|
AndroidVPNActivity::AndroidVPNActivity() {
|
||||||
|
AndroidUtils::runOnAndroidThreadAsync([]() {
|
||||||
|
JNINativeMethod methods[]{
|
||||||
|
{"handleBackButton", "()Z", reinterpret_cast<bool*>(handleBackButton)},
|
||||||
|
{"onServiceMessage", "(ILjava/lang/String;)V",
|
||||||
|
reinterpret_cast<void*>(onServiceMessage)},
|
||||||
|
{"qtOnServiceConnected", "()V",
|
||||||
|
reinterpret_cast<void*>(onServiceConnected)},
|
||||||
|
{"qtOnServiceDisconnected", "()V",
|
||||||
|
reinterpret_cast<void*>(onServiceDisconnected)},
|
||||||
|
};
|
||||||
|
|
||||||
|
QJniObject javaClass(CLASSNAME);
|
||||||
|
QJniEnvironment env;
|
||||||
|
jclass objectClass = env->GetObjectClass(javaClass.object<jobject>());
|
||||||
|
env->RegisterNatives(objectClass, methods, sizeof(methods) / sizeof(methods[0]));
|
||||||
|
env->DeleteLocalRef(objectClass);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidVPNActivity::maybeInit() {
|
||||||
|
if (s_instance == nullptr) {
|
||||||
|
s_instance = new AndroidVPNActivity();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
bool AndroidVPNActivity::handleBackButton(JNIEnv* env, jobject thiz) {
|
||||||
|
Q_UNUSED(env);
|
||||||
|
Q_UNUSED(thiz);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidVPNActivity::connectService() {
|
||||||
|
QJniObject::callStaticMethod<void>(CLASSNAME, "connectService", "()V");
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
AndroidVPNActivity* AndroidVPNActivity::instance() {
|
||||||
|
if (s_instance == nullptr) {
|
||||||
|
AndroidVPNActivity::maybeInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
return s_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
void AndroidVPNActivity::sendToService(ServiceAction type, const QString& data) {
|
||||||
|
int messageType = (int)type;
|
||||||
|
|
||||||
|
QJniEnvironment env;
|
||||||
|
QJniObject::callStaticMethod<void>(
|
||||||
|
CLASSNAME, "sendToService", "(ILjava/lang/String;)V",
|
||||||
|
static_cast<int>(messageType),
|
||||||
|
QJniObject::fromString(data).object<jstring>());
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
void AndroidVPNActivity::onServiceMessage(JNIEnv* env, jobject thiz,
|
||||||
|
jint messageType, jstring body) {
|
||||||
|
Q_UNUSED(thiz);
|
||||||
|
const char* buffer = env->GetStringUTFChars(body, nullptr);
|
||||||
|
if (!buffer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString parcelBody(buffer);
|
||||||
|
env->ReleaseStringUTFChars(body, buffer);
|
||||||
|
AndroidUtils::dispatchToMainThread([messageType, parcelBody] {
|
||||||
|
AndroidVPNActivity::instance()->handleServiceMessage(messageType,
|
||||||
|
parcelBody);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidVPNActivity::handleServiceMessage(int code, const QString& data) {
|
||||||
|
auto mode = (ServiceEvents)code;
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case ServiceEvents::EVENT_INIT:
|
||||||
|
emit eventInitialized(data);
|
||||||
|
break;
|
||||||
|
case ServiceEvents::EVENT_CONNECTED:
|
||||||
|
emit eventConnected(data);
|
||||||
|
break;
|
||||||
|
case ServiceEvents::EVENT_DISCONNECTED:
|
||||||
|
emit eventDisconnected(data);
|
||||||
|
break;
|
||||||
|
case ServiceEvents::EVENT_STATISTIC_UPDATE:
|
||||||
|
emit eventStatisticUpdate(data);
|
||||||
|
break;
|
||||||
|
case ServiceEvents::EVENT_BACKEND_LOGS:
|
||||||
|
emit eventBackendLogs(data);
|
||||||
|
break;
|
||||||
|
case ServiceEvents::EVENT_ACTIVATION_ERROR:
|
||||||
|
emit eventActivationError(data);
|
||||||
|
break;
|
||||||
|
case ServiceEvents::EVENT_CONFIG_IMPORT:
|
||||||
|
emit eventConfigImport(data);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Q_ASSERT(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidVPNActivity::onServiceConnected(JNIEnv* env, jobject thiz) {
|
||||||
|
Q_UNUSED(env);
|
||||||
|
Q_UNUSED(thiz);
|
||||||
|
|
||||||
|
emit AndroidVPNActivity::instance()->serviceConnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidVPNActivity::onServiceDisconnected(JNIEnv* env, jobject thiz) {
|
||||||
|
Q_UNUSED(env);
|
||||||
|
Q_UNUSED(thiz);
|
||||||
|
|
||||||
|
emit AndroidVPNActivity::instance()->serviceDisconnected();
|
||||||
|
}
|
93
client/platforms/android/androidvpnactivity.h
Normal file
93
client/platforms/android/androidvpnactivity.h
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef ANDROIDVPNACTIVITY_H
|
||||||
|
#define ANDROIDVPNACTIVITY_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "jni.h"
|
||||||
|
|
||||||
|
// Binder Codes for VPNServiceBinder
|
||||||
|
// See also - VPNServiceBinder.kt
|
||||||
|
// Actions that are Requestable
|
||||||
|
enum ServiceAction {
|
||||||
|
// Activate the vpn. Body requires a json wg-conf
|
||||||
|
ACTION_ACTIVATE = 1,
|
||||||
|
// Deactivate the vpn. Body is empty
|
||||||
|
ACTION_DEACTIVATE = 2,
|
||||||
|
// Register an IBinder to recieve events body is an Ibinder
|
||||||
|
ACTION_REGISTERLISTENER = 3,
|
||||||
|
// Requests an EVENT_STATISTIC_UPDATE to be send
|
||||||
|
ACTION_REQUEST_STATISTIC = 4,
|
||||||
|
ACTION_REQUEST_GET_LOG = 5,
|
||||||
|
// Requests to clean up the internal log
|
||||||
|
ACTION_REQUEST_CLEANUP_LOG = 6,
|
||||||
|
// Retry activation using the last config
|
||||||
|
// Used when the activation is aborted for VPN-Permission prompt
|
||||||
|
ACTION_RESUME_ACTIVATE = 7,
|
||||||
|
// Sets the current notification text.
|
||||||
|
// Does nothing if there is no notification
|
||||||
|
ACTION_SET_NOTIFICATION_TEXT = 8,
|
||||||
|
// Sets the fallback text if the OS triggered the VPN-Service
|
||||||
|
// to show a notification
|
||||||
|
ACTION_SET_NOTIFICATION_FALLBACK = 9,
|
||||||
|
// Share used config
|
||||||
|
ACTION_SHARE_CONFIG = 10,
|
||||||
|
};
|
||||||
|
typedef enum ServiceAction ServiceAction;
|
||||||
|
|
||||||
|
// Event Types that will be Dispatched after registration
|
||||||
|
enum ServiceEvents {
|
||||||
|
// The Service has Accecpted our Binder
|
||||||
|
// Responds with the current status of the vpn.
|
||||||
|
EVENT_INIT = 0,
|
||||||
|
// WG-Go has enabled the adapter (empty response)
|
||||||
|
EVENT_CONNECTED = 1,
|
||||||
|
// WG-Go has disabled the adapter (empty response)
|
||||||
|
EVENT_DISCONNECTED = 2,
|
||||||
|
// Contains the Current transfered bytes to endpoint x.
|
||||||
|
EVENT_STATISTIC_UPDATE = 3,
|
||||||
|
EVENT_BACKEND_LOGS = 4,
|
||||||
|
// An Error happened during activation
|
||||||
|
// Contains the error message
|
||||||
|
EVENT_ACTIVATION_ERROR = 5,
|
||||||
|
EVENT_NEED_PERMISSION = 6,
|
||||||
|
// Import of existing config
|
||||||
|
EVENT_CONFIG_IMPORT = 7,
|
||||||
|
};
|
||||||
|
typedef enum ServiceEvents ServiceEvents;
|
||||||
|
|
||||||
|
class AndroidVPNActivity : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void maybeInit();
|
||||||
|
static AndroidVPNActivity* instance();
|
||||||
|
static bool handleBackButton(JNIEnv* env, jobject thiz);
|
||||||
|
static void sendToService(ServiceAction type, const QString& data);
|
||||||
|
static void connectService();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void serviceConnected();
|
||||||
|
void serviceDisconnected();
|
||||||
|
void eventInitialized(const QString& data);
|
||||||
|
void eventConnected(const QString& data);
|
||||||
|
void eventDisconnected(const QString& data);
|
||||||
|
void eventStatisticUpdate(const QString& data);
|
||||||
|
void eventBackendLogs(const QString& data);
|
||||||
|
void eventActivationError(const QString& data);
|
||||||
|
void eventConfigImport(const QString& data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
AndroidVPNActivity();
|
||||||
|
|
||||||
|
static void onServiceMessage(JNIEnv* env, jobject thiz, jint messageType, jstring body);
|
||||||
|
static void onServiceConnected(JNIEnv* env, jobject thiz);
|
||||||
|
static void onServiceDisconnected(JNIEnv* env, jobject thiz);
|
||||||
|
void handleServiceMessage(int code, const QString& data);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ANDROIDVPNACTIVITY_H
|
|
@ -7,11 +7,7 @@
|
||||||
#include <QTextCodec>
|
#include <QTextCodec>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
#include <QtCore/private/qandroidextras_p.h>
|
|
||||||
|
|
||||||
|
|
||||||
#include "android_vpnprotocol.h"
|
#include "android_vpnprotocol.h"
|
||||||
#include "core/errorstrings.h"
|
|
||||||
|
|
||||||
#include "platforms/android/android_controller.h"
|
#include "platforms/android/android_controller.h"
|
||||||
|
|
||||||
|
@ -19,9 +15,7 @@
|
||||||
AndroidVpnProtocol::AndroidVpnProtocol(Proto protocol, const QJsonObject &configuration, QObject* parent)
|
AndroidVpnProtocol::AndroidVpnProtocol(Proto protocol, const QJsonObject &configuration, QObject* parent)
|
||||||
: VpnProtocol(configuration, parent),
|
: VpnProtocol(configuration, parent),
|
||||||
m_protocol(protocol)
|
m_protocol(protocol)
|
||||||
{
|
{ }
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode AndroidVpnProtocol::start()
|
ErrorCode AndroidVpnProtocol::start()
|
||||||
{
|
{
|
||||||
|
@ -35,3 +29,11 @@ void AndroidVpnProtocol::stop()
|
||||||
AndroidController::instance()->stop();
|
AndroidController::instance()->stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AndroidVpnProtocol::connectionDataUpdated(QString totalRx, QString totalTx, QString endpoint, QString deviceIPv4)
|
||||||
|
{
|
||||||
|
quint64 rxBytes = totalRx.toLongLong();
|
||||||
|
quint64 txBytes = totalTx.toLongLong();
|
||||||
|
|
||||||
|
setBytesChanged(rxBytes, txBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,11 @@
|
||||||
#ifndef ANDROID_VPNPROTOCOL_H
|
#ifndef ANDROID_VPNPROTOCOL_H
|
||||||
#define ANDROID_VPNPROTOCOL_H
|
#define ANDROID_VPNPROTOCOL_H
|
||||||
|
|
||||||
#include <QAndroidBinder>
|
|
||||||
#include <QAndroidServiceConnection>
|
|
||||||
|
|
||||||
#include "vpnprotocol.h"
|
#include "vpnprotocol.h"
|
||||||
#include "protocols/protocols_defs.h"
|
#include "protocols/protocols_defs.h"
|
||||||
|
|
||||||
using namespace amnezia;
|
using namespace amnezia;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class AndroidVpnProtocol : public VpnProtocol
|
class AndroidVpnProtocol : public VpnProtocol
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -25,6 +20,9 @@ public:
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void connectionDataUpdated(QString totalRx, QString totalTx, QString endpoint, QString deviceIPv4);
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -32,7 +30,6 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Proto m_protocol;
|
Proto m_protocol;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ANDROID_VPNPROTOCOL_H
|
#endif // ANDROID_VPNPROTOCOL_H
|
||||||
|
|
|
@ -36,6 +36,7 @@ public:
|
||||||
void cleanupBackendLogs();
|
void cleanupBackendLogs();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
void newTransmitedDataCount(quint64 rxBytes, quint64 txBytes);
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,10 @@ Proto currentProto = amnezia::Proto::Any;
|
||||||
}
|
}
|
||||||
|
|
||||||
IOSVpnProtocol::IOSVpnProtocol(Proto proto, const QJsonObject &configuration, QObject* parent)
|
IOSVpnProtocol::IOSVpnProtocol(Proto proto, const QJsonObject &configuration, QObject* parent)
|
||||||
: VpnProtocol(configuration, parent),
|
: VpnProtocol(configuration, parent), m_protocol(proto)
|
||||||
m_protocol(proto) {}
|
{
|
||||||
|
connect(this, &IOSVpnProtocol::newTransmitedDataCount, this, &IOSVpnProtocol::setBytesChanged);
|
||||||
|
}
|
||||||
|
|
||||||
IOSVpnProtocol* IOSVpnProtocol::instance() {
|
IOSVpnProtocol* IOSVpnProtocol::instance() {
|
||||||
return s_instance;
|
return s_instance;
|
||||||
|
@ -207,8 +209,7 @@ void IOSVpnProtocol::checkStatus()
|
||||||
qDebug() << "ServerIpv4Gateway:" << QString::fromNSString(serverIpv4Gateway)
|
qDebug() << "ServerIpv4Gateway:" << QString::fromNSString(serverIpv4Gateway)
|
||||||
<< "DeviceIpv4Address:" << QString::fromNSString(deviceIpv4Address)
|
<< "DeviceIpv4Address:" << QString::fromNSString(deviceIpv4Address)
|
||||||
<< "RxBytes:" << rxBytes << "TxBytes:" << txBytes;
|
<< "RxBytes:" << rxBytes << "TxBytes:" << txBytes;
|
||||||
emit bytesChanged(rxBytes, txBytes);
|
emit newTransmitedDataCount(rxBytes, txBytes);
|
||||||
|
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,7 +67,10 @@ VpnProtocol::VpnConnectionState VpnProtocol::connectionState() const
|
||||||
|
|
||||||
void VpnProtocol::setBytesChanged(quint64 receivedBytes, quint64 sentBytes)
|
void VpnProtocol::setBytesChanged(quint64 receivedBytes, quint64 sentBytes)
|
||||||
{
|
{
|
||||||
emit bytesChanged(receivedBytes - m_receivedBytes, sentBytes - m_sentBytes);
|
quint64 rxDiff = receivedBytes - m_receivedBytes;
|
||||||
|
quint64 txDiff = sentBytes - m_sentBytes;
|
||||||
|
|
||||||
|
emit bytesChanged(rxDiff, txDiff);
|
||||||
|
|
||||||
m_receivedBytes = receivedBytes;
|
m_receivedBytes = receivedBytes;
|
||||||
m_sentBytes = sentBytes;
|
m_sentBytes = sentBytes;
|
||||||
|
|
|
@ -3,10 +3,6 @@
|
||||||
#include "ui/uilogic.h"
|
#include "ui/uilogic.h"
|
||||||
#include "ui/pages_logic/StartPageLogic.h"
|
#include "ui/pages_logic/StartPageLogic.h"
|
||||||
|
|
||||||
#if defined(Q_OS_ANDROID)
|
|
||||||
#include "android_controller.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using namespace amnezia;
|
using namespace amnezia;
|
||||||
using namespace PageEnumNS;
|
using namespace PageEnumNS;
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
#include "vpnconnection.h"
|
#include "vpnconnection.h"
|
||||||
|
|
||||||
#include "../uilogic.h"
|
#include "../uilogic.h"
|
||||||
#include "ServerListLogic.h"
|
|
||||||
#include "ShareConnectionLogic.h"
|
#include "ShareConnectionLogic.h"
|
||||||
#include "VpnLogic.h"
|
#include "VpnLogic.h"
|
||||||
|
|
||||||
|
@ -11,9 +10,7 @@
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
#if defined(Q_OS_ANDROID)
|
#if defined(Q_OS_ANDROID)
|
||||||
#include <QAndroidJniObject>
|
#include "../../platforms/android/androidutils.h"
|
||||||
#include <QAndroidJniEnvironment>
|
|
||||||
#include <QtAndroid>
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ServerSettingsLogic::ServerSettingsLogic(UiLogic *logic, QObject *parent):
|
ServerSettingsLogic::ServerSettingsLogic(UiLogic *logic, QObject *parent):
|
||||||
|
@ -24,9 +21,7 @@ ServerSettingsLogic::ServerSettingsLogic(UiLogic *logic, QObject *parent):
|
||||||
m_pushButtonShareFullVisible{true},
|
m_pushButtonShareFullVisible{true},
|
||||||
m_pushButtonClearText{tr("Clear server from Amnezia software")},
|
m_pushButtonClearText{tr("Clear server from Amnezia software")},
|
||||||
m_pushButtonClearClientCacheText{tr("Clear client cached profile")}
|
m_pushButtonClearClientCacheText{tr("Clear client cached profile")}
|
||||||
{
|
{ }
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void ServerSettingsLogic::onUpdatePage()
|
void ServerSettingsLogic::onUpdatePage()
|
||||||
{
|
{
|
||||||
|
@ -140,7 +135,7 @@ void ServerSettingsLogic::onLineEditDescriptionEditingFinished()
|
||||||
|
|
||||||
#if defined(Q_OS_ANDROID)
|
#if defined(Q_OS_ANDROID)
|
||||||
/* Auth result handler for Android */
|
/* Auth result handler for Android */
|
||||||
void authResultReceiver::handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data)
|
void authResultReceiver::handleActivityResult(int receiverRequestCode, int resultCode, const QJniObject &data)
|
||||||
{
|
{
|
||||||
qDebug() << "receiverRequestCode" << receiverRequestCode << "resultCode" << resultCode;
|
qDebug() << "receiverRequestCode" << receiverRequestCode << "resultCode" << resultCode;
|
||||||
|
|
||||||
|
@ -155,16 +150,17 @@ void ServerSettingsLogic::onPushButtonShareFullClicked()
|
||||||
{
|
{
|
||||||
#if defined(Q_OS_ANDROID)
|
#if defined(Q_OS_ANDROID)
|
||||||
/* We use builtin keyguard for ssh key export protection on Android */
|
/* We use builtin keyguard for ssh key export protection on Android */
|
||||||
auto appContext = QtAndroid::androidActivity().callObjectMethod(
|
QJniObject activity = AndroidUtils::getActivity();
|
||||||
|
auto appContext = activity.callObjectMethod(
|
||||||
"getApplicationContext", "()Landroid/content/Context;");
|
"getApplicationContext", "()Landroid/content/Context;");
|
||||||
if (appContext.isValid()) {
|
if (appContext.isValid()) {
|
||||||
QAndroidActivityResultReceiver *receiver = new authResultReceiver(uiLogic(), uiLogic()->selectedServerIndex);
|
QAndroidActivityResultReceiver *receiver = new authResultReceiver(uiLogic(), uiLogic()->selectedServerIndex);
|
||||||
auto intent = QAndroidJniObject::callStaticObjectMethod(
|
auto intent = QJniObject::callStaticObjectMethod(
|
||||||
"org/amnezia/vpn/AuthHelper", "getAuthIntent",
|
"org/amnezia/vpn/AuthHelper", "getAuthIntent",
|
||||||
"(Landroid/content/Context;)Landroid/content/Intent;", appContext.object());
|
"(Landroid/content/Context;)Landroid/content/Intent;", appContext.object());
|
||||||
if (intent.isValid()) {
|
if (intent.isValid()) {
|
||||||
if (intent.object<jobject>() != nullptr) {
|
if (intent.object<jobject>() != nullptr) {
|
||||||
QtAndroid::startActivity(intent.object<jobject>(), 1, receiver);
|
QtAndroidPrivate::startActivity(intent.object<jobject>(), 1, receiver);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
uiLogic()->pageLogic<ShareConnectionLogic>()->updateSharingPage(uiLogic()->selectedServerIndex, DockerContainer::None);
|
uiLogic()->pageLogic<ShareConnectionLogic>()->updateSharingPage(uiLogic()->selectedServerIndex, DockerContainer::None);
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
#include "PageLogicBase.h"
|
#include "PageLogicBase.h"
|
||||||
|
|
||||||
#if defined(Q_OS_ANDROID)
|
#if defined(Q_OS_ANDROID)
|
||||||
#include <QAndroidActivityResultReceiver>
|
#include <QJniObject>
|
||||||
|
#include <private/qandroidextras_p.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class UiLogic;
|
class UiLogic;
|
||||||
|
@ -52,7 +53,7 @@ public:
|
||||||
~authResultReceiver() {}
|
~authResultReceiver() {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data) override;
|
void handleActivityResult(int receiverRequestCode, int resultCode, const QJniObject &data) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_serverIndex;
|
int m_serverIndex;
|
||||||
|
|
|
@ -11,8 +11,8 @@
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
|
||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
#include <QtAndroid>
|
#include <QJniObject>
|
||||||
#include "platforms/android/android_controller.h"
|
#include "../../platforms/android/androidutils.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -56,8 +56,9 @@ StartPageLogic::StartPageLogic(UiLogic *logic, QObject *parent):
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
// Set security screen for Android app
|
// Set security screen for Android app
|
||||||
QtAndroid::runOnAndroidThread([]() {
|
AndroidUtils::runOnAndroidThreadSync([]() {
|
||||||
QAndroidJniObject window = QtAndroid::androidActivity().callObjectMethod("getWindow", "()Landroid/view/Window;");
|
QJniObject activity = AndroidUtils::getActivity();
|
||||||
|
QJniObject window = activity.callObjectMethod("getWindow", "()Landroid/view/Window;");
|
||||||
if (window.isValid()){
|
if (window.isValid()){
|
||||||
const int FLAG_SECURE = 8192;
|
const int FLAG_SECURE = 8192;
|
||||||
window.callMethod<void>("addFlags", "(I)V", FLAG_SECURE);
|
window.callMethod<void>("addFlags", "(I)V", FLAG_SECURE);
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
#include "android_controller.h"
|
#include "../../platforms/android/android_controller.h"
|
||||||
#include "protocols/android_vpnprotocol.h"
|
#include "protocols/android_vpnprotocol.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -36,8 +36,6 @@ VpnConnection::VpnConnection(std::shared_ptr<Settings> settings,
|
||||||
m_settings(settings),
|
m_settings(settings),
|
||||||
m_configurator(configurator),
|
m_configurator(configurator),
|
||||||
m_serverController(serverController),
|
m_serverController(serverController),
|
||||||
m_receivedBytes(0),
|
|
||||||
m_sentBytes(0),
|
|
||||||
m_isIOSConnected(false)
|
m_isIOSConnected(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -52,11 +50,7 @@ VpnConnection::~VpnConnection()
|
||||||
|
|
||||||
void VpnConnection::onBytesChanged(quint64 receivedBytes, quint64 sentBytes)
|
void VpnConnection::onBytesChanged(quint64 receivedBytes, quint64 sentBytes)
|
||||||
{
|
{
|
||||||
emit bytesChanged(receivedBytes - m_receivedBytes, sentBytes - m_sentBytes);
|
emit bytesChanged(receivedBytes, sentBytes);
|
||||||
|
|
||||||
m_receivedBytes = receivedBytes;
|
|
||||||
m_sentBytes = sentBytes;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VpnConnection::onConnectionStateChanged(VpnProtocol::VpnConnectionState state)
|
void VpnConnection::onConnectionStateChanged(VpnProtocol::VpnConnectionState state)
|
||||||
|
@ -362,6 +356,7 @@ void VpnConnection::connectToVpn(int serverIndex,
|
||||||
Proto proto = ContainerProps::defaultProtocol(container);
|
Proto proto = ContainerProps::defaultProtocol(container);
|
||||||
AndroidVpnProtocol *androidVpnProtocol = new AndroidVpnProtocol(proto, m_vpnConfiguration);
|
AndroidVpnProtocol *androidVpnProtocol = new AndroidVpnProtocol(proto, m_vpnConfiguration);
|
||||||
connect(AndroidController::instance(), &AndroidController::connectionStateChanged, androidVpnProtocol, &AndroidVpnProtocol::setConnectionState);
|
connect(AndroidController::instance(), &AndroidController::connectionStateChanged, androidVpnProtocol, &AndroidVpnProtocol::setConnectionState);
|
||||||
|
connect(AndroidController::instance(), &AndroidController::statusUpdated, androidVpnProtocol, &AndroidVpnProtocol::connectionDataUpdated);
|
||||||
|
|
||||||
m_vpnProtocol.reset(androidVpnProtocol);
|
m_vpnProtocol.reset(androidVpnProtocol);
|
||||||
#elif defined Q_OS_IOS
|
#elif defined Q_OS_IOS
|
||||||
|
|
|
@ -93,8 +93,6 @@ private:
|
||||||
QJsonObject m_vpnConfiguration;
|
QJsonObject m_vpnConfiguration;
|
||||||
QJsonObject m_routeMode;
|
QJsonObject m_routeMode;
|
||||||
QString m_remoteAddress;
|
QString m_remoteAddress;
|
||||||
quint64 m_receivedBytes;
|
|
||||||
quint64 m_sentBytes;
|
|
||||||
bool m_isIOSConnected; //remove later move to isConnected,
|
bool m_isIOSConnected; //remove later move to isConnected,
|
||||||
|
|
||||||
#ifdef AMNEZIA_DESKTOP
|
#ifdef AMNEZIA_DESKTOP
|
||||||
|
|
|
@ -20,46 +20,39 @@ APP_DOMAIN=org.amneziavpn.package
|
||||||
OUT_APP_DIR=$BUILD_DIR/client
|
OUT_APP_DIR=$BUILD_DIR/client
|
||||||
BUNDLE_DIR=$OUT_APP_DIR/$APP_FILENAME
|
BUNDLE_DIR=$OUT_APP_DIR/$APP_FILENAME
|
||||||
|
|
||||||
INSTALLER_DATA_DIR=$BUILD_DIR/installer/packages/$APP_DOMAIN/data
|
|
||||||
INSTALLER_BUNDLE_DIR=$BUILD_DIR/installer/$APP_FILENAME
|
|
||||||
|
|
||||||
PRO_FILE_PATH=$PROJECT_DIR/$APP_NAME.pro
|
|
||||||
QMAKE_STASH_FILE=$PROJECT_DIR/.qmake_stash
|
|
||||||
|
|
||||||
# Seacrh Qt
|
# Seacrh Qt
|
||||||
if [ -z "${QT_VERSION+x}" ]; then
|
if [ -z "${QT_VERSION+x}" ]; then
|
||||||
QT_VERSION=5.15.2;
|
QT_VERSION=6.4.1;
|
||||||
QT_BIN_DIR=$HOME/Qt/$QT_VERSION/android/bin
|
QT_BIN_DIR=$HOME/Qt/$QT_VERSION/$ANDROID_CURRENT_ARCH/bin
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Using Qt in $QT_BIN_DIR"
|
echo "Using Qt in $QT_BIN_DIR"
|
||||||
echo "Using Android SDK in $ANDROID_SDK_ROOT"
|
echo "Using Android SDK in $ANDROID_SDK_ROOT"
|
||||||
echo "Using Android NDK in $ANDROID_NDK_ROOT"
|
echo "Using Android NDK in $ANDROID_NDK_ROOT"
|
||||||
|
|
||||||
# Checking env
|
|
||||||
$QT_BIN_DIR/qmake -v
|
|
||||||
$ANDROID_NDK_HOME/prebuilt/linux-x86_64/bin/make -v
|
|
||||||
|
|
||||||
# Build App
|
# Build App
|
||||||
echo "Building App..."
|
echo "Building App..."
|
||||||
cd $BUILD_DIR
|
cd $BUILD_DIR
|
||||||
|
|
||||||
$QT_BIN_DIR/qmake -r -spec android-clang CONFIG+=qtquickcompiler ANDROID_ABIS="armeabi-v7a arm64-v8a x86 x86_64" $PROJECT_DIR/AmneziaVPN.pro
|
echo "HOST Qt: $QT_HOST_PATH"
|
||||||
echo "Executing make... may take long time"
|
|
||||||
$ANDROID_NDK_HOME/prebuilt/linux-x86_64/bin/make -j2
|
|
||||||
echo "Make install..."
|
|
||||||
$ANDROID_NDK_HOME/prebuilt/linux-x86_64/bin/make install INSTALL_ROOT=android
|
|
||||||
echo "Build OK"
|
|
||||||
|
|
||||||
echo "............Deploy.................."
|
$QT_BIN_DIR/qt-cmake -S $PROJECT_DIR \
|
||||||
|
-DQT_NO_GLOBAL_APK_TARGET_PART_OF_ALL="ON" \
|
||||||
|
-DQT_HOST_PATH=$QT_HOST_PATH \
|
||||||
|
-DCMAKE_BUILD_TYPE="Release"
|
||||||
|
|
||||||
|
cmake --build . --config release
|
||||||
|
|
||||||
|
echo "............APK generation.................."
|
||||||
cd $OUT_APP_DIR
|
cd $OUT_APP_DIR
|
||||||
|
|
||||||
$QT_BIN_DIR/androiddeployqt \
|
$QT_HOST_PATH/bin/androiddeployqt \
|
||||||
--output $OUT_APP_DIR/android \
|
--output $OUT_APP_DIR/android-build \
|
||||||
--gradle \
|
--gradle \
|
||||||
--release \
|
--release \
|
||||||
--input android-AmneziaVPN-deployment-settings.json
|
--input android-AmneziaVPN-deployment-settings.json \
|
||||||
|
--android-platform android-31
|
||||||
|
|
||||||
echo "............Copy apk.................."
|
echo "............Copy apk.................."
|
||||||
cp $OUT_APP_DIR/android/build/outputs/apk/release/android-release-unsigned.apk \
|
cp $OUT_APP_DIR/android-build/build/outputs/apk/release/android-build-release-unsigned.apk \
|
||||||
$PROJECT_DIR/AmneziaVPN-release-unsigned.apk
|
$PROJECT_DIR/AmneziaVPN-release-unsigned.apk
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue