@@ -235,6 +235,7 @@
|
|||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
||||||
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
|
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
|
||||||
|
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ class App : Application() {
|
|||||||
// alternative to PreferenceManager.getDefaultSharedPreferencesName(this)
|
// alternative to PreferenceManager.getDefaultSharedPreferencesName(this)
|
||||||
deviceStorage.moveSharedPreferencesFrom(this, PreferenceManager(this).sharedPreferencesName)
|
deviceStorage.moveSharedPreferencesFrom(this, PreferenceManager(this).sharedPreferencesName)
|
||||||
deviceStorage.moveDatabaseFrom(this, AppDatabase.DB_NAME)
|
deviceStorage.moveDatabaseFrom(this, AppDatabase.DB_NAME)
|
||||||
|
BootReceiver.migrateIfNecessary(this, deviceStorage)
|
||||||
} else deviceStorage = this
|
} else deviceStorage = this
|
||||||
Services.init { this }
|
Services.init { this }
|
||||||
|
|
||||||
|
|||||||
@@ -5,31 +5,112 @@ import android.content.ComponentName
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import androidx.core.content.ContextCompat
|
import android.os.Parcelable
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import be.mygod.librootkotlinx.toByteArray
|
||||||
|
import be.mygod.librootkotlinx.toParcelable
|
||||||
import be.mygod.vpnhotspot.App.Companion.app
|
import be.mygod.vpnhotspot.App.Companion.app
|
||||||
import be.mygod.vpnhotspot.util.Services
|
import kotlinx.parcelize.Parcelize
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.io.DataInputStream
|
||||||
|
import java.io.DataOutputStream
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileNotFoundException
|
||||||
|
|
||||||
class BootReceiver : BroadcastReceiver() {
|
class BootReceiver : BroadcastReceiver() {
|
||||||
companion object {
|
companion object {
|
||||||
|
const val KEY = "service.autoStart"
|
||||||
|
|
||||||
private val componentName by lazy { ComponentName(app, BootReceiver::class.java) }
|
private val componentName by lazy { ComponentName(app, BootReceiver::class.java) }
|
||||||
var enabled: Boolean
|
private var enabled: Boolean
|
||||||
get() = app.packageManager.getComponentEnabledSetting(componentName) ==
|
get() = app.packageManager.getComponentEnabledSetting(componentName) ==
|
||||||
PackageManager.COMPONENT_ENABLED_STATE_ENABLED
|
PackageManager.COMPONENT_ENABLED_STATE_ENABLED
|
||||||
set(value) = app.packageManager.setComponentEnabledSetting(componentName,
|
set(value) = app.packageManager.setComponentEnabledSetting(componentName,
|
||||||
if (value) PackageManager.COMPONENT_ENABLED_STATE_ENABLED
|
if (value) PackageManager.COMPONENT_ENABLED_STATE_ENABLED
|
||||||
else PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP)
|
else PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP)
|
||||||
|
fun onUserSettingUpdated(shouldStart: Boolean) {
|
||||||
|
enabled = shouldStart && try {
|
||||||
|
config
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.w(e)
|
||||||
|
null
|
||||||
|
}?.startables?.isEmpty() == false
|
||||||
|
}
|
||||||
|
private fun onConfigUpdated(isNotEmpty: Boolean) {
|
||||||
|
enabled = isNotEmpty && app.pref.getBoolean(KEY, false)
|
||||||
|
}
|
||||||
|
|
||||||
private var started = false
|
private var started = false
|
||||||
|
|
||||||
|
private const val FILENAME = "bootconfig"
|
||||||
|
private val configFile by lazy { File(app.deviceStorage.noBackupFilesDir, FILENAME) }
|
||||||
|
private var config: Config?
|
||||||
|
get() = try {
|
||||||
|
DataInputStream(configFile.inputStream()).use { it.readBytes().toParcelable() }
|
||||||
|
} catch (_: FileNotFoundException) {
|
||||||
|
null
|
||||||
}
|
}
|
||||||
|
set(value) = DataOutputStream(configFile.outputStream()).use { it.write(value.toByteArray()) }
|
||||||
|
|
||||||
|
fun add(key: String, value: Startable) = try {
|
||||||
|
synchronized(BootReceiver) {
|
||||||
|
val c = config ?: Config()
|
||||||
|
c.startables[key] = value
|
||||||
|
config = c
|
||||||
|
}
|
||||||
|
onConfigUpdated(true)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.w(e)
|
||||||
|
}
|
||||||
|
fun delete(key: String) = try {
|
||||||
|
onConfigUpdated(synchronized(BootReceiver) {
|
||||||
|
val c = config ?: Config()
|
||||||
|
c.startables.remove(key)
|
||||||
|
config = c
|
||||||
|
c
|
||||||
|
}.startables.isNotEmpty())
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.w(e)
|
||||||
|
}
|
||||||
|
inline fun <reified T> add(value: Startable) = add(T::class.java.name, value)
|
||||||
|
inline fun <reified T> delete() = delete(T::class.java.name)
|
||||||
|
|
||||||
|
@RequiresApi(24)
|
||||||
|
fun migrateIfNecessary(old: Context, new: Context) {
|
||||||
|
val oldFile = File(old.noBackupFilesDir, FILENAME)
|
||||||
|
if (oldFile.canRead()) try {
|
||||||
|
val newFile = File(new.noBackupFilesDir, FILENAME)
|
||||||
|
if (!newFile.exists()) oldFile.copyTo(newFile)
|
||||||
|
if (!oldFile.delete()) oldFile.deleteOnExit()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.w(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Startable : Parcelable {
|
||||||
|
fun start(context: Context)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
private data class Config(var startables: MutableMap<String, Startable> = mutableMapOf()) : Parcelable
|
||||||
|
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
if (started) return
|
if (started) return
|
||||||
when (intent.action) {
|
val isUpdate = when (intent.action) {
|
||||||
Intent.ACTION_BOOT_COMPLETED, Intent.ACTION_LOCKED_BOOT_COMPLETED -> started = true
|
Intent.ACTION_BOOT_COMPLETED, Intent.ACTION_LOCKED_BOOT_COMPLETED -> false
|
||||||
|
Intent.ACTION_MY_PACKAGE_REPLACED -> true
|
||||||
else -> return
|
else -> return
|
||||||
}
|
}
|
||||||
if (Services.p2p != null) {
|
started = true
|
||||||
ContextCompat.startForegroundService(context, Intent(context, RepeaterService::class.java))
|
val config = try {
|
||||||
|
synchronized(BootReceiver) { config }
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.w(e)
|
||||||
|
if (isUpdate) null else return
|
||||||
}
|
}
|
||||||
|
if (config == null || config.startables.isEmpty()) {
|
||||||
|
enabled = false
|
||||||
|
} else for (startable in config.startables.values) startable.start(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package be.mygod.vpnhotspot
|
package be.mygod.vpnhotspot
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.IntentFilter
|
import android.content.IntentFilter
|
||||||
import android.net.wifi.WifiManager
|
import android.net.wifi.WifiManager
|
||||||
@@ -15,6 +16,7 @@ import be.mygod.vpnhotspot.util.Services
|
|||||||
import be.mygod.vpnhotspot.util.StickyEvent1
|
import be.mygod.vpnhotspot.util.StickyEvent1
|
||||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.net.Inet4Address
|
import java.net.Inet4Address
|
||||||
|
|
||||||
@@ -45,6 +47,13 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService(), CoroutineScope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
class Starter : BootReceiver.Startable {
|
||||||
|
override fun start(context: Context) {
|
||||||
|
context.startForegroundService(Intent(context, LocalOnlyHotspotService::class.java))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val binder = Binder()
|
private val binder = Binder()
|
||||||
private var reservation: WifiManager.LocalOnlyHotspotReservation? = null
|
private var reservation: WifiManager.LocalOnlyHotspotReservation? = null
|
||||||
/**
|
/**
|
||||||
@@ -86,6 +95,7 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService(), CoroutineScope {
|
|||||||
return stopService()
|
return stopService()
|
||||||
}
|
}
|
||||||
binder.iface = iface
|
binder.iface = iface
|
||||||
|
BootReceiver.add<LocalOnlyHotspotService>(Starter())
|
||||||
launch {
|
launch {
|
||||||
check(routingManager == null)
|
check(routingManager == null)
|
||||||
routingManager = RoutingManager.LocalOnly(this@LocalOnlyHotspotService, iface).apply { start() }
|
routingManager = RoutingManager.LocalOnly(this@LocalOnlyHotspotService, iface).apply { start() }
|
||||||
@@ -140,6 +150,7 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService(), CoroutineScope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun stopService() {
|
private fun stopService() {
|
||||||
|
BootReceiver.delete<LocalOnlyHotspotService>()
|
||||||
binder.iface = null
|
binder.iface = null
|
||||||
unregisterReceiver()
|
unregisterReceiver()
|
||||||
ServiceNotification.stopForeground(this)
|
ServiceNotification.stopForeground(this)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package be.mygod.vpnhotspot
|
|||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.annotation.TargetApi
|
import android.annotation.TargetApi
|
||||||
import android.app.Service
|
import android.app.Service
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
@@ -14,6 +15,7 @@ import android.os.Looper
|
|||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import be.mygod.vpnhotspot.App.Companion.app
|
import be.mygod.vpnhotspot.App.Companion.app
|
||||||
import be.mygod.vpnhotspot.net.MacAddressCompat
|
import be.mygod.vpnhotspot.net.MacAddressCompat
|
||||||
@@ -33,6 +35,7 @@ import be.mygod.vpnhotspot.root.RootManager
|
|||||||
import be.mygod.vpnhotspot.util.*
|
import be.mygod.vpnhotspot.util.*
|
||||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.lang.reflect.InvocationTargetException
|
import java.lang.reflect.InvocationTargetException
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
@@ -203,6 +206,13 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
class Starter : BootReceiver.Startable {
|
||||||
|
override fun start(context: Context) {
|
||||||
|
ContextCompat.startForegroundService(context, Intent(context, RepeaterService::class.java))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val p2pManager get() = Services.p2p!!
|
private val p2pManager get() = Services.p2p!!
|
||||||
private var channel: WifiP2pManager.Channel? = null
|
private var channel: WifiP2pManager.Channel? = null
|
||||||
private val binder = Binder()
|
private val binder = Binder()
|
||||||
@@ -463,6 +473,7 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene
|
|||||||
routingManager = RoutingManager.LocalOnly(this@RepeaterService, group.`interface`!!).apply { start() }
|
routingManager = RoutingManager.LocalOnly(this@RepeaterService, group.`interface`!!).apply { start() }
|
||||||
status = Status.ACTIVE
|
status = Status.ACTIVE
|
||||||
showNotification(group)
|
showNotification(group)
|
||||||
|
BootReceiver.add<RepeaterService>(Starter())
|
||||||
}
|
}
|
||||||
private fun startFailure(msg: CharSequence, group: WifiP2pGroup? = null, showWifiEnable: Boolean = false) {
|
private fun startFailure(msg: CharSequence, group: WifiP2pGroup? = null, showWifiEnable: Boolean = false) {
|
||||||
SmartSnackbar.make(msg).apply {
|
SmartSnackbar.make(msg).apply {
|
||||||
@@ -493,6 +504,7 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
private fun cleanLocked() {
|
private fun cleanLocked() {
|
||||||
|
BootReceiver.delete<RepeaterService>()
|
||||||
if (receiverRegistered) {
|
if (receiverRegistered) {
|
||||||
ensureReceiverUnregistered(receiver)
|
ensureReceiverUnregistered(receiver)
|
||||||
p2pPoller?.cancel()
|
p2pPoller?.cancel()
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ abstract class RoutingManager(private val caller: Any, val downstream: String, p
|
|||||||
private var routing: Routing? = null
|
private var routing: Routing? = null
|
||||||
private var isWifi = forceWifi || TetherType.ofInterface(downstream).isWifi
|
private var isWifi = forceWifi || TetherType.ofInterface(downstream).isWifi
|
||||||
|
|
||||||
fun start() = synchronized(RoutingManager) {
|
fun start(fromMonitor: Boolean = false) = synchronized(RoutingManager) {
|
||||||
started = true
|
started = true
|
||||||
when (val other = active.putIfAbsent(downstream, this)) {
|
when (val other = active.putIfAbsent(downstream, this)) {
|
||||||
null -> {
|
null -> {
|
||||||
@@ -78,14 +78,14 @@ abstract class RoutingManager(private val caller: Any, val downstream: String, p
|
|||||||
isWifi = isWifiNow
|
isWifi = isWifiNow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
initRoutingLocked()
|
initRoutingLocked(fromMonitor)
|
||||||
}
|
}
|
||||||
this -> true // already started
|
this -> true // already started
|
||||||
else -> error("Double routing detected for $downstream from $caller != ${other.caller}")
|
else -> error("Double routing detected for $downstream from $caller != ${other.caller}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initRoutingLocked() = try {
|
private fun initRoutingLocked(fromMonitor: Boolean = false) = try {
|
||||||
routing = Routing(caller, downstream).apply {
|
routing = Routing(caller, downstream).apply {
|
||||||
try {
|
try {
|
||||||
configure()
|
configure()
|
||||||
@@ -97,10 +97,10 @@ abstract class RoutingManager(private val caller: Any, val downstream: String, p
|
|||||||
true
|
true
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
when (e) {
|
when (e) {
|
||||||
is Routing.InterfaceNotFoundException -> Timber.d(e)
|
is Routing.InterfaceNotFoundException -> if (!fromMonitor) Timber.d(e)
|
||||||
!is CancellationException -> Timber.w(e)
|
!is CancellationException -> Timber.w(e)
|
||||||
}
|
}
|
||||||
SmartSnackbar.make(e).show()
|
if (e !is Routing.InterfaceNotFoundException || !fromMonitor) SmartSnackbar.make(e).show()
|
||||||
routing = null
|
routing = null
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,14 +66,10 @@ class SettingsPreferenceFragment : PreferenceFragmentCompat() {
|
|||||||
}
|
}
|
||||||
} else parent!!.removePreference(this)
|
} else parent!!.removePreference(this)
|
||||||
}
|
}
|
||||||
val boot = findPreference<SwitchPreference>("service.repeater.startOnBoot")!!
|
findPreference<SwitchPreference>(BootReceiver.KEY)!!.setOnPreferenceChangeListener { _, value ->
|
||||||
if (Services.p2p != null) {
|
BootReceiver.onUserSettingUpdated(value as Boolean)
|
||||||
boot.setOnPreferenceChangeListener { _, value ->
|
|
||||||
BootReceiver.enabled = value as Boolean
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
boot.isChecked = BootReceiver.enabled
|
|
||||||
} else boot.parent!!.removePreference(boot)
|
|
||||||
if (Services.p2p == null || !RepeaterService.safeModeConfigurable) {
|
if (Services.p2p == null || !RepeaterService.safeModeConfigurable) {
|
||||||
val safeMode = findPreference<Preference>(RepeaterService.KEY_SAFE_MODE)!!
|
val safeMode = findPreference<Preference>(RepeaterService.KEY_SAFE_MODE)!!
|
||||||
safeMode.parent!!.removePreference(safeMode)
|
safeMode.parent!!.removePreference(safeMode)
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package be.mygod.vpnhotspot
|
package be.mygod.vpnhotspot
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import be.mygod.vpnhotspot.App.Companion.app
|
import be.mygod.vpnhotspot.App.Companion.app
|
||||||
import be.mygod.vpnhotspot.net.Routing
|
import be.mygod.vpnhotspot.net.Routing
|
||||||
import be.mygod.vpnhotspot.net.TetheringManager
|
import be.mygod.vpnhotspot.net.TetheringManager
|
||||||
@@ -10,6 +12,7 @@ import be.mygod.vpnhotspot.net.monitor.IpNeighbourMonitor
|
|||||||
import be.mygod.vpnhotspot.util.Event0
|
import be.mygod.vpnhotspot.util.Event0
|
||||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
@@ -17,6 +20,7 @@ class TetheringService : IpNeighbourMonitoringService(), TetheringManager.Tether
|
|||||||
companion object {
|
companion object {
|
||||||
const val EXTRA_ADD_INTERFACES = "interface.add"
|
const val EXTRA_ADD_INTERFACES = "interface.add"
|
||||||
const val EXTRA_ADD_INTERFACE_MONITOR = "interface.add.monitor"
|
const val EXTRA_ADD_INTERFACE_MONITOR = "interface.add.monitor"
|
||||||
|
const val EXTRA_ADD_INTERFACES_MONITOR = "interface.adds.monitor"
|
||||||
const val EXTRA_REMOVE_INTERFACE = "interface.remove"
|
const val EXTRA_REMOVE_INTERFACE = "interface.remove"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,6 +43,15 @@ class TetheringService : IpNeighbourMonitoringService(), TetheringManager.Tether
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
data class Starter(val monitored: ArrayList<String>) : BootReceiver.Startable {
|
||||||
|
override fun start(context: Context) {
|
||||||
|
ContextCompat.startForegroundService(context, Intent(context, TetheringService::class.java).apply {
|
||||||
|
putStringArrayListExtra(EXTRA_ADD_INTERFACES_MONITOR, monitored)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes and critical reads to downstreams should be protected with this context.
|
* Writes and critical reads to downstreams should be protected with this context.
|
||||||
*/
|
*/
|
||||||
@@ -55,7 +68,7 @@ class TetheringService : IpNeighbourMonitoringService(), TetheringManager.Tether
|
|||||||
val toRemove = downstreams.toMutableMap() // make a copy
|
val toRemove = downstreams.toMutableMap() // make a copy
|
||||||
for (iface in interfaces) {
|
for (iface in interfaces) {
|
||||||
val downstream = toRemove.remove(iface) ?: continue
|
val downstream = toRemove.remove(iface) ?: continue
|
||||||
if (downstream.monitor) downstream.start()
|
if (downstream.monitor && !downstream.start()) downstream.stop()
|
||||||
}
|
}
|
||||||
for ((iface, downstream) in toRemove) {
|
for ((iface, downstream) in toRemove) {
|
||||||
if (!downstream.monitor) check(downstreams.remove(iface, downstream))
|
if (!downstream.monitor) check(downstreams.remove(iface, downstream))
|
||||||
@@ -81,6 +94,10 @@ class TetheringService : IpNeighbourMonitoringService(), TetheringManager.Tether
|
|||||||
ServiceNotification.stopForeground(this)
|
ServiceNotification.stopForeground(this)
|
||||||
stopSelf()
|
stopSelf()
|
||||||
} else {
|
} else {
|
||||||
|
binder.monitoredIfaces.also {
|
||||||
|
if (it.isEmpty()) BootReceiver.delete<TetheringService>()
|
||||||
|
else BootReceiver.add<TetheringService>(Starter(ArrayList(it)))
|
||||||
|
}
|
||||||
if (!callbackRegistered) {
|
if (!callbackRegistered) {
|
||||||
callbackRegistered = true
|
callbackRegistered = true
|
||||||
TetheringManager.registerTetheringEventCallbackCompat(this, this)
|
TetheringManager.registerTetheringEventCallbackCompat(this, this)
|
||||||
@@ -103,10 +120,12 @@ class TetheringService : IpNeighbourMonitoringService(), TetheringManager.Tether
|
|||||||
if (start()) check(downstreams.put(iface, this) == null) else stop()
|
if (start()) check(downstreams.put(iface, this) == null) else stop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
intent.getStringExtra(EXTRA_ADD_INTERFACE_MONITOR)?.also { iface ->
|
val monitorList = intent.getStringArrayListExtra(EXTRA_ADD_INTERFACES_MONITOR) ?:
|
||||||
|
intent.getStringExtra(EXTRA_ADD_INTERFACE_MONITOR)?.let { listOf(it) }
|
||||||
|
if (!monitorList.isNullOrEmpty()) for (iface in monitorList) {
|
||||||
val downstream = downstreams[iface]
|
val downstream = downstreams[iface]
|
||||||
if (downstream == null) Downstream(this@TetheringService, iface, true).apply {
|
if (downstream == null) Downstream(this@TetheringService, iface, true).apply {
|
||||||
start()
|
if (!start(true)) stop()
|
||||||
check(downstreams.put(iface, this) == null)
|
check(downstreams.put(iface, this) == null)
|
||||||
downstreams[iface] = this
|
downstreams[iface] = this
|
||||||
} else downstream.monitor = true
|
} else downstream.monitor = true
|
||||||
@@ -120,6 +139,7 @@ class TetheringService : IpNeighbourMonitoringService(), TetheringManager.Tether
|
|||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
launch {
|
launch {
|
||||||
|
BootReceiver.delete<TetheringService>()
|
||||||
unregisterReceiver()
|
unregisterReceiver()
|
||||||
downstreams.values.forEach { it.stop() } // force clean to prevent leakage
|
downstreams.values.forEach { it.stop() } // force clean to prevent leakage
|
||||||
cancel()
|
cancel()
|
||||||
|
|||||||
@@ -91,7 +91,6 @@
|
|||||||
<string name="settings_service_masquerade_netd">Servizio Android Netd</string>
|
<string name="settings_service_masquerade_netd">Servizio Android Netd</string>
|
||||||
<string name="settings_service_disable_ipv6">Disabilita tethering IPv6</string>
|
<string name="settings_service_disable_ipv6">Disabilita tethering IPv6</string>
|
||||||
<string name="settings_service_disable_ipv6_summary">Abilitando questa funzione si preveniranno perdite della VPN via IPv6.</string>
|
<string name="settings_service_disable_ipv6_summary">Abilitando questa funzione si preveniranno perdite della VPN via IPv6.</string>
|
||||||
<string name="settings_service_repeater_start_on_boot">Avvia ripetitore all\'avvio</string>
|
|
||||||
<string name="settings_service_wifi_lock">Tieni il Wi\u2011Fi attivo</string>
|
<string name="settings_service_wifi_lock">Tieni il Wi\u2011Fi attivo</string>
|
||||||
<string name="settings_service_wifi_lock_none">Default di sistema</string>
|
<string name="settings_service_wifi_lock_none">Default di sistema</string>
|
||||||
<string name="settings_service_wifi_lock_full">Attivo</string>
|
<string name="settings_service_wifi_lock_full">Attivo</string>
|
||||||
|
|||||||
@@ -111,7 +111,8 @@
|
|||||||
<string name="settings_service_masquerade_netd">Android Netd 服务</string>
|
<string name="settings_service_masquerade_netd">Android Netd 服务</string>
|
||||||
<string name="settings_service_disable_ipv6">禁用 IPv6 共享</string>
|
<string name="settings_service_disable_ipv6">禁用 IPv6 共享</string>
|
||||||
<string name="settings_service_disable_ipv6_summary">防止 VPN 通过 IPv6 泄漏。</string>
|
<string name="settings_service_disable_ipv6_summary">防止 VPN 通过 IPv6 泄漏。</string>
|
||||||
<string name="settings_service_repeater_start_on_boot">开机自启动中继</string>
|
<string name="settings_service_auto_start">自动启动服务</string>
|
||||||
|
<string name="settings_service_auto_start_summary">设备重启或应用升级后自动恢复之前运行的服务</string>
|
||||||
<string name="settings_service_repeater_safe_mode">中继安全模式</string>
|
<string name="settings_service_repeater_safe_mode">中继安全模式</string>
|
||||||
<string name="settings_service_repeater_safe_mode_summary">不对系统配置进行修改,但是可能须要较长的网络名称。</string>
|
<string name="settings_service_repeater_safe_mode_summary">不对系统配置进行修改,但是可能须要较长的网络名称。</string>
|
||||||
<string name="settings_service_repeater_safe_mode_warning">使用短名称可能需要关闭安全模式。</string>
|
<string name="settings_service_repeater_safe_mode_warning">使用短名称可能需要关闭安全模式。</string>
|
||||||
|
|||||||
@@ -109,7 +109,6 @@
|
|||||||
<string name="settings_service_masquerade_netd">Android Netd 服務</string>
|
<string name="settings_service_masquerade_netd">Android Netd 服務</string>
|
||||||
<string name="settings_service_disable_ipv6">停用 IPv6 共用</string>
|
<string name="settings_service_disable_ipv6">停用 IPv6 共用</string>
|
||||||
<string name="settings_service_disable_ipv6_summary">防止 VPN 通過 IPv6 洩漏</string>
|
<string name="settings_service_disable_ipv6_summary">防止 VPN 通過 IPv6 洩漏</string>
|
||||||
<string name="settings_service_repeater_start_on_boot">開機時自動啟動中繼器</string>
|
|
||||||
<string name="settings_service_repeater_safe_mode">中繼安全模式</string>
|
<string name="settings_service_repeater_safe_mode">中繼安全模式</string>
|
||||||
<string name="settings_service_repeater_safe_mode_summary">不對系統設定值進行任何修改,但是可能需要較長的 SSID。</string>
|
<string name="settings_service_repeater_safe_mode_summary">不對系統設定值進行任何修改,但是可能需要較長的 SSID。</string>
|
||||||
<string name="settings_service_repeater_safe_mode_warning">使用短 SSID 可能需要關閉安全模式。</string>
|
<string name="settings_service_repeater_safe_mode_warning">使用短 SSID 可能需要關閉安全模式。</string>
|
||||||
|
|||||||
@@ -128,7 +128,9 @@
|
|||||||
<string name="settings_service_masquerade_netd">Android Netd Service</string>
|
<string name="settings_service_masquerade_netd">Android Netd Service</string>
|
||||||
<string name="settings_service_disable_ipv6">Disable IPv6 tethering</string>
|
<string name="settings_service_disable_ipv6">Disable IPv6 tethering</string>
|
||||||
<string name="settings_service_disable_ipv6_summary">Enabling this option will prevent VPN leaks via IPv6.</string>
|
<string name="settings_service_disable_ipv6_summary">Enabling this option will prevent VPN leaks via IPv6.</string>
|
||||||
<string name="settings_service_repeater_start_on_boot">Start repeater on boot</string>
|
<string name="settings_service_auto_start">Auto start services</string>
|
||||||
|
<string name="settings_service_auto_start_summary">Restore services if they were running before device reboot or app
|
||||||
|
update</string>
|
||||||
<string name="settings_service_repeater_safe_mode">Repeater safe mode</string>
|
<string name="settings_service_repeater_safe_mode">Repeater safe mode</string>
|
||||||
<string name="settings_service_repeater_safe_mode_summary">Makes no changes to your system configuration but might
|
<string name="settings_service_repeater_safe_mode_summary">Makes no changes to your system configuration but might
|
||||||
not work with short network names.</string>
|
not work with short network names.</string>
|
||||||
|
|||||||
@@ -59,9 +59,10 @@
|
|||||||
app:title="@string/settings_service_wifi_lock"
|
app:title="@string/settings_service_wifi_lock"
|
||||||
app:useSimpleSummaryProvider="true"/>
|
app:useSimpleSummaryProvider="true"/>
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
app:key="service.repeater.startOnBoot"
|
app:key="service.autoStart"
|
||||||
app:icon="@drawable/ic_action_autorenew"
|
app:icon="@drawable/ic_action_autorenew"
|
||||||
app:title="@string/settings_service_repeater_start_on_boot"/>
|
app:title="@string/settings_service_auto_start"
|
||||||
|
app:summary="@string/settings_service_auto_start_summary"/>
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
app:key="service.repeater.safeMode"
|
app:key="service.repeater.safeMode"
|
||||||
app:icon="@drawable/ic_alert_warning"
|
app:icon="@drawable/ic_alert_warning"
|
||||||
|
|||||||
Reference in New Issue
Block a user