Merge branch 'master' into r

This commit is contained in:
Mygod
2020-05-29 20:54:26 -04:00
7 changed files with 52 additions and 61 deletions

View File

@@ -12,12 +12,12 @@ buildscript {
} }
dependencies { dependencies {
classpath(kotlin("gradle-plugin", kotlinVersion))
classpath("com.android.tools.build:gradle:4.0.0") classpath("com.android.tools.build:gradle:4.0.0")
classpath("com.github.ben-manes:gradle-versions-plugin:0.28.0") classpath("com.github.ben-manes:gradle-versions-plugin:0.28.0")
classpath("com.google.firebase:firebase-crashlytics-gradle:2.1.1") classpath("com.google.firebase:firebase-crashlytics-gradle:2.1.1")
classpath("com.google.android.gms:oss-licenses-plugin:0.10.2") classpath("com.google.android.gms:oss-licenses-plugin:0.10.2")
classpath("com.google.gms:google-services:4.3.3") classpath("com.google.gms:google-services:4.3.3")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
} }
} }

View File

@@ -1,32 +0,0 @@
package be.mygod.vpnhotspot.manage
import android.content.IntentFilter
import androidx.annotation.RequiresApi
import be.mygod.vpnhotspot.net.TetheringManager
import be.mygod.vpnhotspot.net.TetheringManager.tetheredIfaces
import be.mygod.vpnhotspot.util.KillableTileService
import be.mygod.vpnhotspot.util.broadcastReceiver
@RequiresApi(24)
abstract class TetherListeningTileService : KillableTileService() {
protected var tethered: List<String>? = null
private val receiver = broadcastReceiver { _, intent ->
tethered = intent.tetheredIfaces ?: return@broadcastReceiver
updateTile()
}
override fun onStartListening() {
super.onStartListening()
tethered = registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED))
?.tetheredIfaces
updateTile()
}
override fun onStopListening() {
unregisterReceiver(receiver)
super.onStopListening()
}
protected abstract fun updateTile()
}

View File

@@ -3,6 +3,7 @@ package be.mygod.vpnhotspot.manage
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter
import android.graphics.drawable.Icon import android.graphics.drawable.Icon
import android.os.IBinder import android.os.IBinder
import android.service.quicksettings.Tile import android.service.quicksettings.Tile
@@ -13,7 +14,10 @@ import be.mygod.vpnhotspot.R
import be.mygod.vpnhotspot.TetheringService import be.mygod.vpnhotspot.TetheringService
import be.mygod.vpnhotspot.net.TetherType import be.mygod.vpnhotspot.net.TetherType
import be.mygod.vpnhotspot.net.TetheringManager import be.mygod.vpnhotspot.net.TetheringManager
import be.mygod.vpnhotspot.net.TetheringManager.tetheredIfaces
import be.mygod.vpnhotspot.net.wifi.WifiApManager import be.mygod.vpnhotspot.net.wifi.WifiApManager
import be.mygod.vpnhotspot.util.KillableTileService
import be.mygod.vpnhotspot.util.broadcastReceiver
import be.mygod.vpnhotspot.util.readableMessage import be.mygod.vpnhotspot.util.readableMessage
import be.mygod.vpnhotspot.util.stopAndUnbind import be.mygod.vpnhotspot.util.stopAndUnbind
import timber.log.Timber import timber.log.Timber
@@ -21,27 +25,37 @@ import java.io.IOException
import java.lang.reflect.InvocationTargetException import java.lang.reflect.InvocationTargetException
@RequiresApi(24) @RequiresApi(24)
sealed class TetheringTileService : TetherListeningTileService(), TetheringManager.StartTetheringCallback { sealed class TetheringTileService : KillableTileService(), TetheringManager.StartTetheringCallback {
protected val tileOff by lazy { Icon.createWithResource(application, icon) } protected val tileOff by lazy { Icon.createWithResource(application, icon) }
protected val tileOn by lazy { Icon.createWithResource(application, R.drawable.ic_quick_settings_tile_on) } protected val tileOn by lazy { Icon.createWithResource(application, R.drawable.ic_quick_settings_tile_on) }
protected abstract val labelString: Int protected abstract val labelString: Int
protected abstract val tetherType: TetherType protected abstract val tetherType: TetherType
protected open val icon get() = tetherType.icon protected open val icon get() = tetherType.icon
protected var tethered: List<String>? = null
protected val interested get() = tethered?.filter { TetherType.ofInterface(it) == tetherType } protected val interested get() = tethered?.filter { TetherType.ofInterface(it) == tetherType }
protected var binder: TetheringService.Binder? = null protected var binder: TetheringService.Binder? = null
private val receiver = broadcastReceiver { _, intent ->
tethered = intent.tetheredIfaces ?: return@broadcastReceiver
updateTile()
}
protected abstract fun start() protected abstract fun start()
protected abstract fun stop() protected abstract fun stop()
override fun onStartListening() { override fun onStartListening() {
bindService(Intent(this, TetheringService::class.java), this, Context.BIND_AUTO_CREATE)
super.onStartListening() super.onStartListening()
bindService(Intent(this, TetheringService::class.java), this, Context.BIND_AUTO_CREATE)
tethered = registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED))
?.tetheredIfaces
updateTile()
} }
override fun onStopListening() { override fun onStopListening() {
super.onStopListening() unregisterReceiver(receiver)
stopAndUnbind(this) stopAndUnbind(this)
super.onStopListening()
} }
override fun onServiceConnected(name: ComponentName?, service: IBinder?) { override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
@@ -55,7 +69,7 @@ sealed class TetheringTileService : TetherListeningTileService(), TetheringManag
binder = null binder = null
} }
override fun updateTile() { protected open fun updateTile() {
qsTile?.run { qsTile?.run {
val interested = interested val interested = interested
when { when {

View File

@@ -1,7 +1,6 @@
package be.mygod.vpnhotspot.net.monitor package be.mygod.vpnhotspot.net.monitor
import android.util.LongSparseArray import android.util.LongSparseArray
import androidx.core.os.postDelayed
import be.mygod.vpnhotspot.net.Routing.Companion.IPTABLES import be.mygod.vpnhotspot.net.Routing.Companion.IPTABLES
import be.mygod.vpnhotspot.room.AppDatabase import be.mygod.vpnhotspot.room.AppDatabase
import be.mygod.vpnhotspot.room.TrafficRecord import be.mygod.vpnhotspot.room.TrafficRecord
@@ -9,6 +8,7 @@ import be.mygod.vpnhotspot.util.Event2
import be.mygod.vpnhotspot.util.RootSession import be.mygod.vpnhotspot.util.RootSession
import be.mygod.vpnhotspot.util.parseNumericAddress import be.mygod.vpnhotspot.util.parseNumericAddress
import be.mygod.vpnhotspot.widget.SmartSnackbar import be.mygod.vpnhotspot.widget.SmartSnackbar
import kotlinx.coroutines.*
import timber.log.Timber import timber.log.Timber
import java.net.InetAddress import java.net.InetAddress
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@@ -16,7 +16,6 @@ import java.util.concurrent.TimeUnit
object TrafficRecorder { object TrafficRecorder {
private const val ANYWHERE = "0.0.0.0/0" private const val ANYWHERE = "0.0.0.0/0"
private var scheduled = false
private var lastUpdate = 0L private var lastUpdate = 0L
private val records = mutableMapOf<Pair<InetAddress, String>, TrafficRecord>() private val records = mutableMapOf<Pair<InetAddress, String>, TrafficRecord>()
val foregroundListeners = Event2<Collection<TrafficRecord>, LongSparseArray<TrafficRecord>>() val foregroundListeners = Event2<Collection<TrafficRecord>, LongSparseArray<TrafficRecord>>()
@@ -36,18 +35,21 @@ object TrafficRecorder {
if (records.remove(Pair(ip, downstream)) == null) Timber.w("Failed to find traffic record for $ip%$downstream.") if (records.remove(Pair(ip, downstream)) == null) Timber.w("Failed to find traffic record for $ip%$downstream.")
} }
private var updateJob: Job? = null
private fun unscheduleUpdateLocked() { private fun unscheduleUpdateLocked() {
RootSession.handler.removeCallbacksAndMessages(this) updateJob?.cancel()
scheduled = false updateJob = null
} }
private fun scheduleUpdateLocked() { private fun scheduleUpdateLocked() {
if (scheduled) return if (updateJob != null) return
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
val minute = TimeUnit.MINUTES.toMillis(1) val minute = TimeUnit.MINUTES.toMillis(1)
var timeout = minute - now % minute var timeout = minute - now % minute
if (foregroundListeners.isNotEmpty() && timeout > 1000) timeout = 1000 if (foregroundListeners.isNotEmpty() && timeout > 1000) timeout = 1000
RootSession.handler.postDelayed(timeout, this) { update(true) } updateJob = GlobalScope.launch(start = CoroutineStart.UNDISPATCHED) {
scheduled = true delay(timeout)
update(true)
}
} }
fun rescheduleUpdate() = synchronized(this) { fun rescheduleUpdate() = synchronized(this) {
@@ -122,7 +124,6 @@ object TrafficRecorder {
} }
fun update(timeout: Boolean = false) { fun update(timeout: Boolean = false) {
synchronized(this) { synchronized(this) {
if (timeout) scheduled = false
if (records.isEmpty()) return if (records.isEmpty()) return
val timestamp = System.currentTimeMillis() val timestamp = System.currentTimeMillis()
if (!timeout && timestamp - lastUpdate <= 100) return if (!timeout && timestamp - lastUpdate <= 100) return
@@ -133,6 +134,7 @@ object TrafficRecorder {
SmartSnackbar.make(e).show() SmartSnackbar.make(e).show()
} }
lastUpdate = timestamp lastUpdate = timestamp
updateJob = null
scheduleUpdateLocked() scheduleUpdateLocked()
} }
} }

View File

@@ -51,7 +51,7 @@ object VpnMonitor : UpstreamMonitor() {
when (ifname) { when (ifname) {
null -> Timber.w("interfaceName became null: $oldProperties -> $properties") null -> Timber.w("interfaceName became null: $oldProperties -> $properties")
oldProperties.interfaceName -> losing = false oldProperties.interfaceName -> losing = false
else -> Timber.w(RuntimeException("interfaceName changed: $oldProperties -> $properties")) else -> Timber.w("interfaceName changed: $oldProperties -> $properties")
} }
callbacks.toList() callbacks.toList()
}.forEach { }.forEach {

View File

@@ -93,6 +93,7 @@ object WifiP2pManagerHelper {
arrayOf(interfacePersistentGroupInfoListener)) { proxy, method, args -> arrayOf(interfacePersistentGroupInfoListener)) { proxy, method, args ->
if (method.name == "onPersistentGroupInfoAvailable") { if (method.name == "onPersistentGroupInfoAvailable") {
if (args.size != 1) Timber.w(IllegalArgumentException("Unexpected args: $args")) if (args.size != 1) Timber.w(IllegalArgumentException("Unexpected args: $args"))
@Suppress("UNCHECKED_CAST")
listener(getGroupList.invoke(args[0]) as Collection<WifiP2pGroup>) listener(getGroupList.invoke(args[0]) as Collection<WifiP2pGroup>)
null null
} else { } else {

View File

@@ -1,11 +1,9 @@
package be.mygod.vpnhotspot.util package be.mygod.vpnhotspot.util
import android.os.Handler
import android.os.HandlerThread
import android.os.Looper import android.os.Looper
import androidx.annotation.WorkerThread import androidx.annotation.WorkerThread
import androidx.core.os.postDelayed
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.*
import timber.log.Timber import timber.log.Timber
import java.util.* import java.util.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@@ -15,13 +13,9 @@ import kotlin.concurrent.withLock
class RootSession : AutoCloseable { class RootSession : AutoCloseable {
companion object { companion object {
private const val TAG = "RootSession"
val handler = Handler(HandlerThread("$TAG-HandlerThread").apply { start() }.looper)
private val monitor = ReentrantLock() private val monitor = ReentrantLock()
private fun onUnlock() { private fun onUnlock() {
if (monitor.holdCount == 1) instance?.startTimeout() if (monitor.holdCount == 1) instance?.startTimeoutLocked()
} }
private fun unlock() { private fun unlock() {
onUnlock() onUnlock()
@@ -36,7 +30,7 @@ class RootSession : AutoCloseable {
} }
fun <T> use(operation: (RootSession) -> T) = monitor.withLock { fun <T> use(operation: (RootSession) -> T) = monitor.withLock {
val instance = ensureInstance() val instance = ensureInstance()
instance.haltTimeout() instance.haltTimeoutLocked()
operation(instance).also { onUnlock() } operation(instance).also { onUnlock() }
} }
fun beginTransaction(): Transaction { fun beginTransaction(): Transaction {
@@ -47,14 +41,14 @@ class RootSession : AutoCloseable {
unlock() unlock()
throw e throw e
} }
instance.haltTimeout() instance.haltTimeoutLocked()
return instance.Transaction() return instance.Transaction()
} }
@WorkerThread @WorkerThread
fun trimMemory() = monitor.withLock { fun trimMemory() = monitor.withLock {
val instance = instance ?: return val instance = instance ?: return
instance.haltTimeout() instance.haltTimeoutLocked()
instance.close() instance.close()
} }
@@ -85,10 +79,22 @@ class RootSession : AutoCloseable {
shell.close() shell.close()
if (instance == this) instance = null if (instance == this) instance = null
} }
private fun startTimeout() = handler.postDelayed(TimeUnit.MINUTES.toMillis(5), this) {
monitor.withLock { close() } private var timeoutJob: Job? = null
private fun startTimeoutLocked() {
check(timeoutJob == null)
timeoutJob = GlobalScope.launch(start = CoroutineStart.UNDISPATCHED) {
delay(TimeUnit.MINUTES.toMillis(5))
monitor.withLock {
close()
timeoutJob = null
}
}
}
private fun haltTimeoutLocked() {
timeoutJob?.cancel()
timeoutJob = null
} }
private fun haltTimeout() = handler.removeCallbacksAndMessages(this)
/** /**
* Don't care about the results, but still sync. * Don't care about the results, but still sync.
@@ -141,7 +147,7 @@ class RootSession : AutoCloseable {
locked = true locked = true
ensureInstance() ensureInstance()
} }
shell.haltTimeout() shell.haltTimeoutLocked()
revertCommands.forEach { shell.submit(it) } revertCommands.forEach { shell.submit(it) }
} catch (e: RuntimeException) { // if revert fails, it should fail silently } catch (e: RuntimeException) { // if revert fails, it should fail silently
Timber.d(e) Timber.d(e)