Files
vpnhotspotmod/mobile/src/main/java/be/mygod/vpnhotspot/BootReceiver.kt
2021-10-29 23:13:52 -04:00

121 lines
4.5 KiB
Kotlin

package be.mygod.vpnhotspot
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
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 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() {
companion object {
const val KEY = "service.autoStart"
private val componentName by lazy { ComponentName(app, BootReceiver::class.java) }
private var enabled: Boolean
get() = app.packageManager.getComponentEnabledSetting(componentName) ==
PackageManager.COMPONENT_ENABLED_STATE_ENABLED
set(value) = app.packageManager.setComponentEnabledSetting(componentName,
if (value) PackageManager.COMPONENT_ENABLED_STATE_ENABLED
else PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP)
private val userEnabled get() = app.pref.getBoolean(KEY, false)
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 && userEnabled
}
private const val FILENAME = "bootconfig"
private val configFile by lazy { File(app.deviceStorage.noBackupFilesDir, FILENAME) }
private val config: Config? get() = try {
DataInputStream(configFile.inputStream()).use { it.readBytes().toParcelable() }
} catch (_: FileNotFoundException) {
null
}
private fun updateConfig(work: Config.() -> Unit) = synchronized(BootReceiver) {
val config = try {
config
} catch (e: Exception) {
Timber.w(e)
null
} ?: Config()
config.work()
DataOutputStream(configFile.outputStream()).use { it.write(config.toByteArray()) }
config
}
fun add(key: String, value: Startable) = try {
updateConfig { startables[key] = value }
onConfigUpdated(true)
} catch (e: Exception) {
Timber.w(e)
}
fun delete(key: String) = try {
onConfigUpdated(updateConfig { startables.remove(key) }.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() {
val oldFile = File(app.noBackupFilesDir, FILENAME)
if (oldFile.canRead()) try {
if (!configFile.exists()) oldFile.copyTo(configFile)
if (!oldFile.delete()) oldFile.deleteOnExit()
} catch (e: Exception) {
Timber.w(e)
}
}
private var started = false
private fun startIfNecessary() {
if (started) return
val config = try {
synchronized(BootReceiver) { config }
} catch (e: Exception) {
Timber.w(e)
null
}
if (config == null || config.startables.isEmpty()) {
enabled = false
} else for (startable in config.startables.values) startable.start(app)
started = true
}
fun startIfEnabled() {
if (started && userEnabled) startIfNecessary()
}
}
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) {
when (intent.action) {
Intent.ACTION_BOOT_COMPLETED, Intent.ACTION_LOCKED_BOOT_COMPLETED, Intent.ACTION_MY_PACKAGE_REPLACED -> {
if (userEnabled) startIfNecessary() else enabled = false
}
}
}
}