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)"