Vpn service refactoring
This commit is contained in:
parent
8ef16781eb
commit
385a52f676
10 changed files with 408 additions and 1262 deletions
|
@ -68,6 +68,14 @@
|
||||||
android:taskAffinity=""
|
android:taskAffinity=""
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".VpnRequestActivity"
|
||||||
|
android:excludeFromRecents="true"
|
||||||
|
android:launchMode="singleTask"
|
||||||
|
android:taskAffinity=""
|
||||||
|
android:exported="false"
|
||||||
|
android:theme="@style/Translucent" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ImportConfigActivity"
|
android:name=".ImportConfigActivity"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
|
|
|
@ -4,6 +4,5 @@ enum class ProtocolState {
|
||||||
CONNECTED,
|
CONNECTED,
|
||||||
CONNECTING,
|
CONNECTING,
|
||||||
DISCONNECTED,
|
DISCONNECTED,
|
||||||
DISCONNECTING,
|
DISCONNECTING
|
||||||
UNKNOWN
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,4 +4,14 @@
|
||||||
<item name="android:windowActionBar">false</item>
|
<item name="android:windowActionBar">false</item>
|
||||||
<item name="android:windowNoTitle">true</item>
|
<item name="android:windowNoTitle">true</item>
|
||||||
</style>
|
</style>
|
||||||
|
<style name="Translucent" parent="NoActionBar">
|
||||||
|
<item name="android:windowBackground">@android:color/transparent</item>
|
||||||
|
<item name="android:windowFrame">@null</item>
|
||||||
|
<item name="android:windowIsFloating">true</item>
|
||||||
|
<item name="android:windowIsTranslucent">true</item>
|
||||||
|
<item name="android:windowContentOverlay">@null</item>
|
||||||
|
<item name="android:windowAnimationStyle">@null</item>
|
||||||
|
<item name="android:backgroundDimEnabled">false</item>
|
||||||
|
<item name="android:windowCloseOnTouchOutside">false</item>
|
||||||
|
</style>
|
||||||
</resources>
|
</resources>
|
|
@ -6,13 +6,11 @@ import android.content.ServiceConnection
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.net.VpnService
|
import android.net.VpnService
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.DeadObjectException
|
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.os.Message
|
import android.os.Message
|
||||||
import android.os.Messenger
|
import android.os.Messenger
|
||||||
import android.os.RemoteException
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.annotation.MainThread
|
import androidx.annotation.MainThread
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
@ -76,6 +74,9 @@ class AmneziaActivity : QtActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
ServiceEvent.ERROR -> {
|
ServiceEvent.ERROR -> {
|
||||||
|
msg.data?.getString(ERROR_MSG)?.let { error ->
|
||||||
|
Log.e(TAG, "From VpnService: $error")
|
||||||
|
}
|
||||||
// todo: add error reporting to Qt
|
// todo: add error reporting to Qt
|
||||||
QtAndroidController.onServiceError()
|
QtAndroidController.onServiceError()
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,14 +3,32 @@ package org.amnezia.vpn
|
||||||
import androidx.camera.camera2.Camera2Config
|
import androidx.camera.camera2.Camera2Config
|
||||||
import androidx.camera.core.CameraSelector
|
import androidx.camera.core.CameraSelector
|
||||||
import androidx.camera.core.CameraXConfig
|
import androidx.camera.core.CameraXConfig
|
||||||
|
import androidx.core.app.NotificationChannelCompat.Builder
|
||||||
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import org.qtproject.qt.android.bindings.QtApplication
|
import org.qtproject.qt.android.bindings.QtApplication
|
||||||
|
|
||||||
|
const val NOTIFICATION_CHANNEL_ID: String = "org.amnezia.vpn.notification"
|
||||||
|
|
||||||
class AmneziaApplication : QtApplication(), CameraXConfig.Provider {
|
class AmneziaApplication : QtApplication(), CameraXConfig.Provider {
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
createNotificationChannel()
|
||||||
|
}
|
||||||
|
|
||||||
override fun getCameraXConfig(): CameraXConfig = CameraXConfig.Builder
|
override fun getCameraXConfig(): CameraXConfig = CameraXConfig.Builder
|
||||||
.fromConfig(Camera2Config.defaultConfig())
|
.fromConfig(Camera2Config.defaultConfig())
|
||||||
.setMinimumLoggingLevel(android.util.Log.ERROR)
|
.setMinimumLoggingLevel(android.util.Log.ERROR)
|
||||||
.setAvailableCamerasLimiter(CameraSelector.DEFAULT_BACK_CAMERA)
|
.setAvailableCamerasLimiter(CameraSelector.DEFAULT_BACK_CAMERA)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
private fun createNotificationChannel() {
|
||||||
|
NotificationManagerCompat.from(this).createNotificationChannel(
|
||||||
|
Builder(NOTIFICATION_CHANNEL_ID, NotificationManagerCompat.IMPORTANCE_LOW)
|
||||||
|
.setName("AmneziaVPN")
|
||||||
|
.setDescription("AmneziaVPN service notification")
|
||||||
|
.setShowBadge(false)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -153,7 +153,7 @@ class NetworkState(var service: AmneziaVpnService) {
|
||||||
defaultNetwork = NetworkTransports(network, newTransports)
|
defaultNetwork = NetworkTransports(network, newTransports)
|
||||||
}
|
}
|
||||||
if (capabilitiesChanged) {
|
if (capabilitiesChanged) {
|
||||||
mService.networkChange()
|
// mService.networkChange()
|
||||||
|
|
||||||
Log.i(tag, "onCapabilitiesChanged capabilitiesChanged $network $networkCapabilities")
|
Log.i(tag, "onCapabilitiesChanged capabilitiesChanged $network $networkCapabilities")
|
||||||
defaultNetworkCapabilities = newCapabilities
|
defaultNetworkCapabilities = newCapabilities
|
||||||
|
|
|
@ -1,115 +0,0 @@
|
||||||
/* 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
|
|
||||||
|
|
||||||
import android.app.NotificationChannel
|
|
||||||
import android.app.NotificationManager
|
|
||||||
import android.app.PendingIntent
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Build
|
|
||||||
import android.os.Parcel
|
|
||||||
import androidx.core.app.NotificationCompat
|
|
||||||
import org.json.JSONObject
|
|
||||||
|
|
||||||
object NotificationUtil {
|
|
||||||
var sCurrentContext: Context? = null
|
|
||||||
private var sNotificationBuilder: NotificationCompat.Builder? = null
|
|
||||||
|
|
||||||
const val NOTIFICATION_CHANNEL_ID = "com.amnezia.vpnNotification"
|
|
||||||
const val CONNECTED_NOTIFICATION_ID = 1337
|
|
||||||
const val tag = "NotificationUtil"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the current shown notification from a
|
|
||||||
* Parcel - Gets called from AndroidController.cpp
|
|
||||||
*/
|
|
||||||
fun update(data: Parcel) {
|
|
||||||
// [data] is here a json containing the notification content
|
|
||||||
val buffer = data.createByteArray()
|
|
||||||
val json = buffer?.let { String(it) }
|
|
||||||
val content = JSONObject(json)
|
|
||||||
|
|
||||||
update(content.getString("title"), content.getString("message"))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the current shown notification
|
|
||||||
*/
|
|
||||||
fun update(heading: String, message: String) {
|
|
||||||
if (sCurrentContext == null) return
|
|
||||||
val notificationManager: NotificationManager =
|
|
||||||
sCurrentContext?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
||||||
|
|
||||||
sNotificationBuilder?.let {
|
|
||||||
it.setContentTitle(heading)
|
|
||||||
.setContentText(message)
|
|
||||||
notificationManager.notify(CONNECTED_NOTIFICATION_ID, it.build())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves the default translated "connected" notification, in case the vpn gets started
|
|
||||||
* without the app.
|
|
||||||
*/
|
|
||||||
fun saveFallBackMessage(data: Parcel, context: Context) {
|
|
||||||
// [data] is here a json containing the notification content
|
|
||||||
val buffer = data.createByteArray()
|
|
||||||
val json = buffer?.let { String(it) }
|
|
||||||
val content = JSONObject(json)
|
|
||||||
|
|
||||||
val prefs = Prefs.get(context)
|
|
||||||
prefs.edit()
|
|
||||||
.putString("fallbackNotificationHeader", content.getString("title"))
|
|
||||||
.putString("fallbackNotificationMessage", content.getString("message"))
|
|
||||||
.apply()
|
|
||||||
Log.v(tag, "Saved new fallback message -> ${content.getString("title")}")
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Creates a new Notification using the current set of Strings
|
|
||||||
* Shows the notification in the given {context}
|
|
||||||
*/
|
|
||||||
fun show(service: AmneziaVpnService) {
|
|
||||||
sNotificationBuilder = NotificationCompat.Builder(service, NOTIFICATION_CHANNEL_ID)
|
|
||||||
sCurrentContext = service
|
|
||||||
val notificationManager: NotificationManager =
|
|
||||||
sCurrentContext?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
||||||
// From Oreo on we need to have a "notification channel" to post to.
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
val name = "vpn"
|
|
||||||
val descriptionText = " "
|
|
||||||
val importance = NotificationManager.IMPORTANCE_LOW
|
|
||||||
val channel = NotificationChannel(NOTIFICATION_CHANNEL_ID, name, importance).apply {
|
|
||||||
description = descriptionText
|
|
||||||
}
|
|
||||||
// Register the channel with the system
|
|
||||||
notificationManager.createNotificationChannel(channel)
|
|
||||||
}
|
|
||||||
// In case we do not have gotten a message to show from the Frontend
|
|
||||||
// try to populate the notification with a translated Fallback message
|
|
||||||
val prefs = Prefs.get(service)
|
|
||||||
val message =
|
|
||||||
"" + prefs.getString("fallbackNotificationMessage", "Running in the Background")
|
|
||||||
val header = "" + prefs.getString("fallbackNotificationHeader", "Amnezia VPN")
|
|
||||||
|
|
||||||
// Create the Intent that Should be Fired if the User Clicks the notification
|
|
||||||
val mainActivityName = "org.amnezia.vpn.AmneziaActivity"
|
|
||||||
val activity = Class.forName(mainActivityName)
|
|
||||||
val intent = Intent(service, activity)
|
|
||||||
val pendingIntent = PendingIntent.getActivity(service, 0, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
|
|
||||||
// Build our notification
|
|
||||||
sNotificationBuilder?.let {
|
|
||||||
it.setSmallIcon(R.drawable.ic_amnezia_round)
|
|
||||||
.setContentTitle(header)
|
|
||||||
.setContentText(message)
|
|
||||||
.setOnlyAlertOnce(true)
|
|
||||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
|
||||||
.setContentIntent(pendingIntent)
|
|
||||||
|
|
||||||
service.startForeground(CONNECTED_NOTIFICATION_ID, it.build())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,211 +0,0 @@
|
||||||
/* 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
|
|
||||||
import android.os.Binder
|
|
||||||
import android.os.DeadObjectException
|
|
||||||
import android.os.IBinder
|
|
||||||
import android.os.Parcel
|
|
||||||
import com.wireguard.config.*
|
|
||||||
import org.json.JSONObject
|
|
||||||
import java.lang.Exception
|
|
||||||
|
|
||||||
class VPNServiceBinder(service: AmneziaVpnService) : Binder() {
|
|
||||||
|
|
||||||
private val mService = service
|
|
||||||
private val tag = "VPNServiceBinder"
|
|
||||||
private var mListener: IBinder? = null
|
|
||||||
private var mResumeConfig: JSONObject? = null
|
|
||||||
private var mImportedConfig: String? = null
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The codes this Binder does accept in [onTransact]
|
|
||||||
*/
|
|
||||||
object ACTIONS {
|
|
||||||
const val activate = 1
|
|
||||||
const val deactivate = 2
|
|
||||||
const val registerEventListener = 3
|
|
||||||
const val requestStatistic = 4
|
|
||||||
const val requestGetLog = 5
|
|
||||||
const val requestCleanupLog = 6
|
|
||||||
const val resumeActivate = 7
|
|
||||||
const val setNotificationText = 8
|
|
||||||
const val setFallBackNotification = 9
|
|
||||||
const val importConfig = 11
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets called when the VPNServiceBinder gets a request from a Client.
|
|
||||||
* The [code] determines what action is requested. - see [ACTIONS]
|
|
||||||
* [data] may contain a utf-8 encoded json string with optional args or is null.
|
|
||||||
* [reply] is a pointer to a buffer in the clients memory, to reply results.
|
|
||||||
* we use this to send result data.
|
|
||||||
*
|
|
||||||
* returns true if the [code] was accepted
|
|
||||||
*/
|
|
||||||
override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean {
|
|
||||||
Log.i(tag, "GOT TRANSACTION " + code)
|
|
||||||
|
|
||||||
when (code) {
|
|
||||||
ACTIONS.activate -> {
|
|
||||||
try {
|
|
||||||
Log.i(tag, "Activation Requested, parsing Config")
|
|
||||||
// [data] is here a json containing the wireguard/openvpn conf
|
|
||||||
val buffer = data.createByteArray()
|
|
||||||
val json = buffer?.let { String(it) }
|
|
||||||
val config = JSONObject(json)
|
|
||||||
Log.v(tag, "Stored new Tunnel config in Service")
|
|
||||||
Log.i(tag, "Config: $config")
|
|
||||||
if (!mService.checkPermissions()) {
|
|
||||||
mResumeConfig = config
|
|
||||||
// The Permission prompt was already
|
|
||||||
// send, in case it's accepted we will
|
|
||||||
// receive ACTIONS.resumeActivate
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
this.mService.turnOn(config)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(tag, "An Error occurred while enabling the VPN: ${e.localizedMessage}")
|
|
||||||
dispatchEvent(EVENTS.activationError, e.localizedMessage)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
ACTIONS.resumeActivate -> {
|
|
||||||
// [data] is empty
|
|
||||||
// Activate the current tunnel
|
|
||||||
Log.i(tag, "resume activate")
|
|
||||||
try {
|
|
||||||
mResumeConfig?.let { this.mService.turnOn(it) }
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(tag, "An Error occurred while enabling the VPN: ${e.localizedMessage}")
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
ACTIONS.deactivate -> {
|
|
||||||
// [data] here is empty
|
|
||||||
this.mService.turnOff()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
ACTIONS.registerEventListener -> {
|
|
||||||
Log.i(tag, "register: start")
|
|
||||||
// [data] contains the Binder that we need to dispatch the Events
|
|
||||||
val binder = data.readStrongBinder()
|
|
||||||
mListener = binder
|
|
||||||
val obj = JSONObject()
|
|
||||||
obj.put("connected", mService.isUp)
|
|
||||||
obj.put("time", mService.connectionTime)
|
|
||||||
dispatchEvent(EVENTS.init, obj.toString())
|
|
||||||
|
|
||||||
////
|
|
||||||
if (mImportedConfig != null) {
|
|
||||||
Log.i(tag, "register: config not null")
|
|
||||||
dispatchEvent(EVENTS.configImport, mImportedConfig)
|
|
||||||
mImportedConfig = null
|
|
||||||
} else {
|
|
||||||
Log.i(tag, "register: config is null")
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
ACTIONS.requestStatistic -> {
|
|
||||||
dispatchEvent(EVENTS.statisticUpdate, mService.status.toString())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
ACTIONS.requestGetLog -> {
|
|
||||||
// Grabs all the Logs and dispatch new Log Event
|
|
||||||
// dispatchEvent(EVENTS.backendLogs, Log.getContent())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
ACTIONS.requestCleanupLog -> {
|
|
||||||
// Log.clearFile()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
ACTIONS.setNotificationText -> {
|
|
||||||
NotificationUtil.update(data)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
ACTIONS.setFallBackNotification -> {
|
|
||||||
NotificationUtil.saveFallBackMessage(data, mService)
|
|
||||||
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 -> {
|
|
||||||
Log.e(tag, "The OS Requested to shut down the VPN")
|
|
||||||
this.mService.turnOff()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
Log.e(tag, "Received invalid bind request \t Code -> $code")
|
|
||||||
// If we're hitting this there is probably something wrong in the client.
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dispatches an Event to all registered Binders
|
|
||||||
* [code] the Event that happened - see [EVENTS]
|
|
||||||
* To register an Eventhandler use [onTransact] with
|
|
||||||
* [ACTIONS.registerEventListener]
|
|
||||||
*/
|
|
||||||
fun dispatchEvent(code: Int, payload: String?) {
|
|
||||||
try {
|
|
||||||
mListener?.let {
|
|
||||||
if (it.isBinderAlive) {
|
|
||||||
val data = Parcel.obtain()
|
|
||||||
data.writeByteArray(payload?.toByteArray(charset("UTF-8")))
|
|
||||||
it.transact(code, data, Parcel.obtain(), 0)
|
|
||||||
} else {
|
|
||||||
Log.i(tag, "Dispatching event: binder NOT alive")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: DeadObjectException) {
|
|
||||||
// If the QT Process is killed (not just inactive)
|
|
||||||
// we cant access isBinderAlive, so nothing to do here.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The codes we Are Using in case of [dispatchEvent]
|
|
||||||
* Qt codes in the androidvpnactivity.h
|
|
||||||
*/
|
|
||||||
object EVENTS {
|
|
||||||
const val init = 0
|
|
||||||
const val connected = 1
|
|
||||||
const val disconnected = 2
|
|
||||||
const val statisticUpdate = 3
|
|
||||||
const val backendLogs = 4
|
|
||||||
const val activationError = 5
|
|
||||||
const val permissionRequired = 6
|
|
||||||
const val configImport = 7
|
|
||||||
}
|
|
||||||
}
|
|
69
client/android/src/org/amnezia/vpn/VpnRequestActivity.kt
Normal file
69
client/android/src/org/amnezia/vpn/VpnRequestActivity.kt
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
package org.amnezia.vpn
|
||||||
|
|
||||||
|
import android.app.KeyguardManager
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
|
import android.net.VpnService
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.activity.result.ActivityResult
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.content.getSystemService
|
||||||
|
|
||||||
|
private const val TAG = "VpnRequestActivity"
|
||||||
|
|
||||||
|
class VpnRequestActivity : ComponentActivity() {
|
||||||
|
|
||||||
|
private var userPresentReceiver: BroadcastReceiver? = null
|
||||||
|
private val requestLauncher =
|
||||||
|
registerForActivityResult(StartActivityForResult(), ::checkRequestResult)
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
Log.v(TAG, "Start request activity")
|
||||||
|
val requestIntent = VpnService.prepare(applicationContext)
|
||||||
|
if (requestIntent != null) {
|
||||||
|
if (getSystemService<KeyguardManager>()!!.isKeyguardLocked) {
|
||||||
|
userPresentReceiver = object : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context?, intent: Intent?) =
|
||||||
|
requestLauncher.launch(requestIntent)
|
||||||
|
}
|
||||||
|
registerReceiver(userPresentReceiver, IntentFilter(Intent.ACTION_USER_PRESENT))
|
||||||
|
} else {
|
||||||
|
requestLauncher.launch(requestIntent)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
onPermissionGranted()
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
userPresentReceiver?.let {
|
||||||
|
unregisterReceiver(it)
|
||||||
|
}
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkRequestResult(result: ActivityResult) {
|
||||||
|
when (result.resultCode) {
|
||||||
|
RESULT_OK -> onPermissionGranted()
|
||||||
|
else -> Toast.makeText(this, "Vpn permission denied", Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onPermissionGranted() {
|
||||||
|
Toast.makeText(this, "Vpn permission granted", Toast.LENGTH_LONG).show()
|
||||||
|
Intent(applicationContext, AmneziaVpnService::class.java).apply {
|
||||||
|
putExtra(AFTER_PERMISSION_CHECK, true)
|
||||||
|
}.also {
|
||||||
|
ContextCompat.startForegroundService(this, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue