Fix crashes

This commit is contained in:
Mygod
2020-07-03 10:55:21 +08:00
parent 4b1101f41e
commit 05c4ba5b81
10 changed files with 37 additions and 28 deletions

View File

@@ -193,13 +193,14 @@ class RootServer @JvmOverloads constructor(private val warnLogger: (String) -> U
} }
try { try {
callbackSpin() callbackSpin()
if (active) throw RemoteException("Root process exited unexpectedly")
} catch (e: Throwable) { } catch (e: Throwable) {
process.destroy() process.destroy()
throw e throw e
} finally { } finally {
if (DEBUG) Log.d(TAG, "Waiting for exit") if (DEBUG) Log.d(TAG, "Waiting for exit")
process.waitFor() process.waitFor()
closeInternal(true) withContext(NonCancellable) { closeInternal(true) }
} }
check(process.errorStream.available() == 0) // stderr should not be used check(process.errorStream.available() == 0) // stderr should not be used
} }
@@ -280,7 +281,7 @@ class RootServer @JvmOverloads constructor(private val warnLogger: (String) -> U
private suspend fun closeInternal(fromWorker: Boolean = false) = mutex.withLock { private suspend fun closeInternal(fromWorker: Boolean = false) = mutex.withLock {
if (active) { if (active) {
active = false active = false
if (DEBUG) Log.d(TAG, "Shutting down from client") if (DEBUG) Log.d(TAG, if (fromWorker) "Shutting down from worker" else "Shutting down from client")
try { try {
sendLocked(Shutdown()) sendLocked(Shutdown())
output.close() output.close()

View File

@@ -42,8 +42,9 @@ abstract class RootSession {
} }
private suspend fun closeLocked() { private suspend fun closeLocked() {
val server = server
this.server = null
server?.close() server?.close()
server = null
} }
private fun startTimeoutLocked() { private fun startTimeoutLocked() {
check(timeoutJob == null) check(timeoutJob == null)
@@ -51,8 +52,8 @@ abstract class RootSession {
delay(timeout) delay(timeout)
mutex.withLock { mutex.withLock {
check(usersCount == 0L) check(usersCount == 0L)
closeLocked()
timeoutJob = null timeoutJob = null
closeLocked()
} }
} }
} }
@@ -75,14 +76,14 @@ abstract class RootSession {
when { when {
!server.active -> { !server.active -> {
usersCount = 0 usersCount = 0
closeLocked()
closePending = false closePending = false
closeLocked()
return@withLock return@withLock
} }
--usersCount > 0L -> return@withLock --usersCount > 0L -> return@withLock
closePending -> { closePending -> {
closeLocked()
closePending = false closePending = false
closeLocked()
} }
else -> startTimeoutLocked() else -> startTimeoutLocked()
} }

View File

@@ -39,7 +39,7 @@ interface RootCommandChannel<T : Parcelable?> : Parcelable {
} }
@Parcelize @Parcelize
internal class CancelCommand(val index: Long) : RootCommandOneWay { internal data class CancelCommand(val index: Long) : RootCommandOneWay {
override suspend fun execute() = error("Internal implementation") override suspend fun execute() = error("Internal implementation")
} }

View File

@@ -68,7 +68,7 @@ class App : Application() {
} else { } else {
if (priority >= Log.WARN || priority == Log.DEBUG) { if (priority >= Log.WARN || priority == Log.DEBUG) {
Log.println(priority, tag, message) Log.println(priority, tag, message)
Log.d(tag, message, t) Log.w(tag, message, t)
} }
if (priority >= Log.INFO && t !is NoShellException) { if (priority >= Log.INFO && t !is NoShellException) {
FirebaseCrashlytics.getInstance().recordException(t) FirebaseCrashlytics.getInstance().recordException(t)

View File

@@ -76,7 +76,7 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene
set(value) = app.pref.edit { putString(KEY_PASSPHRASE, value) } set(value) = app.pref.edit { putString(KEY_PASSPHRASE, value) }
var operatingBand: Int var operatingBand: Int
@SuppressLint("InlinedApi") @SuppressLint("InlinedApi")
get() = app.pref.getInt(KEY_OPERATING_BAND, WifiP2pConfig.GROUP_OWNER_BAND_AUTO) get() = app.pref.getInt(KEY_OPERATING_BAND, SoftApConfigurationCompat.BAND_ANY)
set(value) = app.pref.edit { putInt(KEY_OPERATING_BAND, value) } set(value) = app.pref.edit { putInt(KEY_OPERATING_BAND, value) }
var operatingChannel: Int var operatingChannel: Int
get() { get() {
@@ -366,11 +366,11 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene
setNetworkName(PLACEHOLDER_NETWORK_NAME) setNetworkName(PLACEHOLDER_NETWORK_NAME)
setPassphrase(passphrase) setPassphrase(passphrase)
operatingChannel.let { oc -> operatingChannel.let { oc ->
if (oc == 0) setGroupOperatingBand(when (operatingBand) { if (oc == 0) setGroupOperatingBand(when (val band = operatingBand) {
SoftApConfigurationCompat.BAND_ANY -> WifiP2pConfig.GROUP_OWNER_BAND_AUTO SoftApConfigurationCompat.BAND_ANY -> WifiP2pConfig.GROUP_OWNER_BAND_AUTO
SoftApConfigurationCompat.BAND_2GHZ -> WifiP2pConfig.GROUP_OWNER_BAND_2GHZ SoftApConfigurationCompat.BAND_2GHZ -> WifiP2pConfig.GROUP_OWNER_BAND_2GHZ
SoftApConfigurationCompat.BAND_5GHZ -> WifiP2pConfig.GROUP_OWNER_BAND_5GHZ SoftApConfigurationCompat.BAND_5GHZ -> WifiP2pConfig.GROUP_OWNER_BAND_5GHZ
else -> throw IllegalArgumentException("Unknown band") else -> throw IllegalArgumentException("Unknown band $band")
}) })
else setGroupOperatingFrequency(SoftApConfigurationCompat.channelToFrequency(operatingBand, oc)) else setGroupOperatingFrequency(SoftApConfigurationCompat.channelToFrequency(operatingBand, oc))
} }

View File

@@ -67,7 +67,7 @@ enum class TetherType(@DrawableRes val icon: Int) {
@RequiresApi(30) @RequiresApi(30)
override fun onTetherableInterfaceRegexpsChanged(args: Array<out Any?>?) = synchronized(this) { override fun onTetherableInterfaceRegexpsChanged(args: Array<out Any?>?) = synchronized(this) {
if (requiresUpdate) return@synchronized if (requiresUpdate) return@synchronized
Timber.i("onTetherableInterfaceRegexpsChanged: ${args?.contentToString()}") Timber.i("onTetherableInterfaceRegexpsChanged: ${args?.contentDeepToString()}")
TetheringManager.unregisterTetheringEventCallback(this) TetheringManager.unregisterTetheringEventCallback(this)
requiresUpdate = true requiresUpdate = true
listener() listener()

View File

@@ -605,12 +605,12 @@ object TetheringManager {
*/ */
fun getLastTetherError(iface: String): Int = getLastTetherError(Services.connectivity, iface) as Int fun getLastTetherError(iface: String): Int = getLastTetherError(Services.connectivity, iface) as Int
val tetherErrorLookup = ConstantLookup(clazz, "TETHER_ERROR_", val tetherErrorLookup = ConstantLookup("TETHER_ERROR_",
"TETHER_ERROR_NO_ERROR", "TETHER_ERROR_UNKNOWN_IFACE", "TETHER_ERROR_SERVICE_UNAVAIL", "TETHER_ERROR_NO_ERROR", "TETHER_ERROR_UNKNOWN_IFACE", "TETHER_ERROR_SERVICE_UNAVAIL",
"TETHER_ERROR_UNSUPPORTED", "TETHER_ERROR_UNAVAIL_IFACE", "TETHER_ERROR_MASTER_ERROR", "TETHER_ERROR_UNSUPPORTED", "TETHER_ERROR_UNAVAIL_IFACE", "TETHER_ERROR_MASTER_ERROR",
"TETHER_ERROR_TETHER_IFACE_ERROR", "TETHER_ERROR_UNTETHER_IFACE_ERROR", "TETHER_ERROR_ENABLE_NAT_ERROR", "TETHER_ERROR_TETHER_IFACE_ERROR", "TETHER_ERROR_UNTETHER_IFACE_ERROR", "TETHER_ERROR_ENABLE_NAT_ERROR",
"TETHER_ERROR_DISABLE_NAT_ERROR", "TETHER_ERROR_IFACE_CFG_ERROR", "TETHER_ERROR_PROVISION_FAILED", "TETHER_ERROR_DISABLE_NAT_ERROR", "TETHER_ERROR_IFACE_CFG_ERROR", "TETHER_ERROR_PROVISION_FAILED",
"TETHER_ERROR_DHCPSERVER_ERROR", "TETHER_ERROR_ENTITLEMENT_UNKNOWN") "TETHER_ERROR_DHCPSERVER_ERROR", "TETHER_ERROR_ENTITLEMENT_UNKNOWN") { clazz }
val Intent.tetheredIfaces get() = getStringArrayListExtra( val Intent.tetheredIfaces get() = getStringArrayListExtra(
if (Build.VERSION.SDK_INT >= 26) EXTRA_ACTIVE_TETHER else EXTRA_ACTIVE_TETHER_LEGACY) if (Build.VERSION.SDK_INT >= 26) EXTRA_ACTIVE_TETHER else EXTRA_ACTIVE_TETHER_LEGACY)

View File

@@ -117,7 +117,7 @@ object WifiApManager {
private val getFrequency by lazy { classSoftApInfo.getDeclaredMethod("getFrequency") } private val getFrequency by lazy { classSoftApInfo.getDeclaredMethod("getFrequency") }
private val getBandwidth by lazy { classSoftApInfo.getDeclaredMethod("getBandwidth") } private val getBandwidth by lazy { classSoftApInfo.getDeclaredMethod("getBandwidth") }
@RequiresApi(30) @RequiresApi(30)
val channelWidthLookup = ConstantLookup(classSoftApInfo, "CHANNEL_WIDTH_") val channelWidthLookup = ConstantLookup("CHANNEL_WIDTH_") { classSoftApInfo }
const val CHANNEL_WIDTH_INVALID = 0 const val CHANNEL_WIDTH_INVALID = 0
private val classSoftApCapability by lazy { Class.forName("android.net.wifi.SoftApCapability") } private val classSoftApCapability by lazy { Class.forName("android.net.wifi.SoftApCapability") }
@@ -125,15 +125,16 @@ object WifiApManager {
private val areFeaturesSupported by lazy { private val areFeaturesSupported by lazy {
classSoftApCapability.getDeclaredMethod("areFeaturesSupported", Long::class.java) classSoftApCapability.getDeclaredMethod("areFeaturesSupported", Long::class.java)
} }
@RequiresApi(30) @get:RequiresApi(30)
val featureLookup = LongConstantLookup(classSoftApCapability, "SOFTAP_FEATURE_") val featureLookup by lazy { LongConstantLookup(classSoftApCapability, "SOFTAP_FEATURE_") }
private val methods29 = setOf("onStateChanged", "onNumClientsChanged")
@RequiresApi(28) @RequiresApi(28)
fun registerSoftApCallback(callback: SoftApCallbackCompat, executor: Executor): Any { fun registerSoftApCallback(callback: SoftApCallbackCompat, executor: Executor): Any {
val proxy = Proxy.newProxyInstance(interfaceSoftApCallback.classLoader, val proxy = Proxy.newProxyInstance(interfaceSoftApCallback.classLoader,
arrayOf(interfaceSoftApCallback), object : InvocationHandler { arrayOf(interfaceSoftApCallback), object : InvocationHandler {
override fun invoke(proxy: Any, method: Method, args: Array<out Any?>?): Any? { override fun invoke(proxy: Any, method: Method, args: Array<out Any?>?): Any? {
return if (Build.VERSION.SDK_INT >= 30) invokeActual(proxy, method, args) else { return if (Build.VERSION.SDK_INT >= 30 || method.name !in methods29) invokeActual(proxy, method, args) else {
executor.execute { invokeActual(proxy, method, args) } executor.execute { invokeActual(proxy, method, args) }
null // no return value as of API 30 null // no return value as of API 30
} }

View File

@@ -7,10 +7,11 @@ import be.mygod.vpnhotspot.App.Companion.app
import be.mygod.vpnhotspot.R import be.mygod.vpnhotspot.R
import timber.log.Timber import timber.log.Timber
class ConstantLookup(private val clazz: Class<*>, private val prefix: String, private val lookup29: Array<out String>) { class ConstantLookup(private val prefix: String, private val lookup29: Array<out String>,
private val clazz: () -> Class<*>) {
private val lookup by lazy { private val lookup by lazy {
SparseArrayCompat<String>().apply { SparseArrayCompat<String>().apply {
for (field in clazz.declaredFields) try { for (field in clazz().declaredFields) try {
if (field.name.startsWith(prefix)) put(field.getInt(null), field.name) if (field.name.startsWith(prefix)) put(field.getInt(null), field.name)
} catch (e: Exception) { } catch (e: Exception) {
Timber.w(e) Timber.w(e)
@@ -30,10 +31,11 @@ class ConstantLookup(private val clazz: Class<*>, private val prefix: String, pr
} }
@Suppress("FunctionName") @Suppress("FunctionName")
fun ConstantLookup(clazz: Class<*>, prefix: String, vararg lookup29: String) = ConstantLookup(clazz, prefix, lookup29) fun ConstantLookup(prefix: String, vararg lookup29: String, clazz: () -> Class<*>) =
ConstantLookup(prefix, lookup29, clazz)
@Suppress("FunctionName") @Suppress("FunctionName")
inline fun <reified T> ConstantLookup(prefix: String, vararg lookup29: String) = inline fun <reified T> ConstantLookup(prefix: String, vararg lookup29: String) =
ConstantLookup(T::class.java, prefix, lookup29) ConstantLookup(prefix, lookup29) { T::class.java }
class LongConstantLookup(private val clazz: Class<*>, private val prefix: String) { class LongConstantLookup(private val clazz: Class<*>, private val prefix: String) {
private val lookup = LongSparseArray<String>().apply { private val lookup = LongSparseArray<String>().apply {

View File

@@ -22,6 +22,7 @@ import androidx.fragment.app.FragmentManager
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
import be.mygod.vpnhotspot.widget.SmartSnackbar import be.mygod.vpnhotspot.widget.SmartSnackbar
import timber.log.Timber
import java.lang.invoke.MethodHandles import java.lang.invoke.MethodHandles
import java.lang.reflect.InvocationHandler import java.lang.reflect.InvocationHandler
import java.lang.reflect.InvocationTargetException import java.lang.reflect.InvocationTargetException
@@ -134,12 +135,15 @@ private val newLookup by lazy @TargetApi(26) {
* *
* See also: https://stackoverflow.com/a/49532463/2245107 * See also: https://stackoverflow.com/a/49532463/2245107
*/ */
fun InvocationHandler.callSuper(interfaceClass: Class<*>, proxy: Any, method: Method, args: Array<out Any?>?) = when { fun InvocationHandler.callSuper(interfaceClass: Class<*>, proxy: Any, method: Method, args: Array<out Any?>?): Any? {
Build.VERSION.SDK_INT >= 26 -> newLookup.newInstance(interfaceClass, 0xf) // ALL_MODES return if (Build.VERSION.SDK_INT >= 26 && method.isDefault) newLookup.newInstance(interfaceClass, 0xf) // ALL_MODES
.`in`(interfaceClass).unreflectSpecial(method, interfaceClass).bindTo(proxy).run { .`in`(interfaceClass).unreflectSpecial(method, interfaceClass).bindTo(proxy).run {
if (args == null) invokeWithArguments() else invokeWithArguments(*args) if (args == null) invokeWithArguments() else invokeWithArguments(*args)
} else if (method.declaringClass === Object::class.java) {
// otherwise, we just redispatch it to InvocationHandler
if (args == null) method(this) else method(this, *args)
} else {
Timber.w("Unhandled method: $method(${args?.contentDeepToString()})")
null
} }
// only Java 8+ has default interface methods; otherwise, we just redispatch it to InvocationHandler
args == null -> method(this)
else -> method(this, *args)
} }