diff --git a/README.md b/README.md
index 76d33c08..b7660054 100644
--- a/README.md
+++ b/README.md
@@ -163,6 +163,8 @@ Hidden whitelisted APIs: (same catch as above, however, things in this list are
* `Landroid/net/TetheringManager;->EXTRA_ACTIVE_TETHER:Ljava/lang/String;,system-api,test-api,whitelist`
* `Landroid/net/TetheringManager;->EXTRA_ERRORED_TETHER:Ljava/lang/String;,system-api,test-api,whitelist`
* (since API 24) `Landroid/net/TetheringManager;->TETHERING_BLUETOOTH:I,system-api,test-api,whitelist`
+* (since API 30) `Landroid/net/TetheringManager;->TETHERING_ETHERNET:I,system-api,test-api,whitelist`
+* (since API 30) `Landroid/net/TetheringManager;->TETHERING_NCM:I,system-api,test-api,whitelist`
* (since API 24) `Landroid/net/TetheringManager;->TETHERING_USB:I,system-api,test-api,whitelist`
* (since API 24) `Landroid/net/TetheringManager;->TETHERING_WIFI:I,system-api,test-api,whitelist`
* `Landroid/net/TetheringManager;->TETHER_ERROR_*:I,system-api,test-api,whitelist`
@@ -181,8 +183,10 @@ Undocumented system configurations:
* `@android:array/config_tether_usb_regexs`
* `@android:array/config_tether_wifi_regexs`
+* `@android:array/config_tether_wifi_p2p_regexs`
* `@android:array/config_tether_wimax_regexs`
* `@android:array/config_tether_bluetooth_regexs`
+* `@android:array/config_tether_ncm_regexs`
* (since API 28) `@android:integer/config_wifi_framework_soft_ap_timeout_delay`
Other:
diff --git a/mobile/src/main/AndroidManifest.xml b/mobile/src/main/AndroidManifest.xml
index 57be5156..344db496 100644
--- a/mobile/src/main/AndroidManifest.xml
+++ b/mobile/src/main/AndroidManifest.xml
@@ -34,6 +34,8 @@
+
@@ -144,6 +146,30 @@
+
+
+
+
+
+
+
+
+
+
InterfaceManager.ViewHolder(ListitemInterfaceBinding.inflate(inflater, parent, false))
VIEW_TYPE_MANAGE -> ManageBar.ViewHolder(ListitemManageBinding.inflate(inflater, parent, false))
- VIEW_TYPE_WIFI, VIEW_TYPE_USB, VIEW_TYPE_BLUETOOTH, VIEW_TYPE_WIFI_LEGACY ->
+ VIEW_TYPE_WIFI,
+ VIEW_TYPE_USB,
+ VIEW_TYPE_BLUETOOTH,
+ VIEW_TYPE_ETHERNET,
+ VIEW_TYPE_NCM,
+ VIEW_TYPE_WIFI_LEGACY -> {
TetherManager.ViewHolder(ListitemInterfaceBinding.inflate(inflater, parent, false))
+ }
VIEW_TYPE_LOCAL_ONLY_HOTSPOT -> @TargetApi(26) {
LocalOnlyHotspotManager.ViewHolder(ListitemInterfaceBinding.inflate(inflater, parent, false))
}
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 215aa0a4..603b6e17 100644
--- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt
+++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt
@@ -157,6 +157,24 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(),
onTetheringStarted() // force flush state
}
}
+ @RequiresApi(30)
+ class Ethernet(parent: TetheringFragment) : TetherManager(parent) {
+ override val title get() = parent.getString(R.string.tethering_manage_ethernet)
+ override val tetherType get() = TetherType.ETHERNET
+ override val type get() = VIEW_TYPE_ETHERNET
+
+ override fun start() = TetheringManager.startTethering(TetheringManager.TETHERING_ETHERNET, true, this)
+ override fun stop() = TetheringManager.stopTethering(TetheringManager.TETHERING_ETHERNET)
+ }
+ @RequiresApi(30)
+ class Ncm(parent: TetheringFragment) : TetherManager(parent) {
+ override val title get() = parent.getString(R.string.tethering_manage_ncm)
+ override val tetherType get() = TetherType.NCM
+ override val type get() = VIEW_TYPE_NCM
+
+ override fun start() = TetheringManager.startTethering(TetheringManager.TETHERING_NCM, true, this)
+ override fun stop() = TetheringManager.stopTethering(TetheringManager.TETHERING_NCM)
+ }
@Suppress("DEPRECATION")
@Deprecated("Not usable since API 26, malfunctioning on API 25")
diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt
index 0669dd8b..c0d207da 100644
--- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt
+++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt
@@ -10,8 +10,10 @@ import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
+import androidx.annotation.RequiresApi
import androidx.appcompat.widget.Toolbar
import androidx.core.content.ContextCompat
+import androidx.core.os.BuildCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DefaultItemAnimator
@@ -46,12 +48,18 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick
inner class ManagerAdapter : ListAdapter(Manager) {
internal val repeaterManager by lazy { RepeaterManager(this@TetheringFragment) }
+ @get:RequiresApi(26)
internal val localOnlyHotspotManager by lazy @TargetApi(26) { LocalOnlyHotspotManager(this@TetheringFragment) }
+ @get:RequiresApi(24)
private val tetherManagers by lazy @TargetApi(24) {
listOf(TetherManager.Wifi(this@TetheringFragment),
TetherManager.Usb(this@TetheringFragment),
TetherManager.Bluetooth(this@TetheringFragment))
}
+ @get:RequiresApi(30)
+ private val tetherManagers30 by lazy @TargetApi(30) {
+ listOf(TetherManager.Ethernet(this@TetheringFragment), TetherManager.Ncm(this@TetheringFragment))
+ }
private val wifiManagerLegacy by lazy @Suppress("Deprecation") {
TetherManager.WifiLegacy(this@TetheringFragment)
}
@@ -78,6 +86,10 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick
list.addAll(tetherManagers)
tetherManagers.forEach { it.updateErrorMessage(erroredIfaces) }
}
+ if (BuildCompat.isAtLeastR()) {
+ list.addAll(tetherManagers30)
+ tetherManagers30.forEach { it.updateErrorMessage(erroredIfaces) }
+ }
if (Build.VERSION.SDK_INT < 26) {
list.add(wifiManagerLegacy)
wifiManagerLegacy.onTetheringStarted()
diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringTileService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringTileService.kt
index 48bc1f9f..f3e5f405 100644
--- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringTileService.kt
+++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringTileService.kt
@@ -191,6 +191,22 @@ sealed class TetheringTileService : TetherListeningTileService(), TetheringManag
}
}
}
+ @RequiresApi(30)
+ class Ethernet : TetheringTileService() {
+ override val labelString get() = R.string.tethering_manage_ethernet
+ override val tetherType get() = TetherType.ETHERNET
+
+ override fun start() = TetheringManager.startTethering(TetheringManager.TETHERING_ETHERNET, true, this)
+ override fun stop() = TetheringManager.stopTethering(TetheringManager.TETHERING_ETHERNET)
+ }
+ @RequiresApi(30)
+ class Ncm : TetheringTileService() {
+ override val labelString get() = R.string.tethering_manage_ncm
+ override val tetherType get() = TetherType.NCM
+
+ override fun start() = TetheringManager.startTethering(TetheringManager.TETHERING_NCM, true, this)
+ override fun stop() = TetheringManager.stopTethering(TetheringManager.TETHERING_NCM)
+ }
@Suppress("DEPRECATION")
@Deprecated("Not usable since API 25")
diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/TetherType.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/TetherType.kt
index 2e4ac67f..22a21244 100644
--- a/mobile/src/main/java/be/mygod/vpnhotspot/net/TetherType.kt
+++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/TetherType.kt
@@ -1,12 +1,13 @@
package be.mygod.vpnhotspot.net
import android.content.res.Resources
+import androidx.core.os.BuildCompat
import be.mygod.vpnhotspot.App.Companion.app
import be.mygod.vpnhotspot.R
import java.util.regex.Pattern
enum class TetherType {
- NONE, WIFI_P2P, USB, WIFI, WIMAX, BLUETOOTH;
+ NONE, WIFI_P2P, USB, WIFI, WIMAX, BLUETOOTH, NCM, ETHERNET;
val icon get() = when (this) {
USB -> R.drawable.ic_device_usb
@@ -14,6 +15,9 @@ enum class TetherType {
WIFI -> R.drawable.ic_device_network_wifi
WIMAX -> R.drawable.ic_action_contactless
BLUETOOTH -> R.drawable.ic_device_bluetooth
+ // if you have an issue with these Ethernet icon namings, blame Google
+ NCM -> R.drawable.ic_action_settings_ethernet
+ ETHERNET -> R.drawable.ic_content_inbox
else -> R.drawable.ic_device_wifi_tethering
}
val isWifi get() = when (this) {
@@ -24,43 +28,48 @@ enum class TetherType {
companion object {
private val usbRegexs: List
private val wifiRegexs: List
+ private val wifiP2pRegexs: List
private val wimaxRegexs: List
private val bluetoothRegexs: List
+ private val ncmRegexs: List
+ private val ethernetRegex: Pattern?
/**
- * Source: https://android.googlesource.com/platform/frameworks/base/+/61fa313/core/res/res/values/config.xml#328
+ * Source: https://android.googlesource.com/platform/frameworks/base/+/32e772f/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java#93
*/
init {
val appRes = app.resources
val sysRes = Resources.getSystem()
- usbRegexs = appRes.getStringArray(sysRes
- .getIdentifier("config_tether_usb_regexs", "array", "android"))
- .filterNotNull()
- .map { it.toPattern() }
- wifiRegexs = appRes.getStringArray(sysRes
- .getIdentifier("config_tether_wifi_regexs", "array", "android"))
- .filterNotNull()
- .map { it.toPattern() }
- wimaxRegexs = appRes.getStringArray(sysRes
- .getIdentifier("config_tether_wimax_regexs", "array", "android"))
- .filterNotNull()
- .map { it.toPattern() }
- bluetoothRegexs = appRes.getStringArray(sysRes
- .getIdentifier("config_tether_bluetooth_regexs", "array", "android"))
+ fun getRegexs(name: String) = appRes.getStringArray(sysRes
+ .getIdentifier(name, "array", "android"))
.filterNotNull()
.map { it.toPattern() }
+ usbRegexs = getRegexs("config_tether_usb_regexs")
+ wifiRegexs = getRegexs("config_tether_wifi_regexs")
+ wifiP2pRegexs = if (BuildCompat.isAtLeastR()) getRegexs("config_tether_wifi_p2p_regexs") else emptyList()
+ wimaxRegexs = getRegexs("config_tether_wimax_regexs")
+ bluetoothRegexs = getRegexs("config_tether_bluetooth_regexs")
+ ncmRegexs = if (BuildCompat.isAtLeastR()) getRegexs("config_tether_ncm_regexs") else emptyList()
+ ethernetRegex = if (BuildCompat.isAtLeastR()) {
+ appRes.getString(sysRes.getIdentifier("config_ethernet_iface_regex", "string", "android")).run {
+ if (isEmpty()) null else toPattern()
+ }
+ } else null
}
/**
- * Based on: https://android.googlesource.com/platform/frameworks/base/+/0e3d092/services/core/java/com/android/server/connectivity/Tethering.java#311
+ * Based on: https://android.googlesource.com/platform/frameworks/base/+/5d36f01/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java#479
*/
fun ofInterface(iface: String?, p2pDev: String? = null) = when {
iface == null -> NONE
iface == p2pDev -> WIFI_P2P
wifiRegexs.any { it.matcher(iface).matches() } -> WIFI
+ wifiP2pRegexs.any { it.matcher(iface).matches() } -> WIFI_P2P
usbRegexs.any { it.matcher(iface).matches() } -> USB
bluetoothRegexs.any { it.matcher(iface).matches() } -> BLUETOOTH
+ ncmRegexs.any { it.matcher(iface).matches() } -> NCM
wimaxRegexs.any { it.matcher(iface).matches() } -> WIMAX
+ ethernetRegex?.matcher(iface)?.matches() == true -> ETHERNET
else -> NONE
}
}
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 5642830b..b4a651d2 100644
--- a/mobile/src/main/java/be/mygod/vpnhotspot/net/TetheringManager.kt
+++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/TetheringManager.kt
@@ -79,6 +79,7 @@ object TetheringManager {
*/
const val EXTRA_ERRORED_TETHER = "erroredArray"
+ // tethering types supported by enableTetheringInternal: https://android.googlesource.com/platform/frameworks/base/+/5d36f01/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java#549
/**
* Wifi tethering type.
* @see [startTethering].
@@ -103,6 +104,22 @@ object TetheringManager {
*/
@RequiresApi(24)
const val TETHERING_BLUETOOTH = 2
+ /**
+ * Ncm local tethering type.
+ *
+ * Requires NETWORK_SETTINGS permission, which is sadly not obtainable.
+ * @see [startTethering]
+ */
+ @RequiresApi(30)
+ const val TETHERING_NCM = 4
+ /**
+ * Ethernet tethering type.
+ *
+ * Requires MANAGE_USB permission, also.
+ * @see [startTethering]
+ */
+ @RequiresApi(30)
+ const val TETHERING_ETHERNET = 5
@get:RequiresApi(30)
private val clazz by lazy { Class.forName("android.net.TetheringManager") }
diff --git a/mobile/src/main/res/drawable/ic_content_inbox.xml b/mobile/src/main/res/drawable/ic_content_inbox.xml
new file mode 100644
index 00000000..028c1333
--- /dev/null
+++ b/mobile/src/main/res/drawable/ic_content_inbox.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/mobile/src/main/res/values-v30/bools.xml b/mobile/src/main/res/values-v30/bools.xml
new file mode 100644
index 00000000..1fb7b846
--- /dev/null
+++ b/mobile/src/main/res/values-v30/bools.xml
@@ -0,0 +1,4 @@
+
+
+ true
+
diff --git a/mobile/src/main/res/values/bools.xml b/mobile/src/main/res/values/bools.xml
index bfcb1477..a84b400b 100644
--- a/mobile/src/main/res/values/bools.xml
+++ b/mobile/src/main/res/values/bools.xml
@@ -1,5 +1,6 @@
+ false
false
true
true
diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml
index fbd108db..61639c14 100644
--- a/mobile/src/main/res/values/strings.xml
+++ b/mobile/src/main/res/values/strings.xml
@@ -6,6 +6,7 @@
* @string/usb_tethering_button_text
* @string/wifi_hotspot_checkbox_text
* @string/bluetooth_tether_checkbox_text
+ * @string/ethernet_tether_checkbox_text
-->
VPN Hotspot
@@ -56,6 +57,8 @@
Wi\u2011Fi hotspot
Wi\u2011Fi hotspot (legacy)
Bluetooth tethering
+ Ethernet tethering
+ USB tethering (NCM)
" (connecting)"
" (reachable)"