Preliminary support for Ethernet and NCM tethering

This commit is contained in:
Mygod
2020-05-28 00:06:34 -04:00
parent 3314f01dc9
commit 1ca69cf3a6
12 changed files with 146 additions and 18 deletions

View File

@@ -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_ACTIVE_TETHER:Ljava/lang/String;,system-api,test-api,whitelist`
* `Landroid/net/TetheringManager;->EXTRA_ERRORED_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 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_USB:I,system-api,test-api,whitelist`
* (since API 24) `Landroid/net/TetheringManager;->TETHERING_WIFI: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` * `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_usb_regexs`
* `@android:array/config_tether_wifi_regexs` * `@android:array/config_tether_wifi_regexs`
* `@android:array/config_tether_wifi_p2p_regexs`
* `@android:array/config_tether_wimax_regexs` * `@android:array/config_tether_wimax_regexs`
* `@android:array/config_tether_bluetooth_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` * (since API 28) `@android:integer/config_wifi_framework_soft_ap_timeout_delay`
Other: Other:

View File

@@ -34,6 +34,8 @@
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.MANAGE_USB" <uses-permission android:name="android.permission.MANAGE_USB"
tools:ignore="ProtectedPermissions"/> tools:ignore="ProtectedPermissions"/>
<uses-permission android:name="android.permission.NETWORK_SETTINGS"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.OVERRIDE_WIFI_CONFIG" <uses-permission android:name="android.permission.OVERRIDE_WIFI_CONFIG"
tools:ignore="ProtectedPermissions"/> tools:ignore="ProtectedPermissions"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
@@ -144,6 +146,30 @@
<action android:name="android.service.quicksettings.action.QS_TILE" /> <action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter> </intent-filter>
</service> </service>
<service
android:name=".manage.TetheringTileService$Ethernet"
android:directBootAware="true"
android:enabled="@bool/api_ge_30"
android:icon="@drawable/ic_content_inbox"
android:label="@string/tethering_manage_ethernet"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
tools:targetApi="30">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
</service>
<service
android:name=".manage.TetheringTileService$Ncm"
android:directBootAware="true"
android:enabled="@bool/api_ge_30"
android:icon="@drawable/ic_action_settings_ethernet"
android:label="@string/tethering_manage_ncm"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
tools:targetApi="30">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
</service>
<!--suppress DeprecatedClassUsageInspection --> <!--suppress DeprecatedClassUsageInspection -->
<service <service
android:name=".manage.TetheringTileService$WifiLegacy" android:name=".manage.TetheringTileService$WifiLegacy"

View File

@@ -17,6 +17,8 @@ abstract class Manager {
const val VIEW_TYPE_WIFI = 2 const val VIEW_TYPE_WIFI = 2
const val VIEW_TYPE_USB = 3 const val VIEW_TYPE_USB = 3
const val VIEW_TYPE_BLUETOOTH = 4 const val VIEW_TYPE_BLUETOOTH = 4
const val VIEW_TYPE_ETHERNET = 8
const val VIEW_TYPE_NCM = 9
const val VIEW_TYPE_WIFI_LEGACY = 5 const val VIEW_TYPE_WIFI_LEGACY = 5
const val VIEW_TYPE_LOCAL_ONLY_HOTSPOT = 6 const val VIEW_TYPE_LOCAL_ONLY_HOTSPOT = 6
const val VIEW_TYPE_REPEATER = 7 const val VIEW_TYPE_REPEATER = 7
@@ -29,8 +31,14 @@ abstract class Manager {
VIEW_TYPE_INTERFACE -> VIEW_TYPE_INTERFACE ->
InterfaceManager.ViewHolder(ListitemInterfaceBinding.inflate(inflater, parent, false)) InterfaceManager.ViewHolder(ListitemInterfaceBinding.inflate(inflater, parent, false))
VIEW_TYPE_MANAGE -> ManageBar.ViewHolder(ListitemManageBinding.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)) TetherManager.ViewHolder(ListitemInterfaceBinding.inflate(inflater, parent, false))
}
VIEW_TYPE_LOCAL_ONLY_HOTSPOT -> @TargetApi(26) { VIEW_TYPE_LOCAL_ONLY_HOTSPOT -> @TargetApi(26) {
LocalOnlyHotspotManager.ViewHolder(ListitemInterfaceBinding.inflate(inflater, parent, false)) LocalOnlyHotspotManager.ViewHolder(ListitemInterfaceBinding.inflate(inflater, parent, false))
} }

View File

@@ -157,6 +157,24 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(),
onTetheringStarted() // force flush state 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") @Suppress("DEPRECATION")
@Deprecated("Not usable since API 26, malfunctioning on API 25") @Deprecated("Not usable since API 26, malfunctioning on API 25")

View File

@@ -10,8 +10,10 @@ import android.view.LayoutInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.annotation.RequiresApi
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.os.BuildCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.DefaultItemAnimator
@@ -46,12 +48,18 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick
inner class ManagerAdapter : ListAdapter<Manager, RecyclerView.ViewHolder>(Manager) { inner class ManagerAdapter : ListAdapter<Manager, RecyclerView.ViewHolder>(Manager) {
internal val repeaterManager by lazy { RepeaterManager(this@TetheringFragment) } internal val repeaterManager by lazy { RepeaterManager(this@TetheringFragment) }
@get:RequiresApi(26)
internal val localOnlyHotspotManager by lazy @TargetApi(26) { LocalOnlyHotspotManager(this@TetheringFragment) } internal val localOnlyHotspotManager by lazy @TargetApi(26) { LocalOnlyHotspotManager(this@TetheringFragment) }
@get:RequiresApi(24)
private val tetherManagers by lazy @TargetApi(24) { private val tetherManagers by lazy @TargetApi(24) {
listOf(TetherManager.Wifi(this@TetheringFragment), listOf(TetherManager.Wifi(this@TetheringFragment),
TetherManager.Usb(this@TetheringFragment), TetherManager.Usb(this@TetheringFragment),
TetherManager.Bluetooth(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") { private val wifiManagerLegacy by lazy @Suppress("Deprecation") {
TetherManager.WifiLegacy(this@TetheringFragment) TetherManager.WifiLegacy(this@TetheringFragment)
} }
@@ -78,6 +86,10 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick
list.addAll(tetherManagers) list.addAll(tetherManagers)
tetherManagers.forEach { it.updateErrorMessage(erroredIfaces) } tetherManagers.forEach { it.updateErrorMessage(erroredIfaces) }
} }
if (BuildCompat.isAtLeastR()) {
list.addAll(tetherManagers30)
tetherManagers30.forEach { it.updateErrorMessage(erroredIfaces) }
}
if (Build.VERSION.SDK_INT < 26) { if (Build.VERSION.SDK_INT < 26) {
list.add(wifiManagerLegacy) list.add(wifiManagerLegacy)
wifiManagerLegacy.onTetheringStarted() wifiManagerLegacy.onTetheringStarted()

View File

@@ -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") @Suppress("DEPRECATION")
@Deprecated("Not usable since API 25") @Deprecated("Not usable since API 25")

View File

@@ -1,12 +1,13 @@
package be.mygod.vpnhotspot.net package be.mygod.vpnhotspot.net
import android.content.res.Resources import android.content.res.Resources
import androidx.core.os.BuildCompat
import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.App.Companion.app
import be.mygod.vpnhotspot.R import be.mygod.vpnhotspot.R
import java.util.regex.Pattern import java.util.regex.Pattern
enum class TetherType { enum class TetherType {
NONE, WIFI_P2P, USB, WIFI, WIMAX, BLUETOOTH; NONE, WIFI_P2P, USB, WIFI, WIMAX, BLUETOOTH, NCM, ETHERNET;
val icon get() = when (this) { val icon get() = when (this) {
USB -> R.drawable.ic_device_usb USB -> R.drawable.ic_device_usb
@@ -14,6 +15,9 @@ enum class TetherType {
WIFI -> R.drawable.ic_device_network_wifi WIFI -> R.drawable.ic_device_network_wifi
WIMAX -> R.drawable.ic_action_contactless WIMAX -> R.drawable.ic_action_contactless
BLUETOOTH -> R.drawable.ic_device_bluetooth 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 else -> R.drawable.ic_device_wifi_tethering
} }
val isWifi get() = when (this) { val isWifi get() = when (this) {
@@ -24,43 +28,48 @@ enum class TetherType {
companion object { companion object {
private val usbRegexs: List<Pattern> private val usbRegexs: List<Pattern>
private val wifiRegexs: List<Pattern> private val wifiRegexs: List<Pattern>
private val wifiP2pRegexs: List<Pattern>
private val wimaxRegexs: List<Pattern> private val wimaxRegexs: List<Pattern>
private val bluetoothRegexs: List<Pattern> private val bluetoothRegexs: List<Pattern>
private val ncmRegexs: List<Pattern>
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 { init {
val appRes = app.resources val appRes = app.resources
val sysRes = Resources.getSystem() val sysRes = Resources.getSystem()
usbRegexs = appRes.getStringArray(sysRes fun getRegexs(name: String) = appRes.getStringArray(sysRes
.getIdentifier("config_tether_usb_regexs", "array", "android")) .getIdentifier(name, "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"))
.filterNotNull() .filterNotNull()
.map { it.toPattern() } .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 { fun ofInterface(iface: String?, p2pDev: String? = null) = when {
iface == null -> NONE iface == null -> NONE
iface == p2pDev -> WIFI_P2P iface == p2pDev -> WIFI_P2P
wifiRegexs.any { it.matcher(iface).matches() } -> WIFI wifiRegexs.any { it.matcher(iface).matches() } -> WIFI
wifiP2pRegexs.any { it.matcher(iface).matches() } -> WIFI_P2P
usbRegexs.any { it.matcher(iface).matches() } -> USB usbRegexs.any { it.matcher(iface).matches() } -> USB
bluetoothRegexs.any { it.matcher(iface).matches() } -> BLUETOOTH bluetoothRegexs.any { it.matcher(iface).matches() } -> BLUETOOTH
ncmRegexs.any { it.matcher(iface).matches() } -> NCM
wimaxRegexs.any { it.matcher(iface).matches() } -> WIMAX wimaxRegexs.any { it.matcher(iface).matches() } -> WIMAX
ethernetRegex?.matcher(iface)?.matches() == true -> ETHERNET
else -> NONE else -> NONE
} }
} }

View File

@@ -79,6 +79,7 @@ object TetheringManager {
*/ */
const val EXTRA_ERRORED_TETHER = "erroredArray" 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. * Wifi tethering type.
* @see [startTethering]. * @see [startTethering].
@@ -103,6 +104,22 @@ object TetheringManager {
*/ */
@RequiresApi(24) @RequiresApi(24)
const val TETHERING_BLUETOOTH = 2 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) @get:RequiresApi(30)
private val clazz by lazy { Class.forName("android.net.TetheringManager") } private val clazz by lazy { Class.forName("android.net.TetheringManager") }

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M19,3L4.99,3c-1.11,0 -1.98,0.89 -1.98,2L3,19c0,1.1 0.88,2 1.99,2L19,21c1.1,0 2,-0.9 2,-2L21,5c0,-1.11 -0.9,-2 -2,-2zM19,15h-4c0,1.66 -1.35,3 -3,3s-3,-1.34 -3,-3L4.99,15L4.99,5L19,5v10z"/>
</vector>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="api_ge_30">true</bool>
</resources>

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<bool name="api_ge_30">false</bool>
<bool name="api_ge_26">false</bool> <bool name="api_ge_26">false</bool>
<bool name="api_lt_25">true</bool> <bool name="api_lt_25">true</bool>
<bool name="is_day">true</bool> <bool name="is_day">true</bool>

View File

@@ -6,6 +6,7 @@
* @string/usb_tethering_button_text * @string/usb_tethering_button_text
* @string/wifi_hotspot_checkbox_text * @string/wifi_hotspot_checkbox_text
* @string/bluetooth_tether_checkbox_text * @string/bluetooth_tether_checkbox_text
* @string/ethernet_tether_checkbox_text
--> -->
<resources> <resources>
<string name="app_name">VPN Hotspot</string> <string name="app_name">VPN Hotspot</string>
@@ -56,6 +57,8 @@
<string name="tethering_manage_wifi">Wi\u2011Fi hotspot</string> <string name="tethering_manage_wifi">Wi\u2011Fi hotspot</string>
<string name="tethering_manage_wifi_legacy">Wi\u2011Fi hotspot (legacy)</string> <string name="tethering_manage_wifi_legacy">Wi\u2011Fi hotspot (legacy)</string>
<string name="tethering_manage_bluetooth">Bluetooth tethering</string> <string name="tethering_manage_bluetooth">Bluetooth tethering</string>
<string name="tethering_manage_ethernet">Ethernet tethering</string>
<string name="tethering_manage_ncm">USB tethering (NCM)</string>
<string name="connected_state_incomplete">" (connecting)"</string> <string name="connected_state_incomplete">" (connecting)"</string>
<string name="connected_state_valid">" (reachable)"</string> <string name="connected_state_valid">" (reachable)"</string>