diff --git a/README.md b/README.md
index 4c84b6f0..dfe1db90 100644
--- a/README.md
+++ b/README.md
@@ -212,6 +212,7 @@ Hidden whitelisted APIs: (same catch as above, however, things in this list are
* (since API 24) [`Landroid/net/TetheringManager;->TETHERING_USB:I,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#148944)
* (since API 24) [`Landroid/net/TetheringManager;->TETHERING_WIFI:I,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#148945)
* [`Landroid/net/TetheringManager;->TETHER_ERROR_*:I,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#148947)
+* (since API 30) [`Landroid/net/TetheringManager;->TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION:I,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#148954)
* (since API 30) [`Landroid/net/TetheringManager;->TETHER_HARDWARE_OFFLOAD_FAILED:I,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#148964)
* (since API 30) [`Landroid/net/TetheringManager;->TETHER_HARDWARE_OFFLOAD_STARTED:I,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#148965)
* (since API 30) [`Landroid/net/TetheringManager;->TETHER_HARDWARE_OFFLOAD_STOPPED:I,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#148966)
diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt
index 92beeeef..a6cbb350 100644
--- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt
+++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt
@@ -92,7 +92,13 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(),
override fun onTetheringStarted() = data.notifyChange()
override fun onTetheringFailed(error: Int?) {
Timber.d("onTetheringFailed: $error")
- error?.let { SmartSnackbar.make("$tetherType: ${TetheringManager.tetherErrorLookup(it)}").show() }
+ if (Build.VERSION.SDK_INT < 30 || error != TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION) {
+ error?.let { SmartSnackbar.make("$tetherType: ${TetheringManager.tetherErrorLookup(it)}").show() }
+ } else GlobalScope.launch(Dispatchers.Main.immediate) {
+ val context = parent.context ?: app
+ Toast.makeText(context, R.string.permission_missing, Toast.LENGTH_LONG).show()
+ ManageBar.start(context)
+ }
data.notifyChange()
}
override fun onException(e: Exception) {
diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/TetheringManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/TetheringManager.kt
index d99c8229..645e9367 100644
--- a/mobile/src/main/java/be/mygod/vpnhotspot/net/TetheringManager.kt
+++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/TetheringManager.kt
@@ -297,31 +297,38 @@ object TetheringManager {
fun startTethering(type: Int, showProvisioningUi: Boolean, callback: StartTetheringCallback,
handler: Handler? = null, cacheDir: File = app.deviceStorage.codeCacheDir) {
if (Build.VERSION.SDK_INT >= 30) try {
- val proxy = proxy(callback)
val executor = handler.makeExecutor()
- try {
- startTethering(type, true, showProvisioningUi, executor, proxy)
- } catch (e1: InvocationTargetException) {
- if (e1.targetException is SecurityException) GlobalScope.launch(Dispatchers.Unconfined) {
- val result = try {
- RootManager.use { it.execute(StartTethering(type, showProvisioningUi)) }
- } catch (e2: Exception) {
- e2.addSuppressed(e1)
- try {
- // last resort: start tethering without trying to bypass entitlement check
- startTethering(type, false, showProvisioningUi, executor, proxy)
- Timber.w(e2)
- } catch (e3: Exception) {
- e3.addSuppressed(e2)
- Timber.w(e3)
- callback.onException(e3)
+ startTethering(type, true, showProvisioningUi,
+ executor, proxy(object : StartTetheringCallback {
+ override fun onTetheringStarted() = callback.onTetheringStarted()
+ override fun onTetheringFailed(error: Int?) {
+ if (error != TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION) callback.onTetheringFailed(error)
+ else GlobalScope.launch(Dispatchers.Unconfined) {
+ val result = try {
+ RootManager.use { it.execute(StartTethering(type, showProvisioningUi)) }
+ } catch (eRoot: Exception) {
+ try { // last resort: start tethering without trying to bypass entitlement check
+ startTethering(type, false, showProvisioningUi, executor, proxy(callback))
+ Timber.w(eRoot)
+ } catch (e: Exception) {
+ e.addSuppressed(eRoot)
+ callback.onException(e)
+ }
+ return@launch
+ }
+ when {
+ result == null -> callback.onTetheringStarted()
+ result.value == TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION -> try {
+ startTethering(type, false, showProvisioningUi, executor, proxy(callback))
+ } catch (e: Exception) {
+ callback.onException(e)
+ }
+ else -> callback.onTetheringFailed(result.value)
}
- return@launch
}
- if (result == null) callback.onTetheringStarted()
- else callback.onTetheringFailed(result.value)
- } else callback.onException(e1)
- }
+ }
+ override fun onException(e: Exception) = callback.onException(e)
+ }))
} catch (e: Exception) {
callback.onException(e)
} else @Suppress("DEPRECATION") try {
@@ -607,6 +614,8 @@ object TetheringManager {
"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_DHCPSERVER_ERROR", "TETHER_ERROR_ENTITLEMENT_UNKNOWN") { clazz }
+ @RequiresApi(30)
+ const val TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14
val Intent.tetheredIfaces get() = getStringArrayListExtra(
if (Build.VERSION.SDK_INT >= 26) EXTRA_ACTIVE_TETHER else EXTRA_ACTIVE_TETHER_LEGACY)
diff --git a/mobile/src/main/res/values-zh-rCN/strings.xml b/mobile/src/main/res/values-zh-rCN/strings.xml
index 51a51ca8..642211d0 100644
--- a/mobile/src/main/res/values-zh-rCN/strings.xml
+++ b/mobile/src/main/res/values-zh-rCN/strings.xml
@@ -151,6 +151,7 @@
未知 #%d
错误:未找到下游接口
发生异常,详情请查看调试信息。
+ 权限不足
设置 WLAN
使用 QR 码分享
diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml
index 0043149f..4e93ef1d 100644
--- a/mobile/src/main/res/values/strings.xml
+++ b/mobile/src/main/res/values/strings.xml
@@ -1,8 +1,8 @@
@@ -171,6 +171,7 @@
unknown #%d
Fatal: Downstream interface not found
Something went wrong, please check the debug information.
+ Permission missing
Wi\u2011Fi configuration
Share via QR code