From 0ac5b25f85a0ec0b7179ffefa6feb7a934d796a3 Mon Sep 17 00:00:00 2001 From: Mygod Date: Mon, 12 Apr 2021 23:43:05 -0400 Subject: [PATCH 001/112] Migrate from using deprecated getLastTetherError --- README.md | 2 +- .../mygod/vpnhotspot/manage/TetherManager.kt | 6 +++-- .../vpnhotspot/manage/TetheringFragment.kt | 23 ++++++++++++++----- .../mygod/vpnhotspot/net/TetheringManager.kt | 1 + 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index f02531c8..c78eed87 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,7 @@ This is only meant to be an index. You can read more in the source code. Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded or implicitly used) -* [`Landroid/net/ConnectivityManager;->getLastTetherError(Ljava/lang/String;)I,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#144306) +* (prior to API 30) [`Landroid/net/ConnectivityManager;->getLastTetherError(Ljava/lang/String;)I,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#144306) * (since API 30) `Landroid/net/ConnectivityModuleConnector;->IN_PROCESS_SUFFIX:Ljava/lang/String;` * (since API 30) [`Landroid/net/TetheringManager$TetheringEventCallback;->onTetherableInterfaceRegexpsChanged(Landroid/net/TetheringManager$TetheringInterfaceRegexps;)V,blacklist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#148899) * (since API 30) `Landroid/net/TetheringManager;->TETHERING_WIGIG:I` 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 36b86630..df3889e4 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt @@ -119,11 +119,13 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), (viewHolder as ViewHolder).manager = this } - fun updateErrorMessage(errored: List) { + fun updateErrorMessage(errored: List, lastErrors: Map) { val interested = errored.filter { TetherType.ofInterface(it) == tetherType } baseError = if (interested.isEmpty()) null else interested.joinToString("\n") { iface -> "$iface: " + try { - TetheringManager.tetherErrorLookup(TetheringManager.getLastTetherError(iface)) + TetheringManager.tetherErrorLookup(if (Build.VERSION.SDK_INT < 30) @Suppress("DEPRECATION") { + TetheringManager.getLastTetherError(iface) + } else lastErrors[iface] ?: 0) } catch (e: InvocationTargetException) { if (Build.VERSION.SDK_INT !in 24..25 || e.cause !is SecurityException) Timber.w(e) else Timber.d(e) e.readableMessage 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 7284ac35..e3f0b3fe 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt @@ -42,7 +42,8 @@ import java.net.NetworkInterface import java.net.SocketException class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClickListener { - inner class ManagerAdapter : ListAdapter(Manager) { + inner class ManagerAdapter : ListAdapter(Manager), + TetheringManager.TetheringEventCallback { internal val repeaterManager by lazy { RepeaterManager(this@TetheringFragment) } @get:RequiresApi(26) internal val localOnlyHotspotManager by lazy @TargetApi(26) { LocalOnlyHotspotManager(this@TetheringFragment) } @@ -69,6 +70,11 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick this@TetheringFragment.enabledTypes = enabledIfaces.map { TetherType.ofInterface(it) }.toSet() } + val lastErrors = mutableMapOf() + override fun onError(ifName: String, error: Int) { + if (error == 0) lastErrors.remove(ifName) else lastErrors[ifName] = error + } + suspend fun notifyInterfaceChanged(lastList: List? = null) { @Suppress("NAME_SHADOWING") val lastList = lastList ?: listDeferred.await() val first = lastList.indexOfFirst { it is InterfaceManager } @@ -104,11 +110,11 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick list.add(ManageBar) if (Build.VERSION.SDK_INT >= 24) { list.addAll(tetherManagers) - tetherManagers.forEach { it.updateErrorMessage(erroredIfaces) } + tetherManagers.forEach { it.updateErrorMessage(erroredIfaces, lastErrors) } } if (Build.VERSION.SDK_INT >= 30) { list.addAll(tetherManagers30) - tetherManagers30.forEach { it.updateErrorMessage(erroredIfaces) } + tetherManagers30.forEach { it.updateErrorMessage(erroredIfaces, lastErrors) } } if (Build.VERSION.SDK_INT < 26) { list.add(wifiManagerLegacy) @@ -279,15 +285,20 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick lifecycleScope.launchWhenStarted { adapter.notifyInterfaceChanged() } } requireContext().registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED)) - if (Build.VERSION.SDK_INT >= 30) TetherType.listener[this] = { - lifecycleScope.launchWhenStarted { adapter.notifyTetherTypeChanged() } + if (Build.VERSION.SDK_INT >= 30) { + TetheringManager.registerTetheringEventCallback(null, adapter) + TetherType.listener[this] = { lifecycleScope.launchWhenStarted { adapter.notifyTetherTypeChanged() } } } } override fun onServiceDisconnected(name: ComponentName?) { (binder ?: return).routingsChanged -= this binder = null - if (Build.VERSION.SDK_INT >= 30) TetherType.listener -= this + if (Build.VERSION.SDK_INT >= 30) { + TetherType.listener -= this + TetheringManager.unregisterTetheringEventCallback(adapter) + adapter.lastErrors.clear() + } requireContext().unregisterReceiver(receiver) } } 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 5d32771d..e60406b2 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/TetheringManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/TetheringManager.kt @@ -629,6 +629,7 @@ object TetheringManager { * @return error The error code of the last error tethering or untethering the named * interface */ + @Deprecated("Use {@link TetheringEventCallback#onError(String, int)} instead.") fun getLastTetherError(iface: String): Int = getLastTetherError(Services.connectivity, iface) as Int val tetherErrorLookup = ConstantLookup("TETHER_ERROR_", From a67d435c764cdb7fb2589e2a187ce9551bf3de9f Mon Sep 17 00:00:00 2001 From: Mygod Date: Thu, 22 Apr 2021 14:36:15 -0400 Subject: [PATCH 002/112] Bump compileSdkVersion --- mobile/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/build.gradle.kts b/mobile/build.gradle.kts index 73faba57..2d102416 100644 --- a/mobile/build.gradle.kts +++ b/mobile/build.gradle.kts @@ -11,13 +11,13 @@ plugins { android { val javaVersion = JavaVersion.VERSION_1_8 val targetSdk = 29 - buildToolsVersion = "30.0.3" + buildToolsVersion = "31.0.0-rc4" compileOptions { isCoreLibraryDesugaringEnabled = true sourceCompatibility = javaVersion targetCompatibility = javaVersion } - compileSdk = 30 + compileSdkPreview = "android-S" kotlinOptions.jvmTarget = javaVersion.toString() defaultConfig { applicationId = "be.mygod.vpnhotspot" From 4b55227ed04736a195fe6ef4b5e4d217a7b10ad0 Mon Sep 17 00:00:00 2001 From: Mygod Date: Thu, 22 Apr 2021 16:31:59 -0400 Subject: [PATCH 003/112] Try to use registerBestMatchingNetworkCallback --- .../net/monitor/DefaultNetworkMonitor.kt | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/DefaultNetworkMonitor.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/DefaultNetworkMonitor.kt index f329461a..6597cb9e 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/DefaultNetworkMonitor.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/DefaultNetworkMonitor.kt @@ -6,6 +6,8 @@ import android.net.LinkProperties import android.net.Network import android.net.NetworkCapabilities import android.os.Build +import android.os.Handler +import android.os.Looper import be.mygod.vpnhotspot.util.Services import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch @@ -51,15 +53,22 @@ object DefaultNetworkMonitor : UpstreamMonitor() { callback.onAvailable(currentLinkProperties) } } else { - if (Build.VERSION.SDK_INT in 24..27) @TargetApi(24) { - Services.connectivity.registerDefaultNetworkCallback(networkCallback) - } else try { - Services.connectivity.requestNetwork(networkRequest, networkCallback) - } catch (e: SecurityException) { - // SecurityException would be thrown in requestNetwork on Android 6.0 thanks to Google's stupid bug - if (Build.VERSION.SDK_INT != 23) throw e - GlobalScope.launch { callback.onFallback() } - return + when (Build.VERSION.SDK_INT) { + in 31..Int.MAX_VALUE -> @TargetApi(31) { + Services.connectivity.registerBestMatchingNetworkCallback(networkRequest, networkCallback, + Handler(Looper.getMainLooper())) + } + in 24..27 -> @TargetApi(24) { + Services.connectivity.registerDefaultNetworkCallback(networkCallback) + } + else -> try { + Services.connectivity.requestNetwork(networkRequest, networkCallback) + } catch (e: SecurityException) { + // SecurityException would be thrown in requestNetwork on Android 6.0 thanks to Google's stupid bug + if (Build.VERSION.SDK_INT != 23) throw e + GlobalScope.launch { callback.onFallback() } + return + } } registered = true } From 54e92a25db9cb3c3db6450e4bb47945c5b32bb9b Mon Sep 17 00:00:00 2001 From: Mygod Date: Wed, 19 May 2021 13:27:30 -0400 Subject: [PATCH 004/112] Allow testing for targeting latest SDK --- mobile/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/build.gradle.kts b/mobile/build.gradle.kts index 2d102416..89472845 100644 --- a/mobile/build.gradle.kts +++ b/mobile/build.gradle.kts @@ -22,7 +22,7 @@ android { defaultConfig { applicationId = "be.mygod.vpnhotspot" minSdk = 21 - this.targetSdk = targetSdk + if (targetSdk == 31) targetSdkPreview = "S" else this.targetSdk = targetSdk resourceConfigurations.addAll(arrayOf("it", "ru", "zh-rCN", "zh-rTW")) versionCode = 260 versionName = "2.11.7" From 6b00eb7cf9a6a225ddb93ba9c8066c904c65e6aa Mon Sep 17 00:00:00 2001 From: Mygod Date: Wed, 19 May 2021 13:27:42 -0400 Subject: [PATCH 005/112] Add mandatory exported attributes --- mobile/src/main/AndroidManifest.xml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/mobile/src/main/AndroidManifest.xml b/mobile/src/main/AndroidManifest.xml index c16a50a7..78768505 100644 --- a/mobile/src/main/AndroidManifest.xml +++ b/mobile/src/main/AndroidManifest.xml @@ -69,6 +69,7 @@ @@ -100,6 +101,7 @@ + android:enabled="false" + android:exported="true"> From d379b1bfc0a025621428ccb0e97b05fb011e3492 Mon Sep 17 00:00:00 2001 From: Mygod Date: Wed, 19 May 2021 19:07:26 -0400 Subject: [PATCH 006/112] Support new SoftAp callback methods on Android 12 beta 1 --- .../vpnhotspot/net/wifi/WifiApManager.kt | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt index 69715336..748478bf 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt @@ -9,6 +9,7 @@ import android.net.wifi.WifiManager import android.os.Build import android.os.Handler import androidx.annotation.RequiresApi +import androidx.core.os.BuildCompat import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.net.wifi.SoftApConfigurationCompat.Companion.toCompat import be.mygod.vpnhotspot.util.ConstantLookup @@ -140,6 +141,9 @@ object WifiApManager { null // no return value as of API 30 } else invokeActual(proxy, method, args) + @RequiresApi(30) + private fun dispatchInfoChanged(softApInfo: Any?) = + callback.onInfoChanged(getFrequency(softApInfo) as Int, getBandwidth(softApInfo) as Int) private fun invokeActual(proxy: Any, method: Method, args: Array?): Any? { val noArgs = args?.size ?: 0 return when (val name = method.name) { @@ -154,17 +158,29 @@ object WifiApManager { } "onConnectedClientsChanged" -> @TargetApi(30) { if (Build.VERSION.SDK_INT < 30) Timber.w(Exception("Unexpected onConnectedClientsChanged")) - if (noArgs != 1) Timber.w("Unexpected args for $name: ${args?.contentToString()}") - callback.onConnectedClientsChanged((args!![0] as? Iterable<*> ?: return null) - .map { getMacAddress(it) as MacAddress }) + callback.onConnectedClientsChanged(when (noArgs) { + 1 -> args!![0] as? Iterable<*> ?: return null + 2 -> { + if (!BuildCompat.isAtLeastS()) Timber.w(Exception( + "Unexpected onConnectedClientsChanged API 31+")) + dispatchInfoChanged(args!![0]) + args[1] as? Iterable<*> ?: return null + } + else -> { + Timber.w("Unexpected args for $name: ${args?.contentToString()}") + return null + } + }.map { getMacAddress(it) as MacAddress }) } "onInfoChanged" -> @TargetApi(30) { if (Build.VERSION.SDK_INT < 30) Timber.w(Exception("Unexpected onInfoChanged")) if (noArgs != 1) Timber.w("Unexpected args for $name: ${args?.contentToString()}") - val softApInfo = args!![0] - if (softApInfo != null && classSoftApInfo.isAssignableFrom(softApInfo.javaClass)) { - callback.onInfoChanged(getFrequency(softApInfo) as Int, getBandwidth(softApInfo) as Int) - } else null + val arg = args!![0] + if (arg is List<*>) { + if (!BuildCompat.isAtLeastS()) Timber.w(Exception("Unexpected onInfoChanged API 31+")) + if (arg.size != 1) Timber.w("Unexpected args for $name: ${args.contentToString()}") + else dispatchInfoChanged(arg[0]) + } else dispatchInfoChanged(arg) } "onCapabilityChanged" -> @TargetApi(30) { if (Build.VERSION.SDK_INT < 30) Timber.w(Exception("Unexpected onCapabilityChanged")) From e01dfe992bf125c9761422270475e400668e593e Mon Sep 17 00:00:00 2001 From: Mygod Date: Wed, 19 May 2021 19:08:57 -0400 Subject: [PATCH 007/112] Use Manifest.permission --- .../be/mygod/vpnhotspot/manage/TetherManager.kt | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) 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 df3889e4..151a0a85 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt @@ -15,12 +15,10 @@ import androidx.annotation.RequiresApi import androidx.core.net.toUri import androidx.core.os.BuildCompat import androidx.core.view.updatePaddingRelative -import androidx.fragment.app.Fragment import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import androidx.recyclerview.widget.RecyclerView import be.mygod.vpnhotspot.App.Companion.app -import be.mygod.vpnhotspot.BuildConfig import be.mygod.vpnhotspot.MainActivity import be.mygod.vpnhotspot.R import be.mygod.vpnhotspot.databinding.ListitemInterfaceBinding @@ -223,11 +221,6 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), } @RequiresApi(24) class Bluetooth(parent: TetheringFragment) : TetherManager(parent), DefaultLifecycleObserver { - companion object { - // TODO: migrate to framework Manifest.permission when stable - private const val BLUETOOTH_CONNECT = "android.permission.BLUETOOTH_CONNECT" - } - private val tethering = BluetoothTethering(parent.requireContext()) { data.notifyChange() } init { @@ -236,11 +229,11 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), fun ensureInit(context: Context) = tethering.ensureInit(context) override fun onResume(owner: LifecycleOwner) { - if (!BuildCompat.isAtLeastS() || parent.requireContext().checkSelfPermission(BLUETOOTH_CONNECT) == - PackageManager.PERMISSION_GRANTED) { - ensureInit(parent.requireContext()) - } else if (parent.shouldShowRequestPermissionRationale(BLUETOOTH_CONNECT)) { - parent.requestBluetooth.launch(BLUETOOTH_CONNECT) + if (!BuildCompat.isAtLeastS() || parent.requireContext().checkSelfPermission( + Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED) { + ensureInit(parent.requireContext()) + } else if (parent.shouldShowRequestPermissionRationale(Manifest.permission.BLUETOOTH_CONNECT)) { + parent.requestBluetooth.launch(Manifest.permission.BLUETOOTH_CONNECT) } } override fun onDestroy(owner: LifecycleOwner) = tethering.close() From c6e71a45a0fc1acbaebea6005acf5ef069e2a6f5 Mon Sep 17 00:00:00 2001 From: Mygod Date: Wed, 26 May 2021 16:01:02 -0400 Subject: [PATCH 008/112] Update reflection API to latest commit --- README.md | 244 +++++++++++++++++++++++++++--------------------------- 1 file changed, 123 insertions(+), 121 deletions(-) diff --git a/README.md b/README.md index c78eed87..cfea16ac 100644 --- a/README.md +++ b/README.md @@ -144,140 +144,142 @@ You can also use WPS to connect your 2.4GHz-only device to force the repeater to _a.k.a. things that can go wrong if this app doesn't work._ This is a list of stuff that might impact this app's functionality if unavailable. -This is only meant to be an index. You can read more in the source code. +This is only meant to be an index. +You can read more in the source code. +API restrictions are updated up to [commit `ebe7044`](https://android.googlesource.com/platform/prebuilts/runtime/+/ebe7044/appcompat/hiddenapi-flags.csv). Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded or implicitly used) -* (prior to API 30) [`Landroid/net/ConnectivityManager;->getLastTetherError(Ljava/lang/String;)I,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#144306) +* (prior to API 30) `Landroid/net/ConnectivityManager;->getLastTetherError(Ljava/lang/String;)I,max-target-r` * (since API 30) `Landroid/net/ConnectivityModuleConnector;->IN_PROCESS_SUFFIX:Ljava/lang/String;` -* (since API 30) [`Landroid/net/TetheringManager$TetheringEventCallback;->onTetherableInterfaceRegexpsChanged(Landroid/net/TetheringManager$TetheringInterfaceRegexps;)V,blacklist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#148899) -* (since API 30) `Landroid/net/TetheringManager;->TETHERING_WIGIG:I` -* (prior to API 30) [`Landroid/net/wifi/WifiConfiguration$KeyMgmt;->FT_PSK:I,greylist-max-o`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153923) -* (prior to API 30) [`Landroid/net/wifi/WifiConfiguration$KeyMgmt;->WPA_PSK_SHA256:I,blacklist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153936) -* (since API 23, prior to API 30) [`Landroid/net/wifi/WifiConfiguration;->AP_BAND_2GHZ:I,greylist-max-o`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#154057) -* (since API 23, prior to API 30) [`Landroid/net/wifi/WifiConfiguration;->AP_BAND_5GHZ:I,greylist-max-o`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#154058) -* (since API 23, prior to API 30) [`Landroid/net/wifi/WifiConfiguration;->AP_BAND_ANY:I,greylist-max-o`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#154059) -* (since API 23, prior to API 30) [`Landroid/net/wifi/WifiConfiguration;->apBand:I,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#154101) -* (since API 23, prior to API 30) [`Landroid/net/wifi/WifiConfiguration;->apChannel:I,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#154102) -* (since API 28, prior to API 30) [`Landroid/net/wifi/WifiManager$SoftApCallback;->onNumClientsChanged(I)V,greylist-max-o`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#132005) -* (since API 26) [`Landroid/net/wifi/WifiManager;->cancelLocalOnlyHotspotRequest()V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#154947s) +* (since API 30) `Landroid/net/TetheringManager$TetheringEventCallback;->onTetherableInterfaceRegexpsChanged(Landroid/net/TetheringManager$TetheringInterfaceRegexps;)V,blocked` +* (since API 30) `Landroid/net/TetheringManager;->TETHERING_WIGIG:I,blocked` +* (prior to API 30) `Landroid/net/wifi/WifiConfiguration$KeyMgmt;->FT_PSK:I,lo-prio,max-target-o` +* (prior to API 30) `Landroid/net/wifi/WifiConfiguration$KeyMgmt;->WPA_PSK_SHA256:I,blocked` +* (since API 23, prior to API 30) `Landroid/net/wifi/WifiConfiguration;->AP_BAND_2GHZ:I,lo-prio,max-target-o` +* (since API 23, prior to API 30) `Landroid/net/wifi/WifiConfiguration;->AP_BAND_5GHZ:I,lo-prio,max-target-o` +* (since API 23, prior to API 30) `Landroid/net/wifi/WifiConfiguration;->AP_BAND_ANY:I,lo-prio,max-target-o` +* (since API 23, prior to API 30) `Landroid/net/wifi/WifiConfiguration;->apBand:I,unsupported` +* (since API 23, prior to API 30) `Landroid/net/wifi/WifiConfiguration;->apChannel:I,unsupported` +* (since API 28, prior to API 30) `Landroid/net/wifi/WifiManager$SoftApCallback;->onNumClientsChanged(I)V,greylist-max-o` +* (since API 26) `Landroid/net/wifi/WifiManager;->cancelLocalOnlyHotspotRequest()V,unsupported` * (prior to API 26) `Landroid/net/wifi/WifiManager;->setWifiApEnabled(Landroid/net/wifi/WifiConfiguration;Z)Z` -* [`Landroid/net/wifi/p2p/WifiP2pConfig$Builder;->MAC_ANY_ADDRESS:Landroid/net/MacAddress;,blacklist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#157883) -* [`Landroid/net/wifi/p2p/WifiP2pManager;->startWps(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Landroid/net/wifi/WpsInfo;Landroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#158332) -* (since API 28) [`Landroid/provider/Settings$Global;->SOFT_AP_TIMEOUT_ENABLED:Ljava/lang/String;,greylist-max-o`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#183735) -* (prior to API 30) [`Lcom/android/internal/R$array;->config_tether_bluetooth_regexs:I,greylist-max-q`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#272546) -* (prior to API 30) [`Lcom/android/internal/R$array;->config_tether_usb_regexs:I,greylist-max-q`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#272549) -* (prior to API 30) [`Lcom/android/internal/R$array;->config_tether_wifi_regexs:I,greylist-max-q`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#272551) -* (since API 28, prior to API 30) [`Lcom/android/internal/R$integer;->config_wifi_framework_soft_ap_timeout_delay:I,greylist-max-o`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#245007) -* [`Lcom/android/internal/R$string;->config_ethernet_iface_regex:I,greylist-max-o`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#276573) +* `Landroid/net/wifi/p2p/WifiP2pConfig$Builder;->MAC_ANY_ADDRESS:Landroid/net/MacAddress;,blocked` +* `Landroid/net/wifi/p2p/WifiP2pManager;->startWps(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Landroid/net/wifi/WpsInfo;Landroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V,max-target-r` +* (since API 28, prior to API 30) `Landroid/provider/Settings$Global;->SOFT_AP_TIMEOUT_ENABLED:Ljava/lang/String;,lo-prio,max-target-o` +* (prior to API 30) `Lcom/android/internal/R$array;->config_tether_bluetooth_regexs:I,max-target-q` +* (prior to API 30) `Lcom/android/internal/R$array;->config_tether_usb_regexs:I,max-target-q` +* (prior to API 30) `Lcom/android/internal/R$array;->config_tether_wifi_regexs:I,max-target-q` +* (since API 28, prior to API 30) `Lcom/android/internal/R$integer;->config_wifi_framework_soft_ap_timeout_delay:I,greylist-max-o` +* `Lcom/android/internal/R$string;->config_ethernet_iface_regex:I,lo-prio,max-target-o` * (since API 27) `Lcom/android/server/connectivity/tethering/OffloadHardwareInterface;->DEFAULT_TETHER_OFFLOAD_DISABLED:I` * (since API 30) `Lcom/android/server/wifi/WifiContext;->ACTION_RESOURCES_APK:Ljava/lang/String;` * (since API 29) `Lcom/android/server/wifi/p2p/WifiP2pServiceImpl;->ANONYMIZED_DEVICE_ADDRESS:Ljava/lang/String;` * (since API 30) `Lcom/android/server/SystemServer;->TETHERING_CONNECTOR_CLASS:Ljava/lang/String;` -* (since API 26) [`Ljava/lang/invoke/MethodHandles$Lookup;->(Ljava/lang/Class;I)V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#370415) -* (since API 26) [`Ljava/lang/invoke/MethodHandles$Lookup;->ALL_MODES:I,greylist-max-o`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#370416) -* (prior to API 29) [`Ljava/net/InetAddress;->parseNumericAddress(Ljava/lang/String;)Ljava/net/InetAddress;,core-platform-api,greylist-max-p`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#372578) +* (since API 26) `Ljava/lang/invoke/MethodHandles$Lookup;->(Ljava/lang/Class;I)V,unsupported` +* (since API 26) `Ljava/lang/invoke/MethodHandles$Lookup;->ALL_MODES:I,lo-prio,max-target-o` +* (prior to API 29) `Ljava/net/InetAddress;->parseNumericAddress(Ljava/lang/String;)Ljava/net/InetAddress;,core-platform-api,max-target-p`
Hidden whitelisted APIs: (same catch as above, however, things in this list are less likely to be broken) -* (since API 24) [`Landroid/bluetooth/BluetoothPan;->isTetheringOn()Z,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#35264) -* (since API 24) [`Landroid/bluetooth/BluetoothProfile;->PAN:I,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#35361) -* (since API 30) [`Landroid/content/Context;->TETHERING_SERVICE:Ljava/lang/String;,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#41038) -* (since API 24, prior to API 30) [`Landroid/net/ConnectivityManager$OnStartTetheringCallback;->()V,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#144095) -* (since API 24, prior to API 30) [`Landroid/net/ConnectivityManager$OnStartTetheringCallback;->onTetheringFailed()V,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#144096) -* (since API 24, prior to API 30) [`Landroid/net/ConnectivityManager$OnStartTetheringCallback;->onTetheringStarted()V,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#144097) -* (since API 24, prior to API 30) [`Landroid/net/ConnectivityManager;->startTethering(IZLandroid/net/ConnectivityManager$OnStartTetheringCallback;Landroid/os/Handler;)V,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#144406) -* (since API 24, prior to API 30) [`Landroid/net/ConnectivityManager;->stopTethering(I)V,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#144408) -* [`Landroid/net/LinkProperties;->getAllInterfaceNames()Ljava/util/List;,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#146558) -* [`Landroid/net/LinkProperties;->getAllRoutes()Ljava/util/List;,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#146560) -* (since API 30) [`Landroid/net/TetheringManager$StartTetheringCallback;->onTetheringFailed(I)V,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#148881) -* (since API 30) [`Landroid/net/TetheringManager$StartTetheringCallback;->onTetheringStarted()V,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#148882) -* (since API 30) [`Landroid/net/TetheringManager$TetheringEventCallback;->onClientsChanged(Ljava/util/Collection;)V,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#148896) -* (since API 30) [`Landroid/net/TetheringManager$TetheringEventCallback;->onError(Ljava/lang/String;I)V,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#148897) -* (since API 30) [`Landroid/net/TetheringManager$TetheringEventCallback;->onOffloadStatusChanged(I)V,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#148898) -* (since API 30) [`Landroid/net/TetheringManager$TetheringEventCallback;->onTetherableInterfacesChanged(Ljava/util/List;)V,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#148900) -* (since API 30) [`Landroid/net/TetheringManager$TetheringEventCallback;->onTetheredInterfacesChanged(Ljava/util/List;)V,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#148901) -* (since API 30) [`Landroid/net/TetheringManager$TetheringEventCallback;->onTetheringSupported(Z)V,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#148902) -* (since API 30) [`Landroid/net/TetheringManager$TetheringEventCallback;->onUpstreamChanged(Landroid/net/Network;)V,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#148903) -* (since API 30) [`Landroid/net/TetheringManager$TetheringRequest$Builder;->(I)V,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#148913) -* (since API 30) [`Landroid/net/TetheringManager$TetheringRequest$Builder;->build()Landroid/net/TetheringManager$TetheringRequest;,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#148914) -* (since API 30) [`Landroid/net/TetheringManager$TetheringRequest$Builder;->setExemptFromEntitlementCheck(Z)Landroid/net/TetheringManager$TetheringRequest$Builder;,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#148916) -* (since API 30) [`Landroid/net/TetheringManager$TetheringRequest$Builder;->setShouldShowEntitlementUi(Z)Landroid/net/TetheringManager$TetheringRequest$Builder;,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#148917) -* [`Landroid/net/TetheringManager;->ACTION_TETHER_STATE_CHANGED:Ljava/lang/String;,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#148932) -* (since API 26) [`Landroid/net/TetheringManager;->EXTRA_ACTIVE_LOCAL_ONLY:Ljava/lang/String;,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#148935) -* [`Landroid/net/TetheringManager;->EXTRA_ACTIVE_TETHER:Ljava/lang/String;,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#148936) -* [`Landroid/net/TetheringManager;->EXTRA_ERRORED_TETHER:Ljava/lang/String;,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#148938) -* (since API 24) [`Landroid/net/TetheringManager;->TETHERING_BLUETOOTH:I,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#148940) -* (since API 30) [`Landroid/net/TetheringManager;->TETHERING_ETHERNET:I,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#148941) -* (since API 30) [`Landroid/net/TetheringManager;->TETHERING_NCM:I,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#148943) -* (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) -* (since API 30) [`Landroid/net/TetheringManager;->registerTetheringEventCallback(Ljava/util/concurrent/Executor;Landroid/net/TetheringManager$TetheringEventCallback;)V,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#149003) -* (since API 30) [`Landroid/net/TetheringManager;->startTethering(Landroid/net/TetheringManager$TetheringRequest;Ljava/util/concurrent/Executor;Landroid/net/TetheringManager$StartTetheringCallback;)V,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#149009) -* (since API 30) [`Landroid/net/TetheringManager;->stopTethering(I)V,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#149011) -* (since API 30) [`Landroid/net/TetheringManager;->unregisterTetheringEventCallback(Landroid/net/TetheringManager$TetheringEventCallback;)V,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#149014) -* (since API 30) [`Landroid/net/wifi/SoftApCapability;->SOFTAP_FEATURE_*:J,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153656) -* (since API 30) [`Landroid/net/wifi/SoftApCapability;->areFeaturesSupported(J)Z,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153660) -* (since API 30) [`Landroid/net/wifi/SoftApCapability;->getMaxSupportedClients()I,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153663) -* (since API 30) [`Landroid/net/wifi/SoftApConfiguration$Builder;->()V,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153707) -* (since API 30) [`Landroid/net/wifi/SoftApConfiguration$Builder;->(Landroid/net/wifi/SoftApConfiguration;)V,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153708) -* (since API 30) [`Landroid/net/wifi/SoftApConfiguration$Builder;->build()Landroid/net/wifi/SoftApConfiguration;,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153709) -* (since API 30) [`Landroid/net/wifi/SoftApConfiguration$Builder;->setAllowedClientList(Ljava/util/List;)Landroid/net/wifi/SoftApConfiguration$Builder;,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153723) -* (since API 30) [`Landroid/net/wifi/SoftApConfiguration$Builder;->setAutoShutdownEnabled(Z)Landroid/net/wifi/SoftApConfiguration$Builder;,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153724) -* (since API 30) [`Landroid/net/wifi/SoftApConfiguration$Builder;->setBand(I)Landroid/net/wifi/SoftApConfiguration$Builder;,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153725) -* (since API 30) [`Landroid/net/wifi/SoftApConfiguration$Builder;->setBlockedClientList(Ljava/util/List;)Landroid/net/wifi/SoftApConfiguration$Builder;,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153726) -* (since API 30) [`Landroid/net/wifi/SoftApConfiguration$Builder;->setBssid(Landroid/net/MacAddress;)Landroid/net/wifi/SoftApConfiguration$Builder;,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153727) -* (since API 30) [`Landroid/net/wifi/SoftApConfiguration$Builder;->setChannel(II)Landroid/net/wifi/SoftApConfiguration$Builder;,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153728) -* (since API 30) [`Landroid/net/wifi/SoftApConfiguration$Builder;->setClientControlByUserEnabled(Z)Landroid/net/wifi/SoftApConfiguration$Builder;,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153729) -* (since API 30) [`Landroid/net/wifi/SoftApConfiguration$Builder;->setHiddenSsid(Z)Landroid/net/wifi/SoftApConfiguration$Builder;,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153730) -* (since API 30) [`Landroid/net/wifi/SoftApConfiguration$Builder;->setMaxNumberOfClients(I)Landroid/net/wifi/SoftApConfiguration$Builder;,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153731) -* (since API 30) [`Landroid/net/wifi/SoftApConfiguration$Builder;->setPassphrase(Ljava/lang/String;I)Landroid/net/wifi/SoftApConfiguration$Builder;,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153732) -* (since API 30) [`Landroid/net/wifi/SoftApConfiguration$Builder;->setShutdownTimeoutMillis(J)Landroid/net/wifi/SoftApConfiguration$Builder;,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153733) -* (since API 30) [`Landroid/net/wifi/SoftApConfiguration$Builder;->setSsid(Ljava/lang/String;)Landroid/net/wifi/SoftApConfiguration$Builder;,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153734) -* (since API 30) [`Landroid/net/wifi/SoftApConfiguration;->BAND_2GHZ:I,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153738) -* (since API 30) [`Landroid/net/wifi/SoftApConfiguration;->BAND_5GHZ:I,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153739) -* (since API 30) [`Landroid/net/wifi/SoftApConfiguration;->BAND_6GHZ:I,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153740) -* (since API 30) [`Landroid/net/wifi/SoftApConfiguration;->BAND_ANY:I,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153741) -* (since API 30) [`Landroid/net/wifi/SoftApConfiguration;->getAllowedClientList()Ljava/util/List;,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153773) -* (since API 30) [`Landroid/net/wifi/SoftApConfiguration;->getBand()I,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153774) -* (since API 30) [`Landroid/net/wifi/SoftApConfiguration;->getBlockedClientList()Ljava/util/List;,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153775) -* (since API 30) [`Landroid/net/wifi/SoftApConfiguration;->getChannel()I,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153777) -* (since API 30) [`Landroid/net/wifi/SoftApConfiguration;->getMaxNumberOfClients()I,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153778) -* (since API 30) [`Landroid/net/wifi/SoftApConfiguration;->getShutdownTimeoutMillis()J,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153781) -* (since API 30) [`Landroid/net/wifi/SoftApConfiguration;->isAutoShutdownEnabled()Z,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153784) -* (since API 30) [`Landroid/net/wifi/SoftApConfiguration;->isClientControlByUserEnabled()Z,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153787) -* (since API 30) [`Landroid/net/wifi/SoftApInfo;->CHANNEL_WIDTH_*:I,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153813) -* (since API 30) [`Landroid/net/wifi/SoftApInfo;->CHANNEL_WIDTH_INVALID:I,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153819) -* (since API 30) [`Landroid/net/wifi/SoftApInfo;->getBandwidth()I,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153825) -* (since API 30) [`Landroid/net/wifi/SoftApInfo;->getFrequency()I,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153826) -* (since API 30) [`Landroid/net/wifi/WifiClient;->getMacAddress()Landroid/net/MacAddress;,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153881) -* (prior to API 30) [`Landroid/net/wifi/WifiConfiguration$KeyMgmt;->WPA2_PSK:I,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#153932) -* (since API 30) [`Landroid/net/wifi/WifiManager$SoftApCallback;->onBlockedClientConnecting(Landroid/net/wifi/WifiClient;I)V,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#154681) -* (since API 30) [`Landroid/net/wifi/WifiManager$SoftApCallback;->onCapabilityChanged(Landroid/net/wifi/SoftApCapability;)V,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#154682) -* (since API 30) [`Landroid/net/wifi/WifiManager$SoftApCallback;->onConnectedClientsChanged(Ljava/util/List;)V,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#154683) -* (since API 30) [`Landroid/net/wifi/WifiManager$SoftApCallback;->onInfoChanged(Landroid/net/wifi/SoftApInfo;)V,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#154684) -* (since API 28) [`Landroid/net/wifi/WifiManager$SoftApCallback;->onStateChanged(II)V,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#154685) -* (since API 30) [`Landroid/net/wifi/WifiManager;->SAP_CLIENT_BLOCK_REASON_CODE_*:I,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#154840) -* (since API 28) [`Landroid/net/wifi/WifiManager;->SAP_START_FAILURE_*:I,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#154843) -* (since API 28) [`Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_FAILED:I,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#154868) -* (since API 30) [`Landroid/net/wifi/WifiManager;->getSoftApConfiguration()Landroid/net/wifi/SoftApConfiguration;,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#154985) -* (prior to API 30) [`Landroid/net/wifi/WifiManager;->getWifiApConfiguration()Landroid/net/wifi/WifiConfiguration;,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#154989) -* (since API 28) [`Landroid/net/wifi/WifiManager;->registerSoftApCallback(Ljava/util/concurrent/Executor;Landroid/net/wifi/WifiManager$SoftApCallback;)V,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#155042) -* (since API 30) [`Landroid/net/wifi/WifiManager;->setSoftApConfiguration(Landroid/net/wifi/SoftApConfiguration;)Z,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#155063) -* (prior to API 30) [`Landroid/net/wifi/WifiManager;->setWifiApConfiguration(Landroid/net/wifi/WifiConfiguration;)Z,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#155067) -* (since API 28) [`Landroid/net/wifi/WifiManager;->unregisterSoftApCallback(Landroid/net/wifi/WifiManager$SoftApCallback;)V,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#155087) -* [`Landroid/net/wifi/p2p/WifiP2pGroupList;->getGroupList()Ljava/util/List;,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#158079) -* [`Landroid/net/wifi/p2p/WifiP2pManager$PersistentGroupInfoListener;->onPersistentGroupInfoAvailable(Landroid/net/wifi/p2p/WifiP2pGroupList;)V,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#158156) -* [`Landroid/net/wifi/p2p/WifiP2pManager;->deletePersistentGroup(Landroid/net/wifi/p2p/WifiP2pManager$Channel;ILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#158296) -* [`Landroid/net/wifi/p2p/WifiP2pManager;->requestPersistentGroupInfo(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Landroid/net/wifi/p2p/WifiP2pManager$PersistentGroupInfoListener;)V,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#158320) -* [`Landroid/net/wifi/p2p/WifiP2pManager;->setWifiP2pChannels(Landroid/net/wifi/p2p/WifiP2pManager$Channel;IILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#158330) -* [`Landroid/provider/Settings$Global;->TETHER_OFFLOAD_DISABLED:Ljava/lang/String;,system-api,test-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/4601d91/appcompat/hiddenapi-flags.csv#183757) +* (since API 24) `Landroid/bluetooth/BluetoothPan;->isTetheringOn()Z,sdk,system-api,test-api` +* (since API 24) `Landroid/bluetooth/BluetoothProfile;->PAN:I,sdk,system-api,test-api` +* (since API 30) `Landroid/content/Context;->TETHERING_SERVICE:Ljava/lang/String;,sdk,system-api,test-api` +* (since API 24, prior to API 30) `Landroid/net/ConnectivityManager$OnStartTetheringCallback;->()V,sdk,system-api,test-api` +* (since API 24, prior to API 30) `Landroid/net/ConnectivityManager$OnStartTetheringCallback;->onTetheringFailed()V,sdk,system-api,test-api` +* (since API 24, prior to API 30) `Landroid/net/ConnectivityManager$OnStartTetheringCallback;->onTetheringStarted()V,sdk,system-api,test-api` +* (since API 24, prior to API 30) `Landroid/net/ConnectivityManager;->startTethering(IZLandroid/net/ConnectivityManager$OnStartTetheringCallback;Landroid/os/Handler;)V,sdk,system-api,test-api` +* (since API 24, prior to API 30) `Landroid/net/ConnectivityManager;->stopTethering(I)V,sdk,system-api,test-api` +* `Landroid/net/LinkProperties;->getAllInterfaceNames()Ljava/util/List;,sdk,system-api,test-api` +* `Landroid/net/LinkProperties;->getAllRoutes()Ljava/util/List;,sdk,system-api,test-api` +* (since API 30) `Landroid/net/TetheringManager$StartTetheringCallback;->onTetheringFailed(I)V,sdk,system-api,test-api` +* (since API 30) `Landroid/net/TetheringManager$StartTetheringCallback;->onTetheringStarted()V,sdk,system-api,test-api` +* (since API 30) `Landroid/net/TetheringManager$TetheringEventCallback;->onClientsChanged(Ljava/util/Collection;)V,sdk,system-api,test-api` +* (since API 30) `Landroid/net/TetheringManager$TetheringEventCallback;->onError(Ljava/lang/String;I)V,sdk,system-api,test-api` +* (since API 30) `Landroid/net/TetheringManager$TetheringEventCallback;->onOffloadStatusChanged(I)V,sdk,system-api,test-api` +* (since API 30) `Landroid/net/TetheringManager$TetheringEventCallback;->onTetherableInterfacesChanged(Ljava/util/List;)V,sdk,system-api,test-api` +* (since API 30) `Landroid/net/TetheringManager$TetheringEventCallback;->onTetheredInterfacesChanged(Ljava/util/List;)V,sdk,system-api,test-api` +* (since API 30) `Landroid/net/TetheringManager$TetheringEventCallback;->onTetheringSupported(Z)V,sdk,system-api,test-api` +* (since API 30) `Landroid/net/TetheringManager$TetheringEventCallback;->onUpstreamChanged(Landroid/net/Network;)V,sdk,system-api,test-api` +* (since API 30) `Landroid/net/TetheringManager$TetheringRequest$Builder;->(I)V,sdk,system-api,test-api` +* (since API 30) `Landroid/net/TetheringManager$TetheringRequest$Builder;->build()Landroid/net/TetheringManager$TetheringRequest;,sdk,system-api,test-api` +* (since API 30) `Landroid/net/TetheringManager$TetheringRequest$Builder;->setExemptFromEntitlementCheck(Z)Landroid/net/TetheringManager$TetheringRequest$Builder;,sdk,system-api,test-api` +* (since API 30) `Landroid/net/TetheringManager$TetheringRequest$Builder;->setShouldShowEntitlementUi(Z)Landroid/net/TetheringManager$TetheringRequest$Builder;,sdk,system-api,test-api` +* `Landroid/net/TetheringManager;->ACTION_TETHER_STATE_CHANGED:Ljava/lang/String;,sdk,system-api,test-api` +* (since API 26) `Landroid/net/TetheringManager;->EXTRA_ACTIVE_LOCAL_ONLY:Ljava/lang/String;,sdk,system-api,test-api` +* `Landroid/net/TetheringManager;->EXTRA_ACTIVE_TETHER:Ljava/lang/String;,sdk,system-api,test-api` +* `Landroid/net/TetheringManager;->EXTRA_ERRORED_TETHER:Ljava/lang/String;,sdk,system-api,test-api` +* (since API 24) `Landroid/net/TetheringManager;->TETHERING_BLUETOOTH:I,sdk,system-api,test-api` +* (since API 30) `Landroid/net/TetheringManager;->TETHERING_ETHERNET:I,sdk,system-api,test-api` +* (since API 30) `Landroid/net/TetheringManager;->TETHERING_NCM:I,sdk,system-api,test-api` +* (since API 24) `Landroid/net/TetheringManager;->TETHERING_USB:I,sdk,system-api,test-api` +* (since API 24) `Landroid/net/TetheringManager;->TETHERING_WIFI:I,sdk,system-api,test-api` +* `Landroid/net/TetheringManager;->TETHER_ERROR_*:I,sdk,system-api,test-api` +* (since API 30) `Landroid/net/TetheringManager;->TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION:I,sdk,system-api,test-api` +* (since API 30) `Landroid/net/TetheringManager;->TETHER_HARDWARE_OFFLOAD_FAILED:I,sdk,system-api,test-api` +* (since API 30) `Landroid/net/TetheringManager;->TETHER_HARDWARE_OFFLOAD_STARTED:I,sdk,system-api,test-api` +* (since API 30) `Landroid/net/TetheringManager;->TETHER_HARDWARE_OFFLOAD_STOPPED:I,sdk,system-api,test-api` +* (since API 30) `Landroid/net/TetheringManager;->registerTetheringEventCallback(Ljava/util/concurrent/Executor;Landroid/net/TetheringManager$TetheringEventCallback;)V,sdk,system-api,test-api` +* (since API 30) `Landroid/net/TetheringManager;->startTethering(Landroid/net/TetheringManager$TetheringRequest;Ljava/util/concurrent/Executor;Landroid/net/TetheringManager$StartTetheringCallback;)V,sdk,system-api,test-api` +* (since API 30) `Landroid/net/TetheringManager;->stopTethering(I)V,sdk,system-api,test-api` +* (since API 30) `Landroid/net/TetheringManager;->unregisterTetheringEventCallback(Landroid/net/TetheringManager$TetheringEventCallback;)V,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApCapability;->SOFTAP_FEATURE_*:J,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApCapability;->areFeaturesSupported(J)Z,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApCapability;->getMaxSupportedClients()I,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->()V,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->(Landroid/net/wifi/SoftApConfiguration;)V,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->build()Landroid/net/wifi/SoftApConfiguration;,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setAllowedClientList(Ljava/util/List;)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setAutoShutdownEnabled(Z)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setBand(I)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setBlockedClientList(Ljava/util/List;)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setBssid(Landroid/net/MacAddress;)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setChannel(II)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setClientControlByUserEnabled(Z)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setHiddenSsid(Z)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setMaxNumberOfClients(I)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setPassphrase(Ljava/lang/String;I)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setShutdownTimeoutMillis(J)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setSsid(Ljava/lang/String;)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApConfiguration;->BAND_2GHZ:I,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApConfiguration;->BAND_5GHZ:I,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApConfiguration;->BAND_6GHZ:I,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApConfiguration;->BAND_ANY:I,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApConfiguration;->getAllowedClientList()Ljava/util/List;,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApConfiguration;->getBand()I,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApConfiguration;->getBlockedClientList()Ljava/util/List;,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApConfiguration;->getChannel()I,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApConfiguration;->getMaxNumberOfClients()I,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApConfiguration;->getShutdownTimeoutMillis()J,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApConfiguration;->isAutoShutdownEnabled()Z,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApConfiguration;->isClientControlByUserEnabled()Z,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApInfo;->CHANNEL_WIDTH_*:I,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApInfo;->CHANNEL_WIDTH_INVALID:I,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApInfo;->getBandwidth()I,system-api,whitelist` +* (since API 30) `Landroid/net/wifi/SoftApInfo;->getFrequency()I,system-api,whitelist` +* (since API 30) `Landroid/net/wifi/WifiClient;->getMacAddress()Landroid/net/MacAddress;,sdk,system-api,test-api` +* (prior to API 30) `Landroid/net/wifi/WifiConfiguration$KeyMgmt;->WPA2_PSK:I,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/WifiManager$SoftApCallback;->onBlockedClientConnecting(Landroid/net/wifi/WifiClient;I)V,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/WifiManager$SoftApCallback;->onCapabilityChanged(Landroid/net/wifi/SoftApCapability;)V,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/WifiManager$SoftApCallback;->onConnectedClientsChanged(Ljava/util/List;)V,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/WifiManager$SoftApCallback;->onInfoChanged(Landroid/net/wifi/SoftApInfo;)V,sdk,system-api,test-api` +* (since API 28) `Landroid/net/wifi/WifiManager$SoftApCallback;->onStateChanged(II)V,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/WifiManager;->SAP_CLIENT_BLOCK_REASON_CODE_*:I,sdk,system-api,test-api` +* (since API 28) `Landroid/net/wifi/WifiManager;->SAP_START_FAILURE_*:I,sdk,system-api,test-api` +* (since API 28) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_FAILED:I,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/WifiManager;->getSoftApConfiguration()Landroid/net/wifi/SoftApConfiguration;,sdk,system-api,test-api` +* (prior to API 30) `Landroid/net/wifi/WifiManager;->getWifiApConfiguration()Landroid/net/wifi/WifiConfiguration;,sdk,system-api,test-api` +* (since API 28) `Landroid/net/wifi/WifiManager;->registerSoftApCallback(Ljava/util/concurrent/Executor;Landroid/net/wifi/WifiManager$SoftApCallback;)V,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/WifiManager;->setSoftApConfiguration(Landroid/net/wifi/SoftApConfiguration;)Z,sdk,system-api,test-api` +* (prior to API 30) `Landroid/net/wifi/WifiManager;->setWifiApConfiguration(Landroid/net/wifi/WifiConfiguration;)Z,sdk,system-api,test-api` +* (since API 28) `Landroid/net/wifi/WifiManager;->unregisterSoftApCallback(Landroid/net/wifi/WifiManager$SoftApCallback;)V,sdk,system-api,test-api` +* `Landroid/net/wifi/p2p/WifiP2pGroupList;->getGroupList()Ljava/util/List;,sdk,system-api,test-api` +* `Landroid/net/wifi/p2p/WifiP2pManager$PersistentGroupInfoListener;->onPersistentGroupInfoAvailable(Landroid/net/wifi/p2p/WifiP2pGroupList;)V,sdk,system-api,test-api` +* `Landroid/net/wifi/p2p/WifiP2pManager;->deletePersistentGroup(Landroid/net/wifi/p2p/WifiP2pManager$Channel;ILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V,sdk,system-api,test-api` +* `Landroid/net/wifi/p2p/WifiP2pManager;->requestPersistentGroupInfo(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Landroid/net/wifi/p2p/WifiP2pManager$PersistentGroupInfoListener;)V,sdk,system-api,test-api` +* `Landroid/net/wifi/p2p/WifiP2pManager;->setWifiP2pChannels(Landroid/net/wifi/p2p/WifiP2pManager$Channel;IILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V,sdk,system-api,test-api` +* `Landroid/provider/Settings$Global;->TETHER_OFFLOAD_DISABLED:Ljava/lang/String;,sdk,system-api,test-api`
From b365db3d14d29edaf93fa91be143ef22feb52204 Mon Sep 17 00:00:00 2001 From: Mygod Date: Wed, 26 May 2021 16:22:23 -0400 Subject: [PATCH 009/112] Disable backup on Android 12 --- mobile/src/main/AndroidManifest.xml | 1 + mobile/src/main/res/xml/no_backup.xml | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 mobile/src/main/res/xml/no_backup.xml diff --git a/mobile/src/main/AndroidManifest.xml b/mobile/src/main/AndroidManifest.xml index 78768505..e694330c 100644 --- a/mobile/src/main/AndroidManifest.xml +++ b/mobile/src/main/AndroidManifest.xml @@ -58,6 +58,7 @@ + + + + + + + + From b31522680b48451555e1d6200982df2e88fef838 Mon Sep 17 00:00:00 2001 From: Mygod Date: Wed, 26 May 2021 17:35:24 -0400 Subject: [PATCH 010/112] Consider new onConnectedClientsChanged unexpected --- .../java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt index 748478bf..67bf6a0b 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt @@ -161,10 +161,9 @@ object WifiApManager { callback.onConnectedClientsChanged(when (noArgs) { 1 -> args!![0] as? Iterable<*> ?: return null 2 -> { - if (!BuildCompat.isAtLeastS()) Timber.w(Exception( - "Unexpected onConnectedClientsChanged API 31+")) - dispatchInfoChanged(args!![0]) - args[1] as? Iterable<*> ?: return null + Timber.w(Exception("Unexpected onConnectedClientsChanged API 31+")) + // dispatchInfoChanged(args!![0]) + args!![1] as? Iterable<*> ?: return null } else -> { Timber.w("Unexpected args for $name: ${args?.contentToString()}") From 7a40927bcc7c7e5f4178b33930876d8b9cb2e169 Mon Sep 17 00:00:00 2001 From: Mygod Date: Wed, 26 May 2021 19:11:20 -0400 Subject: [PATCH 011/112] Support new onInfoChanged method --- README.md | 1 + .../mygod/vpnhotspot/manage/TetherManager.kt | 22 ++++++++------ .../mygod/vpnhotspot/net/wifi/SoftApInfo.kt | 20 +++++++++++++ .../vpnhotspot/net/wifi/WifiApManager.kt | 29 +++++++++---------- .../mygod/vpnhotspot/root/WifiApCommands.kt | 8 ++--- 5 files changed, 51 insertions(+), 29 deletions(-) create mode 100644 mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApInfo.kt diff --git a/README.md b/README.md index cfea16ac..d51975fd 100644 --- a/README.md +++ b/README.md @@ -264,6 +264,7 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (since API 30) `Landroid/net/wifi/WifiManager$SoftApCallback;->onCapabilityChanged(Landroid/net/wifi/SoftApCapability;)V,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/WifiManager$SoftApCallback;->onConnectedClientsChanged(Ljava/util/List;)V,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/WifiManager$SoftApCallback;->onInfoChanged(Landroid/net/wifi/SoftApInfo;)V,sdk,system-api,test-api` +* (since API 31) `Landroid/net/wifi/WifiManager$SoftApCallback;->onInfoChanged(Ljava/util/List;)V,sdk,system-api,test-api` * (since API 28) `Landroid/net/wifi/WifiManager$SoftApCallback;->onStateChanged(II)V,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/WifiManager;->SAP_CLIENT_BLOCK_REASON_CODE_*:I,sdk,system-api,test-api` * (since API 28) `Landroid/net/wifi/WifiManager;->SAP_START_FAILURE_*:I,sdk,system-api,test-api` 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 151a0a85..b25cd043 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt @@ -8,6 +8,7 @@ import android.content.Intent import android.content.pm.PackageManager import android.net.MacAddress import android.os.Build +import android.os.Parcelable import android.provider.Settings import android.view.View import android.widget.Toast @@ -25,6 +26,7 @@ import be.mygod.vpnhotspot.databinding.ListitemInterfaceBinding import be.mygod.vpnhotspot.net.TetherType import be.mygod.vpnhotspot.net.TetheringManager import be.mygod.vpnhotspot.net.wifi.SoftApConfigurationCompat +import be.mygod.vpnhotspot.net.wifi.SoftApInfo import be.mygod.vpnhotspot.net.wifi.WifiApManager import be.mygod.vpnhotspot.root.WifiApCommands import be.mygod.vpnhotspot.util.readableMessage @@ -137,8 +139,7 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), WifiApManager.SoftApCallbackCompat { private var failureReason: Int? = null private var numClients: Int? = null - private var frequency = 0 - private var bandwidth = WifiApManager.CHANNEL_WIDTH_INVALID + private var info = emptyList() private var capability: Pair? = null init { @@ -166,9 +167,8 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), this.numClients = numClients if (Build.VERSION.SDK_INT >= 30) data.notifyChange() // only emits when onCapabilityChanged can be called } - override fun onInfoChanged(frequency: Int, bandwidth: Int) { - this.frequency = frequency - this.bandwidth = bandwidth + override fun onInfoChanged(info: List) { + this.info = info data.notifyChange() } override fun onCapabilityChanged(maxSupportedClients: Int, supportedFeatures: Long) { @@ -189,11 +189,15 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), override val tetherType get() = TetherType.WIFI override val type get() = VIEW_TYPE_WIFI override val text get() = listOfNotNull(failureReason?.let { WifiApManager.failureReasonLookup(it) }, baseError, - if (frequency != 0 || bandwidth != WifiApManager.CHANNEL_WIDTH_INVALID) { - parent.getString(R.string.tethering_manage_wifi_info, frequency, + info.run { + if (isEmpty()) null else joinToString("\n") @TargetApi(30) { + val info = SoftApInfo(it) + val frequency = info.frequency + parent.getString(R.string.tethering_manage_wifi_info, frequency, SoftApConfigurationCompat.frequencyToChannel(frequency), - WifiApManager.channelWidthLookup(bandwidth, true)) - } else null, + SoftApInfo.channelWidthLookup(info.bandwidth, true)) + } + }, capability?.let { (maxSupportedClients, supportedFeatures) -> app.resources.getQuantityString(R.plurals.tethering_manage_wifi_capabilities, maxSupportedClients, numClients ?: "?", maxSupportedClients, sequence { diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApInfo.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApInfo.kt new file mode 100644 index 00000000..c5de91de --- /dev/null +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApInfo.kt @@ -0,0 +1,20 @@ +package be.mygod.vpnhotspot.net.wifi + +import android.os.Parcelable +import androidx.annotation.RequiresApi +import be.mygod.vpnhotspot.util.ConstantLookup + +@JvmInline +@RequiresApi(30) +value class SoftApInfo(val inner: Parcelable) { + companion object { + private val classSoftApInfo by lazy { Class.forName("android.net.wifi.SoftApInfo") } + private val getFrequency by lazy { classSoftApInfo.getDeclaredMethod("getFrequency") } + private val getBandwidth by lazy { classSoftApInfo.getDeclaredMethod("getBandwidth") } + + val channelWidthLookup = ConstantLookup("CHANNEL_WIDTH_") { classSoftApInfo } + } + + val frequency get() = getFrequency.invoke(inner) as Int + val bandwidth get() = getBandwidth.invoke(inner) as Int +} diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt index 67bf6a0b..565c2eb5 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt @@ -8,6 +8,7 @@ import android.net.wifi.SoftApConfiguration import android.net.wifi.WifiManager import android.os.Build import android.os.Handler +import android.os.Parcelable import androidx.annotation.RequiresApi import androidx.core.os.BuildCompat import be.mygod.vpnhotspot.App.Companion.app @@ -87,7 +88,7 @@ object WifiApManager { } @RequiresApi(30) - fun onInfoChanged(frequency: Int, bandwidth: Int) { } + fun onInfoChanged(info: List) { } @RequiresApi(30) fun onCapabilityChanged(maxSupportedClients: Int, supportedFeatures: Long) { } @@ -116,13 +117,6 @@ object WifiApManager { Class.forName("android.net.wifi.WifiClient").getDeclaredMethod("getMacAddress") } - private val classSoftApInfo by lazy { Class.forName("android.net.wifi.SoftApInfo") } - private val getFrequency by lazy { classSoftApInfo.getDeclaredMethod("getFrequency") } - private val getBandwidth by lazy { classSoftApInfo.getDeclaredMethod("getBandwidth") } - @RequiresApi(30) - val channelWidthLookup = ConstantLookup("CHANNEL_WIDTH_") { classSoftApInfo } - const val CHANNEL_WIDTH_INVALID = 0 - private val classSoftApCapability by lazy { Class.forName("android.net.wifi.SoftApCapability") } private val getMaxSupportedClients by lazy { classSoftApCapability.getDeclaredMethod("getMaxSupportedClients") } private val areFeaturesSupported by lazy { @@ -141,9 +135,6 @@ object WifiApManager { null // no return value as of API 30 } else invokeActual(proxy, method, args) - @RequiresApi(30) - private fun dispatchInfoChanged(softApInfo: Any?) = - callback.onInfoChanged(getFrequency(softApInfo) as Int, getBandwidth(softApInfo) as Int) private fun invokeActual(proxy: Any, method: Method, args: Array?): Any? { val noArgs = args?.size ?: 0 return when (val name = method.name) { @@ -172,14 +163,22 @@ object WifiApManager { }.map { getMacAddress(it) as MacAddress }) } "onInfoChanged" -> @TargetApi(30) { - if (Build.VERSION.SDK_INT < 30) Timber.w(Exception("Unexpected onInfoChanged")) if (noArgs != 1) Timber.w("Unexpected args for $name: ${args?.contentToString()}") val arg = args!![0] if (arg is List<*>) { if (!BuildCompat.isAtLeastS()) Timber.w(Exception("Unexpected onInfoChanged API 31+")) - if (arg.size != 1) Timber.w("Unexpected args for $name: ${args.contentToString()}") - else dispatchInfoChanged(arg[0]) - } else dispatchInfoChanged(arg) + @Suppress("UNCHECKED_CAST") + callback.onInfoChanged(arg as List) + } else { + when (Build.VERSION.SDK_INT) { + 30 -> { } + in 31..Int.MAX_VALUE -> return null // ignore old version calls + else -> Timber.w(Exception("Unexpected onInfoChanged API 30")) + } + val info = SoftApInfo(arg as Parcelable) + callback.onInfoChanged( // check for legacy empty info with CHANNEL_WIDTH_INVALID + if (info.frequency == 0 && info.bandwidth == 0) emptyList() else listOf(arg)) + } } "onCapabilityChanged" -> @TargetApi(30) { if (Build.VERSION.SDK_INT < 30) Timber.w(Exception("Unexpected onCapabilityChanged")) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt b/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt index 29cb1e24..2883bf47 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt @@ -38,9 +38,8 @@ object WifiApCommands { } @Parcelize @RequiresApi(30) - data class OnInfoChanged(val frequency: Int, val bandwidth: Int) : SoftApCallbackParcel() { - override fun dispatch(callback: WifiApManager.SoftApCallbackCompat) = - callback.onInfoChanged(frequency, bandwidth) + data class OnInfoChanged(val info: List) : SoftApCallbackParcel() { + override fun dispatch(callback: WifiApManager.SoftApCallbackCompat) = callback.onInfoChanged(info) } @Parcelize @RequiresApi(30) @@ -78,8 +77,7 @@ object WifiApCommands { override fun onConnectedClientsChanged(clients: List) = push(SoftApCallbackParcel.OnConnectedClientsChanged(clients)) @RequiresApi(30) - override fun onInfoChanged(frequency: Int, bandwidth: Int) = - push(SoftApCallbackParcel.OnInfoChanged(frequency, bandwidth)) + override fun onInfoChanged(info: List) = push(SoftApCallbackParcel.OnInfoChanged(info)) @RequiresApi(30) override fun onCapabilityChanged(maxSupportedClients: Int, supportedFeatures: Long) = push(SoftApCallbackParcel.OnCapabilityChanged(maxSupportedClients, supportedFeatures)) From a69d635f9388b873702f5bbea6c8a247e2213037 Mon Sep 17 00:00:00 2001 From: Mygod Date: Wed, 26 May 2021 19:49:45 -0400 Subject: [PATCH 012/112] Rewrite SpanFormatter for more kotlin --- .../vpnhotspot/client/ClientsFragment.kt | 7 +- .../preference/UpstreamsPreference.kt | 6 +- .../be/mygod/vpnhotspot/util/SpanFormatter.kt | 84 ------------------- .../java/be/mygod/vpnhotspot/util/Utils.kt | 52 +++++++++++- 4 files changed, 56 insertions(+), 93 deletions(-) delete mode 100644 mobile/src/main/java/be/mygod/vpnhotspot/util/SpanFormatter.kt diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientsFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientsFragment.kt index 914fab11..db48cb38 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientsFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientsFragment.kt @@ -37,7 +37,7 @@ import be.mygod.vpnhotspot.net.monitor.TrafficRecorder import be.mygod.vpnhotspot.room.AppDatabase import be.mygod.vpnhotspot.room.ClientStats import be.mygod.vpnhotspot.room.TrafficRecord -import be.mygod.vpnhotspot.util.SpanFormatter +import be.mygod.vpnhotspot.util.format import be.mygod.vpnhotspot.util.showAllowingStateLoss import be.mygod.vpnhotspot.util.toPluralInt import be.mygod.vpnhotspot.widget.SmartSnackbar @@ -82,10 +82,11 @@ class ClientsFragment : Fragment() { data class StatsArg(val title: CharSequence, val stats: ClientStats) : Parcelable class StatsDialogFragment : AlertDialogFragment() { override fun AlertDialog.Builder.prepare(listener: DialogInterface.OnClickListener) { - setTitle(SpanFormatter.format(getText(R.string.clients_stats_title), arg.title)) val context = context val resources = resources - val format = NumberFormat.getIntegerInstance(resources.configuration.locale) + val locale = resources.configuration.locale + setTitle(getText(R.string.clients_stats_title).format(locale, arg.title)) + val format = NumberFormat.getIntegerInstance(locale) setMessage("%s\n%s\n%s".format( resources.getQuantityString(R.plurals.clients_stats_message_1, arg.stats.count.toPluralInt(), format.format(arg.stats.count), DateUtils.formatDateTime(context, arg.stats.timestamp, diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/preference/UpstreamsPreference.kt b/mobile/src/main/java/be/mygod/vpnhotspot/preference/UpstreamsPreference.kt index a5b2056a..bc6e7fec 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/preference/UpstreamsPreference.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/preference/UpstreamsPreference.kt @@ -13,8 +13,8 @@ import androidx.preference.Preference import be.mygod.vpnhotspot.R import be.mygod.vpnhotspot.net.monitor.FallbackUpstreamMonitor import be.mygod.vpnhotspot.net.monitor.UpstreamMonitor -import be.mygod.vpnhotspot.util.SpanFormatter import be.mygod.vpnhotspot.util.allRoutes +import be.mygod.vpnhotspot.util.format import be.mygod.vpnhotspot.util.parseNumericAddress import timber.log.Timber @@ -72,7 +72,7 @@ class UpstreamsPreference(context: Context, attrs: AttributeSet) : Preference(co } private fun onUpdate() = (context as LifecycleOwner).lifecycleScope.launchWhenStarted { - summary = SpanFormatter.format(context.getText(R.string.settings_service_upstream_monitor_summary), - primary.charSequence, fallback.charSequence) + summary = context.getText(R.string.settings_service_upstream_monitor_summary).format( + context.resources.configuration.locale, primary.charSequence, fallback.charSequence) } } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/util/SpanFormatter.kt b/mobile/src/main/java/be/mygod/vpnhotspot/util/SpanFormatter.kt deleted file mode 100644 index e3d056fe..00000000 --- a/mobile/src/main/java/be/mygod/vpnhotspot/util/SpanFormatter.kt +++ /dev/null @@ -1,84 +0,0 @@ -package be.mygod.vpnhotspot.util - -import android.text.Spannable -import android.text.SpannableStringBuilder -import android.text.Spanned -import android.text.SpannedString -import java.util.* - -/** - * Provides [String.format] style functions that work with [Spanned] strings and preserve formatting. - * - * https://github.com/george-steel/android-utils/blob/289aff11e53593a55d780f9f5986e49343a79e55/src/org/oshkimaadziig/george/androidutils/SpanFormatter.java - * - * @author George T. Steel - */ -object SpanFormatter { - private val formatSequence = "%([0-9]+\\$| "%" - "n" -> "\n" - else -> { - val argItem = args[when (argTerm) { - "" -> ++argAt - "<" -> argAt - else -> Integer.parseInt(argTerm.substring(0, argTerm.length - 1)) - 1 - }] - if (typeTerm == "s" && argItem is Spanned) argItem else { - String.format(locale, "%$modTerm$typeTerm", argItem) - } - } - } - - out.replace(i, exprEnd, cookedArg) - i += cookedArg.length - } - - return SpannedString(out) - } -} diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt b/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt index 2b024db6..998b464e 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt @@ -12,9 +12,7 @@ import android.os.RemoteException import android.system.ErrnoException import android.system.Os import android.system.OsConstants -import android.text.Spannable -import android.text.SpannableString -import android.text.SpannableStringBuilder +import android.text.* import android.view.MenuItem import android.view.View import android.widget.ImageView @@ -39,6 +37,7 @@ import java.lang.reflect.Method import java.net.InetAddress import java.net.NetworkInterface import java.net.SocketException +import java.util.* tailrec fun Throwable.getRootCause(): Throwable { if (this is InvocationTargetException || this is RemoteException) return (cause ?: return this).getRootCause() @@ -80,6 +79,53 @@ fun setVisibility(view: View, value: Boolean) { view.isVisible = value } +private val formatSequence = "%([0-9]+\\$| "%" + "n" -> "\n" + else -> { + val argItem = args[when (argTerm) { + "" -> ++argAt + "<" -> argAt + else -> Integer.parseInt(argTerm.substring(0, argTerm.length - 1)) - 1 + }] + if (typeTerm == "s" && argItem is Spanned) argItem else { + String.format(locale, "%$modTerm$typeTerm", argItem) + } + } + } + replace(i, exprEnd, cookedArg) + i += cookedArg.length + } +} + fun makeIpSpan(ip: InetAddress) = ip.hostAddress.let { // exclude all bogon IP addresses supported by Android APIs if (!app.hasTouch || ip.isMulticastAddress || ip.isAnyLocalAddress || ip.isLoopbackAddress || From 50de5a269c1e43ad35af5a994389fdd62a851287 Mon Sep 17 00:00:00 2001 From: Mygod Date: Wed, 26 May 2021 20:56:44 -0400 Subject: [PATCH 013/112] Support new fields in SoftApInfo --- README.md | 5 ++ .../mygod/vpnhotspot/manage/TetherManager.kt | 59 ++++++++++++------- .../mygod/vpnhotspot/net/wifi/SoftApInfo.kt | 35 +++++++++-- .../mygod/vpnhotspot/util/UnblockCentral.kt | 32 ++++++++++ .../java/be/mygod/vpnhotspot/util/Utils.kt | 9 +++ mobile/src/main/res/values-zh-rCN/strings.xml | 4 ++ mobile/src/main/res/values/strings.xml | 4 ++ 7 files changed, 123 insertions(+), 25 deletions(-) create mode 100644 mobile/src/main/java/be/mygod/vpnhotspot/util/UnblockCentral.kt diff --git a/README.md b/README.md index d51975fd..56e8fcaf 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,7 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (since API 30) `Landroid/net/ConnectivityModuleConnector;->IN_PROCESS_SUFFIX:Ljava/lang/String;` * (since API 30) `Landroid/net/TetheringManager$TetheringEventCallback;->onTetherableInterfaceRegexpsChanged(Landroid/net/TetheringManager$TetheringInterfaceRegexps;)V,blocked` * (since API 30) `Landroid/net/TetheringManager;->TETHERING_WIGIG:I,blocked` +* (since API 31) `Landroid/net/wifi/SoftApInfo;->getApInstanceIdentifier()Ljava/lang/String;,blocked` * (prior to API 30) `Landroid/net/wifi/WifiConfiguration$KeyMgmt;->FT_PSK:I,lo-prio,max-target-o` * (prior to API 30) `Landroid/net/wifi/WifiConfiguration$KeyMgmt;->WPA_PSK_SHA256:I,blocked` * (since API 23, prior to API 30) `Landroid/net/wifi/WifiConfiguration;->AP_BAND_2GHZ:I,lo-prio,max-target-o` @@ -176,6 +177,7 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (since API 30) `Lcom/android/server/wifi/WifiContext;->ACTION_RESOURCES_APK:Ljava/lang/String;` * (since API 29) `Lcom/android/server/wifi/p2p/WifiP2pServiceImpl;->ANONYMIZED_DEVICE_ADDRESS:Ljava/lang/String;` * (since API 30) `Lcom/android/server/SystemServer;->TETHERING_CONNECTOR_CLASS:Ljava/lang/String;` +* (since API 31) `Ldalvik/system/VMDebug;->allowHiddenApiReflectionFrom(Ljava/lang/Class;)V,unsupported` * (since API 26) `Ljava/lang/invoke/MethodHandles$Lookup;->(Ljava/lang/Class;I)V,unsupported` * (since API 26) `Ljava/lang/invoke/MethodHandles$Lookup;->ALL_MODES:I,lo-prio,max-target-o` * (prior to API 29) `Ljava/net/InetAddress;->parseNumericAddress(Ljava/lang/String;)Ljava/net/InetAddress;,core-platform-api,max-target-p` @@ -256,8 +258,11 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (since API 30) `Landroid/net/wifi/SoftApConfiguration;->isClientControlByUserEnabled()Z,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApInfo;->CHANNEL_WIDTH_*:I,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApInfo;->CHANNEL_WIDTH_INVALID:I,sdk,system-api,test-api` +* (since API 31) `Landroid/net/wifi/SoftApInfo;->getAutoShutdownTimeoutMillis()J,sdk,system-api,test-api` +* (since API 31) `Landroid/net/wifi/SoftApInfo;->getBssid()Landroid/net/MacAddress;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApInfo;->getBandwidth()I,system-api,whitelist` * (since API 30) `Landroid/net/wifi/SoftApInfo;->getFrequency()I,system-api,whitelist` +* (since API 31) `Landroid/net/wifi/SoftApInfo;->getWifiStandard()I,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/WifiClient;->getMacAddress()Landroid/net/MacAddress;,sdk,system-api,test-api` * (prior to API 30) `Landroid/net/wifi/WifiConfiguration$KeyMgmt;->WPA2_PSK:I,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/WifiManager$SoftApCallback;->onBlockedClientConnecting(Landroid/net/wifi/WifiClient;I)V,sdk,system-api,test-api` 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 b25cd043..84d3d425 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt @@ -10,6 +10,8 @@ import android.net.MacAddress import android.os.Build import android.os.Parcelable import android.provider.Settings +import android.text.SpannableStringBuilder +import android.text.format.DateUtils import android.view.View import android.widget.Toast import androidx.annotation.RequiresApi @@ -29,6 +31,9 @@ import be.mygod.vpnhotspot.net.wifi.SoftApConfigurationCompat import be.mygod.vpnhotspot.net.wifi.SoftApInfo import be.mygod.vpnhotspot.net.wifi.WifiApManager import be.mygod.vpnhotspot.root.WifiApCommands +import be.mygod.vpnhotspot.util.format +import be.mygod.vpnhotspot.util.joinToSpanned +import be.mygod.vpnhotspot.util.makeMacSpan import be.mygod.vpnhotspot.util.readableMessage import be.mygod.vpnhotspot.widget.SmartSnackbar import kotlinx.coroutines.Dispatchers @@ -188,28 +193,40 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), override val title get() = parent.getString(R.string.tethering_manage_wifi) override val tetherType get() = TetherType.WIFI override val type get() = VIEW_TYPE_WIFI - override val text get() = listOfNotNull(failureReason?.let { WifiApManager.failureReasonLookup(it) }, baseError, - info.run { - if (isEmpty()) null else joinToString("\n") @TargetApi(30) { - val info = SoftApInfo(it) - val frequency = info.frequency - parent.getString(R.string.tethering_manage_wifi_info, frequency, - SoftApConfigurationCompat.frequencyToChannel(frequency), - SoftApInfo.channelWidthLookup(info.bandwidth, true)) - } - }, - capability?.let { (maxSupportedClients, supportedFeatures) -> - app.resources.getQuantityString(R.plurals.tethering_manage_wifi_capabilities, maxSupportedClients, - numClients ?: "?", maxSupportedClients, sequence { - var features = supportedFeatures - if (features == 0L) yield(parent.getString(R.string.tethering_manage_wifi_no_features)) - else while (features != 0L) { - val bit = features.takeLowestOneBit() - yield(WifiApManager.featureLookup(bit, true)) - features = features and bit.inv() + override val text get() = parent.resources.configuration.locale.let { locale -> + listOfNotNull(failureReason?.let { WifiApManager.failureReasonLookup(it) }, baseError, info.run { + if (isEmpty()) null else joinToSpanned("\n") @TargetApi(30) { parcel -> + val info = SoftApInfo(parcel) + val frequency = info.frequency + val channel = SoftApConfigurationCompat.frequencyToChannel(frequency) + val bandwidth = SoftApInfo.channelWidthLookup(info.bandwidth, true) + if (BuildCompat.isAtLeastS()) { + var bssid = makeMacSpan(info.bssid.toString()) + info.apInstanceIdentifier?.let { // take the fast route if possible + bssid = if (bssid is String) "$bssid%$it" else SpannableStringBuilder(bssid).append("%$it") } - }.joinToString()) - }).joinToString("\n") + val timeout = info.autoShutdownTimeoutMillis + parent.getText(if (timeout == 0L) { + R.string.tethering_manage_wifi_info_timeout_disabled + } else R.string.tethering_manage_wifi_info_timeout_enabled).format(locale, + frequency, channel, bandwidth, bssid, info.wifiStandard, + // http://unicode.org/cldr/trac/ticket/3407 + DateUtils.formatElapsedTime(timeout / 1000)) + } else parent.getText(R.string.tethering_manage_wifi_info).format(locale, + frequency, channel, bandwidth) + } + }, capability?.let { (maxSupportedClients, supportedFeatures) -> + app.resources.getQuantityText(R.plurals.tethering_manage_wifi_capabilities, + maxSupportedClients).format(locale, numClients ?: "?", maxSupportedClients, sequence { + var features = supportedFeatures + if (features != 0L) while (features != 0L) { + val bit = features.takeLowestOneBit() + yield(WifiApManager.featureLookup(bit, true)) + features = features and bit.inv() + } else yield(parent.getText(R.string.tethering_manage_wifi_no_features)) + }.joinToSpanned()) + }).joinToSpanned("\n") + } override fun start() = TetheringManager.startTethering(TetheringManager.TETHERING_WIFI, true, this) override fun stop() = TetheringManager.stopTethering(TetheringManager.TETHERING_WIFI, this::onException) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApInfo.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApInfo.kt index c5de91de..1b6e0d73 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApInfo.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApInfo.kt @@ -1,20 +1,47 @@ package be.mygod.vpnhotspot.net.wifi +import android.annotation.TargetApi +import android.net.MacAddress import android.os.Parcelable import androidx.annotation.RequiresApi import be.mygod.vpnhotspot.util.ConstantLookup +import be.mygod.vpnhotspot.util.UnblockCentral +import timber.log.Timber @JvmInline @RequiresApi(30) value class SoftApInfo(val inner: Parcelable) { companion object { - private val classSoftApInfo by lazy { Class.forName("android.net.wifi.SoftApInfo") } - private val getFrequency by lazy { classSoftApInfo.getDeclaredMethod("getFrequency") } - private val getBandwidth by lazy { classSoftApInfo.getDeclaredMethod("getBandwidth") } + private val clazz by lazy { Class.forName("android.net.wifi.SoftApInfo") } + private val getFrequency by lazy { clazz.getDeclaredMethod("getFrequency") } + private val getBandwidth by lazy { clazz.getDeclaredMethod("getBandwidth") } + @get:RequiresApi(31) + private val getBssid by lazy { clazz.getDeclaredMethod("getBssid") } + @get:RequiresApi(31) + private val getWifiStandard by lazy { clazz.getDeclaredMethod("getWifiStandard") } + @get:RequiresApi(31) + private val getApInstanceIdentifier by lazy @TargetApi(31) { + UnblockCentral.SoftApInfo_getApInstanceIdentifier(clazz) + } + @get:RequiresApi(31) + private val getAutoShutdownTimeoutMillis by lazy { clazz.getDeclaredMethod("getAutoShutdownTimeoutMillis") } - val channelWidthLookup = ConstantLookup("CHANNEL_WIDTH_") { classSoftApInfo } + val channelWidthLookup = ConstantLookup("CHANNEL_WIDTH_") { clazz } } val frequency get() = getFrequency.invoke(inner) as Int val bandwidth get() = getBandwidth.invoke(inner) as Int + @get:RequiresApi(31) + val bssid get() = getBssid.invoke(inner) as MacAddress + @get:RequiresApi(31) + val wifiStandard get() = getWifiStandard.invoke(inner) as Int + @get:RequiresApi(31) + val apInstanceIdentifier get() = try { + getApInstanceIdentifier.invoke(inner) as? String + } catch (e: ReflectiveOperationException) { + Timber.w(e) + null + } + @get:RequiresApi(31) + val autoShutdownTimeoutMillis get() = getAutoShutdownTimeoutMillis.invoke(inner) as Long } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/util/UnblockCentral.kt b/mobile/src/main/java/be/mygod/vpnhotspot/util/UnblockCentral.kt new file mode 100644 index 00000000..2eceab4e --- /dev/null +++ b/mobile/src/main/java/be/mygod/vpnhotspot/util/UnblockCentral.kt @@ -0,0 +1,32 @@ +package be.mygod.vpnhotspot.util + +import android.annotation.SuppressLint +import androidx.annotation.RequiresApi +import timber.log.Timber + +/** + * The central object for accessing all the useful blocked APIs. Thanks Google! + */ +@SuppressLint("DiscouragedPrivateApi") +@Suppress("FunctionName") +object UnblockCentral { + /** + * Retrieve this property before doing dangerous shit. + */ + @get:RequiresApi(28) + private val init by lazy { + try { + Class.forName("dalvik.system.VMDebug").getDeclaredMethod("allowHiddenApiReflectionFrom", Class::class.java) + .invoke(null, UnblockCentral::class.java) + true + } catch (e: ReflectiveOperationException) { + Timber.w(e) + false + } + } + + @RequiresApi(31) + fun SoftApInfo_getApInstanceIdentifier(clazz: Class<*>) = init.let { + clazz.getDeclaredMethod("getApInstanceIdentifier") + } +} diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt b/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt index 998b464e..49b0ac70 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt @@ -126,6 +126,15 @@ fun CharSequence.format(locale: Locale, vararg args: Any) = SpannableStringBuild } } +fun Iterable.joinToSpanned(separator: CharSequence = ", ", prefix: CharSequence = "", postfix: CharSequence = "", + limit: Int = -1, truncated: CharSequence = "...", + transform: ((T) -> CharSequence)? = null) = + joinTo(SpannableStringBuilder(), separator, prefix, postfix, limit, truncated, transform) +fun Sequence.joinToSpanned(separator: CharSequence = ", ", prefix: CharSequence = "", postfix: CharSequence = "", + limit: Int = -1, truncated: CharSequence = "...", + transform: ((T) -> CharSequence)? = null) = + joinTo(SpannableStringBuilder(), separator, prefix, postfix, limit, truncated, transform) + fun makeIpSpan(ip: InetAddress) = ip.hostAddress.let { // exclude all bogon IP addresses supported by Android APIs if (!app.hasTouch || ip.isMulticastAddress || ip.isAnyLocalAddress || ip.isLoopbackAddress || diff --git a/mobile/src/main/res/values-zh-rCN/strings.xml b/mobile/src/main/res/values-zh-rCN/strings.xml index 37095766..52011a0f 100644 --- a/mobile/src/main/res/values-zh-rCN/strings.xml +++ b/mobile/src/main/res/values-zh-rCN/strings.xml @@ -59,6 +59,10 @@ USB 网络共享 (NCM) WiGig 热点 %1$d MHz, 频道 %2$d, 频宽 %3$s + %4$s: Wi\u2011Fi %5$d, %1$d MHz, 频道 %2$d, 频宽 %3$s, + 关闭延迟 %6$s + %4$s: Wi\u2011Fi %5$d, %1$d MHz, 频道 %2$d, 频宽 %3$s, + 不自动关闭 已连接 %1$s/%2$d 个设备\n支持功能:%3$s diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml index f3ce86b8..4a6154e9 100644 --- a/mobile/src/main/res/values/strings.xml +++ b/mobile/src/main/res/values/strings.xml @@ -70,6 +70,10 @@ USB tethering (NCM) WiGig hotspot %1$d MHz, channel %2$d, width %3$s + %4$s: Wi\u2011Fi %5$d, %1$d MHz, channel %2$d, + width %3$s, idle timeout in %6$s + %4$s: Wi\u2011Fi %5$d, %1$d MHz, channel %2$d, + width %3$s, idle timeout disabled %1$s/%2$d client connected\nSupported features: %3$s %1$s/%2$d clients connected\nSupported features: %3$s From 168c9ff6f1f580c982cf3653b0e6e9f1a7817457 Mon Sep 17 00:00:00 2001 From: Mygod Date: Wed, 26 May 2021 21:23:18 -0400 Subject: [PATCH 014/112] Refactor SoftApCapability --- .../mygod/vpnhotspot/manage/TetherManager.kt | 36 +++++++++++-------- .../vpnhotspot/net/wifi/SoftApCapability.kt | 27 ++++++++++++++ .../vpnhotspot/net/wifi/WifiApManager.kt | 22 ++---------- .../mygod/vpnhotspot/root/WifiApCommands.kt | 9 +++-- 4 files changed, 55 insertions(+), 39 deletions(-) create mode 100644 mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApCapability.kt 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 84d3d425..4170bcd9 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt @@ -27,6 +27,7 @@ import be.mygod.vpnhotspot.R import be.mygod.vpnhotspot.databinding.ListitemInterfaceBinding import be.mygod.vpnhotspot.net.TetherType import be.mygod.vpnhotspot.net.TetheringManager +import be.mygod.vpnhotspot.net.wifi.SoftApCapability import be.mygod.vpnhotspot.net.wifi.SoftApConfigurationCompat import be.mygod.vpnhotspot.net.wifi.SoftApInfo import be.mygod.vpnhotspot.net.wifi.WifiApManager @@ -41,6 +42,7 @@ import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import timber.log.Timber import java.lang.reflect.InvocationTargetException +import java.util.* sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), TetheringManager.StartTetheringCallback { @@ -145,7 +147,7 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), private var failureReason: Int? = null private var numClients: Int? = null private var info = emptyList() - private var capability: Pair? = null + private var capability: Parcelable? = null init { if (Build.VERSION.SDK_INT >= 28) parent.viewLifecycleOwner.lifecycle.addObserver(this) @@ -176,8 +178,8 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), this.info = info data.notifyChange() } - override fun onCapabilityChanged(maxSupportedClients: Int, supportedFeatures: Long) { - capability = maxSupportedClients to supportedFeatures + override fun onCapabilityChanged(capability: Parcelable) { + this.capability = capability data.notifyChange() } override fun onBlockedClientConnecting(client: MacAddress, blockedReason: Int) { @@ -193,6 +195,22 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), override val title get() = parent.getString(R.string.tethering_manage_wifi) override val tetherType get() = TetherType.WIFI override val type get() = VIEW_TYPE_WIFI + + @TargetApi(30) + private fun formatCapability(locale: Locale) = capability?.let { + val capability = SoftApCapability(it) + val maxClients = capability.maxSupportedClients + val supportedFeatures = capability.supportedFeatures + app.resources.getQuantityText(R.plurals.tethering_manage_wifi_capabilities, maxClients).format(locale, + numClients ?: "?", maxClients, sequence { + var features = supportedFeatures + if (features != 0L) while (features != 0L) { + val bit = features.takeLowestOneBit() + yield(SoftApCapability.featureLookup(bit, true)) + features = features and bit.inv() + } else yield(parent.getText(R.string.tethering_manage_wifi_no_features)) + }.joinToSpanned()) + } override val text get() = parent.resources.configuration.locale.let { locale -> listOfNotNull(failureReason?.let { WifiApManager.failureReasonLookup(it) }, baseError, info.run { if (isEmpty()) null else joinToSpanned("\n") @TargetApi(30) { parcel -> @@ -215,17 +233,7 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), } else parent.getText(R.string.tethering_manage_wifi_info).format(locale, frequency, channel, bandwidth) } - }, capability?.let { (maxSupportedClients, supportedFeatures) -> - app.resources.getQuantityText(R.plurals.tethering_manage_wifi_capabilities, - maxSupportedClients).format(locale, numClients ?: "?", maxSupportedClients, sequence { - var features = supportedFeatures - if (features != 0L) while (features != 0L) { - val bit = features.takeLowestOneBit() - yield(WifiApManager.featureLookup(bit, true)) - features = features and bit.inv() - } else yield(parent.getText(R.string.tethering_manage_wifi_no_features)) - }.joinToSpanned()) - }).joinToSpanned("\n") + }, formatCapability(locale)).joinToSpanned("\n") } override fun start() = TetheringManager.startTethering(TetheringManager.TETHERING_WIFI, true, this) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApCapability.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApCapability.kt new file mode 100644 index 00000000..85d0c057 --- /dev/null +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApCapability.kt @@ -0,0 +1,27 @@ +package be.mygod.vpnhotspot.net.wifi + +import android.os.Parcelable +import androidx.annotation.RequiresApi +import be.mygod.vpnhotspot.util.LongConstantLookup + +@JvmInline +@RequiresApi(30) +value class SoftApCapability(val inner: Parcelable) { + companion object { + private val clazz by lazy { Class.forName("android.net.wifi.SoftApCapability") } + private val getMaxSupportedClients by lazy { clazz.getDeclaredMethod("getMaxSupportedClients") } + private val areFeaturesSupported by lazy { clazz.getDeclaredMethod("areFeaturesSupported", Long::class.java) } + val featureLookup by lazy { LongConstantLookup(clazz, "SOFTAP_FEATURE_") } + } + + val maxSupportedClients get() = getMaxSupportedClients.invoke(inner) as Int + val supportedFeatures: Long get() { + var supportedFeatures = 0L + var probe = 1L + while (probe != 0L) { + if (areFeaturesSupported(inner, probe) as Boolean) supportedFeatures = supportedFeatures or probe + probe += probe + } + return supportedFeatures + } +} diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt index 565c2eb5..7d9354cf 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt @@ -14,7 +14,6 @@ import androidx.core.os.BuildCompat import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.net.wifi.SoftApConfigurationCompat.Companion.toCompat import be.mygod.vpnhotspot.util.ConstantLookup -import be.mygod.vpnhotspot.util.LongConstantLookup import be.mygod.vpnhotspot.util.Services import be.mygod.vpnhotspot.util.callSuper import timber.log.Timber @@ -91,7 +90,7 @@ object WifiApManager { fun onInfoChanged(info: List) { } @RequiresApi(30) - fun onCapabilityChanged(maxSupportedClients: Int, supportedFeatures: Long) { } + fun onCapabilityChanged(capability: Parcelable) { } @RequiresApi(30) fun onBlockedClientConnecting(client: MacAddress, blockedReason: Int) { } @@ -117,14 +116,6 @@ object WifiApManager { Class.forName("android.net.wifi.WifiClient").getDeclaredMethod("getMacAddress") } - private val classSoftApCapability by lazy { Class.forName("android.net.wifi.SoftApCapability") } - private val getMaxSupportedClients by lazy { classSoftApCapability.getDeclaredMethod("getMaxSupportedClients") } - private val areFeaturesSupported by lazy { - classSoftApCapability.getDeclaredMethod("areFeaturesSupported", Long::class.java) - } - @get:RequiresApi(30) - val featureLookup by lazy { LongConstantLookup(classSoftApCapability, "SOFTAP_FEATURE_") } - @RequiresApi(28) fun registerSoftApCallback(callback: SoftApCallbackCompat, executor: Executor): Any { val proxy = Proxy.newProxyInstance(interfaceSoftApCallback.classLoader, @@ -183,16 +174,7 @@ object WifiApManager { "onCapabilityChanged" -> @TargetApi(30) { if (Build.VERSION.SDK_INT < 30) Timber.w(Exception("Unexpected onCapabilityChanged")) if (noArgs != 1) Timber.w("Unexpected args for $name: ${args?.contentToString()}") - val softApCapability = args!![0] - var supportedFeatures = 0L - var probe = 1L - while (probe != 0L) { - if (areFeaturesSupported(softApCapability, probe) as Boolean) { - supportedFeatures = supportedFeatures or probe - } - probe += probe - } - callback.onCapabilityChanged(getMaxSupportedClients(softApCapability) as Int, supportedFeatures) + callback.onCapabilityChanged(args!![0] as Parcelable) } "onBlockedClientConnecting" -> @TargetApi(30) { if (Build.VERSION.SDK_INT < 30) Timber.w(Exception("Unexpected onBlockedClientConnecting")) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt b/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt index 2883bf47..b3d72dfc 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt @@ -43,10 +43,9 @@ object WifiApCommands { } @Parcelize @RequiresApi(30) - data class OnCapabilityChanged(val maxSupportedClients: Int, - val supportedFeatures: Long) : SoftApCallbackParcel() { + data class OnCapabilityChanged(val capability: Parcelable) : SoftApCallbackParcel() { override fun dispatch(callback: WifiApManager.SoftApCallbackCompat) = - callback.onCapabilityChanged(maxSupportedClients, supportedFeatures) + callback.onCapabilityChanged(capability) } @Parcelize @RequiresApi(30) @@ -79,8 +78,8 @@ object WifiApCommands { @RequiresApi(30) override fun onInfoChanged(info: List) = push(SoftApCallbackParcel.OnInfoChanged(info)) @RequiresApi(30) - override fun onCapabilityChanged(maxSupportedClients: Int, supportedFeatures: Long) = - push(SoftApCallbackParcel.OnCapabilityChanged(maxSupportedClients, supportedFeatures)) + override fun onCapabilityChanged(capability: Parcelable) = + push(SoftApCallbackParcel.OnCapabilityChanged(capability)) @RequiresApi(30) override fun onBlockedClientConnecting(client: MacAddress, blockedReason: Int) = push(SoftApCallbackParcel.OnBlockedClientConnecting(client, blockedReason)) From ea076d96021d9c96baa037bec69b9d6669c4a139 Mon Sep 17 00:00:00 2001 From: Mygod Date: Wed, 26 May 2021 21:52:43 -0400 Subject: [PATCH 015/112] Support ap identifier for WifiClient --- README.md | 1 + .../mygod/vpnhotspot/manage/TetherManager.kt | 20 +++++++------ .../mygod/vpnhotspot/net/wifi/SoftApInfo.kt | 16 +++++------ .../vpnhotspot/net/wifi/WifiApManager.kt | 17 +++++------ .../mygod/vpnhotspot/net/wifi/WifiClient.kt | 28 +++++++++++++++++++ .../mygod/vpnhotspot/root/WifiApCommands.kt | 8 +++--- .../mygod/vpnhotspot/util/UnblockCentral.kt | 4 +-- 7 files changed, 59 insertions(+), 35 deletions(-) create mode 100644 mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiClient.kt diff --git a/README.md b/README.md index 56e8fcaf..adcf90b9 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,7 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (since API 30) `Landroid/net/TetheringManager$TetheringEventCallback;->onTetherableInterfaceRegexpsChanged(Landroid/net/TetheringManager$TetheringInterfaceRegexps;)V,blocked` * (since API 30) `Landroid/net/TetheringManager;->TETHERING_WIGIG:I,blocked` * (since API 31) `Landroid/net/wifi/SoftApInfo;->getApInstanceIdentifier()Ljava/lang/String;,blocked` +* (since API 31) `Landroid/net/wifi/WifiClient;->getApInstanceIdentifier()Ljava/lang/String;,blocked` * (prior to API 30) `Landroid/net/wifi/WifiConfiguration$KeyMgmt;->FT_PSK:I,lo-prio,max-target-o` * (prior to API 30) `Landroid/net/wifi/WifiConfiguration$KeyMgmt;->WPA_PSK_SHA256:I,blocked` * (since API 23, prior to API 30) `Landroid/net/wifi/WifiConfiguration;->AP_BAND_2GHZ:I,lo-prio,max-target-o` 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 4170bcd9..f9d52876 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt @@ -6,7 +6,6 @@ import android.content.ClipData import android.content.Context import android.content.Intent import android.content.pm.PackageManager -import android.net.MacAddress import android.os.Build import android.os.Parcelable import android.provider.Settings @@ -27,10 +26,7 @@ import be.mygod.vpnhotspot.R import be.mygod.vpnhotspot.databinding.ListitemInterfaceBinding import be.mygod.vpnhotspot.net.TetherType import be.mygod.vpnhotspot.net.TetheringManager -import be.mygod.vpnhotspot.net.wifi.SoftApCapability -import be.mygod.vpnhotspot.net.wifi.SoftApConfigurationCompat -import be.mygod.vpnhotspot.net.wifi.SoftApInfo -import be.mygod.vpnhotspot.net.wifi.WifiApManager +import be.mygod.vpnhotspot.net.wifi.* import be.mygod.vpnhotspot.root.WifiApCommands import be.mygod.vpnhotspot.util.format import be.mygod.vpnhotspot.util.joinToSpanned @@ -182,12 +178,18 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), this.capability = capability data.notifyChange() } - override fun onBlockedClientConnecting(client: MacAddress, blockedReason: Int) { + @RequiresApi(30) + override fun onBlockedClientConnecting(client: Parcelable, blockedReason: Int) { + @Suppress("NAME_SHADOWING") + val client = WifiClient(client) + val macAddress = client.macAddress + var name = macAddress.toString() + if (BuildCompat.isAtLeastS()) client.apInstanceIdentifier?.let { name += "%$it" } val reason = WifiApManager.clientBlockLookup(blockedReason, true) - Timber.i("$client blocked from connecting: $reason ($blockedReason)") - SmartSnackbar.make(parent.getString(R.string.tethering_manage_wifi_client_blocked, client, reason)).apply { + Timber.i("$name blocked from connecting: $reason ($blockedReason)") + SmartSnackbar.make(parent.getString(R.string.tethering_manage_wifi_client_blocked, name, reason)).apply { action(R.string.tethering_manage_wifi_copy_mac) { - app.clipboard.setPrimaryClip(ClipData.newPlainText(null, client.toString())) + app.clipboard.setPrimaryClip(ClipData.newPlainText(null, macAddress.toString())) } }.show() } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApInfo.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApInfo.kt index 1b6e0d73..ccec77cf 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApInfo.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApInfo.kt @@ -20,28 +20,26 @@ value class SoftApInfo(val inner: Parcelable) { @get:RequiresApi(31) private val getWifiStandard by lazy { clazz.getDeclaredMethod("getWifiStandard") } @get:RequiresApi(31) - private val getApInstanceIdentifier by lazy @TargetApi(31) { - UnblockCentral.SoftApInfo_getApInstanceIdentifier(clazz) - } + private val getApInstanceIdentifier by lazy @TargetApi(31) { UnblockCentral.getApInstanceIdentifier(clazz) } @get:RequiresApi(31) private val getAutoShutdownTimeoutMillis by lazy { clazz.getDeclaredMethod("getAutoShutdownTimeoutMillis") } val channelWidthLookup = ConstantLookup("CHANNEL_WIDTH_") { clazz } } - val frequency get() = getFrequency.invoke(inner) as Int - val bandwidth get() = getBandwidth.invoke(inner) as Int + val frequency get() = getFrequency(inner) as Int + val bandwidth get() = getBandwidth(inner) as Int @get:RequiresApi(31) - val bssid get() = getBssid.invoke(inner) as MacAddress + val bssid get() = getBssid(inner) as MacAddress @get:RequiresApi(31) - val wifiStandard get() = getWifiStandard.invoke(inner) as Int + val wifiStandard get() = getWifiStandard(inner) as Int @get:RequiresApi(31) val apInstanceIdentifier get() = try { - getApInstanceIdentifier.invoke(inner) as? String + getApInstanceIdentifier(inner) as? String } catch (e: ReflectiveOperationException) { Timber.w(e) null } @get:RequiresApi(31) - val autoShutdownTimeoutMillis get() = getAutoShutdownTimeoutMillis.invoke(inner) as Long + val autoShutdownTimeoutMillis get() = getAutoShutdownTimeoutMillis(inner) as Long } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt index 7d9354cf..16e11e06 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt @@ -81,7 +81,7 @@ object WifiApManager { fun onNumClientsChanged(numClients: Int) { } @RequiresApi(30) - fun onConnectedClientsChanged(clients: List) { + fun onConnectedClientsChanged(clients: List) { @Suppress("DEPRECATION") onNumClientsChanged(clients.size) } @@ -93,7 +93,7 @@ object WifiApManager { fun onCapabilityChanged(capability: Parcelable) { } @RequiresApi(30) - fun onBlockedClientConnecting(client: MacAddress, blockedReason: Int) { } + fun onBlockedClientConnecting(client: Parcelable, blockedReason: Int) { } } @RequiresApi(28) val failureReasonLookup = ConstantLookup("SAP_START_FAILURE_", @@ -112,10 +112,6 @@ object WifiApManager { WifiManager::class.java.getDeclaredMethod("unregisterSoftApCallback", interfaceSoftApCallback) } - private val getMacAddress by lazy { - Class.forName("android.net.wifi.WifiClient").getDeclaredMethod("getMacAddress") - } - @RequiresApi(28) fun registerSoftApCallback(callback: SoftApCallbackCompat, executor: Executor): Any { val proxy = Proxy.newProxyInstance(interfaceSoftApCallback.classLoader, @@ -140,18 +136,19 @@ object WifiApManager { } "onConnectedClientsChanged" -> @TargetApi(30) { if (Build.VERSION.SDK_INT < 30) Timber.w(Exception("Unexpected onConnectedClientsChanged")) + @Suppress("UNCHECKED_CAST") callback.onConnectedClientsChanged(when (noArgs) { - 1 -> args!![0] as? Iterable<*> ?: return null + 1 -> args!![0] as List 2 -> { Timber.w(Exception("Unexpected onConnectedClientsChanged API 31+")) // dispatchInfoChanged(args!![0]) - args!![1] as? Iterable<*> ?: return null + args!![1] as List } else -> { Timber.w("Unexpected args for $name: ${args?.contentToString()}") return null } - }.map { getMacAddress(it) as MacAddress }) + }) } "onInfoChanged" -> @TargetApi(30) { if (noArgs != 1) Timber.w("Unexpected args for $name: ${args?.contentToString()}") @@ -179,7 +176,7 @@ object WifiApManager { "onBlockedClientConnecting" -> @TargetApi(30) { if (Build.VERSION.SDK_INT < 30) Timber.w(Exception("Unexpected onBlockedClientConnecting")) if (noArgs != 2) Timber.w("Unexpected args for $name: ${args?.contentToString()}") - callback.onBlockedClientConnecting(getMacAddress(args!![0]) as MacAddress, args[1] as Int) + callback.onBlockedClientConnecting(args!![0] as Parcelable, args[1] as Int) } else -> callSuper(interfaceSoftApCallback, proxy, method, args) } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiClient.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiClient.kt new file mode 100644 index 00000000..57edc1a3 --- /dev/null +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiClient.kt @@ -0,0 +1,28 @@ +package be.mygod.vpnhotspot.net.wifi + +import android.annotation.TargetApi +import android.net.MacAddress +import android.os.Parcelable +import androidx.annotation.RequiresApi +import be.mygod.vpnhotspot.util.UnblockCentral +import timber.log.Timber + +@JvmInline +@RequiresApi(30) +value class WifiClient(val inner: Parcelable) { + companion object { + private val clazz by lazy { Class.forName("android.net.wifi.WifiClient") } + private val getMacAddress by lazy { clazz.getDeclaredMethod("getMacAddress") } + @get:RequiresApi(31) + private val getApInstanceIdentifier by lazy @TargetApi(31) { UnblockCentral.getApInstanceIdentifier(clazz) } + } + + val macAddress get() = getMacAddress(inner) as MacAddress + @get:RequiresApi(31) + val apInstanceIdentifier get() = try { + getApInstanceIdentifier(inner) as? String + } catch (e: ReflectiveOperationException) { + Timber.w(e) + null + } +} diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt b/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt index b3d72dfc..23c6bbd1 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt @@ -32,7 +32,7 @@ object WifiApCommands { } @Parcelize @RequiresApi(30) - data class OnConnectedClientsChanged(val clients: List) : SoftApCallbackParcel() { + data class OnConnectedClientsChanged(val clients: List) : SoftApCallbackParcel() { override fun dispatch(callback: WifiApManager.SoftApCallbackCompat) = callback.onConnectedClientsChanged(clients) } @@ -49,7 +49,7 @@ object WifiApCommands { } @Parcelize @RequiresApi(30) - data class OnBlockedClientConnecting(val client: MacAddress, val blockedReason: Int) : SoftApCallbackParcel() { + data class OnBlockedClientConnecting(val client: Parcelable, val blockedReason: Int) : SoftApCallbackParcel() { override fun dispatch(callback: WifiApManager.SoftApCallbackCompat) = callback.onBlockedClientConnecting(client, blockedReason) } @@ -73,7 +73,7 @@ object WifiApCommands { override fun onNumClientsChanged(numClients: Int) = push(SoftApCallbackParcel.OnNumClientsChanged(numClients)) @RequiresApi(30) - override fun onConnectedClientsChanged(clients: List) = + override fun onConnectedClientsChanged(clients: List) = push(SoftApCallbackParcel.OnConnectedClientsChanged(clients)) @RequiresApi(30) override fun onInfoChanged(info: List) = push(SoftApCallbackParcel.OnInfoChanged(info)) @@ -81,7 +81,7 @@ object WifiApCommands { override fun onCapabilityChanged(capability: Parcelable) = push(SoftApCallbackParcel.OnCapabilityChanged(capability)) @RequiresApi(30) - override fun onBlockedClientConnecting(client: MacAddress, blockedReason: Int) = + override fun onBlockedClientConnecting(client: Parcelable, blockedReason: Int) = push(SoftApCallbackParcel.OnBlockedClientConnecting(client, blockedReason)) }) { scope.launch { diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/util/UnblockCentral.kt b/mobile/src/main/java/be/mygod/vpnhotspot/util/UnblockCentral.kt index 2eceab4e..a61d802d 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/util/UnblockCentral.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/util/UnblockCentral.kt @@ -26,7 +26,5 @@ object UnblockCentral { } @RequiresApi(31) - fun SoftApInfo_getApInstanceIdentifier(clazz: Class<*>) = init.let { - clazz.getDeclaredMethod("getApInstanceIdentifier") - } + fun getApInstanceIdentifier(clazz: Class<*>) = init.let { clazz.getDeclaredMethod("getApInstanceIdentifier") } } From b6bdf8cfb77428ca69939e58fda397f6e405a190 Mon Sep 17 00:00:00 2001 From: Mygod Date: Thu, 27 May 2021 12:22:47 -0400 Subject: [PATCH 016/112] Use reflection instead of parcel magic to get around networkName check --- README.md | 6 +-- .../be/mygod/vpnhotspot/RepeaterService.kt | 45 ++++--------------- .../mygod/vpnhotspot/util/UnblockCentral.kt | 10 ++++- 3 files changed, 20 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index adcf90b9..89f2d1d2 100644 --- a/README.md +++ b/README.md @@ -167,6 +167,7 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (since API 26) `Landroid/net/wifi/WifiManager;->cancelLocalOnlyHotspotRequest()V,unsupported` * (prior to API 26) `Landroid/net/wifi/WifiManager;->setWifiApEnabled(Landroid/net/wifi/WifiConfiguration;Z)Z` * `Landroid/net/wifi/p2p/WifiP2pConfig$Builder;->MAC_ANY_ADDRESS:Landroid/net/MacAddress;,blocked` +* (since API 29) `Landroid/net/wifi/p2p/WifiP2pConfig$Builder;->mNetworkName:Ljava/lang/String;,blocked` * `Landroid/net/wifi/p2p/WifiP2pManager;->startWps(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Landroid/net/wifi/WpsInfo;Landroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V,max-target-r` * (since API 28, prior to API 30) `Landroid/provider/Settings$Global;->SOFT_AP_TIMEOUT_ENABLED:Ljava/lang/String;,lo-prio,max-target-o` * (prior to API 30) `Lcom/android/internal/R$array;->config_tether_bluetooth_regexs:I,max-target-q` @@ -300,10 +301,7 @@ Nonexported system resources: * (since API 30) `@com.android.networkstack.tethering:array/config_tether_wigig_regexs` * (since API 30) `@com.android.wifi.resources:integer/config_wifiFrameworkSoftApShutDownTimeoutMilliseconds` -Other: - -* (since API 29) `android.net.wifi.p2p.WifiP2pConfig` needs to be parcelized in a very specific order, except for possible extra fields at the end. (used only for safe mode) -* Activity `com.android.settings/.Settings$TetherSettingsActivity` is assumed to be exported. +Other: Activity `com.android.settings/.Settings$TetherSettingsActivity` is assumed to be exported. For `ip rule` priorities, `RULE_PRIORITY_SECURE_VPN` and `RULE_PRIORITY_TETHERING` is assumed to be 12000 and 18000 respectively; (prior to API 24) `RULE_PRIORITY_DEFAULT_NETWORK` is assumed to be 22000 (or at least > 18000). diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt index 21fed36f..56b20c56 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt @@ -13,7 +13,6 @@ import android.os.Looper import android.provider.Settings import androidx.annotation.StringRes import androidx.core.content.edit -import be.mygod.librootkotlinx.useParcel import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.net.MacAddressCompat import be.mygod.vpnhotspot.net.monitor.TetherTimeoutMonitor @@ -48,10 +47,6 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene private const val KEY_AUTO_SHUTDOWN = "service.repeater.autoShutdown" private const val KEY_SHUTDOWN_TIMEOUT = "service.repeater.shutdownTimeout" private const val KEY_DEVICE_ADDRESS = "service.repeater.mac" - /** - * Placeholder for bypassing networkName check. - */ - private const val PLACEHOLDER_NETWORK_NAME = "DIRECT-00-VPNHotspot" var persistentSupported = false @@ -65,6 +60,7 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene val safeModeConfigurable get() = Build.VERSION.SDK_INT >= 29 && hasP2pValidateName val safeMode get() = Build.VERSION.SDK_INT >= 29 && (!hasP2pValidateName || app.pref.getBoolean(KEY_SAFE_MODE, true)) + private val mNetworkName by lazy { UnblockCentral.WifiP2pConfig_Builder_mNetworkName } var networkName: String? get() = app.pref.getString(KEY_NETWORK_NAME, null) @@ -372,7 +368,12 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene p2pManager.createGroup(channel, listener) } else @TargetApi(29) { p2pManager.createGroup(channel, WifiP2pConfig.Builder().apply { - setNetworkName(PLACEHOLDER_NETWORK_NAME) + try { + mNetworkName.set(this, networkName) // bypass networkName check + } catch (e: ReflectiveOperationException) { + Timber.w(e) + setNetworkName(networkName) + } setPassphrase(passphrase) when (val oc = operatingChannel) { 0 -> setGroupOperatingBand(when (val band = operatingBand) { @@ -386,37 +387,9 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene } } setDeviceAddress(deviceAddress?.toPlatform()) - }.build().run { - useParcel { p -> - p.writeParcelable(this, 0) - val end = p.dataPosition() - p.setDataPosition(0) - val creator = p.readString() - val deviceAddress = p.readString() - val wps = p.readParcelable(javaClass.classLoader) - val long = p.readLong() - check(p.readString() == PLACEHOLDER_NETWORK_NAME) - check(p.readString() == passphrase) - val extrasLength = end - p.dataPosition() - check(extrasLength and 3 == 0) // parcel should be padded - if (extrasLength != 4) app.logEvent("p2p_config_extras_unexpected_length") { - param("length", extrasLength.toLong()) - } - val extras = (0 until extrasLength / 4).map { p.readInt() } - p.setDataPosition(0) - p.writeString(creator) - p.writeString(deviceAddress) - p.writeParcelable(wps, 0) - p.writeLong(long) - p.writeString(networkName) - p.writeString(passphrase) - extras.forEach(p::writeInt) - p.setDataPosition(0) - p.readParcelable(javaClass.classLoader) - } - }, listener) + }.build(), listener) } - } catch (e: SecurityException) { + } catch (e: RuntimeException) { Timber.w(e) startFailure(e.readableMessage) } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/util/UnblockCentral.kt b/mobile/src/main/java/be/mygod/vpnhotspot/util/UnblockCentral.kt index a61d802d..2813221b 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/util/UnblockCentral.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/util/UnblockCentral.kt @@ -1,13 +1,16 @@ package be.mygod.vpnhotspot.util import android.annotation.SuppressLint +import android.net.wifi.p2p.WifiP2pConfig import androidx.annotation.RequiresApi import timber.log.Timber /** * The central object for accessing all the useful blocked APIs. Thanks Google! + * + * Lazy cannot be used directly as it will create inner classes. */ -@SuppressLint("DiscouragedPrivateApi") +@SuppressLint("BlockedPrivateApi", "DiscouragedPrivateApi") @Suppress("FunctionName") object UnblockCentral { /** @@ -27,4 +30,9 @@ object UnblockCentral { @RequiresApi(31) fun getApInstanceIdentifier(clazz: Class<*>) = init.let { clazz.getDeclaredMethod("getApInstanceIdentifier") } + + @get:RequiresApi(29) + val WifiP2pConfig_Builder_mNetworkName get() = init.let { + WifiP2pConfig.Builder::class.java.getDeclaredField("mNetworkName").apply { isAccessible = true } + } } From 87a1b8b08de6855ae5ef039cbefc39e33ca72814 Mon Sep 17 00:00:00 2001 From: Mygod Date: Thu, 27 May 2021 13:33:26 -0400 Subject: [PATCH 017/112] Refine code style --- .../be/mygod/librootkotlinx/RootServer.kt | 6 ++-- .../src/main/java/be/mygod/vpnhotspot/App.kt | 1 + .../be/mygod/vpnhotspot/RepeaterService.kt | 5 ++- .../manage/LocalOnlyHotspotManager.kt | 4 +-- .../vpnhotspot/manage/TetheringFragment.kt | 19 ++++++----- .../mygod/vpnhotspot/net/TetheringManager.kt | 4 +-- .../net/wifi/SoftApConfigurationCompat.kt | 34 +++++++++++-------- .../mygod/vpnhotspot/net/wifi/SoftApInfo.kt | 3 +- .../vpnhotspot/net/wifi/WifiApManager.kt | 4 +-- .../mygod/vpnhotspot/net/wifi/WifiClient.kt | 3 +- .../net/wifi/WifiP2pManagerHelper.kt | 5 ++- .../be/mygod/vpnhotspot/root/MiscCommands.kt | 8 ++--- .../be/mygod/vpnhotspot/root/RootManager.kt | 2 ++ .../mygod/vpnhotspot/root/RoutingCommands.kt | 4 +-- .../mygod/vpnhotspot/root/WifiApCommands.kt | 3 +- .../java/be/mygod/vpnhotspot/util/Services.kt | 5 +-- .../java/be/mygod/vpnhotspot/util/Utils.kt | 7 ++-- 17 files changed, 65 insertions(+), 52 deletions(-) diff --git a/mobile/src/main/java/be/mygod/librootkotlinx/RootServer.kt b/mobile/src/main/java/be/mygod/librootkotlinx/RootServer.kt index f6f5fc4e..b962bc87 100644 --- a/mobile/src/main/java/be/mygod/librootkotlinx/RootServer.kt +++ b/mobile/src/main/java/be/mygod/librootkotlinx/RootServer.kt @@ -218,9 +218,9 @@ class RootServer { throw e } finally { Logger.me.d("Waiting for exit") - errorReader.await() + withContext(NonCancellable) { errorReader.await() } process.waitFor() - withContext(NonCancellable) { closeInternal(true) } + closeInternal(true) } } } @@ -290,7 +290,7 @@ class RootServer { } } - private suspend fun closeInternal(fromWorker: Boolean = false) = synchronized(callbackLookup) { + private fun closeInternal(fromWorker: Boolean = false) = synchronized(callbackLookup) { if (active) { active = false Logger.me.d(if (fromWorker) "Shutting down from worker" else "Shutting down from client") diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/App.kt b/mobile/src/main/java/be/mygod/vpnhotspot/App.kt index 9a17ac79..714a162e 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/App.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/App.kt @@ -61,6 +61,7 @@ class App : Application() { } } Timber.plant(object : Timber.DebugTree() { + @SuppressLint("LogNotTimber") override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { if (t == null) { if (priority != Log.DEBUG || BuildConfig.DEBUG) Log.println(priority, tag, message) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt index 56b20c56..72127da6 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt @@ -389,7 +389,10 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene setDeviceAddress(deviceAddress?.toPlatform()) }.build(), listener) } - } catch (e: RuntimeException) { + } catch (e: SecurityException) { + Timber.w(e) + startFailure(e.readableMessage) + } catch (e: IllegalArgumentException) { Timber.w(e) startFailure(e.readableMessage) } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/LocalOnlyHotspotManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/LocalOnlyHotspotManager.kt index c2d7d51e..75906a0c 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/LocalOnlyHotspotManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/LocalOnlyHotspotManager.kt @@ -1,7 +1,6 @@ package be.mygod.vpnhotspot.manage import android.Manifest -import android.annotation.TargetApi import android.content.* import android.location.LocationManager import android.os.Build @@ -9,6 +8,7 @@ import android.os.IBinder import android.provider.Settings import android.view.View import android.widget.Toast +import androidx.annotation.RequiresApi import androidx.core.content.getSystemService import androidx.recyclerview.widget.RecyclerView import be.mygod.vpnhotspot.App.Companion.app @@ -20,7 +20,7 @@ import be.mygod.vpnhotspot.util.formatAddresses import be.mygod.vpnhotspot.widget.SmartSnackbar import java.net.NetworkInterface -@TargetApi(26) +@RequiresApi(26) class LocalOnlyHotspotManager(private val parent: TetheringFragment) : Manager(), ServiceConnection { companion object { val permission = if (Build.VERSION.SDK_INT >= 29) { 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 e3f0b3fe..4637a66c 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt @@ -45,24 +45,27 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick inner class ManagerAdapter : ListAdapter(Manager), TetheringManager.TetheringEventCallback { internal val repeaterManager by lazy { RepeaterManager(this@TetheringFragment) } + @delegate:TargetApi(26) @get:RequiresApi(26) - internal val localOnlyHotspotManager by lazy @TargetApi(26) { LocalOnlyHotspotManager(this@TetheringFragment) } - internal val bluetoothManager by lazy @TargetApi(24) { TetherManager.Bluetooth(this@TetheringFragment) } + internal val localOnlyHotspotManager by lazy { LocalOnlyHotspotManager(this@TetheringFragment) } + @delegate:TargetApi(24) @get:RequiresApi(24) - private val tetherManagers by lazy @TargetApi(24) { + internal val bluetoothManager by lazy { TetherManager.Bluetooth(this@TetheringFragment) } + @delegate:TargetApi(24) + @get:RequiresApi(24) + private val tetherManagers by lazy { listOf(TetherManager.Wifi(this@TetheringFragment), TetherManager.Usb(this@TetheringFragment), bluetoothManager) } + @delegate:TargetApi(30) @get:RequiresApi(30) - private val tetherManagers30 by lazy @TargetApi(30) { + private val tetherManagers30 by lazy { listOf(TetherManager.Ethernet(this@TetheringFragment), TetherManager.Ncm(this@TetheringFragment), TetherManager.WiGig(this@TetheringFragment)) } - private val wifiManagerLegacy by lazy @Suppress("Deprecation") { - TetherManager.WifiLegacy(this@TetheringFragment) - } + private val wifiManagerLegacy by lazy { TetherManager.WifiLegacy(this@TetheringFragment) } private var enabledIfaces = emptyList() private var listDeferred = CompletableDeferred>(emptyList()) @@ -225,7 +228,7 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick } } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { AlertDialogFragment.setResultListener(this) { which, ret -> if (which == DialogInterface.BUTTON_POSITIVE) viewLifecycleOwner.lifecycleScope.launchWhenCreated { val configuration = ret!!.configuration 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 e60406b2..610ca7c3 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/TetheringManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/TetheringManager.kt @@ -7,7 +7,6 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter import android.content.pm.PackageManager -import android.content.pm.ResolveInfo import android.net.ConnectivityManager import android.net.Network import android.os.Build @@ -173,8 +172,9 @@ object TetheringManager { @get:RequiresApi(30) private val clazz by lazy { Class.forName("android.net.TetheringManager") } + @delegate:TargetApi(30) @get:RequiresApi(30) - private val instance by lazy @TargetApi(30) { + private val instance by lazy { @SuppressLint("WrongConstant") // hidden services are not included in constants as of R preview 4 val service = Services.context.getSystemService(TETHERING_SERVICE) service diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt index b6ff123e..fe83a470 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt @@ -108,41 +108,48 @@ data class SoftApConfigurationCompat( android.net.wifi.WifiConfiguration::class.java.getDeclaredField("apChannel") } + @delegate:TargetApi(30) @get:RequiresApi(30) - private val getAllowedClientList by lazy @TargetApi(30) { + private val getAllowedClientList by lazy { SoftApConfiguration::class.java.getDeclaredMethod("getAllowedClientList") } + @delegate:TargetApi(30) @get:RequiresApi(30) - private val getBand by lazy @TargetApi(30) { SoftApConfiguration::class.java.getDeclaredMethod("getBand") } + private val getBand by lazy { SoftApConfiguration::class.java.getDeclaredMethod("getBand") } + @delegate:TargetApi(30) @get:RequiresApi(30) - private val getBlockedClientList by lazy @TargetApi(30) { + private val getBlockedClientList by lazy { SoftApConfiguration::class.java.getDeclaredMethod("getBlockedClientList") } + @delegate:TargetApi(30) @get:RequiresApi(30) - private val getChannel by lazy @TargetApi(30) { - SoftApConfiguration::class.java.getDeclaredMethod("getChannel") - } + private val getChannel by lazy { SoftApConfiguration::class.java.getDeclaredMethod("getChannel") } + @delegate:TargetApi(30) @get:RequiresApi(30) - private val getMaxNumberOfClients by lazy @TargetApi(30) { + private val getMaxNumberOfClients by lazy { SoftApConfiguration::class.java.getDeclaredMethod("getMaxNumberOfClients") } + @delegate:TargetApi(30) @get:RequiresApi(30) - private val getShutdownTimeoutMillis by lazy @TargetApi(30) { + private val getShutdownTimeoutMillis by lazy { SoftApConfiguration::class.java.getDeclaredMethod("getShutdownTimeoutMillis") } + @delegate:TargetApi(30) @get:RequiresApi(30) - private val isAutoShutdownEnabled by lazy @TargetApi(30) { + private val isAutoShutdownEnabled by lazy { SoftApConfiguration::class.java.getDeclaredMethod("isAutoShutdownEnabled") } + @delegate:TargetApi(30) @get:RequiresApi(30) - private val isClientControlByUserEnabled by lazy @TargetApi(30) { + private val isClientControlByUserEnabled by lazy { SoftApConfiguration::class.java.getDeclaredMethod("isClientControlByUserEnabled") } @get:RequiresApi(30) private val classBuilder by lazy { Class.forName("android.net.wifi.SoftApConfiguration\$Builder") } + @delegate:TargetApi(30) @get:RequiresApi(30) - private val newBuilder by lazy @TargetApi(30) { classBuilder.getConstructor(SoftApConfiguration::class.java) } + private val newBuilder by lazy { classBuilder.getConstructor(SoftApConfiguration::class.java) } @get:RequiresApi(30) private val build by lazy { classBuilder.getDeclaredMethod("build") } @get:RequiresApi(30) @@ -159,10 +166,9 @@ data class SoftApConfigurationCompat( private val setBlockedClientList by lazy { classBuilder.getDeclaredMethod("setBlockedClientList", java.util.List::class.java) } + @delegate:TargetApi(30) @get:RequiresApi(30) - private val setBssid by lazy @TargetApi(30) { - classBuilder.getDeclaredMethod("setBssid", MacAddress::class.java) - } + private val setBssid by lazy { classBuilder.getDeclaredMethod("setBssid", MacAddress::class.java) } @get:RequiresApi(30) private val setChannel by lazy { classBuilder.getDeclaredMethod("setChannel", Int::class.java, Int::class.java) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApInfo.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApInfo.kt index ccec77cf..413260b9 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApInfo.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApInfo.kt @@ -19,8 +19,9 @@ value class SoftApInfo(val inner: Parcelable) { private val getBssid by lazy { clazz.getDeclaredMethod("getBssid") } @get:RequiresApi(31) private val getWifiStandard by lazy { clazz.getDeclaredMethod("getWifiStandard") } + @delegate:TargetApi(31) @get:RequiresApi(31) - private val getApInstanceIdentifier by lazy @TargetApi(31) { UnblockCentral.getApInstanceIdentifier(clazz) } + private val getApInstanceIdentifier by lazy { UnblockCentral.getApInstanceIdentifier(clazz) } @get:RequiresApi(31) private val getAutoShutdownTimeoutMillis by lazy { clazz.getDeclaredMethod("getAutoShutdownTimeoutMillis") } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt index 16e11e06..1e3fb1df 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt @@ -3,7 +3,6 @@ package be.mygod.vpnhotspot.net.wifi import android.annotation.TargetApi import android.content.Intent import android.content.pm.PackageManager -import android.net.MacAddress import android.net.wifi.SoftApConfiguration import android.net.wifi.WifiManager import android.os.Build @@ -43,8 +42,9 @@ object WifiApManager { } @get:RequiresApi(30) private val getSoftApConfiguration by lazy { WifiManager::class.java.getDeclaredMethod("getSoftApConfiguration") } + @delegate:TargetApi(30) @get:RequiresApi(30) - private val setSoftApConfiguration by lazy @TargetApi(30) { + private val setSoftApConfiguration by lazy { WifiManager::class.java.getDeclaredMethod("setSoftApConfiguration", SoftApConfiguration::class.java) } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiClient.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiClient.kt index 57edc1a3..c14de2b4 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiClient.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiClient.kt @@ -13,8 +13,9 @@ value class WifiClient(val inner: Parcelable) { companion object { private val clazz by lazy { Class.forName("android.net.wifi.WifiClient") } private val getMacAddress by lazy { clazz.getDeclaredMethod("getMacAddress") } + @delegate:TargetApi(31) @get:RequiresApi(31) - private val getApInstanceIdentifier by lazy @TargetApi(31) { UnblockCentral.getApInstanceIdentifier(clazz) } + private val getApInstanceIdentifier by lazy { UnblockCentral.getApInstanceIdentifier(clazz) } } val macAddress get() = getMacAddress(inner) as MacAddress diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiP2pManagerHelper.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiP2pManagerHelper.kt index bbbc216e..e43dce63 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiP2pManagerHelper.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiP2pManagerHelper.kt @@ -95,10 +95,10 @@ object WifiP2pManagerHelper { return result.future.await() } - private val interfacePersistentGroupInfoListener by lazy @SuppressLint("PrivateApi") { + private val interfacePersistentGroupInfoListener by lazy { Class.forName("android.net.wifi.p2p.WifiP2pManager\$PersistentGroupInfoListener") } - private val getGroupList by lazy @SuppressLint("PrivateApi") { + private val getGroupList by lazy { Class.forName("android.net.wifi.p2p.WifiP2pGroupList").getDeclaredMethod("getGroupList") } private val requestPersistentGroupInfo by lazy { @@ -111,7 +111,6 @@ object WifiP2pManagerHelper { * Requires one of NETWORK_SETTING, NETWORK_STACK, or READ_WIFI_CREDENTIAL permission since API 30. * * @param c is the channel created at {@link #initialize} - * @param listener for callback when persistent group info list is available. Can be null. */ suspend fun WifiP2pManager.requestPersistentGroupInfo(c: WifiP2pManager.Channel): Collection { val result = CompletableDeferred>() diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/root/MiscCommands.kt b/mobile/src/main/java/be/mygod/vpnhotspot/root/MiscCommands.kt index 34773089..7f588c79 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/root/MiscCommands.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/root/MiscCommands.kt @@ -102,8 +102,8 @@ class ProcessListener(private val terminateRegex: Regex, try { launch(parent) { try { - process.inputStream.bufferedReader().useLines { - for (line in it) { + process.inputStream.bufferedReader().useLines { lines -> + for (line in lines) { trySend(ProcessData.StdoutLine(line)).onClosed { return@useLines }.onFailure { throw it!! } if (terminateRegex.containsMatchIn(line)) process.destroy() } @@ -112,8 +112,8 @@ class ProcessListener(private val terminateRegex: Regex, } launch(parent) { try { - process.errorStream.bufferedReader().useLines { - for (line in it) trySend(ProcessData.StdoutLine(line)).onClosed { + process.errorStream.bufferedReader().useLines { lines -> + for (line in lines) trySend(ProcessData.StdoutLine(line)).onClosed { return@useLines }.onFailure { throw it!! } } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/root/RootManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/root/RootManager.kt index d874958f..a9bebf3b 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/root/RootManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/root/RootManager.kt @@ -1,5 +1,6 @@ package be.mygod.vpnhotspot.root +import android.annotation.SuppressLint import android.os.Parcelable import android.util.Log import be.mygod.librootkotlinx.* @@ -13,6 +14,7 @@ object RootManager : RootSession(), Logger { class RootInit : RootCommandNoResult { override suspend fun execute(): Parcelable? { Timber.plant(object : Timber.DebugTree() { + @SuppressLint("LogNotTimber") override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { if (priority >= Log.WARN) { System.err.println("$priority/$tag: $message") diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/root/RoutingCommands.kt b/mobile/src/main/java/be/mygod/vpnhotspot/root/RoutingCommands.kt index 4bef650d..f43fbad9 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/root/RoutingCommands.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/root/RoutingCommands.kt @@ -1,7 +1,6 @@ package be.mygod.vpnhotspot.root import android.os.Parcelable -import android.util.Log import be.mygod.librootkotlinx.RootCommand import be.mygod.librootkotlinx.RootCommandOneWay import be.mygod.vpnhotspot.net.Routing @@ -10,6 +9,7 @@ import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.withContext import kotlinx.parcelize.Parcelize +import timber.log.Timber object RoutingCommands { @Parcelize @@ -20,7 +20,7 @@ object RoutingCommands { process.outputStream.bufferedWriter().use(Routing.Companion::appendCleanCommands) when (val code = process.waitFor()) { 0 -> { } - else -> Log.d("RoutingCommands.Clean", "Unexpected exit code $code") + else -> Timber.w("Unexpected exit code $code") } check(process.waitFor() == 0) } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt b/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt index 23c6bbd1..a8be8569 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt @@ -1,6 +1,5 @@ package be.mygod.vpnhotspot.root -import android.net.MacAddress import android.os.Parcelable import androidx.annotation.RequiresApi import be.mygod.librootkotlinx.ParcelableBoolean @@ -58,7 +57,7 @@ object WifiApCommands { @Parcelize @RequiresApi(28) class RegisterSoftApCallback : RootCommandChannel { - override fun create(scope: CoroutineScope) = scope.produce(capacity = capacity) { + override fun create(scope: CoroutineScope) = scope.produce(capacity = capacity) { val finish = CompletableDeferred() val key = WifiApManager.registerSoftApCallback(object : WifiApManager.SoftApCallbackCompat { private fun push(parcel: SoftApCallbackParcel) { diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/util/Services.kt b/mobile/src/main/java/be/mygod/vpnhotspot/util/Services.kt index 03ae7e29..4d17d675 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/util/Services.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/util/Services.kt @@ -1,15 +1,12 @@ package be.mygod.vpnhotspot.util -import android.annotation.SuppressLint import android.content.Context import android.net.ConnectivityManager import android.net.wifi.WifiManager import android.net.wifi.p2p.WifiP2pManager -import android.util.Log import androidx.core.content.getSystemService import timber.log.Timber -@SuppressLint("LogNotTimber") object Services { private lateinit var contextInit: () -> Context val context by lazy { contextInit() } @@ -22,7 +19,7 @@ object Services { try { context.getSystemService() } catch (e: RuntimeException) { - if (android.os.Process.myUid() == 0) Log.w("WifiP2pManager", e) else Timber.w(e) + Timber.w(e) null } } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt b/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt index 49b0ac70..0e6bc5c3 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt @@ -90,7 +90,6 @@ private val formatSequence = "%([0-9]+\\$| Date: Thu, 27 May 2021 13:35:27 -0400 Subject: [PATCH 018/112] Set immutable flag on PendingIntent --- .../be/mygod/vpnhotspot/ServiceNotification.kt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/ServiceNotification.kt b/mobile/src/main/java/be/mygod/vpnhotspot/ServiceNotification.kt index fd7c8853..6609c2c0 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/ServiceNotification.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/ServiceNotification.kt @@ -21,14 +21,14 @@ object ServiceNotification { private fun buildNotification(context: Context): Notification { val builder = NotificationCompat.Builder(context, CHANNEL) - .setWhen(0) - .setCategory(NotificationCompat.CATEGORY_SERVICE) - .setColor(ContextCompat.getColor(context, R.color.colorPrimary)) - .setContentTitle(context.getText(R.string.notification_tethering_title)) - .setSmallIcon(R.drawable.ic_quick_settings_tile_on) - .setContentIntent(PendingIntent.getActivity(context, 0, - Intent(context, MainActivity::class.java), PendingIntent.FLAG_UPDATE_CURRENT)) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setWhen(0) + .setCategory(NotificationCompat.CATEGORY_SERVICE) + .setColor(ContextCompat.getColor(context, R.color.colorPrimary)) + .setContentTitle(context.getText(R.string.notification_tethering_title)) + .setSmallIcon(R.drawable.ic_quick_settings_tile_on) + .setContentIntent(PendingIntent.getActivity(context, 0, Intent(context, MainActivity::class.java), + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) val deviceCounts = deviceCountsMap.values.flatMap { it.entries }.sortedBy { it.key } val inactive = inactiveMap.values.flatten() var lines = deviceCounts.map { (dev, size) -> From d2f8de24b477a26dc18a2f59f09d522c18c6b90f Mon Sep 17 00:00:00 2001 From: Mygod Date: Thu, 27 May 2021 13:59:17 -0400 Subject: [PATCH 019/112] Fix lint --- .../be/mygod/vpnhotspot/RepeaterService.kt | 3 +- .../mygod/vpnhotspot/net/TetheringManager.kt | 3 +- .../net/wifi/SoftApConfigurationCompat.kt | 34 ++++++++----------- .../mygod/vpnhotspot/net/wifi/SoftApInfo.kt | 3 +- .../vpnhotspot/net/wifi/WifiApManager.kt | 3 +- .../mygod/vpnhotspot/net/wifi/WifiClient.kt | 3 +- .../java/be/mygod/vpnhotspot/util/Utils.kt | 6 ++-- 7 files changed, 21 insertions(+), 34 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt index 72127da6..80287a5a 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt @@ -50,8 +50,7 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene var persistentSupported = false - @delegate:TargetApi(29) - private val hasP2pValidateName by lazy { + private val hasP2pValidateName by lazy @TargetApi(29) { val array = Build.VERSION.SECURITY_PATCH.split('-', limit = 3) val y = array.getOrNull(0)?.toIntOrNull() val m = array.getOrNull(1)?.toIntOrNull() 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 610ca7c3..c5f0a250 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/TetheringManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/TetheringManager.kt @@ -172,9 +172,8 @@ object TetheringManager { @get:RequiresApi(30) private val clazz by lazy { Class.forName("android.net.TetheringManager") } - @delegate:TargetApi(30) @get:RequiresApi(30) - private val instance by lazy { + private val instance by lazy @TargetApi(30) { @SuppressLint("WrongConstant") // hidden services are not included in constants as of R preview 4 val service = Services.context.getSystemService(TETHERING_SERVICE) service diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt index fe83a470..b6ff123e 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt @@ -108,48 +108,41 @@ data class SoftApConfigurationCompat( android.net.wifi.WifiConfiguration::class.java.getDeclaredField("apChannel") } - @delegate:TargetApi(30) @get:RequiresApi(30) - private val getAllowedClientList by lazy { + private val getAllowedClientList by lazy @TargetApi(30) { SoftApConfiguration::class.java.getDeclaredMethod("getAllowedClientList") } - @delegate:TargetApi(30) @get:RequiresApi(30) - private val getBand by lazy { SoftApConfiguration::class.java.getDeclaredMethod("getBand") } - @delegate:TargetApi(30) + private val getBand by lazy @TargetApi(30) { SoftApConfiguration::class.java.getDeclaredMethod("getBand") } @get:RequiresApi(30) - private val getBlockedClientList by lazy { + private val getBlockedClientList by lazy @TargetApi(30) { SoftApConfiguration::class.java.getDeclaredMethod("getBlockedClientList") } - @delegate:TargetApi(30) @get:RequiresApi(30) - private val getChannel by lazy { SoftApConfiguration::class.java.getDeclaredMethod("getChannel") } - @delegate:TargetApi(30) + private val getChannel by lazy @TargetApi(30) { + SoftApConfiguration::class.java.getDeclaredMethod("getChannel") + } @get:RequiresApi(30) - private val getMaxNumberOfClients by lazy { + private val getMaxNumberOfClients by lazy @TargetApi(30) { SoftApConfiguration::class.java.getDeclaredMethod("getMaxNumberOfClients") } - @delegate:TargetApi(30) @get:RequiresApi(30) - private val getShutdownTimeoutMillis by lazy { + private val getShutdownTimeoutMillis by lazy @TargetApi(30) { SoftApConfiguration::class.java.getDeclaredMethod("getShutdownTimeoutMillis") } - @delegate:TargetApi(30) @get:RequiresApi(30) - private val isAutoShutdownEnabled by lazy { + private val isAutoShutdownEnabled by lazy @TargetApi(30) { SoftApConfiguration::class.java.getDeclaredMethod("isAutoShutdownEnabled") } - @delegate:TargetApi(30) @get:RequiresApi(30) - private val isClientControlByUserEnabled by lazy { + private val isClientControlByUserEnabled by lazy @TargetApi(30) { SoftApConfiguration::class.java.getDeclaredMethod("isClientControlByUserEnabled") } @get:RequiresApi(30) private val classBuilder by lazy { Class.forName("android.net.wifi.SoftApConfiguration\$Builder") } - @delegate:TargetApi(30) @get:RequiresApi(30) - private val newBuilder by lazy { classBuilder.getConstructor(SoftApConfiguration::class.java) } + private val newBuilder by lazy @TargetApi(30) { classBuilder.getConstructor(SoftApConfiguration::class.java) } @get:RequiresApi(30) private val build by lazy { classBuilder.getDeclaredMethod("build") } @get:RequiresApi(30) @@ -166,9 +159,10 @@ data class SoftApConfigurationCompat( private val setBlockedClientList by lazy { classBuilder.getDeclaredMethod("setBlockedClientList", java.util.List::class.java) } - @delegate:TargetApi(30) @get:RequiresApi(30) - private val setBssid by lazy { classBuilder.getDeclaredMethod("setBssid", MacAddress::class.java) } + private val setBssid by lazy @TargetApi(30) { + classBuilder.getDeclaredMethod("setBssid", MacAddress::class.java) + } @get:RequiresApi(30) private val setChannel by lazy { classBuilder.getDeclaredMethod("setChannel", Int::class.java, Int::class.java) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApInfo.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApInfo.kt index 413260b9..ccec77cf 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApInfo.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApInfo.kt @@ -19,9 +19,8 @@ value class SoftApInfo(val inner: Parcelable) { private val getBssid by lazy { clazz.getDeclaredMethod("getBssid") } @get:RequiresApi(31) private val getWifiStandard by lazy { clazz.getDeclaredMethod("getWifiStandard") } - @delegate:TargetApi(31) @get:RequiresApi(31) - private val getApInstanceIdentifier by lazy { UnblockCentral.getApInstanceIdentifier(clazz) } + private val getApInstanceIdentifier by lazy @TargetApi(31) { UnblockCentral.getApInstanceIdentifier(clazz) } @get:RequiresApi(31) private val getAutoShutdownTimeoutMillis by lazy { clazz.getDeclaredMethod("getAutoShutdownTimeoutMillis") } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt index 1e3fb1df..f45f537c 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt @@ -42,9 +42,8 @@ object WifiApManager { } @get:RequiresApi(30) private val getSoftApConfiguration by lazy { WifiManager::class.java.getDeclaredMethod("getSoftApConfiguration") } - @delegate:TargetApi(30) @get:RequiresApi(30) - private val setSoftApConfiguration by lazy { + private val setSoftApConfiguration by lazy @TargetApi(30) { WifiManager::class.java.getDeclaredMethod("setSoftApConfiguration", SoftApConfiguration::class.java) } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiClient.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiClient.kt index c14de2b4..57edc1a3 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiClient.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiClient.kt @@ -13,9 +13,8 @@ value class WifiClient(val inner: Parcelable) { companion object { private val clazz by lazy { Class.forName("android.net.wifi.WifiClient") } private val getMacAddress by lazy { clazz.getDeclaredMethod("getMacAddress") } - @delegate:TargetApi(31) @get:RequiresApi(31) - private val getApInstanceIdentifier by lazy { UnblockCentral.getApInstanceIdentifier(clazz) } + private val getApInstanceIdentifier by lazy @TargetApi(31) { UnblockCentral.getApInstanceIdentifier(clazz) } } val macAddress get() = getMacAddress(inner) as MacAddress diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt b/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt index 0e6bc5c3..7e6e3ab9 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt @@ -159,8 +159,7 @@ fun NetworkInterface.formatAddresses(macOnly: Boolean = false) = SpannableString } }.trimEnd() -@delegate:SuppressLint("SoonBlockedPrivateApi") -private val parseNumericAddress by lazy { +private val parseNumericAddress by lazy @SuppressLint("SoonBlockedPrivateApi") { InetAddress::class.java.getDeclaredMethod("parseNumericAddress", String::class.java).apply { isAccessible = true } @@ -201,9 +200,8 @@ fun Resources.findIdentifier(name: String, defType: String, defPackage: String, if (alternativePackage != null && it == 0) getIdentifier(name, defType, alternativePackage) else it } -@delegate:TargetApi(26) @get:RequiresApi(26) -private val newLookup by lazy { +private val newLookup by lazy @TargetApi(26) { MethodHandles.Lookup::class.java.getDeclaredConstructor(Class::class.java, Int::class.java).apply { isAccessible = true } From f75ac30804aa4f24b5961a8666efda3c7c83eaa9 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 28 May 2021 21:20:51 -0400 Subject: [PATCH 020/112] Fix lint again --- .../main/java/be/mygod/vpnhotspot/RepeaterService.kt | 4 +++- .../be/mygod/vpnhotspot/manage/TetheringFragment.kt | 12 ++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt index 80287a5a..d3184f49 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt @@ -11,6 +11,7 @@ import android.net.wifi.p2p.* import android.os.Build import android.os.Looper import android.provider.Settings +import androidx.annotation.RequiresApi import androidx.annotation.StringRes import androidx.core.content.edit import be.mygod.vpnhotspot.App.Companion.app @@ -50,7 +51,8 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene var persistentSupported = false - private val hasP2pValidateName by lazy @TargetApi(29) { + @get:RequiresApi(29) + private val hasP2pValidateName by lazy { val array = Build.VERSION.SECURITY_PATCH.split('-', limit = 3) val y = array.getOrNull(0)?.toIntOrNull() val m = array.getOrNull(1)?.toIntOrNull() 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 4637a66c..25be6bb0 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt @@ -45,22 +45,18 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick inner class ManagerAdapter : ListAdapter(Manager), TetheringManager.TetheringEventCallback { internal val repeaterManager by lazy { RepeaterManager(this@TetheringFragment) } - @delegate:TargetApi(26) @get:RequiresApi(26) - internal val localOnlyHotspotManager by lazy { LocalOnlyHotspotManager(this@TetheringFragment) } - @delegate:TargetApi(24) + internal val localOnlyHotspotManager by lazy @TargetApi(26) { LocalOnlyHotspotManager(this@TetheringFragment) } @get:RequiresApi(24) - internal val bluetoothManager by lazy { TetherManager.Bluetooth(this@TetheringFragment) } - @delegate:TargetApi(24) + internal val bluetoothManager by lazy @TargetApi(24) { TetherManager.Bluetooth(this@TetheringFragment) } @get:RequiresApi(24) - private val tetherManagers by lazy { + private val tetherManagers by lazy @TargetApi(24) { listOf(TetherManager.Wifi(this@TetheringFragment), TetherManager.Usb(this@TetheringFragment), bluetoothManager) } - @delegate:TargetApi(30) @get:RequiresApi(30) - private val tetherManagers30 by lazy { + private val tetherManagers30 by lazy @TargetApi(30) { listOf(TetherManager.Ethernet(this@TetheringFragment), TetherManager.Ncm(this@TetheringFragment), TetherManager.WiGig(this@TetheringFragment)) From 55cefdb26ef20f2c5823ad26945947e1ce586489 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 28 May 2021 22:09:01 -0400 Subject: [PATCH 021/112] Add missing documentations for SoftApCallbackCompat --- .../vpnhotspot/net/wifi/WifiApManager.kt | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt index f45f537c..9d1cc427 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt @@ -63,11 +63,13 @@ object WifiApManager { /** * Called when soft AP state changes. * - * @param state new new AP state. One of {@link #WIFI_AP_STATE_DISABLED}, - * {@link #WIFI_AP_STATE_DISABLING}, {@link #WIFI_AP_STATE_ENABLED}, - * {@link #WIFI_AP_STATE_ENABLING}, {@link #WIFI_AP_STATE_FAILED} + * @param state the new AP state. One of {@link #WIFI_AP_STATE_DISABLED}, + * {@link #WIFI_AP_STATE_DISABLING}, {@link #WIFI_AP_STATE_ENABLED}, + * {@link #WIFI_AP_STATE_ENABLING}, {@link #WIFI_AP_STATE_FAILED} * @param failureReason reason when in failed state. One of - * {@link #SAP_START_FAILURE_GENERAL}, {@link #SAP_START_FAILURE_NO_CHANNEL} + * {@link #SAP_START_FAILURE_GENERAL}, + * {@link #SAP_START_FAILURE_NO_CHANNEL}, + * {@link #SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION} */ fun onStateChanged(state: Int, failureReason: Int) { } @@ -79,18 +81,44 @@ object WifiApManager { @Deprecated("onConnectedClientsChanged") fun onNumClientsChanged(numClients: Int) { } + /** + * Called when the connected clients to soft AP changes. + * + * @param clients the currently connected clients + */ @RequiresApi(30) fun onConnectedClientsChanged(clients: List) { @Suppress("DEPRECATION") onNumClientsChanged(clients.size) } + /** + * Called when information of softap changes. + * + * @param info is the softap information. {@link SoftApInfo} + */ @RequiresApi(30) fun onInfoChanged(info: List) { } + /** + * Called when capability of softap changes. + * + * @param capability is the softap capability. {@link SoftApCapability} + */ @RequiresApi(30) fun onCapabilityChanged(capability: Parcelable) { } + /** + * Called when client trying to connect but device blocked the client with specific reason. + * + * Can be used to ask user to update client to allowed list or blocked list + * when reason is {@link SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER}, or + * indicate the block due to maximum supported client number limitation when reason is + * {@link SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS}. + * + * @param client the currently blocked client. + * @param blockedReason one of blocked reason from {@link SapClientBlockedReason} + */ @RequiresApi(30) fun onBlockedClientConnecting(client: Parcelable, blockedReason: Int) { } } From bb7efb689786ab5b83355fabbc876a1c36bca720 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 28 May 2021 22:10:12 -0400 Subject: [PATCH 022/112] Refine documentation for onInfoChanged --- .../src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt index 9d1cc427..ffb25244 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt @@ -96,6 +96,7 @@ object WifiApManager { * Called when information of softap changes. * * @param info is the softap information. {@link SoftApInfo} + * At most one will be returned on API 30. */ @RequiresApi(30) fun onInfoChanged(info: List) { } From 9d5d7052d36cb272a32776ce0270c53864d1211d Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 28 May 2021 22:25:03 -0400 Subject: [PATCH 023/112] Ignore the new onConnectedClientsChanged for now --- .../vpnhotspot/net/wifi/WifiApManager.kt | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt index ffb25244..892651d1 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt @@ -95,7 +95,7 @@ object WifiApManager { /** * Called when information of softap changes. * - * @param info is the softap information. {@link SoftApInfo} + * @param info is the softap information. [SoftApInfo] * At most one will be returned on API 30. */ @RequiresApi(30) @@ -104,7 +104,7 @@ object WifiApManager { /** * Called when capability of softap changes. * - * @param capability is the softap capability. {@link SoftApCapability} + * @param capability is the softap capability. [SoftApCapability] */ @RequiresApi(30) fun onCapabilityChanged(capability: Parcelable) { } @@ -118,7 +118,7 @@ object WifiApManager { * {@link SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS}. * * @param client the currently blocked client. - * @param blockedReason one of blocked reason from {@link SapClientBlockedReason} + * @param blockedReason one of blocked reason from [SapClientBlockedReason] */ @RequiresApi(30) fun onBlockedClientConnecting(client: Parcelable, blockedReason: Int) { } @@ -165,18 +165,14 @@ object WifiApManager { "onConnectedClientsChanged" -> @TargetApi(30) { if (Build.VERSION.SDK_INT < 30) Timber.w(Exception("Unexpected onConnectedClientsChanged")) @Suppress("UNCHECKED_CAST") - callback.onConnectedClientsChanged(when (noArgs) { - 1 -> args!![0] as List - 2 -> { - Timber.w(Exception("Unexpected onConnectedClientsChanged API 31+")) - // dispatchInfoChanged(args!![0]) - args!![1] as List - } + when (noArgs) { + 1 -> callback.onConnectedClientsChanged(args!![0] as List) + 2 -> null // we use the old method which returns all clients in one call else -> { Timber.w("Unexpected args for $name: ${args?.contentToString()}") - return null + null } - }) + } } "onInfoChanged" -> @TargetApi(30) { if (noArgs != 1) Timber.w("Unexpected args for $name: ${args?.contentToString()}") From 0796c689d3cf02fe1c67f5a6c009508c31aedd25 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 28 May 2021 22:36:16 -0400 Subject: [PATCH 024/112] Format plural for numClients --- .../src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt | 3 ++- .../main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt | 3 ++- .../src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt | 1 - 3 files changed, 4 insertions(+), 3 deletions(-) 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 f9d52876..02e70d0f 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt @@ -201,9 +201,10 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), @TargetApi(30) private fun formatCapability(locale: Locale) = capability?.let { val capability = SoftApCapability(it) + val numClients = numClients val maxClients = capability.maxSupportedClients val supportedFeatures = capability.supportedFeatures - app.resources.getQuantityText(R.plurals.tethering_manage_wifi_capabilities, maxClients).format(locale, + app.resources.getQuantityText(R.plurals.tethering_manage_wifi_capabilities, numClients ?: 0).format(locale, numClients ?: "?", maxClients, sequence { var features = supportedFeatures if (features != 0L) while (features != 0L) { diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt index 892651d1..60e2d157 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt @@ -76,9 +76,10 @@ object WifiApManager { /** * Called when number of connected clients to soft AP changes. * + * It is not recommended to use this legacy method on API 30+. + * * @param numClients number of connected clients */ - @Deprecated("onConnectedClientsChanged") fun onNumClientsChanged(numClients: Int) { } /** diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt b/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt index a8be8569..b51a9eb6 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt @@ -68,7 +68,6 @@ object WifiApCommands { override fun onStateChanged(state: Int, failureReason: Int) = push(SoftApCallbackParcel.OnStateChanged(state, failureReason)) - @Suppress("OverridingDeprecatedMember") override fun onNumClientsChanged(numClients: Int) = push(SoftApCallbackParcel.OnNumClientsChanged(numClients)) @RequiresApi(30) From 257e4497cc867a9ddb1368001f9b1012b453fdae Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 28 May 2021 22:41:25 -0400 Subject: [PATCH 025/112] Support basic clients count on Android 9-10 --- .../main/java/be/mygod/vpnhotspot/manage/TetherManager.kt | 5 ++++- mobile/src/main/res/values-zh-rCN/strings.xml | 3 +++ mobile/src/main/res/values-zh-rTW/strings.xml | 3 +++ mobile/src/main/res/values/strings.xml | 4 ++++ 4 files changed, 14 insertions(+), 1 deletion(-) 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 02e70d0f..0545f87c 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt @@ -168,7 +168,7 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), } override fun onNumClientsChanged(numClients: Int) { this.numClients = numClients - if (Build.VERSION.SDK_INT >= 30) data.notifyChange() // only emits when onCapabilityChanged can be called + data.notifyChange() } override fun onInfoChanged(info: List) { this.info = info @@ -213,6 +213,9 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), features = features and bit.inv() } else yield(parent.getText(R.string.tethering_manage_wifi_no_features)) }.joinToSpanned()) + } ?: numClients?.let { numClients -> + app.resources.getQuantityText(R.plurals.tethering_manage_wifi_clients, numClients).format(locale, + numClients) } override val text get() = parent.resources.configuration.locale.let { locale -> listOfNotNull(failureReason?.let { WifiApManager.failureReasonLookup(it) }, baseError, info.run { diff --git a/mobile/src/main/res/values-zh-rCN/strings.xml b/mobile/src/main/res/values-zh-rCN/strings.xml index 52011a0f..469b1b00 100644 --- a/mobile/src/main/res/values-zh-rCN/strings.xml +++ b/mobile/src/main/res/values-zh-rCN/strings.xml @@ -66,6 +66,9 @@ 已连接 %1$s/%2$d 个设备\n支持功能:%3$s + + 已连接 %d 个设备 + 已屏蔽 %1$s:%2$s 复制 MAC diff --git a/mobile/src/main/res/values-zh-rTW/strings.xml b/mobile/src/main/res/values-zh-rTW/strings.xml index 2cd41f49..e7a0b381 100644 --- a/mobile/src/main/res/values-zh-rTW/strings.xml +++ b/mobile/src/main/res/values-zh-rTW/strings.xml @@ -71,6 +71,9 @@ 已連接 %1$s/%2$d 個設備\n支持功能:%3$s + + 已連接 %d 個設備 + 已隱藏 %1$s:%2$s 複製 MAC diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml index 4a6154e9..c3787e81 100644 --- a/mobile/src/main/res/values/strings.xml +++ b/mobile/src/main/res/values/strings.xml @@ -78,6 +78,10 @@ %1$s/%2$d client connected\nSupported features: %3$s %1$s/%2$d clients connected\nSupported features: %3$s + + %d client connected + %1d clients connected + None Blocked %1$s: %2$s Copy MAC From 4a0e4537e556badaa64e5176fb667a552c48049a Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 28 May 2021 23:07:07 -0400 Subject: [PATCH 026/112] Support reading AP clients from framework --- .../vpnhotspot/client/ClientViewModel.kt | 39 ++++++++++++++++--- .../mygod/vpnhotspot/client/WifiP2pClient.kt | 10 ----- .../vpnhotspot/net/wifi/WifiApManager.kt | 5 +-- .../mygod/vpnhotspot/root/WifiApCommands.kt | 7 ++-- 4 files changed, 37 insertions(+), 24 deletions(-) delete mode 100644 mobile/src/main/java/be/mygod/vpnhotspot/client/WifiP2pClient.kt diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientViewModel.kt b/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientViewModel.kt index a29271d2..53a848dd 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientViewModel.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientViewModel.kt @@ -5,6 +5,9 @@ import android.content.IntentFilter import android.content.ServiceConnection import android.net.wifi.p2p.WifiP2pDevice import android.os.IBinder +import android.os.Parcelable +import androidx.annotation.RequiresApi +import androidx.core.os.BuildCompat import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.MutableLiveData @@ -13,13 +16,19 @@ import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.RepeaterService import be.mygod.vpnhotspot.net.IpNeighbour import be.mygod.vpnhotspot.net.MacAddressCompat +import be.mygod.vpnhotspot.net.MacAddressCompat.Companion.toCompat +import be.mygod.vpnhotspot.net.TetherType import be.mygod.vpnhotspot.net.TetheringManager import be.mygod.vpnhotspot.net.TetheringManager.localOnlyTetheredIfaces import be.mygod.vpnhotspot.net.TetheringManager.tetheredIfaces import be.mygod.vpnhotspot.net.monitor.IpNeighbourMonitor +import be.mygod.vpnhotspot.net.wifi.WifiApManager +import be.mygod.vpnhotspot.net.wifi.WifiClient +import be.mygod.vpnhotspot.root.WifiApCommands import be.mygod.vpnhotspot.util.broadcastReceiver -class ClientViewModel : ViewModel(), ServiceConnection, IpNeighbourMonitor.Callback, DefaultLifecycleObserver { +class ClientViewModel : ViewModel(), ServiceConnection, IpNeighbourMonitor.Callback, DefaultLifecycleObserver, + WifiApManager.SoftApCallbackCompat { private var tetheredInterfaces = emptySet() private val receiver = broadcastReceiver { _, intent -> tetheredInterfaces = (intent.tetheredIfaces ?: return@broadcastReceiver).toSet() + @@ -29,6 +38,7 @@ class ClientViewModel : ViewModel(), ServiceConnection, IpNeighbourMonitor.Callb private var repeater: RepeaterService.Binder? = null private var p2p: Collection = emptyList() + private var wifiAp = emptyList>() private var neighbours: Collection = emptyList() val clients = MutableLiveData>() val fullMode = object : DefaultLifecycleObserver { @@ -42,11 +52,18 @@ class ClientViewModel : ViewModel(), ServiceConnection, IpNeighbourMonitor.Callb private fun populateClients() { val clients = HashMap, Client>() - val group = repeater?.group - val p2pInterface = group?.`interface` - if (p2pInterface != null) { - for (client in p2p) clients[p2pInterface to MacAddressCompat.fromString(client.deviceAddress)] = - WifiP2pClient(p2pInterface, client) + repeater?.group?.`interface`?.let { p2pInterface -> + for (client in p2p) { + val addr = MacAddressCompat.fromString(client.deviceAddress!!) + clients[p2pInterface to addr] = object : Client(addr, p2pInterface) { + override val icon: Int get() = TetherType.WIFI_P2P.icon + } + } + } + for (client in wifiAp) { + clients[client] = object : Client(client.second, client.first) { + override val icon: Int get() = TetherType.WIFI.icon + } } for (neighbour in neighbours) { val key = neighbour.dev to neighbour.lladdr @@ -70,8 +87,10 @@ class ClientViewModel : ViewModel(), ServiceConnection, IpNeighbourMonitor.Callb override fun onStart(owner: LifecycleOwner) { app.registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED)) IpNeighbourMonitor.registerCallback(this, false) + if (BuildCompat.isAtLeastS()) WifiApCommands.registerSoftApCallback(this) } override fun onStop(owner: LifecycleOwner) { + if (BuildCompat.isAtLeastS()) WifiApCommands.unregisterSoftApCallback(this) IpNeighbourMonitor.unregisterCallback(this) app.unregisterReceiver(receiver) } @@ -94,4 +113,12 @@ class ClientViewModel : ViewModel(), ServiceConnection, IpNeighbourMonitor.Callb this.neighbours = neighbours populateClients() } + + @RequiresApi(31) + override fun onConnectedClientsChanged(clients: List) { + wifiAp = clients.mapNotNull { + val client = WifiClient(it) + client.apInstanceIdentifier?.run { this to client.macAddress.toCompat() } + } + } } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/client/WifiP2pClient.kt b/mobile/src/main/java/be/mygod/vpnhotspot/client/WifiP2pClient.kt deleted file mode 100644 index fa805954..00000000 --- a/mobile/src/main/java/be/mygod/vpnhotspot/client/WifiP2pClient.kt +++ /dev/null @@ -1,10 +0,0 @@ -package be.mygod.vpnhotspot.client - -import android.net.wifi.p2p.WifiP2pDevice -import be.mygod.vpnhotspot.net.MacAddressCompat -import be.mygod.vpnhotspot.net.TetherType - -class WifiP2pClient(p2pInterface: String, p2p: WifiP2pDevice) : - Client(MacAddressCompat.fromString(p2p.deviceAddress!!), p2pInterface) { - override val icon: Int get() = TetherType.WIFI_P2P.icon -} diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt index 60e2d157..707fa807 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt @@ -88,10 +88,7 @@ object WifiApManager { * @param clients the currently connected clients */ @RequiresApi(30) - fun onConnectedClientsChanged(clients: List) { - @Suppress("DEPRECATION") - onNumClientsChanged(clients.size) - } + fun onConnectedClientsChanged(clients: List) = onNumClientsChanged(clients.size) /** * Called when information of softap changes. diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt b/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt index b51a9eb6..bea331cd 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt @@ -25,7 +25,6 @@ object WifiApCommands { } @Parcelize data class OnNumClientsChanged(val numClients: Int) : SoftApCallbackParcel() { - @Suppress("DEPRECATION") override fun dispatch(callback: WifiApManager.SoftApCallbackCompat) = callback.onNumClientsChanged(numClients) } @@ -121,7 +120,7 @@ object WifiApCommands { is SoftApCallbackParcel.OnInfoChanged -> synchronized(callbacks) { lastCallback.info = parcel } is SoftApCallbackParcel.OnCapabilityChanged -> synchronized(callbacks) { lastCallback.capability = parcel } } - for (callback in synchronized(callbacks) { callbacks }) parcel.dispatch(callback) + for (callback in synchronized(callbacks) { callbacks.toList() }) parcel.dispatch(callback) } @RequiresApi(28) fun registerSoftApCallback(callback: WifiApManager.SoftApCallbackCompat) = synchronized(callbacks) { @@ -137,8 +136,8 @@ object WifiApCommands { SmartSnackbar.make(e).show() } } - lastCallback - } else null + null + } else lastCallback }?.toSequence()?.forEach { it?.dispatch(callback) } @RequiresApi(28) fun unregisterSoftApCallback(callback: WifiApManager.SoftApCallbackCompat) = synchronized(callbacks) { From ed4475ebb170e9912201d162a8ffa5d6bbd636b9 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 29 May 2021 14:58:46 -0400 Subject: [PATCH 027/112] Support for multi-band SACC --- README.md | 3 + .../be/mygod/vpnhotspot/RepeaterService.kt | 6 +- .../vpnhotspot/manage/RepeaterManager.kt | 7 +- .../net/wifi/SoftApConfigurationCompat.kt | 69 ++++++++++++++----- .../net/wifi/WifiApDialogFragment.kt | 3 +- 5 files changed, 64 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 89f2d1d2..6ddee0ce 100644 --- a/README.md +++ b/README.md @@ -240,6 +240,7 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setBlockedClientList(Ljava/util/List;)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setBssid(Landroid/net/MacAddress;)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setChannel(II)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` +* (since API 31) `Landroid/net/wifi/SoftApConfiguration$Builder;->setChannels(Landroid/util/SparseIntArray;)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setClientControlByUserEnabled(Z)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setHiddenSsid(Z)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setMaxNumberOfClients(I)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` @@ -248,12 +249,14 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setSsid(Ljava/lang/String;)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration;->BAND_2GHZ:I,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration;->BAND_5GHZ:I,sdk,system-api,test-api` +* (since API 31) `Landroid/net/wifi/SoftApConfiguration;->BAND_60GHZ:I,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration;->BAND_6GHZ:I,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration;->BAND_ANY:I,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration;->getAllowedClientList()Ljava/util/List;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration;->getBand()I,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration;->getBlockedClientList()Ljava/util/List;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration;->getChannel()I,sdk,system-api,test-api` +* (since API 31) `Landroid/net/wifi/SoftApConfiguration;->getChannels()Landroid/util/SparseIntArray;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration;->getMaxNumberOfClients()I,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration;->getShutdownTimeoutMillis()J,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration;->isAutoShutdownEnabled()Z,sdk,system-api,test-api` diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt index d3184f49..25b2cf3a 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt @@ -378,10 +378,12 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene setPassphrase(passphrase) when (val oc = operatingChannel) { 0 -> setGroupOperatingBand(when (val band = operatingBand) { - SoftApConfigurationCompat.BAND_ANY -> WifiP2pConfig.GROUP_OWNER_BAND_AUTO SoftApConfigurationCompat.BAND_2GHZ -> WifiP2pConfig.GROUP_OWNER_BAND_2GHZ SoftApConfigurationCompat.BAND_5GHZ -> WifiP2pConfig.GROUP_OWNER_BAND_5GHZ - else -> throw IllegalArgumentException("Unknown band $band") + else -> { + require(SoftApConfigurationCompat.isLegacyEitherBand(band)) { "Unknown band $band" } + WifiP2pConfig.GROUP_OWNER_BAND_AUTO + } }) else -> { setGroupOperatingFrequency(SoftApConfigurationCompat.channelToFrequency(operatingBand, oc)) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt index 239d17c1..0e833300 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt @@ -192,20 +192,17 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic return SoftApConfigurationCompat( ssid = networkName, passphrase = passphrase, - band = RepeaterService.operatingBand, - channel = RepeaterService.operatingChannel, securityType = SoftApConfiguration.SECURITY_TYPE_WPA2_PSK, // is not actually used isAutoShutdownEnabled = RepeaterService.isAutoShutdownEnabled, shutdownTimeoutMillis = RepeaterService.shutdownTimeoutMillis).apply { bssid = RepeaterService.deviceAddress + setChannel(RepeaterService.operatingChannel, RepeaterService.operatingBand) } to false } } else binder?.let { binder -> val group = binder.group ?: binder.fetchPersistentGroup().let { binder.group } if (group != null) return SoftApConfigurationCompat( ssid = group.networkName, - channel = RepeaterService.operatingChannel, - band = SoftApConfigurationCompat.BAND_ANY, securityType = SoftApConfiguration.SECURITY_TYPE_WPA2_PSK, // is not actually used isAutoShutdownEnabled = RepeaterService.isAutoShutdownEnabled, shutdownTimeoutMillis = RepeaterService.shutdownTimeoutMillis).run { @@ -215,6 +212,7 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic holder.config = config passphrase = config.psk bssid = config.bssid + setChannel(RepeaterService.operatingChannel) this to false } catch (e: Exception) { if (e !is CancellationException) Timber.w(e) @@ -230,6 +228,7 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic return null } private suspend fun updateConfiguration(config: SoftApConfigurationCompat) { + config.requireSingleBand() if (RepeaterService.safeMode) { RepeaterService.networkName = config.ssid RepeaterService.deviceAddress = config.bssid diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt index b6ff123e..b0f62894 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt @@ -6,7 +6,9 @@ import android.net.MacAddress import android.net.wifi.SoftApConfiguration import android.os.Build import android.os.Parcelable +import android.util.SparseIntArray import androidx.annotation.RequiresApi +import androidx.core.os.BuildCompat import be.mygod.vpnhotspot.net.MacAddressCompat import be.mygod.vpnhotspot.net.MacAddressCompat.Companion.toCompat import be.mygod.vpnhotspot.net.monitor.TetherTimeoutMonitor @@ -20,9 +22,7 @@ data class SoftApConfigurationCompat( var passphrase: String? = null, var isHiddenSsid: Boolean = false, @TargetApi(23) - var band: Int = BAND_2GHZ, - @TargetApi(23) - var channel: Int = 0, + var channels: SparseIntArray = SparseIntArray(1).apply { put(BAND_2GHZ, 0) }, @TargetApi(30) var maxNumberOfClients: Int = 0, var securityType: Int = SoftApConfiguration.SECURITY_TYPE_OPEN, @@ -41,7 +41,12 @@ data class SoftApConfigurationCompat( const val BAND_2GHZ = 1 const val BAND_5GHZ = 2 const val BAND_6GHZ = 4 - const val BAND_ANY = 7 + const val BAND_60GHZ = 8 + private const val BAND_LEGACY = BAND_2GHZ or BAND_5GHZ + const val BAND_ANY = BAND_LEGACY or BAND_6GHZ + + fun isLegacyEitherBand(band: Int) = band and BAND_LEGACY == BAND_LEGACY + /** * [android.net.wifi.WifiConfiguration.KeyMgmt.WPA2_PSK] */ @@ -67,10 +72,13 @@ data class SoftApConfigurationCompat( in 1..Int.MAX_VALUE -> 5000 + chan * 5 else -> throw IllegalArgumentException("Invalid 5GHz channel $chan") } + // TODO: update for Android 12 BAND_6GHZ -> if (chan in 1..253) { 5940 + chan * 5 } else throw IllegalArgumentException("Invalid 6GHz channel $chan") - // BAND_60GHZ -> if (chan in 1 until 7) 56160 + chan * 2160 + BAND_60GHZ -> if (chan in 1 until 7) { + 56160 + chan * 2160 + } else throw IllegalArgumentException("Invalid 60GHz channel $chan") else -> throw IllegalArgumentException("Invalid band $band") } fun frequencyToChannel(freq: Int) = when (freq) { @@ -122,6 +130,10 @@ data class SoftApConfigurationCompat( private val getChannel by lazy @TargetApi(30) { SoftApConfiguration::class.java.getDeclaredMethod("getChannel") } + @get:RequiresApi(31) + private val getChannels by lazy @TargetApi(31) { + SoftApConfiguration::class.java.getDeclaredMethod("getChannels") + } @get:RequiresApi(30) private val getMaxNumberOfClients by lazy @TargetApi(30) { SoftApConfiguration::class.java.getDeclaredMethod("getMaxNumberOfClients") @@ -167,6 +179,10 @@ data class SoftApConfigurationCompat( private val setChannel by lazy { classBuilder.getDeclaredMethod("setChannel", Int::class.java, Int::class.java) } + @get:RequiresApi(31) + private val setChannels by lazy { + classBuilder.getDeclaredMethod("setChannels", SparseIntArray::class.java) + } @get:RequiresApi(30) private val setClientControlByUserEnabled by lazy { classBuilder.getDeclaredMethod("setClientControlByUserEnabled", Boolean::class.java) @@ -196,13 +212,14 @@ data class SoftApConfigurationCompat( preSharedKey, hiddenSSID, // https://cs.android.com/android/platform/superproject/+/master:frameworks/base/wifi/java/android/net/wifi/SoftApConfToXmlMigrationUtil.java;l=87;drc=aa6527cf41671d1ed417b8ebdb6b3aa614f62344 - if (Build.VERSION.SDK_INT >= 23) when (val band = apBand.getInt(this)) { - 0 -> BAND_2GHZ - 1 -> BAND_5GHZ - -1 -> BAND_2GHZ or BAND_5GHZ - else -> throw IllegalArgumentException("Unexpected band $band") - } else BAND_ANY, - if (Build.VERSION.SDK_INT >= 23) apChannel.getInt(this) else 0, + SparseIntArray(1).apply { + if (Build.VERSION.SDK_INT < 23) put(BAND_ANY, 0) else put(when (val band = apBand.getInt(this)) { + 0 -> BAND_2GHZ + 1 -> BAND_5GHZ + -1 -> BAND_LEGACY + else -> throw IllegalArgumentException("Unexpected band $band") + }, apChannel.getInt(this)) + }, 0, allowedKeyManagement.nextSetBit(0).let { selected -> require(allowedKeyManagement.nextSetBit(selected + 1) < 0) { @@ -234,8 +251,9 @@ data class SoftApConfigurationCompat( bssid?.toCompat()?.addr, passphrase, isHiddenSsid, - getBand(this) as Int, - getChannel(this) as Int, + if (BuildCompat.isAtLeastS()) getChannels(this) as SparseIntArray else SparseIntArray(1).apply { + put(getBand(this) as Int, getChannel(this) as Int) + }, getMaxNumberOfClients(this) as Int, securityType, isAutoShutdownEnabled(this) as Boolean, @@ -252,6 +270,20 @@ data class SoftApConfigurationCompat( set(value) { bssidAddr = value?.addr } + @get:Deprecated("Use channels", ReplaceWith("channels.keyAt(0)")) + @get:TargetApi(23) + val band get() = channels.keyAt(0) + @get:Deprecated("Use channels", ReplaceWith("channels.valueAt(0)")) + @get:TargetApi(23) + val channel get() = channels.valueAt(0) + fun setChannel(channel: Int, band: Int = BAND_ANY) { + channels = SparseIntArray(1).apply { put(band, channel) } + } + + /** + * Only single band/channel can be supplied on API 23-30 + */ + fun requireSingleBand() = require(channels.size() == 1) { "Unsupported number of bands configured" } /** * Based on: @@ -263,6 +295,7 @@ data class SoftApConfigurationCompat( @Deprecated("Class deprecated in framework, use toPlatform().toWifiConfiguration()") @Suppress("DEPRECATION") fun toWifiConfiguration(): android.net.wifi.WifiConfiguration { + requireSingleBand() val wc = underlying as? android.net.wifi.WifiConfiguration val result = if (wc == null) android.net.wifi.WifiConfiguration() else android.net.wifi.WifiConfiguration(wc) val original = wc?.toCompat() @@ -277,7 +310,7 @@ data class SoftApConfigurationCompat( else -> throw IllegalArgumentException("Convert fail, unsupported band setting :$band") }) apChannel.setInt(result, channel) - } else require(band == BAND_ANY) { "Specifying band is unsupported on this platform" } + } else require(isLegacyEitherBand(band)) { "Specifying band is unsupported on this platform" } if (original?.securityType != securityType) { result.allowedKeyManagement.clear() result.allowedKeyManagement.set(when (securityType) { @@ -304,7 +337,11 @@ data class SoftApConfigurationCompat( setSsid(builder, ssid) setPassphrase(builder, if (securityType == SoftApConfiguration.SECURITY_TYPE_OPEN) null else passphrase, securityType) - if (channel == 0) setBand(builder, band) else setChannel(builder, channel, band) + if (BuildCompat.isAtLeastS()) setChannels(builder, channels) else { + requireSingleBand() + @Suppress("DEPRECATION") + if (channel == 0) setBand(builder, band) else setChannel(builder, channel, band) + } setBssid(builder, bssid?.toPlatform()) setMaxNumberOfClients(builder, maxNumberOfClients) setShutdownTimeoutMillis(builder, shutdownTimeoutMillis) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt index be72f8f4..becf1423 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt @@ -118,8 +118,7 @@ class WifiApDialogFragment : AlertDialogFragment= 23 || arg.p2pMode) { val bandOption = dialogView.band.selectedItem as BandOption - band = bandOption.band - channel = bandOption.channel + setChannel(bandOption.channel, bandOption.band) } bssid = if (dialogView.bssid.length() != 0) { MacAddressCompat.fromString(dialogView.bssid.text.toString()) From b38498071f6c5063772d87425d7c63e72f6a0a35 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 29 May 2021 17:50:31 -0400 Subject: [PATCH 028/112] Support showing supported channels --- README.md | 6 +++ .../mygod/vpnhotspot/manage/TetherManager.kt | 53 +++++++++++++++++-- .../vpnhotspot/net/wifi/SoftApCapability.kt | 16 +++++- .../net/wifi/SoftApConfigurationCompat.kt | 14 +++++ .../vpnhotspot/net/wifi/WifiApManager.kt | 3 +- .../mygod/vpnhotspot/util/ConstantLookup.kt | 8 +-- .../mygod/vpnhotspot/util/UnblockCentral.kt | 6 +++ mobile/src/main/res/values-zh-rCN/strings.xml | 1 + mobile/src/main/res/values/strings.xml | 1 + 9 files changed, 96 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 6ddee0ce..58454519 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,7 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (since API 30) `Landroid/net/ConnectivityModuleConnector;->IN_PROCESS_SUFFIX:Ljava/lang/String;` * (since API 30) `Landroid/net/TetheringManager$TetheringEventCallback;->onTetherableInterfaceRegexpsChanged(Landroid/net/TetheringManager$TetheringInterfaceRegexps;)V,blocked` * (since API 30) `Landroid/net/TetheringManager;->TETHERING_WIGIG:I,blocked` +* (since API 31) `Landroid/net/wifi/SoftApConfiguration;->BAND_TYPES:[I,blocked` * (since API 31) `Landroid/net/wifi/SoftApInfo;->getApInstanceIdentifier()Ljava/lang/String;,blocked` * (since API 31) `Landroid/net/wifi/WifiClient;->getApInstanceIdentifier()Ljava/lang/String;,blocked` * (prior to API 30) `Landroid/net/wifi/WifiConfiguration$KeyMgmt;->FT_PSK:I,lo-prio,max-target-o` @@ -228,9 +229,14 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (since API 30) `Landroid/net/TetheringManager;->startTethering(Landroid/net/TetheringManager$TetheringRequest;Ljava/util/concurrent/Executor;Landroid/net/TetheringManager$StartTetheringCallback;)V,sdk,system-api,test-api` * (since API 30) `Landroid/net/TetheringManager;->stopTethering(I)V,sdk,system-api,test-api` * (since API 30) `Landroid/net/TetheringManager;->unregisterTetheringEventCallback(Landroid/net/TetheringManager$TetheringEventCallback;)V,sdk,system-api,test-api` +* (since API 31) `Landroid/net/wifi/SoftApCapability;->SOFTAP_FEATURE_BAND_24G_SUPPORTED:J,sdk,system-api,test-api` +* (since API 31) `Landroid/net/wifi/SoftApCapability;->SOFTAP_FEATURE_BAND_5G_SUPPORTED:J,sdk,system-api,test-api` +* (since API 31) `Landroid/net/wifi/SoftApCapability;->SOFTAP_FEATURE_BAND_60G_SUPPORTED:J,sdk,system-api,test-api` +* (since API 31) `Landroid/net/wifi/SoftApCapability;->SOFTAP_FEATURE_BAND_6G_SUPPORTED:J,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApCapability;->SOFTAP_FEATURE_*:J,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApCapability;->areFeaturesSupported(J)Z,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApCapability;->getMaxSupportedClients()I,sdk,system-api,test-api` +* (since API 31) `Landroid/net/wifi/SoftApCapability;->getSupportedChannelList(I)[I,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->()V,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->(Landroid/net/wifi/SoftApConfiguration;)V,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->build()Landroid/net/wifi/SoftApConfiguration;,sdk,system-api,test-api` 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 0545f87c..d7c8b491 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt @@ -199,13 +199,23 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), override val type get() = VIEW_TYPE_WIFI @TargetApi(30) - private fun formatCapability(locale: Locale) = capability?.let { - val capability = SoftApCapability(it) + private fun formatCapability(locale: Locale) = capability?.let { parcel -> + val capability = SoftApCapability(parcel) val numClients = numClients val maxClients = capability.maxSupportedClients - val supportedFeatures = capability.supportedFeatures - app.resources.getQuantityText(R.plurals.tethering_manage_wifi_capabilities, numClients ?: 0).format(locale, - numClients ?: "?", maxClients, sequence { + var supportedFeatures = capability.supportedFeatures + if (BuildCompat.isAtLeastS()) for ((flag, band) in arrayOf( + SoftApCapability.SOFTAP_FEATURE_BAND_24G_SUPPORTED to SoftApConfigurationCompat.BAND_2GHZ, + SoftApCapability.SOFTAP_FEATURE_BAND_5G_SUPPORTED to SoftApConfigurationCompat.BAND_5GHZ, + SoftApCapability.SOFTAP_FEATURE_BAND_6G_SUPPORTED to SoftApConfigurationCompat.BAND_6GHZ, + SoftApCapability.SOFTAP_FEATURE_BAND_60G_SUPPORTED to SoftApConfigurationCompat.BAND_60GHZ, + )) { + if (capability.getSupportedChannelList(band).isEmpty()) continue + // reduce double reporting + supportedFeatures = supportedFeatures and flag.inv() + } + val result = parent.resources.getQuantityText(R.plurals.tethering_manage_wifi_capabilities, numClients ?: 0) + .format(locale, numClients ?: "?", maxClients, sequence { var features = supportedFeatures if (features != 0L) while (features != 0L) { val bit = features.takeLowestOneBit() @@ -213,6 +223,39 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), features = features and bit.inv() } else yield(parent.getText(R.string.tethering_manage_wifi_no_features)) }.joinToSpanned()) + if (BuildCompat.isAtLeastS()) { + val list = SoftApConfigurationCompat.BAND_TYPES.map { band -> + val channels = capability.getSupportedChannelList(band) + if (channels.isNotEmpty()) StringBuilder().apply { + append(SoftApConfigurationCompat.bandLookup(band, true)) + append(" (") + channels.sort() + var pending: Int? = null + var last = channels[0] + append(last) + for (channel in channels.asSequence().drop(1)) { + if (channel == last + 1) pending = channel else { + pending?.let { + append('-') + append(it) + pending = null + } + append(',') + append(channel) + } + last = channel + } + pending?.let { + append('-') + append(it) + } + append(')') + } else null + }.filterNotNull() + if (list.isNotEmpty()) result.append(parent.getText(R.string.tethering_manage_wifi_supported_channels) + .format(locale, list.joinToString("; "))) + } + result } ?: numClients?.let { numClients -> app.resources.getQuantityText(R.plurals.tethering_manage_wifi_clients, numClients).format(locale, numClients) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApCapability.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApCapability.kt index 85d0c057..24432405 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApCapability.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApCapability.kt @@ -11,10 +11,23 @@ value class SoftApCapability(val inner: Parcelable) { private val clazz by lazy { Class.forName("android.net.wifi.SoftApCapability") } private val getMaxSupportedClients by lazy { clazz.getDeclaredMethod("getMaxSupportedClients") } private val areFeaturesSupported by lazy { clazz.getDeclaredMethod("areFeaturesSupported", Long::class.java) } + @get:RequiresApi(31) + private val getSupportedChannelList by lazy { + clazz.getDeclaredMethod("getSupportedChannelList", Int::class.java) + } + + @RequiresApi(31) + const val SOFTAP_FEATURE_BAND_24G_SUPPORTED = 32L + @RequiresApi(31) + const val SOFTAP_FEATURE_BAND_5G_SUPPORTED = 64L + @RequiresApi(31) + const val SOFTAP_FEATURE_BAND_6G_SUPPORTED = 128L + @RequiresApi(31) + const val SOFTAP_FEATURE_BAND_60G_SUPPORTED = 256L val featureLookup by lazy { LongConstantLookup(clazz, "SOFTAP_FEATURE_") } } - val maxSupportedClients get() = getMaxSupportedClients.invoke(inner) as Int + val maxSupportedClients get() = getMaxSupportedClients(inner) as Int val supportedFeatures: Long get() { var supportedFeatures = 0L var probe = 1L @@ -24,4 +37,5 @@ value class SoftApCapability(val inner: Parcelable) { } return supportedFeatures } + fun getSupportedChannelList(band: Int) = getSupportedChannelList(inner, band) as IntArray } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt index b0f62894..e867b361 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt @@ -12,7 +12,10 @@ import androidx.core.os.BuildCompat import be.mygod.vpnhotspot.net.MacAddressCompat import be.mygod.vpnhotspot.net.MacAddressCompat.Companion.toCompat import be.mygod.vpnhotspot.net.monitor.TetherTimeoutMonitor +import be.mygod.vpnhotspot.util.ConstantLookup +import be.mygod.vpnhotspot.util.UnblockCentral import kotlinx.parcelize.Parcelize +import timber.log.Timber @Parcelize data class SoftApConfigurationCompat( @@ -40,10 +43,21 @@ data class SoftApConfigurationCompat( companion object { const val BAND_2GHZ = 1 const val BAND_5GHZ = 2 + @TargetApi(30) const val BAND_6GHZ = 4 + @TargetApi(31) const val BAND_60GHZ = 8 private const val BAND_LEGACY = BAND_2GHZ or BAND_5GHZ const val BAND_ANY = BAND_LEGACY or BAND_6GHZ + val BAND_TYPES by lazy { + if (BuildCompat.isAtLeastS()) try { + return@lazy UnblockCentral.SoftApConfiguration_BAND_TYPES + } catch (e: ReflectiveOperationException) { + Timber.w(e) + } + intArrayOf(BAND_2GHZ, BAND_5GHZ, BAND_6GHZ, BAND_60GHZ) + } + val bandLookup = ConstantLookup("BAND_", null, "2GHZ", "5GHZ") fun isLegacyEitherBand(band: Int) = band and BAND_LEGACY == BAND_LEGACY diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt index 707fa807..b7fc93b5 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt @@ -122,8 +122,7 @@ object WifiApManager { fun onBlockedClientConnecting(client: Parcelable, blockedReason: Int) { } } @RequiresApi(28) - val failureReasonLookup = ConstantLookup("SAP_START_FAILURE_", - "SAP_START_FAILURE_GENERAL", "SAP_START_FAILURE_NO_CHANNEL") + val failureReasonLookup = ConstantLookup("SAP_START_FAILURE_", "GENERAL", "NO_CHANNEL") @get:RequiresApi(30) val clientBlockLookup by lazy { ConstantLookup("SAP_CLIENT_") } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/util/ConstantLookup.kt b/mobile/src/main/java/be/mygod/vpnhotspot/util/ConstantLookup.kt index c7d3fb17..0e18da97 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/util/ConstantLookup.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/util/ConstantLookup.kt @@ -7,7 +7,7 @@ import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.R import timber.log.Timber -class ConstantLookup(private val prefix: String, private val lookup29: Array, +class ConstantLookup(private val prefix: String, private val lookup29: Array, private val clazz: () -> Class<*>) { private val lookup by lazy { SparseArrayCompat().apply { @@ -25,16 +25,16 @@ class ConstantLookup(private val prefix: String, private val lookup29: Array Class<*>) = +fun ConstantLookup(prefix: String, vararg lookup29: String?, clazz: () -> Class<*>) = ConstantLookup(prefix, lookup29, clazz) @Suppress("FunctionName") -inline fun ConstantLookup(prefix: String, vararg lookup29: String) = +inline fun ConstantLookup(prefix: String, vararg lookup29: String?) = ConstantLookup(prefix, lookup29) { T::class.java } class LongConstantLookup(private val clazz: Class<*>, private val prefix: String) { diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/util/UnblockCentral.kt b/mobile/src/main/java/be/mygod/vpnhotspot/util/UnblockCentral.kt index 2813221b..81e776a7 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/util/UnblockCentral.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/util/UnblockCentral.kt @@ -1,6 +1,7 @@ package be.mygod.vpnhotspot.util import android.annotation.SuppressLint +import android.net.wifi.SoftApConfiguration import android.net.wifi.p2p.WifiP2pConfig import androidx.annotation.RequiresApi import timber.log.Timber @@ -28,6 +29,11 @@ object UnblockCentral { } } + @get:RequiresApi(31) + val SoftApConfiguration_BAND_TYPES get() = init.let { + SoftApConfiguration::class.java.getField("BAND_TYPES").get(null) as IntArray + } + @RequiresApi(31) fun getApInstanceIdentifier(clazz: Class<*>) = init.let { clazz.getDeclaredMethod("getApInstanceIdentifier") } diff --git a/mobile/src/main/res/values-zh-rCN/strings.xml b/mobile/src/main/res/values-zh-rCN/strings.xml index 469b1b00..f6586d35 100644 --- a/mobile/src/main/res/values-zh-rCN/strings.xml +++ b/mobile/src/main/res/values-zh-rCN/strings.xml @@ -69,6 +69,7 @@ 已连接 %d 个设备 + \n支持频道: %s 已屏蔽 %1$s:%2$s 复制 MAC diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml index c3787e81..2b6b9933 100644 --- a/mobile/src/main/res/values/strings.xml +++ b/mobile/src/main/res/values/strings.xml @@ -82,6 +82,7 @@ %d client connected %1d clients connected + \nSupported channels: %s None Blocked %1$s: %2$s Copy MAC From 8a2c3d8e09e893de02c06cf11bc67d671f9da0f2 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 29 May 2021 18:01:25 -0400 Subject: [PATCH 029/112] Correct usage of VMDebug --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 58454519..386fabb2 100644 --- a/README.md +++ b/README.md @@ -180,7 +180,7 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (since API 30) `Lcom/android/server/wifi/WifiContext;->ACTION_RESOURCES_APK:Ljava/lang/String;` * (since API 29) `Lcom/android/server/wifi/p2p/WifiP2pServiceImpl;->ANONYMIZED_DEVICE_ADDRESS:Ljava/lang/String;` * (since API 30) `Lcom/android/server/SystemServer;->TETHERING_CONNECTOR_CLASS:Ljava/lang/String;` -* (since API 31) `Ldalvik/system/VMDebug;->allowHiddenApiReflectionFrom(Ljava/lang/Class;)V,unsupported` +* (since API 29) `Ldalvik/system/VMDebug;->allowHiddenApiReflectionFrom(Ljava/lang/Class;)V,unsupported` * (since API 26) `Ljava/lang/invoke/MethodHandles$Lookup;->(Ljava/lang/Class;I)V,unsupported` * (since API 26) `Ljava/lang/invoke/MethodHandles$Lookup;->ALL_MODES:I,lo-prio,max-target-o` * (prior to API 29) `Ljava/net/InetAddress;->parseNumericAddress(Ljava/lang/String;)Ljava/net/InetAddress;,core-platform-api,max-target-p` From a65891b44f8ec96d11993b148c490eac64fa9031 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 29 May 2021 18:02:56 -0400 Subject: [PATCH 030/112] Update usages in README --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 386fabb2..8806f789 100644 --- a/README.md +++ b/README.md @@ -242,10 +242,10 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->build()Landroid/net/wifi/SoftApConfiguration;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setAllowedClientList(Ljava/util/List;)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setAutoShutdownEnabled(Z)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` -* (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setBand(I)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` +* (on API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setBand(I)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setBlockedClientList(Ljava/util/List;)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setBssid(Landroid/net/MacAddress;)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` -* (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setChannel(II)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` +* (on API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setChannel(II)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` * (since API 31) `Landroid/net/wifi/SoftApConfiguration$Builder;->setChannels(Landroid/util/SparseIntArray;)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setClientControlByUserEnabled(Z)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setHiddenSsid(Z)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` @@ -258,6 +258,7 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (since API 31) `Landroid/net/wifi/SoftApConfiguration;->BAND_60GHZ:I,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration;->BAND_6GHZ:I,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration;->BAND_ANY:I,sdk,system-api,test-api` +* (since API 31) `Landroid/net/wifi/SoftApConfiguration;->BAND_*:I,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration;->getAllowedClientList()Ljava/util/List;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration;->getBand()I,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration;->getBlockedClientList()Ljava/util/List;,sdk,system-api,test-api` @@ -279,7 +280,7 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (since API 30) `Landroid/net/wifi/WifiManager$SoftApCallback;->onBlockedClientConnecting(Landroid/net/wifi/WifiClient;I)V,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/WifiManager$SoftApCallback;->onCapabilityChanged(Landroid/net/wifi/SoftApCapability;)V,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/WifiManager$SoftApCallback;->onConnectedClientsChanged(Ljava/util/List;)V,sdk,system-api,test-api` -* (since API 30) `Landroid/net/wifi/WifiManager$SoftApCallback;->onInfoChanged(Landroid/net/wifi/SoftApInfo;)V,sdk,system-api,test-api` +* (on API 30) `Landroid/net/wifi/WifiManager$SoftApCallback;->onInfoChanged(Landroid/net/wifi/SoftApInfo;)V,sdk,system-api,test-api` * (since API 31) `Landroid/net/wifi/WifiManager$SoftApCallback;->onInfoChanged(Ljava/util/List;)V,sdk,system-api,test-api` * (since API 28) `Landroid/net/wifi/WifiManager$SoftApCallback;->onStateChanged(II)V,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/WifiManager;->SAP_CLIENT_BLOCK_REASON_CODE_*:I,sdk,system-api,test-api` From 396ac1fe07b9a255b65b404f08130e67e4cd87f1 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 29 May 2021 18:31:47 -0400 Subject: [PATCH 031/112] Update frequency calculations --- .../net/wifi/SoftApConfigurationCompat.kt | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt index e867b361..a8f9a2d1 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt @@ -72,7 +72,8 @@ data class SoftApConfigurationCompat( /** * Based on: - * https://elixir.bootlin.com/linux/v5.7.6/source/net/wireless/util.c#L75 + * https://elixir.bootlin.com/linux/v5.12.8/source/net/wireless/util.c#L75 + * TODO: update for Android 12 * https://cs.android.com/android/platform/superproject/+/master:frameworks/base/wifi/java/android/net/wifi/ScanResult.java;l=624;drc=f7ccda05642b55700d67a288462bada488fc7f5e */ fun channelToFrequency(band: Int, chan: Int) = when (band) { @@ -86,10 +87,11 @@ data class SoftApConfigurationCompat( in 1..Int.MAX_VALUE -> 5000 + chan * 5 else -> throw IllegalArgumentException("Invalid 5GHz channel $chan") } - // TODO: update for Android 12 - BAND_6GHZ -> if (chan in 1..253) { - 5940 + chan * 5 - } else throw IllegalArgumentException("Invalid 6GHz channel $chan") + BAND_6GHZ -> when (chan) { + 2 -> 5935 + in 1..233 -> 5950 + chan * 5 + else -> throw IllegalArgumentException("Invalid 6GHz channel $chan") + } BAND_60GHZ -> if (chan in 1 until 7) { 56160 + chan * 2160 } else throw IllegalArgumentException("Invalid 60GHz channel $chan") @@ -99,8 +101,9 @@ data class SoftApConfigurationCompat( 2484 -> 14 in Int.MIN_VALUE until 2484 -> (freq - 2407) / 5 in 4910..4980 -> (freq - 4000) / 5 - in Int.MIN_VALUE until 5945 -> (freq - 5000) / 5 - in Int.MIN_VALUE..45000 -> (freq - 5940) / 5 + in Int.MIN_VALUE until 5925 -> (freq - 5000) / 5 + 5935 -> 2 + in Int.MIN_VALUE..45000 -> (freq - 5950) / 5 in 58320..70200 -> (freq - 56160) / 2160 else -> throw IllegalArgumentException("Invalid frequency $freq") } From eddb74df21354dbaaf6d369ab14f70dcd54ac4b7 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 29 May 2021 18:42:56 -0400 Subject: [PATCH 032/112] Fix wrong order of SACC constructor --- .../mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt index a8f9a2d1..fdb3ec8d 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt @@ -26,9 +26,9 @@ data class SoftApConfigurationCompat( var isHiddenSsid: Boolean = false, @TargetApi(23) var channels: SparseIntArray = SparseIntArray(1).apply { put(BAND_2GHZ, 0) }, + var securityType: Int = SoftApConfiguration.SECURITY_TYPE_OPEN, @TargetApi(30) var maxNumberOfClients: Int = 0, - var securityType: Int = SoftApConfiguration.SECURITY_TYPE_OPEN, @TargetApi(28) var isAutoShutdownEnabled: Boolean = true, @TargetApi(28) @@ -237,7 +237,6 @@ data class SoftApConfigurationCompat( else -> throw IllegalArgumentException("Unexpected band $band") }, apChannel.getInt(this)) }, - 0, allowedKeyManagement.nextSetBit(0).let { selected -> require(allowedKeyManagement.nextSetBit(selected + 1) < 0) { "More than 1 key managements supplied: $allowedKeyManagement" @@ -258,7 +257,7 @@ data class SoftApConfigurationCompat( } } }, - if (Build.VERSION.SDK_INT >= 28) TetherTimeoutMonitor.enabled else false, + isAutoShutdownEnabled = if (Build.VERSION.SDK_INT >= 28) TetherTimeoutMonitor.enabled else false, underlying = this) @RequiresApi(30) @@ -271,8 +270,8 @@ data class SoftApConfigurationCompat( if (BuildCompat.isAtLeastS()) getChannels(this) as SparseIntArray else SparseIntArray(1).apply { put(getBand(this) as Int, getChannel(this) as Int) }, - getMaxNumberOfClients(this) as Int, securityType, + getMaxNumberOfClients(this) as Int, isAutoShutdownEnabled(this) as Boolean, getShutdownTimeoutMillis(this) as Long, isClientControlByUserEnabled(this) as Boolean, From ec88c4dac2fa373e1a7ba2f38d91c581a64855c7 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 29 May 2021 19:07:41 -0400 Subject: [PATCH 033/112] Support new SACC fields in Android 12 --- README.md | 10 ++++ .../net/wifi/SoftApConfigurationCompat.kt | 57 +++++++++++++++++++ .../mygod/vpnhotspot/util/UnblockCentral.kt | 7 ++- 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8806f789..fc16ada5 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,7 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (since API 30) `Landroid/net/ConnectivityModuleConnector;->IN_PROCESS_SUFFIX:Ljava/lang/String;` * (since API 30) `Landroid/net/TetheringManager$TetheringEventCallback;->onTetherableInterfaceRegexpsChanged(Landroid/net/TetheringManager$TetheringInterfaceRegexps;)V,blocked` * (since API 30) `Landroid/net/TetheringManager;->TETHERING_WIGIG:I,blocked` +* (since API 31) `Landroid/net/wifi/SoftApConfiguration$Builder;->setUserConfiguration(Z)Landroid/net/wifi/SoftApConfiguration$Builder;,blocked` * (since API 31) `Landroid/net/wifi/SoftApConfiguration;->BAND_TYPES:[I,blocked` * (since API 31) `Landroid/net/wifi/SoftApInfo;->getApInstanceIdentifier()Ljava/lang/String;,blocked` * (since API 31) `Landroid/net/wifi/WifiClient;->getApInstanceIdentifier()Ljava/lang/String;,blocked` @@ -244,11 +245,14 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setAutoShutdownEnabled(Z)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` * (on API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setBand(I)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setBlockedClientList(Ljava/util/List;)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` +* (since API 31) `Landroid/net/wifi/SoftApConfiguration$Builder;->setBridgedModeOpportunisticShutdownEnabled(Z)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setBssid(Landroid/net/MacAddress;)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` * (on API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setChannel(II)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` * (since API 31) `Landroid/net/wifi/SoftApConfiguration$Builder;->setChannels(Landroid/util/SparseIntArray;)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setClientControlByUserEnabled(Z)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setHiddenSsid(Z)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` +* (since API 31) `Landroid/net/wifi/SoftApConfiguration$Builder;->setIeee80211axEnabled(Z)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` +* (since API 31) `Landroid/net/wifi/SoftApConfiguration$Builder;->setMacRandomizationSetting(I)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setMaxNumberOfClients(I)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setPassphrase(Ljava/lang/String;I)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->setShutdownTimeoutMillis(J)Landroid/net/wifi/SoftApConfiguration$Builder;,sdk,system-api,test-api` @@ -259,15 +263,21 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (since API 30) `Landroid/net/wifi/SoftApConfiguration;->BAND_6GHZ:I,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration;->BAND_ANY:I,sdk,system-api,test-api` * (since API 31) `Landroid/net/wifi/SoftApConfiguration;->BAND_*:I,sdk,system-api,test-api` +* (since API 31) `Landroid/net/wifi/SoftApConfiguration;->RANDOMIZATION_NONE:I,sdk,system-api,test-api` +* (since API 31) `Landroid/net/wifi/SoftApConfiguration;->RANDOMIZATION_PERSISTENT:I,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration;->getAllowedClientList()Ljava/util/List;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration;->getBand()I,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration;->getBlockedClientList()Ljava/util/List;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration;->getChannel()I,sdk,system-api,test-api` * (since API 31) `Landroid/net/wifi/SoftApConfiguration;->getChannels()Landroid/util/SparseIntArray;,sdk,system-api,test-api` +* (since API 31) `Landroid/net/wifi/SoftApConfiguration;->getMacRandomizationSetting()I,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration;->getMaxNumberOfClients()I,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration;->getShutdownTimeoutMillis()J,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration;->isAutoShutdownEnabled()Z,sdk,system-api,test-api` +* (since API 31) `Landroid/net/wifi/SoftApConfiguration;->isBridgedModeOpportunisticShutdownEnabled()Z,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration;->isClientControlByUserEnabled()Z,sdk,system-api,test-api` +* (since API 31) `Landroid/net/wifi/SoftApConfiguration;->isIeee80211axEnabled()Z,sdk,system-api,test-api` +* (since API 31) `Landroid/net/wifi/SoftApConfiguration;->isUserConfiguration()Z,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApInfo;->CHANNEL_WIDTH_*:I,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApInfo;->CHANNEL_WIDTH_INVALID:I,sdk,system-api,test-api` * (since API 31) `Landroid/net/wifi/SoftApInfo;->getAutoShutdownTimeoutMillis()J,sdk,system-api,test-api` diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt index fdb3ec8d..8096c6dd 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt @@ -39,6 +39,14 @@ data class SoftApConfigurationCompat( var blockedClientList: List = emptyList(), @RequiresApi(30) var allowedClientList: List = emptyList(), + @TargetApi(31) + var macRandomizationSetting: Int = RANDOMIZATION_PERSISTENT, + @TargetApi(31) + var isBridgedModeOpportunisticShutdownEnabled: Boolean = true, + @TargetApi(31) + var isIeee80211axEnabled: Boolean = true, + @TargetApi(31) + var isUserConfiguration: Boolean = true, var underlying: Parcelable? = null) : Parcelable { companion object { const val BAND_2GHZ = 1 @@ -59,6 +67,11 @@ data class SoftApConfigurationCompat( } val bandLookup = ConstantLookup("BAND_", null, "2GHZ", "5GHZ") + @TargetApi(31) + const val RANDOMIZATION_NONE = 0 + @TargetApi(31) + const val RANDOMIZATION_PERSISTENT = 1 + fun isLegacyEitherBand(band: Int) = band and BAND_LEGACY == BAND_LEGACY /** @@ -151,6 +164,10 @@ data class SoftApConfigurationCompat( private val getChannels by lazy @TargetApi(31) { SoftApConfiguration::class.java.getDeclaredMethod("getChannels") } + @get:RequiresApi(31) + private val getMacRandomizationSetting by lazy @TargetApi(31) { + SoftApConfiguration::class.java.getDeclaredMethod("getMacRandomizationSetting") + } @get:RequiresApi(30) private val getMaxNumberOfClients by lazy @TargetApi(30) { SoftApConfiguration::class.java.getDeclaredMethod("getMaxNumberOfClients") @@ -163,10 +180,22 @@ data class SoftApConfigurationCompat( private val isAutoShutdownEnabled by lazy @TargetApi(30) { SoftApConfiguration::class.java.getDeclaredMethod("isAutoShutdownEnabled") } + @get:RequiresApi(31) + private val isBridgedModeOpportunisticShutdownEnabled by lazy @TargetApi(31) { + SoftApConfiguration::class.java.getDeclaredMethod("isBridgedModeOpportunisticShutdownEnabled") + } @get:RequiresApi(30) private val isClientControlByUserEnabled by lazy @TargetApi(30) { SoftApConfiguration::class.java.getDeclaredMethod("isClientControlByUserEnabled") } + @get:RequiresApi(31) + private val isIeee80211axEnabled by lazy @TargetApi(31) { + SoftApConfiguration::class.java.getDeclaredMethod("isIeee80211axEnabled") + } + @get:RequiresApi(31) + private val isUserConfiguration by lazy @TargetApi(31) { + SoftApConfiguration::class.java.getDeclaredMethod("isUserConfiguration") + } @get:RequiresApi(30) private val classBuilder by lazy { Class.forName("android.net.wifi.SoftApConfiguration\$Builder") } @@ -188,6 +217,10 @@ data class SoftApConfigurationCompat( private val setBlockedClientList by lazy { classBuilder.getDeclaredMethod("setBlockedClientList", java.util.List::class.java) } + @get:RequiresApi(31) + private val setBridgedModeOpportunisticShutdownEnabled by lazy { + classBuilder.getDeclaredMethod("setBridgedModeOpportunisticShutdownEnabled", Boolean::class.java) + } @get:RequiresApi(30) private val setBssid by lazy @TargetApi(30) { classBuilder.getDeclaredMethod("setBssid", MacAddress::class.java) @@ -206,6 +239,14 @@ data class SoftApConfigurationCompat( } @get:RequiresApi(30) private val setHiddenSsid by lazy { classBuilder.getDeclaredMethod("setHiddenSsid", Boolean::class.java) } + @get:RequiresApi(31) + private val setIeee80211axEnabled by lazy { + classBuilder.getDeclaredMethod("setIeee80211axEnabled", Boolean::class.java) + } + @get:RequiresApi(31) + private val setMacRandomizationSetting by lazy { + classBuilder.getDeclaredMethod("setMacRandomizationSetting", Int::class.java) + } @get:RequiresApi(30) private val setMaxNumberOfClients by lazy { classBuilder.getDeclaredMethod("setMaxNumberOfClients", Int::class.java) @@ -220,6 +261,8 @@ data class SoftApConfigurationCompat( } @get:RequiresApi(30) private val setSsid by lazy { classBuilder.getDeclaredMethod("setSsid", String::class.java) } + @get:RequiresApi(31) + private val setUserConfiguration by lazy @TargetApi(31) { UnblockCentral.setUserConfiguration(classBuilder) } @Deprecated("Class deprecated in framework") @Suppress("DEPRECATION") @@ -277,6 +320,10 @@ data class SoftApConfigurationCompat( isClientControlByUserEnabled(this) as Boolean, getBlockedClientList(this) as List, getAllowedClientList(this) as List, + getMacRandomizationSetting(this) as Int, + isBridgedModeOpportunisticShutdownEnabled(this) as Boolean, + isIeee80211axEnabled(this) as Boolean, + isUserConfiguration(this) as Boolean, this) } @@ -366,6 +413,16 @@ data class SoftApConfigurationCompat( setHiddenSsid(builder, isHiddenSsid) setAllowedClientList(builder, allowedClientList) setBlockedClientList(builder, blockedClientList) + if (BuildCompat.isAtLeastS()) { + setMacRandomizationSetting(builder, macRandomizationSetting) + setBridgedModeOpportunisticShutdownEnabled(builder, isBridgedModeOpportunisticShutdownEnabled) + setIeee80211axEnabled(builder, isIeee80211axEnabled) + if (sac?.let { isUserConfiguration(it) as Boolean } != false != isUserConfiguration) try { + setUserConfiguration(builder, isUserConfiguration) + } catch (e: ReflectiveOperationException) { + Timber.w(e) // as far as we are concerned, this field is not used anywhere so ignore for now + } + } return build(builder) as SoftApConfiguration } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/util/UnblockCentral.kt b/mobile/src/main/java/be/mygod/vpnhotspot/util/UnblockCentral.kt index 81e776a7..0a9a7daa 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/util/UnblockCentral.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/util/UnblockCentral.kt @@ -29,9 +29,14 @@ object UnblockCentral { } } + @RequiresApi(31) + fun setUserConfiguration(clazz: Class<*>) = init.let { + clazz.getDeclaredMethod("setUserConfiguration", Boolean::class.java) + } + @get:RequiresApi(31) val SoftApConfiguration_BAND_TYPES get() = init.let { - SoftApConfiguration::class.java.getField("BAND_TYPES").get(null) as IntArray + SoftApConfiguration::class.java.getDeclaredField("BAND_TYPES").get(null) as IntArray } @RequiresApi(31) From ada55a1bf65008754396e1a3f8fe4ab54400f5a4 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 29 May 2021 20:49:51 -0400 Subject: [PATCH 034/112] Support obtaining p2p randomization setting --- .../mygod/vpnhotspot/manage/RepeaterManager.kt | 5 ++++- .../net/monitor/TetherTimeoutMonitor.kt | 2 +- .../net/wifi/SoftApConfigurationCompat.kt | 4 ++++ .../mygod/vpnhotspot/net/wifi/WifiApManager.kt | 18 ++++++++++++++++++ 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt index 0e833300..b3aabd03 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt @@ -30,6 +30,7 @@ import be.mygod.vpnhotspot.net.MacAddressCompat import be.mygod.vpnhotspot.net.wifi.P2pSupplicantConfiguration import be.mygod.vpnhotspot.net.wifi.SoftApConfigurationCompat import be.mygod.vpnhotspot.net.wifi.WifiApDialogFragment +import be.mygod.vpnhotspot.net.wifi.WifiApManager import be.mygod.vpnhotspot.util.ServiceForegroundConnector import be.mygod.vpnhotspot.util.formatAddresses import be.mygod.vpnhotspot.util.showAllowingStateLoss @@ -197,6 +198,7 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic shutdownTimeoutMillis = RepeaterService.shutdownTimeoutMillis).apply { bssid = RepeaterService.deviceAddress setChannel(RepeaterService.operatingChannel, RepeaterService.operatingBand) + setMacRandomizationEnabled(WifiApManager.p2pMacRandomizationSupported) } to false } } else binder?.let { binder -> @@ -206,13 +208,14 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic securityType = SoftApConfiguration.SECURITY_TYPE_WPA2_PSK, // is not actually used isAutoShutdownEnabled = RepeaterService.isAutoShutdownEnabled, shutdownTimeoutMillis = RepeaterService.shutdownTimeoutMillis).run { + setChannel(RepeaterService.operatingChannel) + setMacRandomizationEnabled(WifiApManager.p2pMacRandomizationSupported) try { val config = P2pSupplicantConfiguration(group) config.init(binder.obtainDeviceAddress()?.toString()) holder.config = config passphrase = config.psk bssid = config.bssid - setChannel(RepeaterService.operatingChannel) this to false } catch (e: Exception) { if (e !is CancellationException) Timber.w(e) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/TetherTimeoutMonitor.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/TetherTimeoutMonitor.kt index 75629031..abba1855 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/TetherTimeoutMonitor.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/TetherTimeoutMonitor.kt @@ -46,7 +46,7 @@ class TetherTimeoutMonitor(private val timeout: Long = 0, val info = WifiApManager.resolvedActivity.activityInfo val resources = app.packageManager.getResourcesForApplication(info.applicationInfo) resources.getInteger(resources.findIdentifier("config_wifiFrameworkSoftApShutDownTimeoutMilliseconds", - "integer", "com.android.wifi.resources", info.packageName)) + "integer", WifiApManager.RESOURCES_PACKAGE, info.packageName)) } } catch (e: RuntimeException) { Timber.w(e) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt index 8096c6dd..6a596c14 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt @@ -343,6 +343,10 @@ data class SoftApConfigurationCompat( channels = SparseIntArray(1).apply { put(band, channel) } } + fun setMacRandomizationEnabled(enabled: Boolean) { + macRandomizationSetting = if (enabled) RANDOMIZATION_PERSISTENT else RANDOMIZATION_NONE + } + /** * Only single band/channel can be supplied on API 23-30 */ diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt index b7fc93b5..81217656 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt @@ -3,6 +3,7 @@ package be.mygod.vpnhotspot.net.wifi import android.annotation.TargetApi import android.content.Intent import android.content.pm.PackageManager +import android.content.res.Resources import android.net.wifi.SoftApConfiguration import android.net.wifi.WifiManager import android.os.Build @@ -15,6 +16,7 @@ import be.mygod.vpnhotspot.net.wifi.SoftApConfigurationCompat.Companion.toCompat import be.mygod.vpnhotspot.util.ConstantLookup import be.mygod.vpnhotspot.util.Services import be.mygod.vpnhotspot.util.callSuper +import be.mygod.vpnhotspot.util.findIdentifier import timber.log.Timber import java.lang.reflect.InvocationHandler import java.lang.reflect.Method @@ -27,6 +29,8 @@ object WifiApManager { */ @RequiresApi(30) private const val ACTION_RESOURCES_APK = "com.android.server.wifi.intent.action.SERVICE_WIFI_RESOURCES_APK" + @RequiresApi(30) + const val RESOURCES_PACKAGE = "com.android.wifi.resources" /** * Based on: https://android.googlesource.com/platform/frameworks/opt/net/wifi/+/000ad45/service/java/com/android/server/wifi/WifiContext.java#66 */ @@ -34,6 +38,20 @@ object WifiApManager { val resolvedActivity get() = app.packageManager.queryIntentActivities(Intent(ACTION_RESOURCES_APK), PackageManager.MATCH_SYSTEM_ONLY).single() + private const val CONFIG_P2P_MAC_RANDOMIZATION_SUPPORTED = "config_wifi_p2p_mac_randomization_supported" + val p2pMacRandomizationSupported get() = when (Build.VERSION.SDK_INT) { + 29 -> Resources.getSystem().run { + getBoolean(getIdentifier(CONFIG_P2P_MAC_RANDOMIZATION_SUPPORTED, "bool", "android")) + } + in 30..Int.MAX_VALUE -> @TargetApi(30) { + val info = resolvedActivity.activityInfo + val resources = app.packageManager.getResourcesForApplication(info.applicationInfo) + resources.getBoolean(resources.findIdentifier(CONFIG_P2P_MAC_RANDOMIZATION_SUPPORTED, "bool", + RESOURCES_PACKAGE, info.packageName)) + } + else -> false + } + private val getWifiApConfiguration by lazy { WifiManager::class.java.getDeclaredMethod("getWifiApConfiguration") } @Suppress("DEPRECATION") private val setWifiApConfiguration by lazy { From a6819a1def8e29664204cda1ea99dce22b3ea4b7 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sun, 30 May 2021 01:03:18 -0400 Subject: [PATCH 035/112] Support latest SACC features --- .../vpnhotspot/manage/RepeaterManager.kt | 6 +- .../net/wifi/SoftApConfigurationCompat.kt | 52 ++++-- .../net/wifi/WifiApDialogFragment.kt | 167 +++++++++++------- mobile/src/main/res/layout/dialog_wifi_ap.xml | 97 +++++++++- mobile/src/main/res/values-zh-rCN/strings.xml | 10 +- mobile/src/main/res/values-zh-rTW/strings.xml | 8 +- mobile/src/main/res/values/strings.xml | 10 +- 7 files changed, 260 insertions(+), 90 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt index b3aabd03..137fd2b1 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt @@ -231,7 +231,7 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic return null } private suspend fun updateConfiguration(config: SoftApConfigurationCompat) { - config.requireSingleBand() + val (band, channel) = config.requireSingleBand() if (RepeaterService.safeMode) { RepeaterService.networkName = config.ssid RepeaterService.deviceAddress = config.bssid @@ -248,8 +248,8 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic } holder.config = null } - RepeaterService.operatingBand = config.band - RepeaterService.operatingChannel = config.channel + RepeaterService.operatingBand = band + RepeaterService.operatingChannel = channel RepeaterService.isAutoShutdownEnabled = config.isAutoShutdownEnabled RepeaterService.shutdownTimeoutMillis = config.shutdownTimeoutMillis } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt index 6a596c14..98b7f145 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt @@ -9,6 +9,7 @@ import android.os.Parcelable import android.util.SparseIntArray import androidx.annotation.RequiresApi import androidx.core.os.BuildCompat +import androidx.core.util.keyIterator import be.mygod.vpnhotspot.net.MacAddressCompat import be.mygod.vpnhotspot.net.MacAddressCompat.Companion.toCompat import be.mygod.vpnhotspot.net.monitor.TetherTimeoutMonitor @@ -24,6 +25,11 @@ data class SoftApConfigurationCompat( var bssidAddr: Long? = null, var passphrase: String? = null, var isHiddenSsid: Boolean = false, + /** + * To read legacy band/channel pair, use [requireSingleBand]. For easy access, see [getChannel]. + * + * You should probably not set or modify this field directly. Use [optimizeChannels] or [setChannel]. + */ @TargetApi(23) var channels: SparseIntArray = SparseIntArray(1).apply { put(BAND_2GHZ, 0) }, var securityType: Int = SoftApConfiguration.SECURITY_TYPE_OPEN, @@ -333,25 +339,38 @@ data class SoftApConfigurationCompat( set(value) { bssidAddr = value?.addr } - @get:Deprecated("Use channels", ReplaceWith("channels.keyAt(0)")) - @get:TargetApi(23) - val band get() = channels.keyAt(0) - @get:Deprecated("Use channels", ReplaceWith("channels.valueAt(0)")) - @get:TargetApi(23) - val channel get() = channels.valueAt(0) + + /** + * Only single band/channel can be supplied on API 23-30 + */ + fun requireSingleBand(): Pair { + require(channels.size() == 1) { "Unsupported number of bands configured" } + return channels.keyAt(0) to channels.valueAt(0) + } + fun getChannel(band: Int): Int { + var result = -1 + for (b in channels.keyIterator()) if (band and b == band) { + require(result == -1) { "Duplicate band found" } + result = channels[b] + } + return result + } fun setChannel(channel: Int, band: Int = BAND_ANY) { channels = SparseIntArray(1).apply { put(band, channel) } } + fun optimizeChannels(channels: SparseIntArray = this.channels) { + this.channels = SparseIntArray(channels.size()).apply { + var setBand = 0 + for (band in channels.keyIterator()) if (channels[band] == 0) setBand = setBand or band + if (setBand != 0) put(setBand, 0) // merge all bands into one + for (band in channels.keyIterator()) if (band and setBand == 0) put(band, channels[band]) + } + } fun setMacRandomizationEnabled(enabled: Boolean) { macRandomizationSetting = if (enabled) RANDOMIZATION_PERSISTENT else RANDOMIZATION_NONE } - /** - * Only single band/channel can be supplied on API 23-30 - */ - fun requireSingleBand() = require(channels.size() == 1) { "Unsupported number of bands configured" } - /** * Based on: * https://android.googlesource.com/platform/packages/apps/Settings/+/android-5.0.0_r1/src/com/android/settings/wifi/WifiApDialog.java#88 @@ -362,7 +381,7 @@ data class SoftApConfigurationCompat( @Deprecated("Class deprecated in framework, use toPlatform().toWifiConfiguration()") @Suppress("DEPRECATION") fun toWifiConfiguration(): android.net.wifi.WifiConfiguration { - requireSingleBand() + val (band, channel) = requireSingleBand() val wc = underlying as? android.net.wifi.WifiConfiguration val result = if (wc == null) android.net.wifi.WifiConfiguration() else android.net.wifi.WifiConfiguration(wc) val original = wc?.toCompat() @@ -373,8 +392,10 @@ data class SoftApConfigurationCompat( apBand.setInt(result, when (band) { BAND_2GHZ -> 0 BAND_5GHZ -> 1 - BAND_2GHZ or BAND_5GHZ, BAND_ANY -> -1 - else -> throw IllegalArgumentException("Convert fail, unsupported band setting :$band") + else -> (-1).also { + require(Build.VERSION.SDK_INT >= 28) { "A band must be specified on this platform" } + require(isLegacyEitherBand(band)) { "Convert fail, unsupported band setting :$band" } + } }) apChannel.setInt(result, channel) } else require(isLegacyEitherBand(band)) { "Specifying band is unsupported on this platform" } @@ -405,8 +426,7 @@ data class SoftApConfigurationCompat( setPassphrase(builder, if (securityType == SoftApConfiguration.SECURITY_TYPE_OPEN) null else passphrase, securityType) if (BuildCompat.isAtLeastS()) setChannels(builder, channels) else { - requireSingleBand() - @Suppress("DEPRECATION") + val (band, channel) = requireSingleBand() if (channel == 0) setBand(builder, band) else setChannel(builder, channel, band) } setBssid(builder, bssid?.toPlatform()) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt index becf1423..5dd1b94a 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt @@ -10,13 +10,16 @@ import android.os.Parcelable import android.text.Editable import android.text.TextWatcher import android.util.Base64 +import android.util.SparseIntArray import android.view.MenuItem import android.view.View import android.widget.AdapterView import android.widget.ArrayAdapter +import android.widget.Spinner import androidx.annotation.RequiresApi import androidx.appcompat.app.AlertDialog import androidx.appcompat.widget.Toolbar +import androidx.core.os.BuildCompat import androidx.core.view.isGone import be.mygod.librootkotlinx.toByteArray import be.mygod.librootkotlinx.toParcelable @@ -32,6 +35,7 @@ import be.mygod.vpnhotspot.util.readableMessage import be.mygod.vpnhotspot.util.showAllowingStateLoss import be.mygod.vpnhotspot.widget.SmartSnackbar import kotlinx.parcelize.Parcelize +import timber.log.Timber /** * Based on: https://android.googlesource.com/platform/packages/apps/Settings/+/39b4674/src/com/android/settings/wifi/WifiApDialog.java @@ -40,27 +44,30 @@ import kotlinx.parcelize.Parcelize * Related: https://android.googlesource.com/platform/packages/apps/Settings/+/defb1183ecb00d6231bac7d934d07f58f90261ea */ class WifiApDialogFragment : AlertDialogFragment(), TextWatcher, - Toolbar.OnMenuItemClickListener { + Toolbar.OnMenuItemClickListener, AdapterView.OnItemSelectedListener { companion object { private const val BASE64_FLAGS = Base64.NO_PADDING or Base64.NO_WRAP private val nonMacChars = "[^0-9a-fA-F:]+".toRegex() - private val channels by lazy { - val list = ArrayList() - for (chan in 1..14) list.add(BandOption.Channel(SoftApConfigurationCompat.BAND_2GHZ, chan)) - for (chan in 1..196) list.add(BandOption.Channel(SoftApConfigurationCompat.BAND_5GHZ, chan)) - if (Build.VERSION.SDK_INT >= 30) { - for (chan in 1..253) list.add(BandOption.Channel(SoftApConfigurationCompat.BAND_6GHZ, chan)) - } - list + private val baseOptions by lazy { listOf(ChannelOption.Disabled, ChannelOption.Auto) } + private val channels2G by lazy { + baseOptions + (1..14).map { ChannelOption(it, SoftApConfigurationCompat.BAND_2GHZ) } + } + private val channels5G by lazy { + baseOptions + (1..196).map { ChannelOption(it, SoftApConfigurationCompat.BAND_5GHZ) } + } + @get:RequiresApi(30) + private val channels6G by lazy { + baseOptions + (1..233).map { ChannelOption(it, SoftApConfigurationCompat.BAND_6GHZ) } + } + @get:RequiresApi(31) + private val channels60G by lazy { + baseOptions + (1..6).map { ChannelOption(it, SoftApConfigurationCompat.BAND_60GHZ) } } /** * Source: https://android.googlesource.com/platform/frameworks/opt/net/wifi/+/c2fc6a1/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceHal.java#1396 */ private val p2pChannels by lazy { - (1..165).map { - val band = if (it <= 14) SoftApConfigurationCompat.BAND_2GHZ else SoftApConfigurationCompat.BAND_5GHZ - BandOption.Channel(band, it) - } + baseOptions + (15..165).map { ChannelOption(it, SoftApConfigurationCompat.BAND_5GHZ) } } } @@ -73,35 +80,20 @@ class WifiApDialogFragment : AlertDialogFragment private lateinit var base: SoftApConfigurationCompat private var started = false + private val currentChannels5G get() = if (arg.p2pMode && !RepeaterService.safeMode) p2pChannels else channels5G override val ret get() = Arg(generateConfig()) private fun generateConfig(full: Boolean = true) = base.copy( @@ -117,8 +109,15 @@ class WifiApDialogFragment : AlertDialogFragment= 23 || arg.p2pMode) { - val bandOption = dialogView.band.selectedItem as BandOption - setChannel(bandOption.channel, bandOption.band) + val channels = SparseIntArray(4) + for ((band, spinner) in arrayOf(SoftApConfigurationCompat.BAND_2GHZ to dialogView.band2G, + SoftApConfigurationCompat.BAND_5GHZ to dialogView.band5G, + SoftApConfigurationCompat.BAND_6GHZ to dialogView.band6G, + SoftApConfigurationCompat.BAND_60GHZ to dialogView.band60G)) { + val channel = (spinner.selectedItem as ChannelOption?)?.channel + if (channel != null && channel >= 0) channels.put(band, channel) + } + optimizeChannels(channels) } bssid = if (dialogView.bssid.length() != 0) { MacAddressCompat.fromString(dialogView.bssid.text.toString()) @@ -131,6 +130,10 @@ class WifiApDialogFragment : AlertDialogFragment= 23 || arg.p2pMode) dialogView.band.apply { - bandOptions = mutableListOf().apply { - if (arg.p2pMode) { - add(BandOption.BandAny) - if (RepeaterService.safeMode) { - add(BandOption.Band2GHz) - add(BandOption.Band5GHz) - addAll(channels) - } else addAll(p2pChannels) - } else { - if (Build.VERSION.SDK_INT >= 28) add(BandOption.BandAny) - add(BandOption.Band2GHz) - add(BandOption.Band5GHz) - if (Build.VERSION.SDK_INT >= 30) add(BandOption.Band6GHz) - addAll(channels) - } - } - adapter = ArrayAdapter(activity, android.R.layout.simple_spinner_item, 0, bandOptions).apply { + fun Spinner.configure(options: List) { + adapter = ArrayAdapter(activity, android.R.layout.simple_spinner_item, 0, options).apply { setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) } - } else dialogView.bandWrapper.isGone = true + onItemSelectedListener = this@WifiApDialogFragment + } + if (Build.VERSION.SDK_INT >= 23 || arg.p2pMode) { + dialogView.band2G.configure(channels2G) + dialogView.band5G.configure(currentChannels5G) + } else { + dialogView.bandWrapper2G.isGone = true + dialogView.bandWrapper5G.isGone = true + } + if (Build.VERSION.SDK_INT >= 30 && !arg.p2pMode) dialogView.band6G.configure(channels6G) + else dialogView.bandWrapper6G.isGone = true + if (BuildCompat.isAtLeastS() && !arg.p2pMode) dialogView.band60G.configure(channels60G) + else dialogView.bandWrapper60G.isGone = true dialogView.bssid.addTextChangedListener(this@WifiApDialogFragment) if (arg.p2pMode) dialogView.hiddenSsid.isGone = true if (arg.p2pMode || Build.VERSION.SDK_INT < 30) { @@ -196,10 +195,25 @@ class WifiApDialogFragment : AlertDialogFragment= 29) dialogView.macRandomization.isEnabled = false + else if (arg.p2pMode || !BuildCompat.isAtLeastS()) dialogView.macRandomization.isGone = true + if (arg.p2pMode || !BuildCompat.isAtLeastS()) { + dialogView.bridgedModeOpportunisticShutdown.isGone = true + dialogView.ieee80211ax.isGone = true + dialogView.userConfig.isGone = true + } base = arg.configuration populateFromConfiguration() } + private fun locate(band: Int, channels: List): Int { + val channel = base.getChannel(band) + val selection = channels.indexOfFirst { it.channel == channel } + return if (selection == -1) { + Timber.w(Exception("Unable to locate $band, $channel, ${arg.p2pMode && !RepeaterService.safeMode}")) + 0 + } else selection + } private fun populateFromConfiguration() { dialogView.ssid.setText(base.ssid) if (!arg.p2pMode) dialogView.security.setSelection(base.securityType) @@ -207,10 +221,10 @@ class WifiApDialogFragment : AlertDialogFragment= 23 || arg.p2pMode) { - val selection = if (base.channel != 0) { - bandOptions.indexOfFirst { it.channel == base.channel } - } else bandOptions.indexOfFirst { it.band == base.band } - dialogView.band.setSelection(if (selection == -1) 0 else selection) + dialogView.band2G.setSelection(locate(SoftApConfigurationCompat.BAND_2GHZ, channels2G)) + dialogView.band5G.setSelection(locate(SoftApConfigurationCompat.BAND_5GHZ, currentChannels5G)) + dialogView.band6G.setSelection(locate(SoftApConfigurationCompat.BAND_6GHZ, channels6G)) + dialogView.band60G.setSelection(locate(SoftApConfigurationCompat.BAND_60GHZ, channels60G)) } dialogView.bssid.setText(base.bssid?.toString()) dialogView.hiddenSsid.isChecked = base.isHiddenSsid @@ -218,6 +232,11 @@ class WifiApDialogFragment : AlertDialogFragment { + val option5G = dialogView.band5G.selectedItem + when (dialogView.band2G.selectedItem) { + is ChannelOption.Disabled -> option5G !is ChannelOption.Disabled && + (!arg.p2pMode || RepeaterService.safeMode || option5G !is ChannelOption.Auto) + is ChannelOption.Auto -> + (arg.p2pMode || Build.VERSION.SDK_INT >= 28) && option5G is ChannelOption.Auto || + (!arg.p2pMode || RepeaterService.safeMode) && option5G is ChannelOption.Disabled + else -> option5G is ChannelOption.Disabled + } + } + Build.VERSION.SDK_INT == 30 -> { + var expected = 1 + var set = 0 + for (s in arrayOf(dialogView.band2G, dialogView.band5G, dialogView.band6G)) when (s.selectedItem) { + is ChannelOption.Auto -> expected = 0 + !is ChannelOption.Disabled -> ++set + } + set == expected + } + else -> true + } dialogView.bssidWrapper.error = null val bssidValid = dialogView.bssid.length() == 0 || try { MacAddressCompat.fromString(dialogView.bssid.text.toString()) @@ -289,7 +331,7 @@ class WifiApDialogFragment : AlertDialogFragment?, view: View?, position: Int, id: Long) = validate() + override fun onNothingSelected(parent: AdapterView<*>?) = error("unreachable") + override fun onMenuItemClick(item: MenuItem?): Boolean { return when (item?.itemId) { android.R.id.copy -> { diff --git a/mobile/src/main/res/layout/dialog_wifi_ap.xml b/mobile/src/main/res/layout/dialog_wifi_ap.xml index 9d44a04a..ed9e4478 100644 --- a/mobile/src/main/res/layout/dialog_wifi_ap.xml +++ b/mobile/src/main/res/layout/dialog_wifi_ap.xml @@ -120,7 +120,7 @@ android:maxLength="19" /> + android:text="@string/wifi_ap_choose_2G" /> + android:prompt="@string/wifi_ap_choose_2G" /> + + + + + + + + + + + + + + + + diff --git a/mobile/src/main/res/values-zh-rCN/strings.xml b/mobile/src/main/res/values-zh-rCN/strings.xml index f6586d35..2ed6c801 100644 --- a/mobile/src/main/res/values-zh-rCN/strings.xml +++ b/mobile/src/main/res/values-zh-rCN/strings.xml @@ -172,16 +172,22 @@ 关闭延迟 默认延迟:%d 毫秒 "AP 频段" + Disabled "自动" "2.4 GHz 频段" - "5.0 GHz 频段" - 6.0 GHz 频段 + "5 GHz 频段" + 6 GHz 频段 + 60 GHz 频段 "MAC 地址" "隐藏的网络" 允许连接设备数上限 过滤可以连接的设备 设备黑名单 设备白名单 + 随机生成 MAC 地址 + 启用桥接模式伺机关闭 + 启用 Wi\u2011Fi 6 + 用户提供配置 "保存" diff --git a/mobile/src/main/res/values-zh-rTW/strings.xml b/mobile/src/main/res/values-zh-rTW/strings.xml index e7a0b381..ac37f4b9 100644 --- a/mobile/src/main/res/values-zh-rTW/strings.xml +++ b/mobile/src/main/res/values-zh-rTW/strings.xml @@ -176,16 +176,20 @@ 關閉延遲時間 默認延遲:%d 毫秒 AP 頻帶 + Disabled 自動 2.4 GHz 頻帶 - 5.0 GHz 頻帶 - 6.0 GHz 頻帶 + 5 GHz 頻帶 + 6 GHz 頻帶 + 60 GHz 頻帶 "MAC 地址" "隱藏的網路" 允許的連接裝置數量 過濾可以連接的裝置 裝置黑名單 裝置白名單 + 隨機化 MAC 位址 + 啟用 Wi\u2011Fi 6 儲存 diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml index 2b6b9933..95eebc0c 100644 --- a/mobile/src/main/res/values/strings.xml +++ b/mobile/src/main/res/values/strings.xml @@ -194,16 +194,22 @@ Inactive timeout Default timeout: %dms AP Band + Disabled Auto 2.4 GHz Band - 5.0 GHz Band - 6.0 GHz Band + 5 GHz Band + 6 GHz Band + 60 GHz Band MAC address Hidden network Maximum number of clients Control which client can use hotspot Blocked list of clients Allowed list of clients + Use randomized MAC + Enable bridged mode opportunistic shutdown + Enable Wi\u2011Fi 6 + User Supplied Configuration Save From 8c502690e6e4c728cde187f9237a6801fe7473bc Mon Sep 17 00:00:00 2001 From: Mygod Date: Sun, 30 May 2021 01:14:09 -0400 Subject: [PATCH 036/112] Refine code style --- .../vpnhotspot/net/wifi/SoftApConfigurationCompat.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt index 98b7f145..a5acad2e 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt @@ -111,9 +111,10 @@ data class SoftApConfigurationCompat( in 1..233 -> 5950 + chan * 5 else -> throw IllegalArgumentException("Invalid 6GHz channel $chan") } - BAND_60GHZ -> if (chan in 1 until 7) { + BAND_60GHZ -> { + require(chan in 1 until 7) { "Invalid 60GHz channel $chan" } 56160 + chan * 2160 - } else throw IllegalArgumentException("Invalid 60GHz channel $chan") + } else -> throw IllegalArgumentException("Invalid band $band") } fun frequencyToChannel(freq: Int) = when (freq) { @@ -392,9 +393,10 @@ data class SoftApConfigurationCompat( apBand.setInt(result, when (band) { BAND_2GHZ -> 0 BAND_5GHZ -> 1 - else -> (-1).also { + else -> { require(Build.VERSION.SDK_INT >= 28) { "A band must be specified on this platform" } require(isLegacyEitherBand(band)) { "Convert fail, unsupported band setting :$band" } + -1 } }) apChannel.setInt(result, channel) From b26fa7df166654452b219643f801b4e8cdb70439 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sun, 30 May 2021 11:36:38 -0400 Subject: [PATCH 037/112] Fix Android S detection --- .../java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt index 5dd1b94a..db76a62d 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt @@ -284,7 +284,7 @@ class WifiApDialogFragment : AlertDialogFragment option5G is ChannelOption.Disabled } } - Build.VERSION.SDK_INT == 30 -> { + Build.VERSION.SDK_INT == 30 && !BuildCompat.isAtLeastS() -> { var expected = 1 var set = 0 for (s in arrayOf(dialogView.band2G, dialogView.band5G, dialogView.band6G)) when (s.selectedItem) { From 5034a11834bb03b5554ee578ec5f07e812d94f69 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sun, 30 May 2021 11:42:27 -0400 Subject: [PATCH 038/112] Reorder MAC settings --- mobile/src/main/res/layout/dialog_wifi_ap.xml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/mobile/src/main/res/layout/dialog_wifi_ap.xml b/mobile/src/main/res/layout/dialog_wifi_ap.xml index ed9e4478..a25688aa 100644 --- a/mobile/src/main/res/layout/dialog_wifi_ap.xml +++ b/mobile/src/main/res/layout/dialog_wifi_ap.xml @@ -212,6 +212,14 @@ android:inputType="textNoSuggestions" android:maxLength="17" /> + - Date: Sun, 30 May 2021 12:26:14 -0400 Subject: [PATCH 039/112] Properly support bridged AP configurations --- .../net/wifi/SoftApConfigurationCompat.kt | 4 ++- .../net/wifi/WifiApDialogFragment.kt | 31 +++++++++++++++++-- mobile/src/main/res/layout/dialog_wifi_ap.xml | 24 +++++++++----- mobile/src/main/res/values-zh-rCN/strings.xml | 1 + mobile/src/main/res/values/strings.xml | 3 +- 5 files changed, 51 insertions(+), 12 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt index a5acad2e..8ac6149e 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt @@ -28,7 +28,9 @@ data class SoftApConfigurationCompat( /** * To read legacy band/channel pair, use [requireSingleBand]. For easy access, see [getChannel]. * - * You should probably not set or modify this field directly. Use [optimizeChannels] or [setChannel]. + * You should probably set or modify this field directly only when you want to use bridged AP, + * see also [android.net.wifi.WifiManager.isBridgedApConcurrencySupported]. + * Otherwise, use [optimizeChannels] or [setChannel]. */ @TargetApi(23) var channels: SparseIntArray = SparseIntArray(1).apply { put(BAND_2GHZ, 0) }, diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt index db76a62d..39881020 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt @@ -80,7 +80,7 @@ class WifiApDialogFragment : AlertDialogFragment= 0) channels.put(band, channel) } - optimizeChannels(channels) + if (!arg.p2pMode && BuildCompat.isAtLeastS() && dialogView.bridgedMode.isChecked) { + this.channels = channels + } else optimizeChannels(channels) } bssid = if (dialogView.bssid.length() != 0) { MacAddressCompat.fromString(dialogView.bssid.text.toString()) @@ -198,6 +200,7 @@ class WifiApDialogFragment : AlertDialogFragment= 29) dialogView.macRandomization.isEnabled = false else if (arg.p2pMode || !BuildCompat.isAtLeastS()) dialogView.macRandomization.isGone = true if (arg.p2pMode || !BuildCompat.isAtLeastS()) { + dialogView.bridgedMode.isGone = true dialogView.bridgedModeOpportunisticShutdown.isGone = true dialogView.ieee80211ax.isGone = true dialogView.userConfig.isGone = true @@ -214,6 +217,26 @@ class WifiApDialogFragment : AlertDialogFragment auto = 1 + !is ChannelOption.Disabled -> ++set + } + if (auto + set > 1) { + if (dialogView.bridgedMode.isEnabled) { + userBridgedMode = dialogView.bridgedMode.isChecked + dialogView.bridgedMode.isEnabled = false + dialogView.bridgedMode.isChecked = true + } + } else if (!dialogView.bridgedMode.isEnabled) { + dialogView.bridgedMode.isEnabled = true + dialogView.bridgedMode.isChecked = userBridgedMode + } + + } private fun populateFromConfiguration() { dialogView.ssid.setText(base.ssid) if (!arg.p2pMode) dialogView.security.setSelection(base.securityType) @@ -225,6 +248,9 @@ class WifiApDialogFragment : AlertDialogFragment 1 + dialogView.bridgedMode.isChecked = userBridgedMode + setBridgedMode() } dialogView.bssid.setText(base.bssid?.toString()) dialogView.hiddenSsid.isChecked = base.isHiddenSsid @@ -295,6 +321,7 @@ class WifiApDialogFragment : AlertDialogFragment true } + setBridgedMode() dialogView.bssidWrapper.error = null val bssidValid = dialogView.bssid.length() == 0 || try { MacAddressCompat.fromString(dialogView.bssid.text.toString()) diff --git a/mobile/src/main/res/layout/dialog_wifi_ap.xml b/mobile/src/main/res/layout/dialog_wifi_ap.xml index a25688aa..4f4485cd 100644 --- a/mobile/src/main/res/layout/dialog_wifi_ap.xml +++ b/mobile/src/main/res/layout/dialog_wifi_ap.xml @@ -195,6 +195,22 @@ android:minHeight="@dimen/touch_target_min" android:prompt="@string/wifi_ap_choose_60G" /> + + - 设备黑名单 设备白名单 随机生成 MAC 地址 + 启用无线接入点桥接模式 启用桥接模式伺机关闭 启用 Wi\u2011Fi 6 用户提供配置 diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml index 95eebc0c..73ca4bb6 100644 --- a/mobile/src/main/res/values/strings.xml +++ b/mobile/src/main/res/values/strings.xml @@ -207,7 +207,8 @@ Blocked list of clients Allowed list of clients Use randomized MAC - Enable bridged mode opportunistic shutdown + Enable Bridged Access point (AP) concurrency + Enable Bridged mode opportunistic shutdown Enable Wi\u2011Fi 6 User Supplied Configuration Save From cb030a487f9203367c1df1493ce150df39f86611 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sun, 30 May 2021 12:33:51 -0400 Subject: [PATCH 040/112] Reduce margin to align --- mobile/src/main/res/layout/dialog_wifi_ap.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mobile/src/main/res/layout/dialog_wifi_ap.xml b/mobile/src/main/res/layout/dialog_wifi_ap.xml index 4f4485cd..650b82a5 100644 --- a/mobile/src/main/res/layout/dialog_wifi_ap.xml +++ b/mobile/src/main/res/layout/dialog_wifi_ap.xml @@ -37,6 +37,8 @@ android:layout_height="wrap_content" android:descendantFocusability="beforeDescendants" android:focusableInTouchMode="true" + android:layout_marginStart="0dp" + android:layout_marginEnd="0dp" style="@style/wifi_item"> Date: Sun, 30 May 2021 13:07:53 -0400 Subject: [PATCH 041/112] Add missing system resources used --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index fc16ada5..2fec273a 100644 --- a/README.md +++ b/README.md @@ -175,6 +175,7 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (prior to API 30) `Lcom/android/internal/R$array;->config_tether_bluetooth_regexs:I,max-target-q` * (prior to API 30) `Lcom/android/internal/R$array;->config_tether_usb_regexs:I,max-target-q` * (prior to API 30) `Lcom/android/internal/R$array;->config_tether_wifi_regexs:I,max-target-q` +* (on API 29) `Lcom/android/internal/R$bool;->config_wifi_p2p_mac_randomization_supported:I,blacklist` * (since API 28, prior to API 30) `Lcom/android/internal/R$integer;->config_wifi_framework_soft_ap_timeout_delay:I,greylist-max-o` * `Lcom/android/internal/R$string;->config_ethernet_iface_regex:I,lo-prio,max-target-o` * (since API 27) `Lcom/android/server/connectivity/tethering/OffloadHardwareInterface;->DEFAULT_TETHER_OFFLOAD_DISABLED:I` @@ -319,6 +320,7 @@ Nonexported system resources: * (since API 30) `@com.android.networkstack.tethering:array/config_tether_wifi_p2p_regexs` * (since API 30) `@com.android.networkstack.tethering:array/config_tether_wifi_regexs` * (since API 30) `@com.android.networkstack.tethering:array/config_tether_wigig_regexs` +* (since API 30) `@com.android.wifi.resources:bool/config_wifi_p2p_mac_randomization_supported` * (since API 30) `@com.android.wifi.resources:integer/config_wifiFrameworkSoftApShutDownTimeoutMilliseconds` Other: Activity `com.android.settings/.Settings$TetherSettingsActivity` is assumed to be exported. From ecf6808f4d0b6a7faa3bc22a684342afd81e2aaf Mon Sep 17 00:00:00 2001 From: Mygod Date: Sun, 30 May 2021 13:09:27 -0400 Subject: [PATCH 042/112] Correct AP_BAND_ANY introduced --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2fec273a..1aca42ab 100644 --- a/README.md +++ b/README.md @@ -162,7 +162,7 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (prior to API 30) `Landroid/net/wifi/WifiConfiguration$KeyMgmt;->WPA_PSK_SHA256:I,blocked` * (since API 23, prior to API 30) `Landroid/net/wifi/WifiConfiguration;->AP_BAND_2GHZ:I,lo-prio,max-target-o` * (since API 23, prior to API 30) `Landroid/net/wifi/WifiConfiguration;->AP_BAND_5GHZ:I,lo-prio,max-target-o` -* (since API 23, prior to API 30) `Landroid/net/wifi/WifiConfiguration;->AP_BAND_ANY:I,lo-prio,max-target-o` +* (since API 28, prior to API 30) `Landroid/net/wifi/WifiConfiguration;->AP_BAND_ANY:I,lo-prio,max-target-o` * (since API 23, prior to API 30) `Landroid/net/wifi/WifiConfiguration;->apBand:I,unsupported` * (since API 23, prior to API 30) `Landroid/net/wifi/WifiConfiguration;->apChannel:I,unsupported` * (since API 28, prior to API 30) `Landroid/net/wifi/WifiManager$SoftApCallback;->onNumClientsChanged(I)V,greylist-max-o` From afa80add947071a72ccb15e7d78f0cc1705546a5 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sun, 30 May 2021 19:30:58 -0400 Subject: [PATCH 043/112] Prevent race condition while reading uuid --- mobile/src/main/java/be/mygod/librootkotlinx/RootServer.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mobile/src/main/java/be/mygod/librootkotlinx/RootServer.kt b/mobile/src/main/java/be/mygod/librootkotlinx/RootServer.kt index b962bc87..845f825b 100644 --- a/mobile/src/main/java/be/mygod/librootkotlinx/RootServer.kt +++ b/mobile/src/main/java/be/mygod/librootkotlinx/RootServer.kt @@ -146,7 +146,9 @@ class RootServer { try { val token2 = UUID.randomUUID().toString() val persistence = File(context.codeCacheDir, ".librootkotlinx-uuid") - val uuid = context.packageName + '@' + if (persistence.canRead()) persistence.readText() else { + val uuid = context.packageName + '@' + try { + persistence.readText() + } catch (_: FileNotFoundException) { UUID.randomUUID().toString().also { persistence.writeText(it) } } val (script, relocated) = AppProcess.relocateScript(uuid) From c1aada8a8b0615c4c32eda6a33129207fc67bc60 Mon Sep 17 00:00:00 2001 From: Mygod Date: Mon, 31 May 2021 00:31:24 -0400 Subject: [PATCH 044/112] Support querying features via WifiManager --- README.md | 1 + .../mygod/vpnhotspot/manage/TetherManager.kt | 26 ++++++++++++------- .../vpnhotspot/net/wifi/WifiApManager.kt | 7 +++++ mobile/src/main/res/values-zh-rCN/strings.xml | 4 +++ mobile/src/main/res/values/strings.xml | 4 +++ 5 files changed, 33 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 1aca42ab..f9ba8375 100644 --- a/README.md +++ b/README.md @@ -299,6 +299,7 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (since API 28) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_FAILED:I,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/WifiManager;->getSoftApConfiguration()Landroid/net/wifi/SoftApConfiguration;,sdk,system-api,test-api` * (prior to API 30) `Landroid/net/wifi/WifiManager;->getWifiApConfiguration()Landroid/net/wifi/WifiConfiguration;,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/WifiManager;->isApMacRandomizationSupported()Z,sdk,system-api,test-api` * (since API 28) `Landroid/net/wifi/WifiManager;->registerSoftApCallback(Ljava/util/concurrent/Executor;Landroid/net/wifi/WifiManager$SoftApCallback;)V,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/WifiManager;->setSoftApConfiguration(Landroid/net/wifi/SoftApConfiguration;)Z,sdk,system-api,test-api` * (prior to API 30) `Landroid/net/wifi/WifiManager;->setWifiApConfiguration(Landroid/net/wifi/WifiConfiguration;)Z,sdk,system-api,test-api` 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 d7c8b491..3bb8b282 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt @@ -28,10 +28,7 @@ import be.mygod.vpnhotspot.net.TetherType import be.mygod.vpnhotspot.net.TetheringManager import be.mygod.vpnhotspot.net.wifi.* import be.mygod.vpnhotspot.root.WifiApCommands -import be.mygod.vpnhotspot.util.format -import be.mygod.vpnhotspot.util.joinToSpanned -import be.mygod.vpnhotspot.util.makeMacSpan -import be.mygod.vpnhotspot.util.readableMessage +import be.mygod.vpnhotspot.util.* import be.mygod.vpnhotspot.widget.SmartSnackbar import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope @@ -203,7 +200,7 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), val capability = SoftApCapability(parcel) val numClients = numClients val maxClients = capability.maxSupportedClients - var supportedFeatures = capability.supportedFeatures + var features = capability.supportedFeatures if (BuildCompat.isAtLeastS()) for ((flag, band) in arrayOf( SoftApCapability.SOFTAP_FEATURE_BAND_24G_SUPPORTED to SoftApConfigurationCompat.BAND_2GHZ, SoftApCapability.SOFTAP_FEATURE_BAND_5G_SUPPORTED to SoftApConfigurationCompat.BAND_5GHZ, @@ -212,17 +209,28 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), )) { if (capability.getSupportedChannelList(band).isEmpty()) continue // reduce double reporting - supportedFeatures = supportedFeatures and flag.inv() + features = features and flag.inv() } val result = parent.resources.getQuantityText(R.plurals.tethering_manage_wifi_capabilities, numClients ?: 0) .format(locale, numClients ?: "?", maxClients, sequence { - var features = supportedFeatures + if (WifiApManager.isApMacRandomizationSupported) yield(parent.getText( + R.string.tethering_manage_wifi_feature_ap_mac_randomization)) + if (Services.wifi.isStaApConcurrencySupported) yield(parent.getText( + R.string.tethering_manage_wifi_feature_sta_ap_concurrency)) + if (BuildCompat.isAtLeastS()) { + if (Services.wifi.isBridgedApConcurrencySupported) yield(parent.getText( + R.string.tethering_manage_wifi_feature_bridged_ap_concurrency)) + if (Services.wifi.isStaBridgedApConcurrencySupported) yield(parent.getText( + R.string.tethering_manage_wifi_feature_sta_bridged_ap_concurrency)) + } if (features != 0L) while (features != 0L) { val bit = features.takeLowestOneBit() yield(SoftApCapability.featureLookup(bit, true)) features = features and bit.inv() - } else yield(parent.getText(R.string.tethering_manage_wifi_no_features)) - }.joinToSpanned()) + } + }.joinToSpanned().let { + if (it.isEmpty()) parent.getText(R.string.tethering_manage_wifi_no_features) else it + }) if (BuildCompat.isAtLeastS()) { val list = SoftApConfigurationCompat.BAND_TYPES.map { band -> val channels = capability.getSupportedChannelList(band) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt index 81217656..b3ef3aa2 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt @@ -52,6 +52,13 @@ object WifiApManager { else -> false } + @get:RequiresApi(30) + private val apMacRandomizationSupported by lazy { + WifiManager::class.java.getDeclaredMethod("isApMacRandomizationSupported") + } + @get:RequiresApi(30) + val isApMacRandomizationSupported get() = apMacRandomizationSupported(Services.wifi) as Boolean + private val getWifiApConfiguration by lazy { WifiManager::class.java.getDeclaredMethod("getWifiApConfiguration") } @Suppress("DEPRECATION") private val setWifiApConfiguration by lazy { diff --git a/mobile/src/main/res/values-zh-rCN/strings.xml b/mobile/src/main/res/values-zh-rCN/strings.xml index f79491fc..69f3e8fc 100644 --- a/mobile/src/main/res/values-zh-rCN/strings.xml +++ b/mobile/src/main/res/values-zh-rCN/strings.xml @@ -70,6 +70,10 @@ 已连接 %d 个设备 \n支持频道: %s + 随机接入点 MAC + 桥接 AP 并发 + STA/AP 并发 + STA/桥接 AP 并发 已屏蔽 %1$s:%2$s 复制 MAC diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml index 73ca4bb6..c4ce4b36 100644 --- a/mobile/src/main/res/values/strings.xml +++ b/mobile/src/main/res/values/strings.xml @@ -83,6 +83,10 @@ %1d clients connected \nSupported channels: %s + Randomized AP MAC + Bridged AP concurrency + STA + AP concurrency + STA + Bridged AP concurrency None Blocked %1$s: %2$s Copy MAC From 57c71d70a83318f0ebd6eba7c7b263a36039a67a Mon Sep 17 00:00:00 2001 From: Mygod Date: Mon, 31 May 2021 01:05:37 -0400 Subject: [PATCH 045/112] Only setBridgedMode when necessary --- .../be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt index 39881020..2715258f 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt @@ -235,7 +235,6 @@ class WifiApDialogFragment : AlertDialogFragment true + else -> { + setBridgedMode() + true + } } - setBridgedMode() dialogView.bssidWrapper.error = null val bssidValid = dialogView.bssid.length() == 0 || try { MacAddressCompat.fromString(dialogView.bssid.text.toString()) From 652875af20a0aac7e96305b95af7242251c3363f Mon Sep 17 00:00:00 2001 From: Mygod Date: Mon, 31 May 2021 01:12:32 -0400 Subject: [PATCH 046/112] Stop using BAND_ANY --- README.md | 1 - .../src/main/java/be/mygod/vpnhotspot/RepeaterService.kt | 2 +- .../mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt | 7 +++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index f9ba8375..08868651 100644 --- a/README.md +++ b/README.md @@ -262,7 +262,6 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (since API 30) `Landroid/net/wifi/SoftApConfiguration;->BAND_5GHZ:I,sdk,system-api,test-api` * (since API 31) `Landroid/net/wifi/SoftApConfiguration;->BAND_60GHZ:I,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration;->BAND_6GHZ:I,sdk,system-api,test-api` -* (since API 30) `Landroid/net/wifi/SoftApConfiguration;->BAND_ANY:I,sdk,system-api,test-api` * (since API 31) `Landroid/net/wifi/SoftApConfiguration;->BAND_*:I,sdk,system-api,test-api` * (since API 31) `Landroid/net/wifi/SoftApConfiguration;->RANDOMIZATION_NONE:I,sdk,system-api,test-api` * (since API 31) `Landroid/net/wifi/SoftApConfiguration;->RANDOMIZATION_PERSISTENT:I,sdk,system-api,test-api` diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt index 25b2cf3a..683848b0 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt @@ -71,7 +71,7 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene set(value) = app.pref.edit { putString(KEY_PASSPHRASE, value) } var operatingBand: Int @SuppressLint("InlinedApi") - get() = app.pref.getInt(KEY_OPERATING_BAND, SoftApConfigurationCompat.BAND_ANY) + get() = app.pref.getInt(KEY_OPERATING_BAND, SoftApConfigurationCompat.BAND_LEGACY) set(value) = app.pref.edit { putInt(KEY_OPERATING_BAND, value) } var operatingChannel: Int get() { diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt index 8ac6149e..59a60d05 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt @@ -63,8 +63,7 @@ data class SoftApConfigurationCompat( const val BAND_6GHZ = 4 @TargetApi(31) const val BAND_60GHZ = 8 - private const val BAND_LEGACY = BAND_2GHZ or BAND_5GHZ - const val BAND_ANY = BAND_LEGACY or BAND_6GHZ + const val BAND_LEGACY = BAND_2GHZ or BAND_5GHZ val BAND_TYPES by lazy { if (BuildCompat.isAtLeastS()) try { return@lazy UnblockCentral.SoftApConfiguration_BAND_TYPES @@ -282,7 +281,7 @@ data class SoftApConfigurationCompat( hiddenSSID, // https://cs.android.com/android/platform/superproject/+/master:frameworks/base/wifi/java/android/net/wifi/SoftApConfToXmlMigrationUtil.java;l=87;drc=aa6527cf41671d1ed417b8ebdb6b3aa614f62344 SparseIntArray(1).apply { - if (Build.VERSION.SDK_INT < 23) put(BAND_ANY, 0) else put(when (val band = apBand.getInt(this)) { + if (Build.VERSION.SDK_INT < 23) put(BAND_LEGACY, 0) else put(when (val band = apBand.getInt(this)) { 0 -> BAND_2GHZ 1 -> BAND_5GHZ -1 -> BAND_LEGACY @@ -358,7 +357,7 @@ data class SoftApConfigurationCompat( } return result } - fun setChannel(channel: Int, band: Int = BAND_ANY) { + fun setChannel(channel: Int, band: Int = BAND_LEGACY) { channels = SparseIntArray(1).apply { put(band, channel) } } fun optimizeChannels(channels: SparseIntArray = this.channels) { From 229b190c22edf27ae048165b71726b7b40867666 Mon Sep 17 00:00:00 2001 From: Mygod Date: Mon, 31 May 2021 01:14:36 -0400 Subject: [PATCH 047/112] Clarify that CHANNEL_WIDTH_INVALID is only used for API 30 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 08868651..a0fd2398 100644 --- a/README.md +++ b/README.md @@ -279,7 +279,7 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (since API 31) `Landroid/net/wifi/SoftApConfiguration;->isIeee80211axEnabled()Z,sdk,system-api,test-api` * (since API 31) `Landroid/net/wifi/SoftApConfiguration;->isUserConfiguration()Z,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApInfo;->CHANNEL_WIDTH_*:I,sdk,system-api,test-api` -* (since API 30) `Landroid/net/wifi/SoftApInfo;->CHANNEL_WIDTH_INVALID:I,sdk,system-api,test-api` +* (on API 30) `Landroid/net/wifi/SoftApInfo;->CHANNEL_WIDTH_INVALID:I,sdk,system-api,test-api` * (since API 31) `Landroid/net/wifi/SoftApInfo;->getAutoShutdownTimeoutMillis()J,sdk,system-api,test-api` * (since API 31) `Landroid/net/wifi/SoftApInfo;->getBssid()Landroid/net/MacAddress;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApInfo;->getBandwidth()I,system-api,whitelist` From 7f78c9da68f0b5f2c09b88a4a1b4d4dc23bb5d97 Mon Sep 17 00:00:00 2001 From: Mygod Date: Mon, 31 May 2021 01:37:15 -0400 Subject: [PATCH 048/112] Prevent calling keyIterator --- .../net/wifi/SoftApConfigurationCompat.kt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt index 59a60d05..1fdb73db 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt @@ -9,7 +9,6 @@ import android.os.Parcelable import android.util.SparseIntArray import androidx.annotation.RequiresApi import androidx.core.os.BuildCompat -import androidx.core.util.keyIterator import be.mygod.vpnhotspot.net.MacAddressCompat import be.mygod.vpnhotspot.net.MacAddressCompat.Companion.toCompat import be.mygod.vpnhotspot.net.monitor.TetherTimeoutMonitor @@ -351,9 +350,10 @@ data class SoftApConfigurationCompat( } fun getChannel(band: Int): Int { var result = -1 - for (b in channels.keyIterator()) if (band and b == band) { + repeat(channels.size()) { i -> + if (band and channels.keyAt(i) != band) return@repeat require(result == -1) { "Duplicate band found" } - result = channels[b] + result = channels.valueAt(i) } return result } @@ -363,9 +363,12 @@ data class SoftApConfigurationCompat( fun optimizeChannels(channels: SparseIntArray = this.channels) { this.channels = SparseIntArray(channels.size()).apply { var setBand = 0 - for (band in channels.keyIterator()) if (channels[band] == 0) setBand = setBand or band + repeat(channels.size()) { i -> if (channels.valueAt(i) == 0) setBand = setBand or channels.keyAt(i) } if (setBand != 0) put(setBand, 0) // merge all bands into one - for (band in channels.keyIterator()) if (band and setBand == 0) put(band, channels[band]) + repeat(channels.size()) { i -> + val band = channels.keyAt(i) + if (band and setBand == 0) put(band, channels.valueAt(i)) + } } } From 453d6bb43016213e42af187cf78a2ac87ab4e124 Mon Sep 17 00:00:00 2001 From: Mygod Date: Mon, 31 May 2021 01:52:00 -0400 Subject: [PATCH 049/112] Make declaration of bandLookup explicit of API requirements --- .../be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt index 1fdb73db..4f3429a3 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt @@ -71,7 +71,8 @@ data class SoftApConfigurationCompat( } intArrayOf(BAND_2GHZ, BAND_5GHZ, BAND_6GHZ, BAND_60GHZ) } - val bandLookup = ConstantLookup("BAND_", null, "2GHZ", "5GHZ") + @RequiresApi(31) + val bandLookup = ConstantLookup("BAND_") @TargetApi(31) const val RANDOMIZATION_NONE = 0 From f7e701199250f25b0f029759222944e0e414e488 Mon Sep 17 00:00:00 2001 From: Mygod Date: Mon, 31 May 2021 01:55:39 -0400 Subject: [PATCH 050/112] Update translations --- mobile/src/main/res/values-ru/strings.xml | 4 +++- mobile/src/main/res/values-zh-rTW/strings.xml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/mobile/src/main/res/values-ru/strings.xml b/mobile/src/main/res/values-ru/strings.xml index 09fb0188..c74ace52 100644 --- a/mobile/src/main/res/values-ru/strings.xml +++ b/mobile/src/main/res/values-ru/strings.xml @@ -61,7 +61,9 @@ "Диапазон частот Wi-Fi" "Авто" "2,4 ГГц" - "5,0 ГГц" + "5 ГГц" + "6 ГГц" + "60 ГГц" "MAC-адрес" "Скрытая сеть" "Сохранить" diff --git a/mobile/src/main/res/values-zh-rTW/strings.xml b/mobile/src/main/res/values-zh-rTW/strings.xml index ac37f4b9..615d40be 100644 --- a/mobile/src/main/res/values-zh-rTW/strings.xml +++ b/mobile/src/main/res/values-zh-rTW/strings.xml @@ -176,7 +176,7 @@ 關閉延遲時間 默認延遲:%d 毫秒 AP 頻帶 - Disabled + 停用 自動 2.4 GHz 頻帶 5 GHz 頻帶 From 0a2e13556a7b9294eb33d1d8d76f63a8b4ff5829 Mon Sep 17 00:00:00 2001 From: Mygod Date: Mon, 31 May 2021 02:07:04 -0400 Subject: [PATCH 051/112] Optimize usage of SparseArray --- .../main/java/be/mygod/librootkotlinx/RootServer.kt | 9 ++++----- .../vpnhotspot/net/wifi/SoftApConfigurationCompat.kt | 12 ++++++------ .../vpnhotspot/net/wifi/WifiApDialogFragment.kt | 2 +- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/mobile/src/main/java/be/mygod/librootkotlinx/RootServer.kt b/mobile/src/main/java/be/mygod/librootkotlinx/RootServer.kt index 845f825b..38f5a574 100644 --- a/mobile/src/main/java/be/mygod/librootkotlinx/RootServer.kt +++ b/mobile/src/main/java/be/mygod/librootkotlinx/RootServer.kt @@ -8,7 +8,6 @@ import android.os.RemoteException import android.system.Os import android.system.OsConstants import androidx.collection.LongSparseArray -import androidx.collection.set import androidx.collection.valueIterator import kotlinx.coroutines.* import kotlinx.coroutines.channels.* @@ -248,7 +247,7 @@ class RootServer { @Suppress("UNCHECKED_CAST") val callback = Callback.Ordinary(this, counter, classLoader, future as CompletableDeferred) if (active) { - callbackLookup[counter] = callback + callbackLookup.append(counter, callback) sendLocked(command) } else future.cancel() callback @@ -279,7 +278,7 @@ class RootServer { @Suppress("UNCHECKED_CAST") val callback = Callback.Channel(this@RootServer, counter, classLoader, this as SendChannel) if (active) { - callbackLookup[counter] = callback + callbackLookup.append(counter, callback) sendLocked(command) } else callback.finish.cancel() callback @@ -434,7 +433,7 @@ class RootServer { } is RootCommand<*> -> { val commandJob = Job() - cancellables[callback] = { commandJob.cancel() } + cancellables.append(callback) { commandJob.cancel() } defaultWorker.launch(commandJob) { val result = try { val result = command.execute(); @@ -452,7 +451,7 @@ class RootServer { val result = try { coroutineScope { command.create(this).also { - cancellables[callback] = { it.cancel() } + cancellables.append(callback) { it.cancel() } }.consumeEach { result -> withContext(callbackWorker) { output.pushResult(callback, result) } } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt index 4f3429a3..b6316fb6 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt @@ -32,7 +32,7 @@ data class SoftApConfigurationCompat( * Otherwise, use [optimizeChannels] or [setChannel]. */ @TargetApi(23) - var channels: SparseIntArray = SparseIntArray(1).apply { put(BAND_2GHZ, 0) }, + var channels: SparseIntArray = SparseIntArray(1).apply { append(BAND_2GHZ, 0) }, var securityType: Int = SoftApConfiguration.SECURITY_TYPE_OPEN, @TargetApi(30) var maxNumberOfClients: Int = 0, @@ -281,12 +281,12 @@ data class SoftApConfigurationCompat( hiddenSSID, // https://cs.android.com/android/platform/superproject/+/master:frameworks/base/wifi/java/android/net/wifi/SoftApConfToXmlMigrationUtil.java;l=87;drc=aa6527cf41671d1ed417b8ebdb6b3aa614f62344 SparseIntArray(1).apply { - if (Build.VERSION.SDK_INT < 23) put(BAND_LEGACY, 0) else put(when (val band = apBand.getInt(this)) { + if (Build.VERSION.SDK_INT >= 23) append(when (val band = apBand.getInt(this)) { 0 -> BAND_2GHZ 1 -> BAND_5GHZ -1 -> BAND_LEGACY else -> throw IllegalArgumentException("Unexpected band $band") - }, apChannel.getInt(this)) + }, apChannel.getInt(this)) else append(BAND_LEGACY, 0) }, allowedKeyManagement.nextSetBit(0).let { selected -> require(allowedKeyManagement.nextSetBit(selected + 1) < 0) { @@ -319,7 +319,7 @@ data class SoftApConfigurationCompat( passphrase, isHiddenSsid, if (BuildCompat.isAtLeastS()) getChannels(this) as SparseIntArray else SparseIntArray(1).apply { - put(getBand(this) as Int, getChannel(this) as Int) + append(getBand(this) as Int, getChannel(this) as Int) }, securityType, getMaxNumberOfClients(this) as Int, @@ -359,13 +359,13 @@ data class SoftApConfigurationCompat( return result } fun setChannel(channel: Int, band: Int = BAND_LEGACY) { - channels = SparseIntArray(1).apply { put(band, channel) } + channels = SparseIntArray(1).apply { append(band, channel) } } fun optimizeChannels(channels: SparseIntArray = this.channels) { this.channels = SparseIntArray(channels.size()).apply { var setBand = 0 repeat(channels.size()) { i -> if (channels.valueAt(i) == 0) setBand = setBand or channels.keyAt(i) } - if (setBand != 0) put(setBand, 0) // merge all bands into one + if (setBand != 0) append(setBand, 0) // merge all bands into one repeat(channels.size()) { i -> val band = channels.keyAt(i) if (band and setBand == 0) put(band, channels.valueAt(i)) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt index 2715258f..02db97d3 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt @@ -115,7 +115,7 @@ class WifiApDialogFragment : AlertDialogFragment= 0) channels.put(band, channel) + if (channel != null && channel >= 0) channels.append(band, channel) } if (!arg.p2pMode && BuildCompat.isAtLeastS() && dialogView.bridgedMode.isChecked) { this.channels = channels From 8e33ab77d7306b44b1e97c2a91f2d67047b0d9fe Mon Sep 17 00:00:00 2001 From: Mygod Date: Mon, 31 May 2021 23:35:24 +0800 Subject: [PATCH 052/112] Fix this overriding --- .../mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt index b6316fb6..1f92e1bb 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt @@ -280,13 +280,13 @@ data class SoftApConfigurationCompat( preSharedKey, hiddenSSID, // https://cs.android.com/android/platform/superproject/+/master:frameworks/base/wifi/java/android/net/wifi/SoftApConfToXmlMigrationUtil.java;l=87;drc=aa6527cf41671d1ed417b8ebdb6b3aa614f62344 - SparseIntArray(1).apply { - if (Build.VERSION.SDK_INT >= 23) append(when (val band = apBand.getInt(this)) { + SparseIntArray(1).also { + if (Build.VERSION.SDK_INT >= 23) it.append(when (val band = apBand.getInt(this)) { 0 -> BAND_2GHZ 1 -> BAND_5GHZ -1 -> BAND_LEGACY else -> throw IllegalArgumentException("Unexpected band $band") - }, apChannel.getInt(this)) else append(BAND_LEGACY, 0) + }, apChannel.getInt(this)) else it.append(BAND_LEGACY, 0) }, allowedKeyManagement.nextSetBit(0).let { selected -> require(allowedKeyManagement.nextSetBit(selected + 1) < 0) { From c3f5f43b02bfc9679b78db46e4ef96bf2a056a1f Mon Sep 17 00:00:00 2001 From: Mygod Date: Mon, 31 May 2021 13:25:01 -0400 Subject: [PATCH 053/112] Add debug for readUnexpectedStderr --- mobile/src/main/java/be/mygod/librootkotlinx/RootServer.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/mobile/src/main/java/be/mygod/librootkotlinx/RootServer.kt b/mobile/src/main/java/be/mygod/librootkotlinx/RootServer.kt index 38f5a574..47559b39 100644 --- a/mobile/src/main/java/be/mygod/librootkotlinx/RootServer.kt +++ b/mobile/src/main/java/be/mygod/librootkotlinx/RootServer.kt @@ -101,6 +101,7 @@ class RootServer { private fun readUnexpectedStderr(): String? { if (!this::process.isInitialized) return null + Logger.me.d("Attempting to read stderr") var available = process.errorStream.available() return if (available <= 0) null else String(ByteArrayOutputStream().apply { try { From 2ac04bc1fa6eb8531b2e51893823954dbdcb4456 Mon Sep 17 00:00:00 2001 From: Mygod Date: Mon, 31 May 2021 13:38:51 -0400 Subject: [PATCH 054/112] Use MaterialAlertDialogBuilder --- .../src/main/java/be/mygod/vpnhotspot/AlertDialogFragment.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/AlertDialogFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/AlertDialogFragment.kt index c20b1ace..0f607081 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/AlertDialogFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/AlertDialogFragment.kt @@ -10,6 +10,7 @@ import androidx.core.os.bundleOf import androidx.fragment.app.Fragment import androidx.fragment.app.setFragmentResult import androidx.fragment.app.setFragmentResultListener +import com.google.android.material.dialog.MaterialAlertDialogBuilder import kotlinx.parcelize.Parcelize /** @@ -44,7 +45,7 @@ abstract class AlertDialogFragment : fun key(resultKey: String = javaClass.name) = args().putString(KEY_RESULT, resultKey) override fun onCreateDialog(savedInstanceState: Bundle?): AlertDialog = - AlertDialog.Builder(requireContext()).also { it.prepare(this) }.create() + MaterialAlertDialogBuilder(requireContext()).also { it.prepare(this) }.create() override fun onClick(dialog: DialogInterface?, which: Int) { setFragmentResult(resultKey ?: return, Bundle().apply { From bfedbfe614d0b98287363c4dec7bbdd68c368395 Mon Sep 17 00:00:00 2001 From: Mygod Date: Mon, 31 May 2021 19:37:08 -0400 Subject: [PATCH 055/112] Set p2p unsafe channel properly --- .../vpnhotspot/net/wifi/SoftApConfigurationCompat.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt index 1f92e1bb..657a9090 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt @@ -359,7 +359,13 @@ data class SoftApConfigurationCompat( return result } fun setChannel(channel: Int, band: Int = BAND_LEGACY) { - channels = SparseIntArray(1).apply { append(band, channel) } + channels = SparseIntArray(1).apply { + append(when { + channel <= 0 || band != BAND_LEGACY -> band + channel > 14 -> BAND_5GHZ + else -> BAND_2GHZ + }, channel) + } } fun optimizeChannels(channels: SparseIntArray = this.channels) { this.channels = SparseIntArray(channels.size()).apply { From 835de089004866467e42116465c840477b450086 Mon Sep 17 00:00:00 2001 From: Mygod Date: Mon, 31 May 2021 20:07:07 -0400 Subject: [PATCH 056/112] Listen for onBlockedClientConnecting whenever callback is registered --- .../mygod/vpnhotspot/manage/TetherManager.kt | 16 ---------------- .../mygod/vpnhotspot/root/WifiApCommands.kt | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 16 deletions(-) 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 3bb8b282..f2f9fa67 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt @@ -2,7 +2,6 @@ package be.mygod.vpnhotspot.manage import android.Manifest import android.annotation.TargetApi -import android.content.ClipData import android.content.Context import android.content.Intent import android.content.pm.PackageManager @@ -175,21 +174,6 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), this.capability = capability data.notifyChange() } - @RequiresApi(30) - override fun onBlockedClientConnecting(client: Parcelable, blockedReason: Int) { - @Suppress("NAME_SHADOWING") - val client = WifiClient(client) - val macAddress = client.macAddress - var name = macAddress.toString() - if (BuildCompat.isAtLeastS()) client.apInstanceIdentifier?.let { name += "%$it" } - val reason = WifiApManager.clientBlockLookup(blockedReason, true) - Timber.i("$name blocked from connecting: $reason ($blockedReason)") - SmartSnackbar.make(parent.getString(R.string.tethering_manage_wifi_client_blocked, name, reason)).apply { - action(R.string.tethering_manage_wifi_copy_mac) { - app.clipboard.setPrimaryClip(ClipData.newPlainText(null, macAddress.toString())) - } - }.show() - } override val title get() = parent.getString(R.string.tethering_manage_wifi) override val tetherType get() = TetherType.WIFI diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt b/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt index bea331cd..e05e7f10 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt @@ -1,12 +1,18 @@ package be.mygod.vpnhotspot.root +import android.annotation.TargetApi +import android.content.ClipData import android.os.Parcelable import androidx.annotation.RequiresApi +import androidx.core.os.BuildCompat import be.mygod.librootkotlinx.ParcelableBoolean import be.mygod.librootkotlinx.RootCommand import be.mygod.librootkotlinx.RootCommandChannel +import be.mygod.vpnhotspot.App.Companion.app +import be.mygod.vpnhotspot.R import be.mygod.vpnhotspot.net.wifi.SoftApConfigurationCompat import be.mygod.vpnhotspot.net.wifi.WifiApManager +import be.mygod.vpnhotspot.net.wifi.WifiClient import be.mygod.vpnhotspot.widget.SmartSnackbar import kotlinx.coroutines.* import kotlinx.coroutines.channels.* @@ -119,6 +125,19 @@ object WifiApCommands { } is SoftApCallbackParcel.OnInfoChanged -> synchronized(callbacks) { lastCallback.info = parcel } is SoftApCallbackParcel.OnCapabilityChanged -> synchronized(callbacks) { lastCallback.capability = parcel } + is SoftApCallbackParcel.OnBlockedClientConnecting -> @TargetApi(30) { // passively consume events + val client = WifiClient(parcel.client) + val macAddress = client.macAddress + var name = macAddress.toString() + if (BuildCompat.isAtLeastS()) client.apInstanceIdentifier?.let { name += "%$it" } + val reason = WifiApManager.clientBlockLookup(parcel.blockedReason, true) + Timber.i("$name blocked from connecting: $reason (${parcel.blockedReason})") + SmartSnackbar.make(app.getString(R.string.tethering_manage_wifi_client_blocked, name, reason)).apply { + action(R.string.tethering_manage_wifi_copy_mac) { + app.clipboard.setPrimaryClip(ClipData.newPlainText(null, macAddress.toString())) + } + }.show() + } } for (callback in synchronized(callbacks) { callbacks.toList() }) parcel.dispatch(callback) } From 96e4fe0e886f96fbb3fa0cc2a6e1d5ebb3b6aa16 Mon Sep 17 00:00:00 2001 From: Mygod Date: Mon, 31 May 2021 20:30:50 -0400 Subject: [PATCH 057/112] v2.12.0 --- mobile/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/build.gradle.kts b/mobile/build.gradle.kts index 89472845..6524a422 100644 --- a/mobile/build.gradle.kts +++ b/mobile/build.gradle.kts @@ -24,8 +24,8 @@ android { minSdk = 21 if (targetSdk == 31) targetSdkPreview = "S" else this.targetSdk = targetSdk resourceConfigurations.addAll(arrayOf("it", "ru", "zh-rCN", "zh-rTW")) - versionCode = 260 - versionName = "2.11.7" + versionCode = 270 + versionName = "2.12.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" javaCompileOptions.annotationProcessorOptions.arguments.apply { put("room.expandProjection", "true") From a0b52f130f335331bff7a07465c5c3f6974eef9a Mon Sep 17 00:00:00 2001 From: Mygod Date: Tue, 1 Jun 2021 09:32:00 +0800 Subject: [PATCH 058/112] Fix testOnly again --- gradle.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/gradle.properties b/gradle.properties index 6fb3e361..feb18146 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,6 +13,7 @@ android.databinding.incremental=true android.enableJetifier=true android.enableR8.fullMode=true android.enableResourceOptimizations=false +android.injected.testOnly=false android.useAndroidX=true kapt.incremental.apt=true org.gradle.jvmargs=-Xmx1536m From b8df744c899cd4b7a50c7f079f4efe5ee3f0229f Mon Sep 17 00:00:00 2001 From: Mygod Date: Tue, 1 Jun 2021 09:32:14 +0800 Subject: [PATCH 059/112] Fix this overriding again --- mobile/build.gradle.kts | 2 +- mobile/src/main/AndroidManifest.xml | 6 +++--- .../mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mobile/build.gradle.kts b/mobile/build.gradle.kts index 6524a422..2c768553 100644 --- a/mobile/build.gradle.kts +++ b/mobile/build.gradle.kts @@ -24,7 +24,7 @@ android { minSdk = 21 if (targetSdk == 31) targetSdkPreview = "S" else this.targetSdk = targetSdk resourceConfigurations.addAll(arrayOf("it", "ru", "zh-rCN", "zh-rTW")) - versionCode = 270 + versionCode = 271 versionName = "2.12.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" javaCompileOptions.annotationProcessorOptions.arguments.apply { diff --git a/mobile/src/main/AndroidManifest.xml b/mobile/src/main/AndroidManifest.xml index e694330c..e4fa8a08 100644 --- a/mobile/src/main/AndroidManifest.xml +++ b/mobile/src/main/AndroidManifest.xml @@ -9,9 +9,6 @@ - @@ -24,6 +21,9 @@ + diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt index 657a9090..4e06acae 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt @@ -318,8 +318,8 @@ data class SoftApConfigurationCompat( bssid?.toCompat()?.addr, passphrase, isHiddenSsid, - if (BuildCompat.isAtLeastS()) getChannels(this) as SparseIntArray else SparseIntArray(1).apply { - append(getBand(this) as Int, getChannel(this) as Int) + if (BuildCompat.isAtLeastS()) getChannels(this) as SparseIntArray else SparseIntArray(1).also { + it.append(getBand(this) as Int, getChannel(this) as Int) }, securityType, getMaxNumberOfClients(this) as Int, From 6cb7f966d66ee62604057e6c4e15601629380b41 Mon Sep 17 00:00:00 2001 From: Mygod Date: Wed, 2 Jun 2021 12:07:33 +0800 Subject: [PATCH 060/112] v2.12.1 --- mobile/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/build.gradle.kts b/mobile/build.gradle.kts index 2c768553..341b3b0c 100644 --- a/mobile/build.gradle.kts +++ b/mobile/build.gradle.kts @@ -24,8 +24,8 @@ android { minSdk = 21 if (targetSdk == 31) targetSdkPreview = "S" else this.targetSdk = targetSdk resourceConfigurations.addAll(arrayOf("it", "ru", "zh-rCN", "zh-rTW")) - versionCode = 271 - versionName = "2.12.0" + versionCode = 272 + versionName = "2.12.1" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" javaCompileOptions.annotationProcessorOptions.arguments.apply { put("room.expandProjection", "true") From 301741dc42acd1d87cb05a3b3f5bc28491b8a398 Mon Sep 17 00:00:00 2001 From: Mygod Date: Tue, 1 Jun 2021 00:04:44 -0400 Subject: [PATCH 061/112] Fix crash on Android 11 --- .../vpnhotspot/net/wifi/SoftApConfigurationCompat.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt index 4e06acae..97b18594 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt @@ -328,10 +328,10 @@ data class SoftApConfigurationCompat( isClientControlByUserEnabled(this) as Boolean, getBlockedClientList(this) as List, getAllowedClientList(this) as List, - getMacRandomizationSetting(this) as Int, - isBridgedModeOpportunisticShutdownEnabled(this) as Boolean, - isIeee80211axEnabled(this) as Boolean, - isUserConfiguration(this) as Boolean, + if (BuildCompat.isAtLeastS()) getMacRandomizationSetting(this) as Int else RANDOMIZATION_PERSISTENT, + !BuildCompat.isAtLeastS() || isBridgedModeOpportunisticShutdownEnabled(this) as Boolean, + !BuildCompat.isAtLeastS() || isIeee80211axEnabled(this) as Boolean, + !BuildCompat.isAtLeastS() || isUserConfiguration(this) as Boolean, this) } From 880843c8cbeb41c5ca4d3d66faf4d9fd4b52686b Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 4 Jun 2021 01:25:07 -0400 Subject: [PATCH 062/112] Fix repeater stuck when Location is off on Android 11+ --- .../src/main/java/be/mygod/vpnhotspot/App.kt | 2 + .../be/mygod/vpnhotspot/RepeaterService.kt | 78 +++++++++++++------ .../manage/LocalOnlyHotspotManager.kt | 2 +- .../net/wifi/WifiP2pManagerHelper.kt | 11 ++- mobile/src/main/res/values-zh-rCN/strings.xml | 2 + mobile/src/main/res/values/strings.xml | 3 + 6 files changed, 73 insertions(+), 25 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/App.kt b/mobile/src/main/java/be/mygod/vpnhotspot/App.kt index 714a162e..9e2eca05 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/App.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/App.kt @@ -4,6 +4,7 @@ import android.annotation.SuppressLint import android.app.Application import android.content.ClipboardManager import android.content.res.Configuration +import android.location.LocationManager import android.os.Build import android.util.Log import androidx.annotation.Size @@ -124,6 +125,7 @@ class App : Application() { } val pref by lazy { PreferenceManager.getDefaultSharedPreferences(deviceStorage) } val clipboard by lazy { getSystemService()!! } + val location by lazy { getSystemService() } val hasTouch by lazy { packageManager.hasSystemFeature("android.hardware.faketouch") } val customTabsIntent by lazy { diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt index 683848b0..fc386f1c 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt @@ -6,6 +6,7 @@ import android.app.Service import android.content.Intent import android.content.SharedPreferences import android.content.pm.PackageManager +import android.location.LocationManager import android.net.wifi.WpsInfo import android.net.wifi.p2p.* import android.os.Build @@ -20,7 +21,10 @@ import be.mygod.vpnhotspot.net.monitor.TetherTimeoutMonitor import be.mygod.vpnhotspot.net.wifi.SoftApConfigurationCompat import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.deletePersistentGroup +import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.requestConnectionInfo import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.requestDeviceAddress +import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.requestGroupInfo +import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.requestP2pState import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.requestPersistentGroupInfo import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.setWifiP2pChannels import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.startWps @@ -212,6 +216,9 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION -> onP2pConnectionChanged( intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO), intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP)) + LocationManager.MODE_CHANGED_ACTION -> @TargetApi(30) { + onLocationModeChanged(intent.getBooleanExtra(LocationManager.EXTRA_LOCATION_ENABLED, false)) + } } } private val deviceListener = broadcastReceiver { _, intent -> @@ -308,6 +315,30 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene } } + private var p2pPoller: Job? = null + @RequiresApi(30) + private fun onLocationModeChanged(enabled: Boolean) = if (enabled) p2pPoller?.cancel() else { + SmartSnackbar.make(R.string.repeater_location_off).apply { + action(R.string.repeater_location_off_configure) { + it.context.startActivity(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)) + } + }.show() + p2pPoller = launch(start = CoroutineStart.UNDISPATCHED) { + while (true) { + delay(1000) + val channel = channel ?: return@launch + coroutineScope { + launch(start = CoroutineStart.UNDISPATCHED) { + if (p2pManager.requestP2pState(channel) == WifiP2pManager.WIFI_P2P_STATE_DISABLED) cleanLocked() + } + val info = async(start = CoroutineStart.UNDISPATCHED) { p2pManager.requestConnectionInfo(channel) } + val group = p2pManager.requestGroupInfo(channel) + onP2pConnectionChanged(info.await(), group) + } + } + } + } + /** * startService Step 1 */ @@ -318,29 +349,25 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene // bump self to foreground location service (API 29+) to use location later, also to avoid getting killed if (Build.VERSION.SDK_INT >= 26) showNotification() launch { - registerReceiver(receiver, intentFilter(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION, - WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)) + val filter = intentFilter(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION, + WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION) + if (Build.VERSION.SDK_INT >= 30) filter.addAction(LocationManager.MODE_CHANGED_ACTION) + registerReceiver(receiver, filter) receiverRegistered = true - try { - p2pManager.requestGroupInfo(channel) { - when { - it == null -> doStart() - it.isGroupOwner -> launch { if (routingManager == null) doStartLocked(it) } - else -> { - Timber.i("Removing old group ($it)") - p2pManager.removeGroup(channel, object : WifiP2pManager.ActionListener { - override fun onSuccess() { - doStart() - } - override fun onFailure(reason: Int) = - startFailure(formatReason(R.string.repeater_remove_old_group_failure, reason)) - }) + val group = p2pManager.requestGroupInfo(channel) + when { + group == null -> doStart() + group.isGroupOwner -> if (routingManager == null) doStartLocked(group) + else -> { + Timber.i("Removing old group ($group)") + p2pManager.removeGroup(channel, object : WifiP2pManager.ActionListener { + override fun onSuccess() { + launch { doStart() } } - } + override fun onFailure(reason: Int) = + startFailure(formatReason(R.string.repeater_remove_old_group_failure, reason)) + }) } - } catch (e: SecurityException) { - Timber.w(e) - startFailure(e.readableMessage) } } return START_NOT_STICKY @@ -348,15 +375,19 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene /** * startService Step 2 (if a group isn't already available) */ - private fun doStart() = launch { + private suspend fun doStart() { val listener = object : WifiP2pManager.ActionListener { override fun onFailure(reason: Int) { startFailure(formatReason(R.string.repeater_create_group_failure, reason), showWifiEnable = reason == WifiP2pManager.BUSY) } - override fun onSuccess() { } // wait for WIFI_P2P_CONNECTION_CHANGED_ACTION to fire to go to step 3 + override fun onSuccess() { + // wait for WIFI_P2P_CONNECTION_CHANGED_ACTION to fire to go to step 3 + // in order for this to happen, we need to make sure that the callbacks are firing + if (Build.VERSION.SDK_INT >= 30) onLocationModeChanged(app.location?.isLocationEnabled == true) + } } - val channel = channel ?: return@launch listener.onFailure(WifiP2pManager.BUSY) + val channel = channel ?: return listener.onFailure(WifiP2pManager.BUSY) if (!safeMode) { binder.fetchPersistentGroup() setOperatingChannel() @@ -466,6 +497,7 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene private fun cleanLocked() { if (receiverRegistered) { ensureReceiverUnregistered(receiver) + p2pPoller?.cancel() receiverRegistered = false } if (Build.VERSION.SDK_INT >= 28) { diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/LocalOnlyHotspotManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/LocalOnlyHotspotManager.kt index 75906a0c..b42d5e24 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/LocalOnlyHotspotManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/LocalOnlyHotspotManager.kt @@ -66,7 +66,7 @@ class LocalOnlyHotspotManager(private val parent: TetheringFragment) : Manager() if (if (Build.VERSION.SDK_INT < 28) @Suppress("DEPRECATION") { Settings.Secure.getInt(context.contentResolver, Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF) == Settings.Secure.LOCATION_MODE_OFF - } else context.getSystemService()?.isLocationEnabled != true) try { + } else app.location?.isLocationEnabled != true) try { context.startActivity(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)) Toast.makeText(context, R.string.tethering_temp_hotspot_location, Toast.LENGTH_LONG).show() } catch (e: ActivityNotFoundException) { diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiP2pManagerHelper.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiP2pManagerHelper.kt index e43dce63..2f26d7a3 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiP2pManagerHelper.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiP2pManagerHelper.kt @@ -3,6 +3,7 @@ package be.mygod.vpnhotspot.net.wifi import android.annotation.SuppressLint import android.net.wifi.WpsInfo import android.net.wifi.p2p.WifiP2pGroup +import android.net.wifi.p2p.WifiP2pInfo import android.net.wifi.p2p.WifiP2pManager import androidx.annotation.RequiresApi import be.mygod.vpnhotspot.App.Companion.app @@ -128,7 +129,9 @@ object WifiP2pManagerHelper { return result.await() } - @SuppressLint("MissingPermission") + suspend fun WifiP2pManager.requestConnectionInfo(c: WifiP2pManager.Channel) = + CompletableDeferred().apply { requestConnectionInfo(c) { complete(it) } }.await() + @SuppressLint("MissingPermission") // missing permission simply leads to null result @RequiresApi(29) suspend fun WifiP2pManager.requestDeviceAddress(c: WifiP2pManager.Channel): MacAddressCompat? { val future = CompletableDeferred() @@ -138,4 +141,10 @@ object WifiP2pManagerHelper { if (address == MacAddressCompat.ANY_ADDRESS) null else address } } + @SuppressLint("MissingPermission") // missing permission simply leads to null result + suspend fun WifiP2pManager.requestGroupInfo(c: WifiP2pManager.Channel) = + CompletableDeferred().apply { requestGroupInfo(c) { complete(it) } }.await() + @RequiresApi(29) + suspend fun WifiP2pManager.requestP2pState(c: WifiP2pManager.Channel) = + CompletableDeferred().apply { requestP2pState(c) { complete(it) } }.await() } diff --git a/mobile/src/main/res/values-zh-rCN/strings.xml b/mobile/src/main/res/values-zh-rCN/strings.xml index 69f3e8fc..e727ef85 100644 --- a/mobile/src/main/res/values-zh-rCN/strings.xml +++ b/mobile/src/main/res/values-zh-rCN/strings.xml @@ -29,6 +29,8 @@ 不支持此操作 服务不可用,请稍后重试 无线中继需要精确位置权限 + 由于系统限制,关闭位置信息服务可能产生问题并导致续航缩短 + 进入设置 临时 WLAN 热点 使用临时热点需要打开位置服务。 diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml index c4ce4b36..4505061f 100644 --- a/mobile/src/main/res/values/strings.xml +++ b/mobile/src/main/res/values/strings.xml @@ -47,6 +47,9 @@ Service unavailable. Try again later Repeater requires permissions for accessing fine location + Due to system restrictions, turning Location off may lead to things not working + properly and increased battery usage + Configure Temporary Wi\u2011Fi hotspot Temporary hotspot requires location to be turned on. From 02e7f06e61b54322532e3e64f41b7d95cb0ff519 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 4 Jun 2021 02:06:06 -0400 Subject: [PATCH 063/112] Remove unnecessary try-catch --- .../be/mygod/vpnhotspot/RepeaterService.kt | 80 +++++++++---------- 1 file changed, 38 insertions(+), 42 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt index fc386f1c..e7f03c16 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt @@ -394,41 +394,39 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene } val networkName = networkName val passphrase = passphrase - try { - if (!safeMode || networkName == null || passphrase == null) { - persistNextGroup = true - p2pManager.createGroup(channel, listener) - } else @TargetApi(29) { - p2pManager.createGroup(channel, WifiP2pConfig.Builder().apply { + @SuppressLint("MissingPermission") // missing permission will simply leading to returning ERROR + if (!safeMode || networkName == null || passphrase == null) { + persistNextGroup = true + p2pManager.createGroup(channel, listener) + } else @TargetApi(29) { + p2pManager.createGroup(channel, WifiP2pConfig.Builder().apply { + try { + mNetworkName.set(this, networkName) // bypass networkName check + } catch (e: ReflectiveOperationException) { + Timber.w(e) try { - mNetworkName.set(this, networkName) // bypass networkName check - } catch (e: ReflectiveOperationException) { - Timber.w(e) setNetworkName(networkName) + } catch (e: IllegalArgumentException) { + Timber.w(e) + return startFailure(e.readableMessage) } - setPassphrase(passphrase) - when (val oc = operatingChannel) { - 0 -> setGroupOperatingBand(when (val band = operatingBand) { - SoftApConfigurationCompat.BAND_2GHZ -> WifiP2pConfig.GROUP_OWNER_BAND_2GHZ - SoftApConfigurationCompat.BAND_5GHZ -> WifiP2pConfig.GROUP_OWNER_BAND_5GHZ - else -> { - require(SoftApConfigurationCompat.isLegacyEitherBand(band)) { "Unknown band $band" } - WifiP2pConfig.GROUP_OWNER_BAND_AUTO - } - }) + } + setPassphrase(passphrase) + when (val oc = operatingChannel) { + 0 -> setGroupOperatingBand(when (val band = operatingBand) { + SoftApConfigurationCompat.BAND_2GHZ -> WifiP2pConfig.GROUP_OWNER_BAND_2GHZ + SoftApConfigurationCompat.BAND_5GHZ -> WifiP2pConfig.GROUP_OWNER_BAND_5GHZ else -> { - setGroupOperatingFrequency(SoftApConfigurationCompat.channelToFrequency(operatingBand, oc)) + require(SoftApConfigurationCompat.isLegacyEitherBand(band)) { "Unknown band $band" } + WifiP2pConfig.GROUP_OWNER_BAND_AUTO } + }) + else -> { + setGroupOperatingFrequency(SoftApConfigurationCompat.channelToFrequency(operatingBand, oc)) } - setDeviceAddress(deviceAddress?.toPlatform()) - }.build(), listener) - } - } catch (e: SecurityException) { - Timber.w(e) - startFailure(e.readableMessage) - } catch (e: IllegalArgumentException) { - Timber.w(e) - startFailure(e.readableMessage) + } + setDeviceAddress(deviceAddress?.toPlatform()) + }.build(), listener) } } /** @@ -481,19 +479,17 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene private fun showNotification(group: WifiP2pGroup? = null) = ServiceNotification.startForeground(this, if (group == null) emptyMap() else mapOf(Pair(group.`interface`, group.clientList?.size ?: 0))) - private fun removeGroup() { - p2pManager.removeGroup(channel, object : WifiP2pManager.ActionListener { - override fun onSuccess() { - launch { cleanLocked() } - } - override fun onFailure(reason: Int) { - if (reason != WifiP2pManager.BUSY) { - SmartSnackbar.make(formatReason(R.string.repeater_remove_group_failure, reason)).show() - } // else assuming it's already gone - launch { cleanLocked() } - } - }) - } + private fun removeGroup() = p2pManager.removeGroup(channel, object : WifiP2pManager.ActionListener { + override fun onSuccess() { + launch { cleanLocked() } + } + override fun onFailure(reason: Int) { + if (reason != WifiP2pManager.BUSY) { + SmartSnackbar.make(formatReason(R.string.repeater_remove_group_failure, reason)).show() + } // else assuming it's already gone + onSuccess() + } + }) private fun cleanLocked() { if (receiverRegistered) { ensureReceiverUnregistered(receiver) From 60c299a44a92f0044be71e90d45fbb7eafd6020c Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 5 Jun 2021 01:41:34 -0400 Subject: [PATCH 064/112] Fix UnblockCentral not working lmao --- README.md | 3 ++- .../mygod/vpnhotspot/util/UnblockCentral.kt | 20 +++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index a0fd2398..0936a5ce 100644 --- a/README.md +++ b/README.md @@ -182,7 +182,8 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (since API 30) `Lcom/android/server/wifi/WifiContext;->ACTION_RESOURCES_APK:Ljava/lang/String;` * (since API 29) `Lcom/android/server/wifi/p2p/WifiP2pServiceImpl;->ANONYMIZED_DEVICE_ADDRESS:Ljava/lang/String;` * (since API 30) `Lcom/android/server/SystemServer;->TETHERING_CONNECTOR_CLASS:Ljava/lang/String;` -* (since API 29) `Ldalvik/system/VMDebug;->allowHiddenApiReflectionFrom(Ljava/lang/Class;)V,unsupported` +* (since API 29) `Ldalvik/system/VMRuntime;->getRuntime()Ldalvik/system/VMRuntime;,core-platform-api,greylist` +* (since API 29) `Ldalvik/system/VMRuntime;->setHiddenApiExemptions([Ljava/lang/String;)V,blacklist,core-platform-api` * (since API 26) `Ljava/lang/invoke/MethodHandles$Lookup;->(Ljava/lang/Class;I)V,unsupported` * (since API 26) `Ljava/lang/invoke/MethodHandles$Lookup;->ALL_MODES:I,lo-prio,max-target-o` * (prior to API 29) `Ljava/net/InetAddress;->parseNumericAddress(Ljava/lang/String;)Ljava/net/InetAddress;,core-platform-api,max-target-p` diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/util/UnblockCentral.kt b/mobile/src/main/java/be/mygod/vpnhotspot/util/UnblockCentral.kt index 0a9a7daa..d5b43755 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/util/UnblockCentral.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/util/UnblockCentral.kt @@ -3,8 +3,9 @@ package be.mygod.vpnhotspot.util import android.annotation.SuppressLint import android.net.wifi.SoftApConfiguration import android.net.wifi.p2p.WifiP2pConfig +import android.os.Build import androidx.annotation.RequiresApi -import timber.log.Timber +import java.lang.reflect.Method /** * The central object for accessing all the useful blocked APIs. Thanks Google! @@ -17,16 +18,15 @@ object UnblockCentral { /** * Retrieve this property before doing dangerous shit. */ - @get:RequiresApi(28) private val init by lazy { - try { - Class.forName("dalvik.system.VMDebug").getDeclaredMethod("allowHiddenApiReflectionFrom", Class::class.java) - .invoke(null, UnblockCentral::class.java) - true - } catch (e: ReflectiveOperationException) { - Timber.w(e) - false - } + if (Build.VERSION.SDK_INT < 28) return@lazy + // TODO: fix this not working when targeting API 30+ + val getDeclaredMethod = Class::class.java.getDeclaredMethod("getDeclaredMethod", + String::class.java, arrayOf>()::class.java) + val clazz = Class.forName("dalvik.system.VMRuntime") + val setHiddenApiExemptions = getDeclaredMethod(clazz, "setHiddenApiExemptions", + arrayOf(Array::class.java)) as Method + setHiddenApiExemptions(clazz.getDeclaredMethod("getRuntime")(null), arrayOf("")) } @RequiresApi(31) From 2e5f5b61927234da23668b3e2086b43c59d6da67 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 5 Jun 2021 01:52:14 -0400 Subject: [PATCH 065/112] Check for field type before attempting to read --- .../src/main/java/be/mygod/vpnhotspot/util/ConstantLookup.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/util/ConstantLookup.kt b/mobile/src/main/java/be/mygod/vpnhotspot/util/ConstantLookup.kt index 0e18da97..135ebdfb 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/util/ConstantLookup.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/util/ConstantLookup.kt @@ -12,7 +12,7 @@ class ConstantLookup(private val prefix: String, private val lookup29: Array().apply { for (field in clazz().declaredFields) try { - if (field.name.startsWith(prefix)) put(field.getInt(null), field.name) + if (field.type == Int::class.java && field.name.startsWith(prefix)) put(field.getInt(null), field.name) } catch (e: Exception) { Timber.w(e) } @@ -40,7 +40,7 @@ inline fun ConstantLookup(prefix: String, vararg lookup29: String?) class LongConstantLookup(private val clazz: Class<*>, private val prefix: String) { private val lookup = LongSparseArray().apply { for (field in clazz.declaredFields) try { - if (field.name.startsWith(prefix)) put(field.getLong(null), field.name) + if (field.type == Long::class.java && field.name.startsWith(prefix)) put(field.getLong(null), field.name) } catch (e: Exception) { Timber.w(e) } From 54e657547bff4925a74477998510db415af56de7 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 5 Jun 2021 02:04:18 -0400 Subject: [PATCH 066/112] v2.12.2 --- mobile/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/build.gradle.kts b/mobile/build.gradle.kts index f74c4ac6..ae7065da 100644 --- a/mobile/build.gradle.kts +++ b/mobile/build.gradle.kts @@ -24,8 +24,8 @@ android { minSdk = 21 if (targetSdk == 31) targetSdkPreview = "S" else this.targetSdk = targetSdk resourceConfigurations.addAll(arrayOf("it", "ru", "zh-rCN", "zh-rTW")) - versionCode = 272 - versionName = "2.12.1" + versionCode = 273 + versionName = "2.12.2" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" javaCompileOptions.annotationProcessorOptions.arguments.apply { put("room.expandProjection", "true") From 9c45c3c49e55412d0a78180f4892fc86cde31aea Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 5 Jun 2021 14:09:42 +0800 Subject: [PATCH 067/112] Refine code style --- .../java/be/mygod/vpnhotspot/manage/LocalOnlyHotspotManager.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/LocalOnlyHotspotManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/LocalOnlyHotspotManager.kt index b42d5e24..62db344d 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/LocalOnlyHotspotManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/LocalOnlyHotspotManager.kt @@ -2,14 +2,12 @@ package be.mygod.vpnhotspot.manage import android.Manifest import android.content.* -import android.location.LocationManager import android.os.Build import android.os.IBinder import android.provider.Settings import android.view.View import android.widget.Toast import androidx.annotation.RequiresApi -import androidx.core.content.getSystemService import androidx.recyclerview.widget.RecyclerView import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.LocalOnlyHotspotService From b402aed983bee62af3fcb6bb4c5238252173efd4 Mon Sep 17 00:00:00 2001 From: Mygod Date: Thu, 10 Jun 2021 13:34:45 +0800 Subject: [PATCH 068/112] Update build tools --- mobile/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/build.gradle.kts b/mobile/build.gradle.kts index ae7065da..2074899f 100644 --- a/mobile/build.gradle.kts +++ b/mobile/build.gradle.kts @@ -11,7 +11,7 @@ plugins { android { val javaVersion = JavaVersion.VERSION_1_8 val targetSdk = 29 - buildToolsVersion = "31.0.0-rc4" + buildToolsVersion = "31.0.0-rc5" compileOptions { isCoreLibraryDesugaringEnabled = true sourceCompatibility = javaVersion From 8918ff2377e5ed9ead8e68817a0500c81458fb3f Mon Sep 17 00:00:00 2001 From: Mygod Date: Thu, 10 Jun 2021 14:20:21 +0800 Subject: [PATCH 069/112] Fix new BluetoothPan constructor --- README.md | 2 ++ .../vpnhotspot/manage/BluetoothTethering.kt | 17 ++++++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 99480ad5..879bbf03 100644 --- a/README.md +++ b/README.md @@ -192,6 +192,8 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded
Hidden whitelisted APIs: (same catch as above, however, things in this list are less likely to be broken) +* (since API 24, prior to API 31) `Landroid/bluetooth/BluetoothPan;->(Landroid/content/Context;Landroid/bluetooth/BluetoothProfile$ServiceListener;)V,unsupported` +* (since API 31) `Landroid/bluetooth/BluetoothPan;->(Landroid/content/Context;Landroid/bluetooth/BluetoothProfile$ServiceListener;Landroid/bluetooth/BluetoothAdapter;)V,unsupported` * (since API 24) `Landroid/bluetooth/BluetoothPan;->isTetheringOn()Z,sdk,system-api,test-api` * (since API 24) `Landroid/bluetooth/BluetoothProfile;->PAN:I,sdk,system-api,test-api` * (since API 30) `Landroid/content/Context;->TETHERING_SERVICE:Ljava/lang/String;,sdk,system-api,test-api` diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt index 5a52d2ab..e76c04dc 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt @@ -27,14 +27,21 @@ class BluetoothTethering(context: Context, val stateListener: () -> Unit) : private const val PAN = 5 private val clazz by lazy { Class.forName("android.bluetooth.BluetoothPan") } private val constructor by lazy { - clazz.getDeclaredConstructor(Context::class.java, BluetoothProfile.ServiceListener::class.java).apply { + (if (BuildCompat.isAtLeastS()) { + clazz.getDeclaredConstructor(Context::class.java, BluetoothProfile.ServiceListener::class.java, + BluetoothAdapter::class.java) + } else { + clazz.getDeclaredConstructor(Context::class.java, BluetoothProfile.ServiceListener::class.java) + }).apply { isAccessible = true } } private val isTetheringOn by lazy { clazz.getDeclaredMethod("isTetheringOn") } - fun pan(context: Context, serviceListener: BluetoothProfile.ServiceListener) = - constructor.newInstance(context, serviceListener) as BluetoothProfile + fun pan(context: Context, serviceListener: BluetoothProfile.ServiceListener, adapter: BluetoothAdapter) = + (if (BuildCompat.isAtLeastS()) { + constructor.newInstance(context, serviceListener, adapter) + } else constructor.newInstance(context, serviceListener)) as BluetoothProfile val BluetoothProfile.isTetheringOn get() = isTetheringOn(this) as Boolean fun BluetoothProfile.closePan() = BluetoothAdapter.getDefaultAdapter()!!.closeProfileProxy(PAN, this) @@ -101,8 +108,8 @@ class BluetoothTethering(context: Context, val stateListener: () -> Unit) : private val receiver = broadcastReceiver { _, _ -> stateListener() } fun ensureInit(context: Context) { - if (pan == null && BluetoothAdapter.getDefaultAdapter() != null) try { - pan = pan(context, this) + if (pan == null) try { + pan = pan(context, this, BluetoothAdapter.getDefaultAdapter() ?: return) } catch (e: ReflectiveOperationException) { if (e.cause is SecurityException && BuildCompat.isAtLeastS()) Timber.d(e.readableMessage) else Timber.w(e) From 5d39ace3f04918ded826abd8c3f4452a23021cc5 Mon Sep 17 00:00:00 2001 From: Mygod Date: Thu, 10 Jun 2021 14:26:56 +0800 Subject: [PATCH 070/112] Use recommended API for getting bluetooth adapter --- .../vpnhotspot/manage/BluetoothTethering.kt | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt index e76c04dc..3e49495c 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt @@ -2,6 +2,7 @@ package be.mygod.vpnhotspot.manage import android.annotation.TargetApi import android.bluetooth.BluetoothAdapter +import android.bluetooth.BluetoothManager import android.bluetooth.BluetoothProfile import android.content.BroadcastReceiver import android.content.Context @@ -9,6 +10,7 @@ import android.content.Intent import android.content.IntentFilter import android.os.Build import androidx.annotation.RequiresApi +import androidx.core.content.getSystemService import androidx.core.os.BuildCompat import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.net.TetheringManager @@ -38,12 +40,12 @@ class BluetoothTethering(context: Context, val stateListener: () -> Unit) : } private val isTetheringOn by lazy { clazz.getDeclaredMethod("isTetheringOn") } - fun pan(context: Context, serviceListener: BluetoothProfile.ServiceListener, adapter: BluetoothAdapter) = - (if (BuildCompat.isAtLeastS()) { - constructor.newInstance(context, serviceListener, adapter) - } else constructor.newInstance(context, serviceListener)) as BluetoothProfile + private val adapter = app.getSystemService()?.adapter + fun pan(context: Context, serviceListener: BluetoothProfile.ServiceListener) = (if (BuildCompat.isAtLeastS()) { + constructor.newInstance(context, serviceListener, adapter) + } else constructor.newInstance(context, serviceListener)) as BluetoothProfile val BluetoothProfile.isTetheringOn get() = isTetheringOn(this) as Boolean - fun BluetoothProfile.closePan() = BluetoothAdapter.getDefaultAdapter()!!.closeProfileProxy(PAN, this) + fun BluetoothProfile.closePan() = adapter!!.closeProfileProxy(PAN, this) private fun registerBluetoothStateListener(receiver: BroadcastReceiver) = app.registerReceiver(receiver, IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)) @@ -72,7 +74,6 @@ class BluetoothTethering(context: Context, val stateListener: () -> Unit) : @RequiresApi(24) fun start(callback: TetheringManager.StartTetheringCallback) { if (pendingCallback != null) return - val adapter = BluetoothAdapter.getDefaultAdapter() try { if (adapter?.state == BluetoothAdapter.STATE_OFF) { registerBluetoothStateListener(this) @@ -95,7 +96,7 @@ class BluetoothTethering(context: Context, val stateListener: () -> Unit) : val pan = pan ?: return null if (!connected) return null activeFailureCause = null - return BluetoothAdapter.getDefaultAdapter()?.state == BluetoothAdapter.STATE_ON && try { + return adapter?.state == BluetoothAdapter.STATE_ON && try { pan.isTetheringOn } catch (e: InvocationTargetException) { activeFailureCause = e.cause ?: e @@ -109,7 +110,7 @@ class BluetoothTethering(context: Context, val stateListener: () -> Unit) : fun ensureInit(context: Context) { if (pan == null) try { - pan = pan(context, this, BluetoothAdapter.getDefaultAdapter() ?: return) + pan = pan(context, this) } catch (e: ReflectiveOperationException) { if (e.cause is SecurityException && BuildCompat.isAtLeastS()) Timber.d(e.readableMessage) else Timber.w(e) From edca8ed0134144b8885060e8e0aa527b40e87201 Mon Sep 17 00:00:00 2001 From: Mygod Date: Thu, 10 Jun 2021 14:59:09 +0800 Subject: [PATCH 071/112] Rely on Android implicitly granting BLUETOOTH_CONNECT --- mobile/src/main/AndroidManifest.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mobile/src/main/AndroidManifest.xml b/mobile/src/main/AndroidManifest.xml index e4fa8a08..69d08080 100644 --- a/mobile/src/main/AndroidManifest.xml +++ b/mobile/src/main/AndroidManifest.xml @@ -52,8 +52,10 @@ - + + Date: Fri, 11 Jun 2021 01:34:53 +0800 Subject: [PATCH 072/112] Clear error when permission is granted --- .../java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt | 1 + .../main/java/be/mygod/vpnhotspot/manage/TetherManager.kt | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt index 3e49495c..cedc9c0a 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt @@ -109,6 +109,7 @@ class BluetoothTethering(context: Context, val stateListener: () -> Unit) : private val receiver = broadcastReceiver { _, _ -> stateListener() } fun ensureInit(context: Context) { + activeFailureCause = null if (pan == null) try { pan = pan(context, this) } catch (e: ReflectiveOperationException) { 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 f2f9fa67..55dd363f 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt @@ -297,11 +297,14 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), parent.viewLifecycleOwner.lifecycle.addObserver(this) } - fun ensureInit(context: Context) = tethering.ensureInit(context) + fun ensureInit(context: Context) { + tethering.ensureInit(context) + onTetheringStarted() // force flush + } override fun onResume(owner: LifecycleOwner) { if (!BuildCompat.isAtLeastS() || parent.requireContext().checkSelfPermission( Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED) { - ensureInit(parent.requireContext()) + tethering.ensureInit(parent.requireContext()) } else if (parent.shouldShowRequestPermissionRationale(Manifest.permission.BLUETOOTH_CONNECT)) { parent.requestBluetooth.launch(Manifest.permission.BLUETOOTH_CONNECT) } From 54c9ae0ceccffd82490b4ad970369b4813c874be Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 11 Jun 2021 01:46:51 +0800 Subject: [PATCH 073/112] Fix turning off bluetooth tethering --- .../vpnhotspot/manage/BluetoothTethering.kt | 48 ++++++++++++------- .../mygod/vpnhotspot/manage/TetherManager.kt | 5 +- .../vpnhotspot/manage/TetheringTileService.kt | 5 +- 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt index cedc9c0a..4f79aa44 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt @@ -67,27 +67,11 @@ class BluetoothTethering(context: Context, val stateListener: () -> Unit) : pendingCallback = null app.unregisterReceiver(this) } - - /** - * https://android.googlesource.com/platform/packages/apps/Settings/+/b1af85d/src/com/android/settings/TetherSettings.java#384 - */ - @RequiresApi(24) - fun start(callback: TetheringManager.StartTetheringCallback) { - if (pendingCallback != null) return - try { - if (adapter?.state == BluetoothAdapter.STATE_OFF) { - registerBluetoothStateListener(this) - pendingCallback = callback - adapter.enable() - } else TetheringManager.startTethering(TetheringManager.TETHERING_BLUETOOTH, true, callback) - } catch (e: SecurityException) { - SmartSnackbar.make(e.readableMessage).shortToast().show() - } - } } private var connected = false private var pan: BluetoothProfile? = null + private var stoppedByUser = false var activeFailureCause: Throwable? = null /** * Based on: https://android.googlesource.com/platform/packages/apps/Settings/+/78d5efd/src/com/android/settings/TetherSettings.java @@ -96,7 +80,7 @@ class BluetoothTethering(context: Context, val stateListener: () -> Unit) : val pan = pan ?: return null if (!connected) return null activeFailureCause = null - return adapter?.state == BluetoothAdapter.STATE_ON && try { + val on = adapter?.state == BluetoothAdapter.STATE_ON && try { pan.isTetheringOn } catch (e: InvocationTargetException) { activeFailureCause = e.cause ?: e @@ -104,6 +88,10 @@ class BluetoothTethering(context: Context, val stateListener: () -> Unit) : else Timber.w(e) return null } + return if (stoppedByUser) { + if (!on) stoppedByUser = false + false + } else on } private val receiver = broadcastReceiver { _, _ -> stateListener() } @@ -125,11 +113,35 @@ class BluetoothTethering(context: Context, val stateListener: () -> Unit) : override fun onServiceDisconnected(profile: Int) { connected = false + stoppedByUser = false } override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) { connected = true stateListener() } + + /** + * https://android.googlesource.com/platform/packages/apps/Settings/+/b1af85d/src/com/android/settings/TetherSettings.java#384 + */ + @RequiresApi(24) + fun start(callback: TetheringManager.StartTetheringCallback) { + if (pendingCallback != null) return + try { + if (adapter?.state == BluetoothAdapter.STATE_OFF) { + registerBluetoothStateListener(BluetoothTethering) + pendingCallback = callback + adapter.enable() + } else TetheringManager.startTethering(TetheringManager.TETHERING_BLUETOOTH, true, callback) + } catch (e: SecurityException) { + SmartSnackbar.make(e.readableMessage).shortToast().show() + } + } + @RequiresApi(24) + fun stop(callback: (Exception) -> Unit) { + TetheringManager.stopTethering(TetheringManager.TETHERING_BLUETOOTH, callback) + stoppedByUser = true + } + override fun close() { app.unregisterReceiver(receiver) pan?.closePan() 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 55dd363f..ac9981ec 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt @@ -319,10 +319,9 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), if (tethering.active == null) tethering.activeFailureCause?.readableMessage else null, baseError).joinToString("\n") - override fun start() = BluetoothTethering.start(this) + override fun start() = tethering.start(this) override fun stop() { - TetheringManager.stopTethering(TetheringManager.TETHERING_BLUETOOTH, this::onException) - Thread.sleep(1) // give others a room to breathe + tethering.stop(this::onException) onTetheringStarted() // force flush state } } 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 6483beea..ed0c74be 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringTileService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringTileService.kt @@ -151,10 +151,9 @@ sealed class TetheringTileService : IpNeighbourMonitoringTileService(), Tetherin override val labelString get() = R.string.tethering_manage_bluetooth override val tetherType get() = TetherType.BLUETOOTH - override fun start() = BluetoothTethering.start(this) + override fun start() = tethering!!.start(this) override fun stop() { - TetheringManager.stopTethering(TetheringManager.TETHERING_BLUETOOTH, this::onException) - Thread.sleep(1) // give others a room to breathe + tethering!!.stop(this::onException) onTetheringStarted() // force flush state } From 6f81f8a6fff95a2bd2475427da7f3d8881ccdadc Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 11 Jun 2021 01:54:22 +0800 Subject: [PATCH 074/112] Reset pendingCallback on failure --- .../java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt index 4f79aa44..d20085be 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt @@ -125,8 +125,7 @@ class BluetoothTethering(context: Context, val stateListener: () -> Unit) : */ @RequiresApi(24) fun start(callback: TetheringManager.StartTetheringCallback) { - if (pendingCallback != null) return - try { + if (pendingCallback == null) try { if (adapter?.state == BluetoothAdapter.STATE_OFF) { registerBluetoothStateListener(BluetoothTethering) pendingCallback = callback @@ -134,6 +133,7 @@ class BluetoothTethering(context: Context, val stateListener: () -> Unit) : } else TetheringManager.startTethering(TetheringManager.TETHERING_BLUETOOTH, true, callback) } catch (e: SecurityException) { SmartSnackbar.make(e.readableMessage).shortToast().show() + pendingCallback = null } } @RequiresApi(24) From 440b22faa19246ca4ab86345ea95d4d1fbc8d10a Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 11 Jun 2021 02:15:49 +0800 Subject: [PATCH 075/112] Properly handle missing bluetooth permissions --- .../vpnhotspot/manage/BluetoothTethering.kt | 2 ++ .../mygod/vpnhotspot/manage/TetherManager.kt | 24 ++++++++++++------- .../vpnhotspot/manage/TetheringTileService.kt | 7 +++--- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt index d20085be..de626edb 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt @@ -1,5 +1,6 @@ package be.mygod.vpnhotspot.manage +import android.annotation.SuppressLint import android.annotation.TargetApi import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothManager @@ -123,6 +124,7 @@ class BluetoothTethering(context: Context, val stateListener: () -> Unit) : /** * https://android.googlesource.com/platform/packages/apps/Settings/+/b1af85d/src/com/android/settings/TetherSettings.java#384 */ + @SuppressLint("MissingPermission") @RequiresApi(24) fun start(callback: TetheringManager.StartTetheringCallback) { if (pendingCallback == null) try { 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 ac9981ec..86290ca2 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt @@ -62,12 +62,16 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), } catch (e: RuntimeException) { app.logEvent("manage_write_settings") { param("message", e.toString()) } } - if (manager.isStarted) try { - manager.stop() - } catch (e: InvocationTargetException) { - if (e.targetException !is SecurityException) Timber.w(e) - manager.onException(e) - } else manager.start() + when (manager.isStarted) { + true -> try { + manager.stop() + } catch (e: InvocationTargetException) { + if (e.targetException !is SecurityException) Timber.w(e) + manager.onException(e) + } + false -> manager.start() + null -> manager.onClickNull() + } } } @@ -78,13 +82,13 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), override val icon get() = tetherType.icon override val title get() = this@TetherManager.title override val text get() = this@TetherManager.text - override val active get() = isStarted + override val active get() = isStarted == true } val data = Data() abstract val title: CharSequence abstract val tetherType: TetherType - open val isStarted get() = parent.enabledTypes.contains(tetherType) + open val isStarted: Boolean? get() = parent.enabledTypes.contains(tetherType) protected open val text: CharSequence get() = baseError ?: "" protected var baseError: String? = null @@ -92,6 +96,7 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), protected abstract fun start() protected abstract fun stop() + protected open fun onClickNull(): Unit = throw UnsupportedOperationException() override fun onTetheringStarted() = data.notifyChange() override fun onTetheringFailed(error: Int?) { @@ -314,7 +319,7 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), override val title get() = parent.getString(R.string.tethering_manage_bluetooth) override val tetherType get() = TetherType.BLUETOOTH override val type get() = VIEW_TYPE_BLUETOOTH - override val isStarted get() = tethering.active == true + override val isStarted get() = tethering.active override val text get() = listOfNotNull( if (tethering.active == null) tethering.activeFailureCause?.readableMessage else null, baseError).joinToString("\n") @@ -324,6 +329,7 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), tethering.stop(this::onException) onTetheringStarted() // force flush state } + override fun onClickNull() = ManageBar.start(parent.requireContext()) } @RequiresApi(30) class Ethernet(parent: TetheringFragment) : TetherManager(parent) { 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 ed0c74be..4efd8174 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringTileService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringTileService.kt @@ -186,7 +186,7 @@ sealed class TetheringTileService : IpNeighbourMonitoringTileService(), Tetherin icon = tileOff } null -> { - state = Tile.STATE_UNAVAILABLE + state = Tile.STATE_INACTIVE icon = tileOff subtitle(tethering?.activeFailureCause?.readableMessage) } @@ -197,7 +197,8 @@ sealed class TetheringTileService : IpNeighbourMonitoringTileService(), Tetherin } override fun onClick() { - when (tethering?.active) { + val tethering = tethering + if (tethering == null) tapPending = true else when (tethering.active) { true -> { val binder = binder if (binder == null) tapPending = true else { @@ -211,7 +212,7 @@ sealed class TetheringTileService : IpNeighbourMonitoringTileService(), Tetherin } } false -> start() - else -> tapPending = true + else -> ManageBar.start(this) } } } From 45638decb1926e65da77fe20af92058c2e3cf72f Mon Sep 17 00:00:00 2001 From: Mygod Date: Thu, 10 Jun 2021 16:24:32 -0400 Subject: [PATCH 076/112] Mark remaining tri-state TileServices as toggleable as well --- mobile/src/main/AndroidManifest.xml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/mobile/src/main/AndroidManifest.xml b/mobile/src/main/AndroidManifest.xml index 69d08080..0dc395d0 100644 --- a/mobile/src/main/AndroidManifest.xml +++ b/mobile/src/main/AndroidManifest.xml @@ -143,6 +143,9 @@ + + + + + + + Date: Thu, 10 Jun 2021 16:41:11 -0400 Subject: [PATCH 077/112] v2.12.3 --- mobile/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/build.gradle.kts b/mobile/build.gradle.kts index 2074899f..14fdaf88 100644 --- a/mobile/build.gradle.kts +++ b/mobile/build.gradle.kts @@ -24,8 +24,8 @@ android { minSdk = 21 if (targetSdk == 31) targetSdkPreview = "S" else this.targetSdk = targetSdk resourceConfigurations.addAll(arrayOf("it", "ru", "zh-rCN", "zh-rTW")) - versionCode = 273 - versionName = "2.12.2" + versionCode = 274 + versionName = "2.12.3" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" javaCompileOptions.annotationProcessorOptions.arguments.apply { put("room.expandProjection", "true") From d87983274e161916123d81a12e03bd82a73dc938 Mon Sep 17 00:00:00 2001 From: Mygod Date: Thu, 10 Jun 2021 22:50:26 -0400 Subject: [PATCH 078/112] Fix permission request --- .../java/be/mygod/vpnhotspot/manage/RepeaterManager.kt | 7 +------ .../java/be/mygod/vpnhotspot/manage/TetheringFragment.kt | 6 +++++- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt index 137fd2b1..796c8282 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt @@ -5,7 +5,6 @@ import android.content.ComponentName import android.content.DialogInterface import android.content.Intent import android.content.ServiceConnection -import android.content.pm.PackageManager import android.net.wifi.SoftApConfiguration import android.net.wifi.p2p.WifiP2pGroup import android.os.Build @@ -90,11 +89,7 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic when (binder?.service?.status) { RepeaterService.Status.IDLE -> if (Build.VERSION.SDK_INT < 29) parent.requireContext().let { context -> ContextCompat.startForegroundService(context, Intent(context, RepeaterService::class.java)) - } else if (parent.requireContext().checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == - PackageManager.PERMISSION_GRANTED || - parent.shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)) { - parent.startRepeater.launch(Manifest.permission.ACCESS_FINE_LOCATION) - } else SmartSnackbar.make(R.string.repeater_missing_location_permissions).shortToast().show() + } else parent.startRepeater.launch(Manifest.permission.ACCESS_FINE_LOCATION) RepeaterService.Status.ACTIVE -> binder.shutdown() else -> { } } 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 25be6bb0..ba2e52af 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt @@ -34,6 +34,7 @@ import be.mygod.vpnhotspot.root.RootManager import be.mygod.vpnhotspot.root.WifiApCommands import be.mygod.vpnhotspot.util.* import be.mygod.vpnhotspot.widget.SmartSnackbar +import com.google.android.material.snackbar.Snackbar import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CompletableDeferred import timber.log.Timber @@ -130,7 +131,10 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick @RequiresApi(29) val startRepeater = registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted -> - if (granted) requireActivity().startForegroundService(Intent(activity, RepeaterService::class.java)) + if (granted) requireActivity().startForegroundService(Intent(activity, RepeaterService::class.java)) else { + Snackbar.make((activity as MainActivity).binding.fragmentHolder, + R.string.repeater_missing_location_permissions, Snackbar.LENGTH_LONG).show() + } } @RequiresApi(26) val startLocalOnlyHotspot = registerForActivityResult(ActivityResultContracts.RequestPermission()) { From 92ed445fc04e30b44da101344933db17d3d52857 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 11 Jun 2021 00:24:48 -0400 Subject: [PATCH 079/112] Properly locate BluetoothPan constructors --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 879bbf03..ead0ce40 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,8 @@ API restrictions are updated up to [commit `ebe7044`](https://android.googlesour Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded or implicitly used) +* (since API 24, prior to API 31) `Landroid/bluetooth/BluetoothPan;->(Landroid/content/Context;Landroid/bluetooth/BluetoothProfile$ServiceListener;)V,unsupported` +* (since API 31) `Landroid/bluetooth/BluetoothPan;->(Landroid/content/Context;Landroid/bluetooth/BluetoothProfile$ServiceListener;Landroid/bluetooth/BluetoothAdapter;)V,unsupported` * (prior to API 30) `Landroid/net/ConnectivityManager;->getLastTetherError(Ljava/lang/String;)I,max-target-r` * (since API 30) `Landroid/net/ConnectivityModuleConnector;->IN_PROCESS_SUFFIX:Ljava/lang/String;` * (since API 30) `Landroid/net/TetheringManager$TetheringEventCallback;->onTetherableInterfaceRegexpsChanged(Landroid/net/TetheringManager$TetheringInterfaceRegexps;)V,blocked` @@ -192,8 +194,6 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded
Hidden whitelisted APIs: (same catch as above, however, things in this list are less likely to be broken) -* (since API 24, prior to API 31) `Landroid/bluetooth/BluetoothPan;->(Landroid/content/Context;Landroid/bluetooth/BluetoothProfile$ServiceListener;)V,unsupported` -* (since API 31) `Landroid/bluetooth/BluetoothPan;->(Landroid/content/Context;Landroid/bluetooth/BluetoothProfile$ServiceListener;Landroid/bluetooth/BluetoothAdapter;)V,unsupported` * (since API 24) `Landroid/bluetooth/BluetoothPan;->isTetheringOn()Z,sdk,system-api,test-api` * (since API 24) `Landroid/bluetooth/BluetoothProfile;->PAN:I,sdk,system-api,test-api` * (since API 30) `Landroid/content/Context;->TETHERING_SERVICE:Ljava/lang/String;,sdk,system-api,test-api` From d17bb74e7b443d650c0fd854ac3a9c45eae05ab8 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 11 Jun 2021 01:08:34 -0400 Subject: [PATCH 080/112] Stop using unsupported API for bluetooth --- README.md | 2 -- .../vpnhotspot/manage/BluetoothTethering.kt | 31 ++++++------------- 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index ead0ce40..99480ad5 100644 --- a/README.md +++ b/README.md @@ -151,8 +151,6 @@ API restrictions are updated up to [commit `ebe7044`](https://android.googlesour Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded or implicitly used) -* (since API 24, prior to API 31) `Landroid/bluetooth/BluetoothPan;->(Landroid/content/Context;Landroid/bluetooth/BluetoothProfile$ServiceListener;)V,unsupported` -* (since API 31) `Landroid/bluetooth/BluetoothPan;->(Landroid/content/Context;Landroid/bluetooth/BluetoothProfile$ServiceListener;Landroid/bluetooth/BluetoothAdapter;)V,unsupported` * (prior to API 30) `Landroid/net/ConnectivityManager;->getLastTetherError(Ljava/lang/String;)I,max-target-r` * (since API 30) `Landroid/net/ConnectivityModuleConnector;->IN_PROCESS_SUFFIX:Ljava/lang/String;` * (since API 30) `Landroid/net/TetheringManager$TetheringEventCallback;->onTetherableInterfaceRegexpsChanged(Landroid/net/TetheringManager$TetheringInterfaceRegexps;)V,blocked` diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt index de626edb..1dce98e2 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt @@ -29,24 +29,10 @@ class BluetoothTethering(context: Context, val stateListener: () -> Unit) : */ private const val PAN = 5 private val clazz by lazy { Class.forName("android.bluetooth.BluetoothPan") } - private val constructor by lazy { - (if (BuildCompat.isAtLeastS()) { - clazz.getDeclaredConstructor(Context::class.java, BluetoothProfile.ServiceListener::class.java, - BluetoothAdapter::class.java) - } else { - clazz.getDeclaredConstructor(Context::class.java, BluetoothProfile.ServiceListener::class.java) - }).apply { - isAccessible = true - } - } private val isTetheringOn by lazy { clazz.getDeclaredMethod("isTetheringOn") } private val adapter = app.getSystemService()?.adapter - fun pan(context: Context, serviceListener: BluetoothProfile.ServiceListener) = (if (BuildCompat.isAtLeastS()) { - constructor.newInstance(context, serviceListener, adapter) - } else constructor.newInstance(context, serviceListener)) as BluetoothProfile - val BluetoothProfile.isTetheringOn get() = isTetheringOn(this) as Boolean - fun BluetoothProfile.closePan() = adapter!!.closeProfileProxy(PAN, this) + private val BluetoothProfile.isTetheringOn get() = isTetheringOn(this) as Boolean private fun registerBluetoothStateListener(receiver: BroadcastReceiver) = app.registerReceiver(receiver, IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)) @@ -70,6 +56,7 @@ class BluetoothTethering(context: Context, val stateListener: () -> Unit) : } } + private var proxyCreated = false private var connected = false private var pan: BluetoothProfile? = null private var stoppedByUser = false @@ -98,12 +85,13 @@ class BluetoothTethering(context: Context, val stateListener: () -> Unit) : private val receiver = broadcastReceiver { _, _ -> stateListener() } fun ensureInit(context: Context) { + val adapter = adapter ?: return activeFailureCause = null - if (pan == null) try { - pan = pan(context, this) - } catch (e: ReflectiveOperationException) { - if (e.cause is SecurityException && BuildCompat.isAtLeastS()) Timber.d(e.readableMessage) - else Timber.w(e) + if (!proxyCreated) try { + check(adapter.getProfileProxy(context, this, PAN)) + proxyCreated = true + } catch (e: SecurityException) { + if (BuildCompat.isAtLeastS()) Timber.d(e.readableMessage) else Timber.w(e) activeFailureCause = e } } @@ -117,6 +105,7 @@ class BluetoothTethering(context: Context, val stateListener: () -> Unit) : stoppedByUser = false } override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) { + pan = proxy connected = true stateListener() } @@ -146,6 +135,6 @@ class BluetoothTethering(context: Context, val stateListener: () -> Unit) : override fun close() { app.unregisterReceiver(receiver) - pan?.closePan() + adapter!!.closeProfileProxy(PAN, pan) } } From cc5cdec0c51591d56bffa732d3f2b5e002ee2d14 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 11 Jun 2021 01:13:18 -0400 Subject: [PATCH 081/112] Fix updating README --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 99480ad5..ac7f4fcb 100644 --- a/README.md +++ b/README.md @@ -183,8 +183,8 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (since API 30) `Lcom/android/server/wifi/WifiContext;->ACTION_RESOURCES_APK:Ljava/lang/String;` * (since API 29) `Lcom/android/server/wifi/p2p/WifiP2pServiceImpl;->ANONYMIZED_DEVICE_ADDRESS:Ljava/lang/String;` * (since API 30) `Lcom/android/server/SystemServer;->TETHERING_CONNECTOR_CLASS:Ljava/lang/String;` -* (since API 29) `Ldalvik/system/VMRuntime;->getRuntime()Ldalvik/system/VMRuntime;,core-platform-api,greylist` -* (since API 29) `Ldalvik/system/VMRuntime;->setHiddenApiExemptions([Ljava/lang/String;)V,blacklist,core-platform-api` +* (since API 29) `Ldalvik/system/VMRuntime;->getRuntime()Ldalvik/system/VMRuntime;,core-platform-api,unsupported` +* (since API 29) `Ldalvik/system/VMRuntime;->setHiddenApiExemptions([Ljava/lang/String;)V,blocked,core-platform-api` * (since API 26) `Ljava/lang/invoke/MethodHandles$Lookup;->(Ljava/lang/Class;I)V,unsupported` * (since API 26) `Ljava/lang/invoke/MethodHandles$Lookup;->ALL_MODES:I,lo-prio,max-target-o` * (prior to API 29) `Ljava/net/InetAddress;->parseNumericAddress(Ljava/lang/String;)Ljava/net/InetAddress;,core-platform-api,max-target-p` @@ -284,8 +284,8 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (on API 30) `Landroid/net/wifi/SoftApInfo;->CHANNEL_WIDTH_INVALID:I,sdk,system-api,test-api` * (since API 31) `Landroid/net/wifi/SoftApInfo;->getAutoShutdownTimeoutMillis()J,sdk,system-api,test-api` * (since API 31) `Landroid/net/wifi/SoftApInfo;->getBssid()Landroid/net/MacAddress;,sdk,system-api,test-api` -* (since API 30) `Landroid/net/wifi/SoftApInfo;->getBandwidth()I,system-api,whitelist` -* (since API 30) `Landroid/net/wifi/SoftApInfo;->getFrequency()I,system-api,whitelist` +* (since API 30) `Landroid/net/wifi/SoftApInfo;->getBandwidth()I,sdk,system-api,test-api` +* (since API 30) `Landroid/net/wifi/SoftApInfo;->getFrequency()I,sdk,system-api,test-api` * (since API 31) `Landroid/net/wifi/SoftApInfo;->getWifiStandard()I,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/WifiClient;->getMacAddress()Landroid/net/MacAddress;,sdk,system-api,test-api` * (prior to API 30) `Landroid/net/wifi/WifiConfiguration$KeyMgmt;->WPA2_PSK:I,sdk,system-api,test-api` From bb80359efbd704a0a4c13011b313c3620fe8c32e Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 11 Jun 2021 02:20:55 -0400 Subject: [PATCH 082/112] Refactor LOHService based on AOSP CarProjectionService --- README.md | 10 ++- .../vpnhotspot/LocalOnlyHotspotService.kt | 68 +++++++-------- .../mygod/vpnhotspot/manage/TetherManager.kt | 4 +- .../mygod/vpnhotspot/net/TetheringManager.kt | 8 +- .../vpnhotspot/net/wifi/WifiApManager.kt | 85 ++++++++++++++++++- 5 files changed, 128 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index ac7f4fcb..221c9d18 100644 --- a/README.md +++ b/README.md @@ -295,9 +295,17 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (on API 30) `Landroid/net/wifi/WifiManager$SoftApCallback;->onInfoChanged(Landroid/net/wifi/SoftApInfo;)V,sdk,system-api,test-api` * (since API 31) `Landroid/net/wifi/WifiManager$SoftApCallback;->onInfoChanged(Ljava/util/List;)V,sdk,system-api,test-api` * (since API 28) `Landroid/net/wifi/WifiManager$SoftApCallback;->onStateChanged(II)V,sdk,system-api,test-api` +* (since API 26) `Landroid/net/wifi/WifiManager;->EXTRA_WIFI_AP_FAILURE_REASON:Ljava/lang/String;,sdk,system-api,test-api` +* (since API 26) `Landroid/net/wifi/WifiManager;->EXTRA_WIFI_AP_INTERFACE_NAME:Ljava/lang/String;,sdk,system-api,test-api` +* (since API 26) `Landroid/net/wifi/WifiManager;->EXTRA_WIFI_AP_STATE:Ljava/lang/String;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/WifiManager;->SAP_CLIENT_BLOCK_REASON_CODE_*:I,sdk,system-api,test-api` * (since API 28) `Landroid/net/wifi/WifiManager;->SAP_START_FAILURE_*:I,sdk,system-api,test-api` -* (since API 28) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_FAILED:I,sdk,system-api,test-api` +* (since API 26) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_CHANGED_ACTION:Ljava/lang/String;,sdk,system-api,test-api` +* (since API 26) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_DISABLED:I,sdk,system-api,test-api` +* (since API 26) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_DISABLING:I,sdk,system-api,test-api` +* (since API 26) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_ENABLED:I,sdk,system-api,test-api` +* (since API 26) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_ENABLING:I,sdk,system-api,test-api` +* (since API 26) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_FAILED:I,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/WifiManager;->getSoftApConfiguration()Landroid/net/wifi/SoftApConfiguration;,sdk,system-api,test-api` * (prior to API 30) `Landroid/net/wifi/WifiManager;->getWifiApConfiguration()Landroid/net/wifi/WifiConfiguration;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/WifiManager;->isApMacRandomizationSupported()Z,sdk,system-api,test-api` diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyHotspotService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyHotspotService.kt index 7c80c71b..1055f2f8 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyHotspotService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyHotspotService.kt @@ -6,16 +6,12 @@ import android.net.wifi.WifiManager import android.os.Build import androidx.annotation.RequiresApi import be.mygod.vpnhotspot.net.IpNeighbour -import be.mygod.vpnhotspot.net.TetherType -import be.mygod.vpnhotspot.net.TetheringManager -import be.mygod.vpnhotspot.net.TetheringManager.localOnlyTetheredIfaces import be.mygod.vpnhotspot.net.monitor.IpNeighbourMonitor import be.mygod.vpnhotspot.net.monitor.TetherTimeoutMonitor import be.mygod.vpnhotspot.net.wifi.SoftApConfigurationCompat.Companion.toCompat import be.mygod.vpnhotspot.net.wifi.WifiApManager import be.mygod.vpnhotspot.util.Services import be.mygod.vpnhotspot.util.StickyEvent1 -import be.mygod.vpnhotspot.util.broadcastReceiver import be.mygod.vpnhotspot.widget.SmartSnackbar import kotlinx.coroutines.* import timber.log.Timber @@ -43,7 +39,8 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService(), CoroutineScope { null -> return // stopped "" -> WifiApManager.cancelLocalOnlyHotspotRequest() } - reservation?.close() ?: stopService() + reservation?.close() + stopService() } } @@ -56,24 +53,6 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService(), CoroutineScope { override val coroutineContext = dispatcher + Job() private var routingManager: RoutingManager? = null private var timeoutMonitor: TetherTimeoutMonitor? = null - private var receiverRegistered = false - private val receiver = broadcastReceiver { _, intent -> - val ifaces = (intent.localOnlyTetheredIfaces ?: return@broadcastReceiver).filter { - TetherType.ofInterface(it) != TetherType.WIFI_P2P - } - Timber.d("onTetherStateChangedLocked: $ifaces") - check(ifaces.size <= 1) - val iface = ifaces.singleOrNull() - binder.iface = iface - if (iface.isNullOrEmpty()) stopService() else launch { - val routingManager = routingManager - if (routingManager == null) { - this@LocalOnlyHotspotService.routingManager = RoutingManager.LocalOnly(this@LocalOnlyHotspotService, - iface).apply { start() } - IpNeighbourMonitor.registerCallback(this@LocalOnlyHotspotService) - } else check(iface == routingManager.downstream) - } - } override val activeIfaces get() = binder.iface.let { if (it.isNullOrEmpty()) emptyList() else listOf(it) } override fun onBind(intent: Intent?) = binder @@ -87,20 +66,37 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService(), CoroutineScope { override fun onStarted(reservation: WifiManager.LocalOnlyHotspotReservation?) { if (reservation == null) onFailed(-2) else { this@LocalOnlyHotspotService.reservation = reservation - if (!receiverRegistered) { - val configuration = binder.configuration!! - if (Build.VERSION.SDK_INT < 30 && configuration.isAutoShutdownEnabled) { - timeoutMonitor = TetherTimeoutMonitor(configuration.shutdownTimeoutMillis, - coroutineContext) { reservation.close() } + val configuration = binder.configuration!! + if (Build.VERSION.SDK_INT < 30 && configuration.isAutoShutdownEnabled) { + timeoutMonitor = TetherTimeoutMonitor(configuration.shutdownTimeoutMillis, + coroutineContext) { reservation.close() } + } + // based on: https://android.googlesource.com/platform/packages/services/Car/+/df5cd06/service/src/com/android/car/CarProjectionService.java#160 + val sticky = registerReceiver(null, IntentFilter(WifiApManager.WIFI_AP_STATE_CHANGED_ACTION))!! + val apState = sticky.getIntExtra(WifiApManager.EXTRA_WIFI_AP_STATE, + WifiApManager.WIFI_AP_STATE_DISABLED) + val iface = sticky.getStringExtra(WifiApManager.EXTRA_WIFI_AP_INTERFACE_NAME) + if (apState != WifiApManager.WIFI_AP_STATE_ENABLED || iface.isNullOrEmpty()) { + if (apState == WifiApManager.WIFI_AP_STATE_FAILED) { + SmartSnackbar.make(getString(R.string.tethering_temp_hotspot_failure, + WifiApManager.failureReasonLookup(sticky.getIntExtra( + WifiApManager.EXTRA_WIFI_AP_FAILURE_REASON, 0)))).show() } - registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED)) - receiverRegistered = true + return stopService() + } + binder.iface = iface + launch { + check(routingManager == null) + routingManager = RoutingManager.LocalOnly( + this@LocalOnlyHotspotService, iface).apply { start() } + IpNeighbourMonitor.registerCallback(this@LocalOnlyHotspotService) } } } override fun onStopped() { Timber.d("LOHCallback.onStopped") + reservation?.close() reservation = null } @@ -152,14 +148,10 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService(), CoroutineScope { } private fun unregisterReceiver(exit: Boolean = false) { - if (receiverRegistered) { - unregisterReceiver(receiver) - IpNeighbourMonitor.unregisterCallback(this) - if (Build.VERSION.SDK_INT >= 28) { - timeoutMonitor?.close() - timeoutMonitor = null - } - receiverRegistered = false + IpNeighbourMonitor.unregisterCallback(this) + if (Build.VERSION.SDK_INT >= 28) { + timeoutMonitor?.close() + timeoutMonitor = null } launch { routingManager?.stop() 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 86290ca2..2e039e13 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt @@ -160,11 +160,11 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), } override fun onStateChanged(state: Int, failureReason: Int) { - if (state < 10 || state > 14) { + if (state < WifiApManager.WIFI_AP_STATE_DISABLING || state > WifiApManager.WIFI_AP_STATE_FAILED) { Timber.w(Exception("Unknown state $state, $failureReason")) return } - this.failureReason = if (state == 14) failureReason else null // WIFI_AP_STATE_FAILED + this.failureReason = if (state == WifiApManager.WIFI_AP_STATE_FAILED) failureReason else null data.notifyChange() } override fun onNumClientsChanged(numClients: Int) { 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 c5f0a250..56ac2303 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/TetheringManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/TetheringManager.kt @@ -134,7 +134,7 @@ object TetheringManager { * Requires MANAGE_USB permission, unfortunately. * * Source: https://android.googlesource.com/platform/frameworks/base/+/7ca5d3a/services/usb/java/com/android/server/usb/UsbService.java#389 - * @see [startTethering]. + * @see startTethering */ @RequiresApi(24) const val TETHERING_USB = 1 @@ -142,14 +142,14 @@ object TetheringManager { * Bluetooth tethering type. * * Requires BLUETOOTH permission. - * @see [startTethering]. + * @see startTethering */ @RequiresApi(24) const val TETHERING_BLUETOOTH = 2 /** * Ncm local tethering type. * - * @see [startTethering] + * @see startTethering */ @RequiresApi(30) const val TETHERING_NCM = 4 @@ -157,7 +157,7 @@ object TetheringManager { * Ethernet tethering type. * * Requires MANAGE_USB permission, also. - * @see [startTethering] + * @see startTethering */ @RequiresApi(30) const val TETHERING_ETHERNET = 5 diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt index b3ef3aa2..811f9d2c 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt @@ -59,6 +59,87 @@ object WifiApManager { @get:RequiresApi(30) val isApMacRandomizationSupported get() = apMacRandomizationSupported(Services.wifi) as Boolean + /** + * Broadcast intent action indicating that Wi-Fi AP has been enabled, disabled, + * enabling, disabling, or failed. + */ + const val WIFI_AP_STATE_CHANGED_ACTION = "android.net.wifi.WIFI_AP_STATE_CHANGED" + /** + * The lookup key for an int that indicates whether Wi-Fi AP is enabled, + * disabled, enabling, disabling, or failed. Retrieve it with [Intent.getIntExtra]. + * + * @see WIFI_AP_STATE_DISABLED + * @see WIFI_AP_STATE_DISABLING + * @see WIFI_AP_STATE_ENABLED + * @see WIFI_AP_STATE_ENABLING + * @see WIFI_AP_STATE_FAILED + */ + const val EXTRA_WIFI_AP_STATE = "wifi_state" + /** + * An extra containing the int error code for Soft AP start failure. + * Can be obtained from the [WIFI_AP_STATE_CHANGED_ACTION] using [Intent.getIntExtra]. + * This extra will only be attached if [EXTRA_WIFI_AP_STATE] is + * attached and is equal to [WIFI_AP_STATE_FAILED]. + * + * The error code will be one of: + * {@link #SAP_START_FAILURE_GENERAL}, + * {@link #SAP_START_FAILURE_NO_CHANNEL}, + * {@link #SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION} + * + * Source: https://android.googlesource.com/platform/frameworks/base/+/android-6.0.0_r1/wifi/java/android/net/wifi/WifiManager.java#210 + */ + @get:RequiresApi(23) + val EXTRA_WIFI_AP_FAILURE_REASON get() = + if (Build.VERSION.SDK_INT >= 30) "android.net.wifi.extra.WIFI_AP_FAILURE_REASON" else "wifi_ap_error_code" + /** + * The lookup key for a String extra that stores the interface name used for the Soft AP. + * This extra is included in the broadcast [WIFI_AP_STATE_CHANGED_ACTION]. + * Retrieve its value with [Intent.getStringExtra]. + * + * Source: https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r1/wifi/java/android/net/wifi/WifiManager.java#413 + */ + @get:RequiresApi(26) + val EXTRA_WIFI_AP_INTERFACE_NAME get() = + if (Build.VERSION.SDK_INT >= 30) "android.net.wifi.extra.WIFI_AP_INTERFACE_NAME" else "wifi_ap_interface_name" + /** + * Wi-Fi AP is currently being disabled. The state will change to + * [WIFI_AP_STATE_DISABLED] if it finishes successfully. + * + * @see WIFI_AP_STATE_CHANGED_ACTION + * @see #getWifiApState() + */ + const val WIFI_AP_STATE_DISABLING = 10 + /** + * Wi-Fi AP is disabled. + * + * @see WIFI_AP_STATE_CHANGED_ACTION + * @see #getWifiState() + */ + const val WIFI_AP_STATE_DISABLED = 11 + /** + * Wi-Fi AP is currently being enabled. The state will change to + * {@link #WIFI_AP_STATE_ENABLED} if it finishes successfully. + * + * @see WIFI_AP_STATE_CHANGED_ACTION + * @see #getWifiApState() + */ + const val WIFI_AP_STATE_ENABLING = 12 + /** + * Wi-Fi AP is enabled. + * + * @see WIFI_AP_STATE_CHANGED_ACTION + * @see #getWifiApState() + */ + const val WIFI_AP_STATE_ENABLED = 13 + /** + * Wi-Fi AP is in a failed state. This state will occur when an error occurs during + * enabling or disabling + * + * @see WIFI_AP_STATE_CHANGED_ACTION + * @see #getWifiApState() + */ + const val WIFI_AP_STATE_FAILED = 14 + private val getWifiApConfiguration by lazy { WifiManager::class.java.getDeclaredMethod("getWifiApConfiguration") } @Suppress("DEPRECATION") private val setWifiApConfiguration by lazy { @@ -88,7 +169,7 @@ object WifiApManager { /** * Called when soft AP state changes. * - * @param state the new AP state. One of {@link #WIFI_AP_STATE_DISABLED}, + * @param state the new AP state. One of [WIFI_AP_STATE_DISABLED], * {@link #WIFI_AP_STATE_DISABLING}, {@link #WIFI_AP_STATE_ENABLED}, * {@link #WIFI_AP_STATE_ENABLING}, {@link #WIFI_AP_STATE_FAILED} * @param failureReason reason when in failed state. One of @@ -146,7 +227,7 @@ object WifiApManager { @RequiresApi(30) fun onBlockedClientConnecting(client: Parcelable, blockedReason: Int) { } } - @RequiresApi(28) + @RequiresApi(23) val failureReasonLookup = ConstantLookup("SAP_START_FAILURE_", "GENERAL", "NO_CHANNEL") @get:RequiresApi(30) val clientBlockLookup by lazy { ConstantLookup("SAP_CLIENT_") } From 2bde3330b6af7cc43bfac8d557d1f7d9448659c4 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 11 Jun 2021 02:35:11 -0400 Subject: [PATCH 083/112] Support ap error codes on Android 6-8.1 --- README.md | 16 +++++++------- .../vpnhotspot/LocalOnlyHotspotService.kt | 3 +-- .../mygod/vpnhotspot/manage/TetherManager.kt | 21 ++++++++++++++----- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 221c9d18..f9e95913 100644 --- a/README.md +++ b/README.md @@ -295,17 +295,17 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (on API 30) `Landroid/net/wifi/WifiManager$SoftApCallback;->onInfoChanged(Landroid/net/wifi/SoftApInfo;)V,sdk,system-api,test-api` * (since API 31) `Landroid/net/wifi/WifiManager$SoftApCallback;->onInfoChanged(Ljava/util/List;)V,sdk,system-api,test-api` * (since API 28) `Landroid/net/wifi/WifiManager$SoftApCallback;->onStateChanged(II)V,sdk,system-api,test-api` -* (since API 26) `Landroid/net/wifi/WifiManager;->EXTRA_WIFI_AP_FAILURE_REASON:Ljava/lang/String;,sdk,system-api,test-api` +* (since API 23) `Landroid/net/wifi/WifiManager;->EXTRA_WIFI_AP_FAILURE_REASON:Ljava/lang/String;,sdk,system-api,test-api` * (since API 26) `Landroid/net/wifi/WifiManager;->EXTRA_WIFI_AP_INTERFACE_NAME:Ljava/lang/String;,sdk,system-api,test-api` -* (since API 26) `Landroid/net/wifi/WifiManager;->EXTRA_WIFI_AP_STATE:Ljava/lang/String;,sdk,system-api,test-api` +* (since API 23) `Landroid/net/wifi/WifiManager;->EXTRA_WIFI_AP_STATE:Ljava/lang/String;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/WifiManager;->SAP_CLIENT_BLOCK_REASON_CODE_*:I,sdk,system-api,test-api` -* (since API 28) `Landroid/net/wifi/WifiManager;->SAP_START_FAILURE_*:I,sdk,system-api,test-api` -* (since API 26) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_CHANGED_ACTION:Ljava/lang/String;,sdk,system-api,test-api` -* (since API 26) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_DISABLED:I,sdk,system-api,test-api` -* (since API 26) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_DISABLING:I,sdk,system-api,test-api` +* (since API 23) `Landroid/net/wifi/WifiManager;->SAP_START_FAILURE_*:I,sdk,system-api,test-api` +* (since API 23) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_CHANGED_ACTION:Ljava/lang/String;,sdk,system-api,test-api` +* (since API 28) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_DISABLED:I,sdk,system-api,test-api` +* (since API 28) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_DISABLING:I,sdk,system-api,test-api` * (since API 26) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_ENABLED:I,sdk,system-api,test-api` -* (since API 26) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_ENABLING:I,sdk,system-api,test-api` -* (since API 26) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_FAILED:I,sdk,system-api,test-api` +* (since API 28) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_ENABLING:I,sdk,system-api,test-api` +* (since API 23) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_FAILED:I,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/WifiManager;->getSoftApConfiguration()Landroid/net/wifi/SoftApConfiguration;,sdk,system-api,test-api` * (prior to API 30) `Landroid/net/wifi/WifiManager;->getWifiApConfiguration()Landroid/net/wifi/WifiConfiguration;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/WifiManager;->isApMacRandomizationSupported()Z,sdk,system-api,test-api` diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyHotspotService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyHotspotService.kt index 1055f2f8..ee6d3352 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyHotspotService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyHotspotService.kt @@ -73,8 +73,7 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService(), CoroutineScope { } // based on: https://android.googlesource.com/platform/packages/services/Car/+/df5cd06/service/src/com/android/car/CarProjectionService.java#160 val sticky = registerReceiver(null, IntentFilter(WifiApManager.WIFI_AP_STATE_CHANGED_ACTION))!! - val apState = sticky.getIntExtra(WifiApManager.EXTRA_WIFI_AP_STATE, - WifiApManager.WIFI_AP_STATE_DISABLED) + val apState = sticky.getIntExtra(WifiApManager.EXTRA_WIFI_AP_STATE, 0) val iface = sticky.getStringExtra(WifiApManager.EXTRA_WIFI_AP_INTERFACE_NAME) if (apState != WifiApManager.WIFI_AP_STATE_ENABLED || iface.isNullOrEmpty()) { if (apState == WifiApManager.WIFI_AP_STATE_FAILED) { 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 2e039e13..738c188a 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt @@ -4,6 +4,7 @@ import android.Manifest import android.annotation.TargetApi import android.content.Context import android.content.Intent +import android.content.IntentFilter import android.content.pm.PackageManager import android.os.Build import android.os.Parcelable @@ -141,22 +142,32 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), @RequiresApi(24) class Wifi(parent: TetheringFragment) : TetherManager(parent), DefaultLifecycleObserver, WifiApManager.SoftApCallbackCompat { + private val receiver = broadcastReceiver { _, intent -> + failureReason = if (intent.getIntExtra(WifiApManager.EXTRA_WIFI_AP_STATE, 0) == + WifiApManager.WIFI_AP_STATE_FAILED) { + intent.getIntExtra(WifiApManager.EXTRA_WIFI_AP_FAILURE_REASON, 0) + } else null + data.notifyChange() + } private var failureReason: Int? = null private var numClients: Int? = null private var info = emptyList() private var capability: Parcelable? = null init { - if (Build.VERSION.SDK_INT >= 28) parent.viewLifecycleOwner.lifecycle.addObserver(this) + if (Build.VERSION.SDK_INT >= 23) parent.viewLifecycleOwner.lifecycle.addObserver(this) } - @TargetApi(28) override fun onStart(owner: LifecycleOwner) { - WifiApCommands.registerSoftApCallback(this) + if (Build.VERSION.SDK_INT < 28) { + parent.requireContext().registerReceiver(receiver, + IntentFilter(WifiApManager.WIFI_AP_STATE_CHANGED_ACTION)) + } else WifiApCommands.registerSoftApCallback(this) } - @TargetApi(28) override fun onStop(owner: LifecycleOwner) { - WifiApCommands.unregisterSoftApCallback(this) + if (Build.VERSION.SDK_INT < 28) { + parent.requireContext().unregisterReceiver(receiver) + } else WifiApCommands.unregisterSoftApCallback(this) } override fun onStateChanged(state: Int, failureReason: Int) { From e9979495e48b62e6a39f5f0544417ce4fd446b32 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 11 Jun 2021 02:41:29 -0400 Subject: [PATCH 084/112] Check wifi ap state everywhere --- README.md | 8 ++++---- .../java/be/mygod/vpnhotspot/LocalOnlyHotspotService.kt | 3 ++- .../java/be/mygod/vpnhotspot/manage/TetherManager.kt | 9 +++------ .../java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt | 9 ++++++++- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index f9e95913..0c80287a 100644 --- a/README.md +++ b/README.md @@ -301,10 +301,10 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (since API 30) `Landroid/net/wifi/WifiManager;->SAP_CLIENT_BLOCK_REASON_CODE_*:I,sdk,system-api,test-api` * (since API 23) `Landroid/net/wifi/WifiManager;->SAP_START_FAILURE_*:I,sdk,system-api,test-api` * (since API 23) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_CHANGED_ACTION:Ljava/lang/String;,sdk,system-api,test-api` -* (since API 28) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_DISABLED:I,sdk,system-api,test-api` -* (since API 28) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_DISABLING:I,sdk,system-api,test-api` -* (since API 26) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_ENABLED:I,sdk,system-api,test-api` -* (since API 28) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_ENABLING:I,sdk,system-api,test-api` +* (since API 23) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_DISABLED:I,sdk,system-api,test-api` +* (since API 23) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_DISABLING:I,sdk,system-api,test-api` +* (since API 23) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_ENABLED:I,sdk,system-api,test-api` +* (since API 23) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_ENABLING:I,sdk,system-api,test-api` * (since API 23) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_FAILED:I,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/WifiManager;->getSoftApConfiguration()Landroid/net/wifi/SoftApConfiguration;,sdk,system-api,test-api` * (prior to API 30) `Landroid/net/wifi/WifiManager;->getWifiApConfiguration()Landroid/net/wifi/WifiConfiguration;,sdk,system-api,test-api` diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyHotspotService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyHotspotService.kt index ee6d3352..95ead806 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyHotspotService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyHotspotService.kt @@ -10,6 +10,7 @@ import be.mygod.vpnhotspot.net.monitor.IpNeighbourMonitor import be.mygod.vpnhotspot.net.monitor.TetherTimeoutMonitor import be.mygod.vpnhotspot.net.wifi.SoftApConfigurationCompat.Companion.toCompat import be.mygod.vpnhotspot.net.wifi.WifiApManager +import be.mygod.vpnhotspot.net.wifi.WifiApManager.wifiApState import be.mygod.vpnhotspot.util.Services import be.mygod.vpnhotspot.util.StickyEvent1 import be.mygod.vpnhotspot.widget.SmartSnackbar @@ -73,7 +74,7 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService(), CoroutineScope { } // based on: https://android.googlesource.com/platform/packages/services/Car/+/df5cd06/service/src/com/android/car/CarProjectionService.java#160 val sticky = registerReceiver(null, IntentFilter(WifiApManager.WIFI_AP_STATE_CHANGED_ACTION))!! - val apState = sticky.getIntExtra(WifiApManager.EXTRA_WIFI_AP_STATE, 0) + val apState = sticky.wifiApState val iface = sticky.getStringExtra(WifiApManager.EXTRA_WIFI_AP_INTERFACE_NAME) if (apState != WifiApManager.WIFI_AP_STATE_ENABLED || iface.isNullOrEmpty()) { if (apState == WifiApManager.WIFI_AP_STATE_FAILED) { 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 738c188a..93caf1c3 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt @@ -27,6 +27,7 @@ import be.mygod.vpnhotspot.databinding.ListitemInterfaceBinding import be.mygod.vpnhotspot.net.TetherType import be.mygod.vpnhotspot.net.TetheringManager import be.mygod.vpnhotspot.net.wifi.* +import be.mygod.vpnhotspot.net.wifi.WifiApManager.wifiApState import be.mygod.vpnhotspot.root.WifiApCommands import be.mygod.vpnhotspot.util.* import be.mygod.vpnhotspot.widget.SmartSnackbar @@ -143,8 +144,7 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), class Wifi(parent: TetheringFragment) : TetherManager(parent), DefaultLifecycleObserver, WifiApManager.SoftApCallbackCompat { private val receiver = broadcastReceiver { _, intent -> - failureReason = if (intent.getIntExtra(WifiApManager.EXTRA_WIFI_AP_STATE, 0) == - WifiApManager.WIFI_AP_STATE_FAILED) { + failureReason = if (intent.wifiApState == WifiApManager.WIFI_AP_STATE_FAILED) { intent.getIntExtra(WifiApManager.EXTRA_WIFI_AP_FAILURE_REASON, 0) } else null data.notifyChange() @@ -171,10 +171,7 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), } override fun onStateChanged(state: Int, failureReason: Int) { - if (state < WifiApManager.WIFI_AP_STATE_DISABLING || state > WifiApManager.WIFI_AP_STATE_FAILED) { - Timber.w(Exception("Unknown state $state, $failureReason")) - return - } + if (!WifiApManager.checkWifiApState(state)) return this.failureReason = if (state == WifiApManager.WIFI_AP_STATE_FAILED) failureReason else null data.notifyChange() } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt index 811f9d2c..03e86367 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt @@ -74,7 +74,7 @@ object WifiApManager { * @see WIFI_AP_STATE_ENABLING * @see WIFI_AP_STATE_FAILED */ - const val EXTRA_WIFI_AP_STATE = "wifi_state" + private const val EXTRA_WIFI_AP_STATE = "wifi_state" /** * An extra containing the int error code for Soft AP start failure. * Can be obtained from the [WIFI_AP_STATE_CHANGED_ACTION] using [Intent.getIntExtra]. @@ -101,6 +101,13 @@ object WifiApManager { @get:RequiresApi(26) val EXTRA_WIFI_AP_INTERFACE_NAME get() = if (Build.VERSION.SDK_INT >= 30) "android.net.wifi.extra.WIFI_AP_INTERFACE_NAME" else "wifi_ap_interface_name" + + fun checkWifiApState(state: Int) = if (state < WIFI_AP_STATE_DISABLING || state > WIFI_AP_STATE_FAILED) { + Timber.w(Exception("Unknown state $state")) + false + } else true + val Intent.wifiApState get() = + getIntExtra(EXTRA_WIFI_AP_STATE, WIFI_AP_STATE_DISABLED).also { checkWifiApState(it) } /** * Wi-Fi AP is currently being disabled. The state will change to * [WIFI_AP_STATE_DISABLED] if it finishes successfully. From 0c0ac98e43103f3a91bf16e47181cf3b14457d35 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 11 Jun 2021 03:00:07 -0400 Subject: [PATCH 085/112] Fix comment links --- .../main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt index 03e86367..9c4cbd37 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt @@ -176,9 +176,8 @@ object WifiApManager { /** * Called when soft AP state changes. * - * @param state the new AP state. One of [WIFI_AP_STATE_DISABLED], - * {@link #WIFI_AP_STATE_DISABLING}, {@link #WIFI_AP_STATE_ENABLED}, - * {@link #WIFI_AP_STATE_ENABLING}, {@link #WIFI_AP_STATE_FAILED} + * @param state the new AP state. One of [WIFI_AP_STATE_DISABLED], [WIFI_AP_STATE_DISABLING], + * [WIFI_AP_STATE_ENABLED], [WIFI_AP_STATE_ENABLING], [WIFI_AP_STATE_FAILED] * @param failureReason reason when in failed state. One of * {@link #SAP_START_FAILURE_GENERAL}, * {@link #SAP_START_FAILURE_NO_CHANNEL}, From 3ae57dd9341981454494c4f561acaea6b03287bb Mon Sep 17 00:00:00 2001 From: Mygod Date: Wed, 16 Jun 2021 03:27:23 -0400 Subject: [PATCH 086/112] Hide notification when service is inactive --- .../mygod/vpnhotspot/ServiceNotification.kt | 44 ++++++++++++------- mobile/src/main/res/values-zh-rCN/strings.xml | 1 + mobile/src/main/res/values/strings.xml | 3 +- 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/ServiceNotification.kt b/mobile/src/main/java/be/mygod/vpnhotspot/ServiceNotification.kt index 6609c2c0..587a0160 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/ServiceNotification.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/ServiceNotification.kt @@ -12,25 +12,29 @@ import be.mygod.vpnhotspot.App.Companion.app import java.util.* object ServiceNotification { - private const val CHANNEL = "tethering" - private const val CHANNEL_ID = 1 + private const val CHANNEL_ACTIVE = "tethering" + private const val CHANNEL_INACTIVE = "tethering-inactive" + private const val NOTIFICATION_ID = 1 private val deviceCountsMap = WeakHashMap>() private val inactiveMap = WeakHashMap>() private val manager = app.getSystemService()!! private fun buildNotification(context: Context): Notification { - val builder = NotificationCompat.Builder(context, CHANNEL) - .setWhen(0) - .setCategory(NotificationCompat.CATEGORY_SERVICE) - .setColor(ContextCompat.getColor(context, R.color.colorPrimary)) - .setContentTitle(context.getText(R.string.notification_tethering_title)) - .setSmallIcon(R.drawable.ic_quick_settings_tile_on) - .setContentIntent(PendingIntent.getActivity(context, 0, Intent(context, MainActivity::class.java), - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) val deviceCounts = deviceCountsMap.values.flatMap { it.entries }.sortedBy { it.key } val inactive = inactiveMap.values.flatten() + val isInactive = inactive.isNotEmpty() && deviceCounts.isEmpty() + val builder = NotificationCompat.Builder(context, if (isInactive) CHANNEL_INACTIVE else CHANNEL_ACTIVE).apply { + setWhen(0) + setCategory(NotificationCompat.CATEGORY_SERVICE) + color = ContextCompat.getColor(context, R.color.colorPrimary) + setContentTitle(context.getText(R.string.notification_tethering_title)) + setSmallIcon(R.drawable.ic_quick_settings_tile_on) + setContentIntent(PendingIntent.getActivity(context, 0, Intent(context, MainActivity::class.java), + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)) + setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + priority = if (isInactive) NotificationCompat.PRIORITY_MIN else NotificationCompat.PRIORITY_LOW + } var lines = deviceCounts.map { (dev, size) -> context.resources.getQuantityString(R.plurals.notification_connected_devices, size, size, dev) } @@ -54,23 +58,29 @@ object ServiceNotification { synchronized(this) { deviceCountsMap[service] = deviceCounts if (inactive.isEmpty()) inactiveMap.remove(service) else inactiveMap[service] = inactive - service.startForeground(CHANNEL_ID, buildNotification(service)) + service.startForeground(NOTIFICATION_ID, buildNotification(service)) } } fun stopForeground(service: Service) = synchronized(this) { deviceCountsMap.remove(service) if (deviceCountsMap.isEmpty()) service.stopForeground(true) else { service.stopForeground(false) - manager.notify(CHANNEL_ID, buildNotification(service)) + manager.notify(NOTIFICATION_ID, buildNotification(service)) } } fun updateNotificationChannels() { if (Build.VERSION.SDK_INT >= 26) @TargetApi(26) { - val tethering = NotificationChannel(CHANNEL, - app.getText(R.string.notification_channel_tethering), NotificationManager.IMPORTANCE_LOW) - tethering.lockscreenVisibility = Notification.VISIBILITY_PUBLIC - manager.createNotificationChannel(tethering) + NotificationChannel(CHANNEL_ACTIVE, + app.getText(R.string.notification_channel_tethering), NotificationManager.IMPORTANCE_LOW).apply { + lockscreenVisibility = Notification.VISIBILITY_PUBLIC + manager.createNotificationChannel(this) + } + NotificationChannel(CHANNEL_INACTIVE, + app.getText(R.string.notification_channel_monitor), NotificationManager.IMPORTANCE_LOW).apply { + lockscreenVisibility = Notification.VISIBILITY_PUBLIC + manager.createNotificationChannel(this) + } // remove old service channels manager.deleteNotificationChannel("hotspot") manager.deleteNotificationChannel("repeater") diff --git a/mobile/src/main/res/values-zh-rCN/strings.xml b/mobile/src/main/res/values-zh-rCN/strings.xml index e727ef85..fb3af0f6 100644 --- a/mobile/src/main/res/values-zh-rCN/strings.xml +++ b/mobile/src/main/res/values-zh-rCN/strings.xml @@ -152,6 +152,7 @@ VPN 共享已启用 VPN 共享服务 + 监视不活跃接口 %d 个设备已连接到 %s diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml index 4505061f..fe9c1dbd 100644 --- a/mobile/src/main/res/values/strings.xml +++ b/mobile/src/main/res/values/strings.xml @@ -170,8 +170,9 @@ Restart this app to apply this setting. Exit - VPN tethering active + VPN tethering VPN Tethering Service + Monitor Inactive Interfaces %d device connected to %s %d devices connected to %s From 4733b294cfa3ffc1585538169e3724b61627db6d Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 18 Jun 2021 02:23:08 -0400 Subject: [PATCH 087/112] Fix removing monitored interface --- .../vpnhotspot/manage/TetheringFragment.kt | 38 +++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) 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 ba2e52af..b799f9f8 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt @@ -64,10 +64,13 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick } private val wifiManagerLegacy by lazy { TetherManager.WifiLegacy(this@TetheringFragment) } - private var enabledIfaces = emptyList() + var activeIfaces = emptyList() + var localOnlyIfaces = emptyList() + var erroredIfaces = emptyList() private var listDeferred = CompletableDeferred>(emptyList()) - private fun updateEnabledTypes() { - this@TetheringFragment.enabledTypes = enabledIfaces.map { TetherType.ofInterface(it) }.toSet() + fun updateEnabledTypes() { + this@TetheringFragment.enabledTypes = + (activeIfaces + localOnlyIfaces).map { TetherType.ofInterface(it) }.toSet() } val lastErrors = mutableMapOf() @@ -75,20 +78,16 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick if (error == 0) lastErrors.remove(ifName) else lastErrors[ifName] = error } - suspend fun notifyInterfaceChanged(lastList: List? = null) { - @Suppress("NAME_SHADOWING") val lastList = lastList ?: listDeferred.await() - val first = lastList.indexOfFirst { it is InterfaceManager } - if (first >= 0) notifyItemRangeChanged(first, lastList.indexOfLast { it is InterfaceManager } - first + 1) - } suspend fun notifyTetherTypeChanged() { updateEnabledTypes() val lastList = listDeferred.await() - notifyInterfaceChanged(lastList) - val first = lastList.indexOfLast { it !is TetherManager } + 1 + var first = lastList.indexOfFirst { it is InterfaceManager } + if (first >= 0) notifyItemRangeChanged(first, lastList.indexOfLast { it is InterfaceManager } - first + 1) + first = lastList.indexOfLast { it !is TetherManager } + 1 notifyItemRangeChanged(first, lastList.size - first) } - fun update(activeIfaces: List, localOnlyIfaces: List, erroredIfaces: List) { + fun update() { val deferred = CompletableDeferred>() listDeferred = deferred ifaceLookup = try { @@ -97,8 +96,6 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick Timber.d(e) emptyMap() } - enabledIfaces = activeIfaces + localOnlyIfaces - updateEnabledTypes() val list = ArrayList() if (Services.p2p != null) list.add(repeaterManager) @@ -151,9 +148,12 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick var binder: TetheringService.Binder? = null private val adapter = ManagerAdapter() private val receiver = broadcastReceiver { _, intent -> - adapter.update(intent.tetheredIfaces ?: return@broadcastReceiver, - intent.localOnlyTetheredIfaces ?: return@broadcastReceiver, - intent.getStringArrayListExtra(TetheringManager.EXTRA_ERRORED_TETHER) ?: return@broadcastReceiver) + adapter.activeIfaces = intent.tetheredIfaces ?: return@broadcastReceiver + adapter.localOnlyIfaces = intent.localOnlyTetheredIfaces ?: return@broadcastReceiver + adapter.erroredIfaces = intent.getStringArrayListExtra(TetheringManager.EXTRA_ERRORED_TETHER) + ?: return@broadcastReceiver + adapter.updateEnabledTypes() + adapter.update() } private fun updateMonitorList(canMonitor: List = emptyList()) { @@ -260,7 +260,7 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick binding.interfaces.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false) binding.interfaces.itemAnimator = DefaultItemAnimator() binding.interfaces.adapter = adapter - adapter.update(emptyList(), emptyList(), emptyList()) + adapter.update() ServiceForegroundConnector(this, this, TetheringService::class) (activity as MainActivity).binding.toolbar.apply { inflateMenu(R.menu.toolbar_tethering) @@ -284,9 +284,7 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick override fun onServiceConnected(name: ComponentName?, service: IBinder?) { binder = service as TetheringService.Binder - service.routingsChanged[this] = { - lifecycleScope.launchWhenStarted { adapter.notifyInterfaceChanged() } - } + service.routingsChanged[this] = { lifecycleScope.launchWhenStarted { adapter.update() } } requireContext().registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED)) if (Build.VERSION.SDK_INT >= 30) { TetheringManager.registerTetheringEventCallback(null, adapter) From 26759753dd5ec967d4d315e368ca873e8bcc2409 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 10 Jul 2021 19:33:12 -0400 Subject: [PATCH 088/112] Fix snackbar being behind dialog --- .../java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt index db3bf1b7..2307253c 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt @@ -16,6 +16,7 @@ import android.view.View import android.widget.AdapterView import android.widget.ArrayAdapter import android.widget.Spinner +import android.widget.Toast import androidx.annotation.RequiresApi import androidx.appcompat.app.AlertDialog import androidx.appcompat.widget.Toolbar @@ -33,7 +34,6 @@ import be.mygod.vpnhotspot.net.monitor.TetherTimeoutMonitor import be.mygod.vpnhotspot.util.QRCodeDialog import be.mygod.vpnhotspot.util.readableMessage import be.mygod.vpnhotspot.util.showAllowingStateLoss -import be.mygod.vpnhotspot.widget.SmartSnackbar import kotlinx.parcelize.Parcelize import timber.log.Timber @@ -390,7 +390,7 @@ class WifiApDialogFragment : AlertDialogFragment { From f7a2507e797d4da5c1093a25ad59db0f5f935410 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 10 Jul 2021 20:37:33 -0400 Subject: [PATCH 089/112] Add section headers to wifi ap config dialog --- mobile/build.gradle.kts | 2 +- .../net/wifi/WifiApDialogFragment.kt | 31 +- mobile/src/main/res/layout/dialog_wifi_ap.xml | 320 ++++++++++-------- mobile/src/main/res/values-zh-rCN/strings.xml | 2 + mobile/src/main/res/values/strings.xml | 2 + mobile/src/main/res/values/styles.xml | 11 + 6 files changed, 206 insertions(+), 162 deletions(-) diff --git a/mobile/build.gradle.kts b/mobile/build.gradle.kts index 836bce8e..ac996760 100644 --- a/mobile/build.gradle.kts +++ b/mobile/build.gradle.kts @@ -83,7 +83,7 @@ dependencies { implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") implementation("com.android.billingclient:billing-ktx:4.0.0") implementation("com.google.android.gms:play-services-oss-licenses:17.0.0") - implementation("com.google.android.material:material:1.4.0") + implementation("com.google.android.material:material:1.5.0-alpha01") implementation("com.google.firebase:firebase-analytics-ktx:19.0.0") implementation("com.google.firebase:firebase-crashlytics:18.1.0") implementation("com.google.zxing:core:3.4.1") diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt index 2307253c..4a280766 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt @@ -177,31 +177,24 @@ class WifiApDialogFragment : AlertDialogFragment= 23 || arg.p2pMode) { dialogView.band2G.configure(channels2G) dialogView.band5G.configure(currentChannels5G) - } else { - dialogView.bandWrapper2G.isGone = true - dialogView.bandWrapper5G.isGone = true - } - if (Build.VERSION.SDK_INT >= 30 && !arg.p2pMode) dialogView.band6G.configure(channels6G) - else dialogView.bandWrapper6G.isGone = true - if (BuildCompat.isAtLeastS() && !arg.p2pMode) dialogView.band60G.configure(channels60G) - else dialogView.bandWrapper60G.isGone = true - dialogView.bssid.addTextChangedListener(this@WifiApDialogFragment) - if (arg.p2pMode) dialogView.hiddenSsid.isGone = true - if (arg.p2pMode || Build.VERSION.SDK_INT < 30) { - dialogView.maxClientWrapper.isGone = true - dialogView.clientUserControl.isGone = true - dialogView.blockedListWrapper.isGone = true - dialogView.allowedListWrapper.isGone = true - } else { + if (Build.VERSION.SDK_INT >= 30 && !arg.p2pMode) dialogView.band6G.configure(channels6G) + else dialogView.bandWrapper6G.isGone = true + if (BuildCompat.isAtLeastS() && !arg.p2pMode) dialogView.band60G.configure(channels60G) else { + dialogView.bandWrapper60G.isGone = true + dialogView.bridgedMode.isGone = true + dialogView.bridgedModeOpportunisticShutdown.isGone = true + } + } else dialogView.bandGroup.isGone = true + if (!arg.p2pMode && Build.VERSION.SDK_INT >= 30) { dialogView.maxClient.addTextChangedListener(this@WifiApDialogFragment) dialogView.blockedList.addTextChangedListener(this@WifiApDialogFragment) dialogView.allowedList.addTextChangedListener(this@WifiApDialogFragment) - } + } else dialogView.accessControlGroup.isGone = true + dialogView.bssid.addTextChangedListener(this@WifiApDialogFragment) + if (arg.p2pMode) dialogView.hiddenSsid.isGone = true if (arg.p2pMode && Build.VERSION.SDK_INT >= 29) dialogView.macRandomization.isEnabled = false else if (arg.p2pMode || !BuildCompat.isAtLeastS()) dialogView.macRandomization.isGone = true if (arg.p2pMode || !BuildCompat.isAtLeastS()) { - dialogView.bridgedMode.isGone = true - dialogView.bridgedModeOpportunisticShutdown.isGone = true dialogView.ieee80211ax.isGone = true dialogView.userConfig.isGone = true } diff --git a/mobile/src/main/res/layout/dialog_wifi_ap.xml b/mobile/src/main/res/layout/dialog_wifi_ap.xml index 650b82a5..cff941e6 100644 --- a/mobile/src/main/res/layout/dialog_wifi_ap.xml +++ b/mobile/src/main/res/layout/dialog_wifi_ap.xml @@ -121,15 +121,25 @@ android:inputType="number" android:maxLength="19" /> + + + - - + + + + + + + + + + + + - + + + + + android:text="@string/wifi_client_user_control" /> + + + + + + + + + + + + - + android:text="@string/wifi_mac_randomization" /> + + + - - - - - - - - - - - - - - - - - - - diff --git a/mobile/src/main/res/values-zh-rCN/strings.xml b/mobile/src/main/res/values-zh-rCN/strings.xml index fb3af0f6..1e7be6c6 100644 --- a/mobile/src/main/res/values-zh-rCN/strings.xml +++ b/mobile/src/main/res/values-zh-rCN/strings.xml @@ -185,6 +185,8 @@ "5 GHz 频段" 6 GHz 频段 60 GHz 频段 + 访问控制 + 高级接入点设置 "MAC 地址" "隐藏的网络" 允许连接设备数上限 diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml index fe9c1dbd..0fbfb5fc 100644 --- a/mobile/src/main/res/values/strings.xml +++ b/mobile/src/main/res/values/strings.xml @@ -208,6 +208,8 @@ 5 GHz Band 6 GHz Band 60 GHz Band + Access Control + Advanced AP Options MAC address Hidden network Maximum number of clients diff --git a/mobile/src/main/res/values/styles.xml b/mobile/src/main/res/values/styles.xml index 013d57fa..8f249d97 100644 --- a/mobile/src/main/res/values/styles.xml +++ b/mobile/src/main/res/values/styles.xml @@ -43,5 +43,16 @@ 4dip 18sp + + From 556ae9eb45af6e748e8ea668dbdbfed3d190831f Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 10 Jul 2021 22:28:39 -0400 Subject: [PATCH 090/112] v2.12.4 --- mobile/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/build.gradle.kts b/mobile/build.gradle.kts index ac996760..476ace50 100644 --- a/mobile/build.gradle.kts +++ b/mobile/build.gradle.kts @@ -24,8 +24,8 @@ android { minSdk = 21 if (targetSdk == 31) targetSdkPreview = "S" else this.targetSdk = targetSdk resourceConfigurations.addAll(arrayOf("it", "ru", "zh-rCN", "zh-rTW")) - versionCode = 274 - versionName = "2.12.3" + versionCode = 275 + versionName = "2.12.4" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" javaCompileOptions.annotationProcessorOptions.arguments.apply { put("room.expandProjection", "true") From 1280df3d0cab6c809a1a737dc936aab1a8616bc3 Mon Sep 17 00:00:00 2001 From: Mygod Date: Wed, 14 Jul 2021 21:47:14 -0400 Subject: [PATCH 091/112] Update to compile against API 31 --- build.gradle.kts | 2 +- gradle.properties | 1 - mobile/build.gradle.kts | 6 +++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 9a4b13e0..eb5f95ad 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,7 +9,7 @@ buildscript { } dependencies { - classpath(kotlin("gradle-plugin", "1.5.20")) + classpath(kotlin("gradle-plugin", "1.5.21")) classpath("com.android.tools.build:gradle:7.0.0-beta05") classpath("com.google.firebase:firebase-crashlytics-gradle:2.7.1") classpath("com.google.android.gms:oss-licenses-plugin:0.10.4") diff --git a/gradle.properties b/gradle.properties index feb18146..6fb3e361 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,7 +13,6 @@ android.databinding.incremental=true android.enableJetifier=true android.enableR8.fullMode=true android.enableResourceOptimizations=false -android.injected.testOnly=false android.useAndroidX=true kapt.incremental.apt=true org.gradle.jvmargs=-Xmx1536m diff --git a/mobile/build.gradle.kts b/mobile/build.gradle.kts index 476ace50..ca30bc95 100644 --- a/mobile/build.gradle.kts +++ b/mobile/build.gradle.kts @@ -11,18 +11,18 @@ plugins { android { val javaVersion = JavaVersion.VERSION_1_8 val targetSdk = 29 - buildToolsVersion = "31.0.0-rc5" + buildToolsVersion = "31.0.0" compileOptions { isCoreLibraryDesugaringEnabled = true sourceCompatibility = javaVersion targetCompatibility = javaVersion } - compileSdkPreview = "android-S" + compileSdk = 31 kotlinOptions.jvmTarget = javaVersion.toString() defaultConfig { applicationId = "be.mygod.vpnhotspot" minSdk = 21 - if (targetSdk == 31) targetSdkPreview = "S" else this.targetSdk = targetSdk + this.targetSdk = targetSdk resourceConfigurations.addAll(arrayOf("it", "ru", "zh-rCN", "zh-rTW")) versionCode = 275 versionName = "2.12.4" From 0c735ce92fc8212b5d193a3e5c78a00e74b0fc1f Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 24 Jul 2021 20:15:11 -0400 Subject: [PATCH 092/112] Update private API commit --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0c80287a..82a59b9e 100644 --- a/README.md +++ b/README.md @@ -147,7 +147,7 @@ _a.k.a. things that can go wrong if this app doesn't work._ This is a list of stuff that might impact this app's functionality if unavailable. This is only meant to be an index. You can read more in the source code. -API restrictions are updated up to [commit `ebe7044`](https://android.googlesource.com/platform/prebuilts/runtime/+/ebe7044/appcompat/hiddenapi-flags.csv). +API restrictions are updated up to [SHA-256 checksum `156715dfa705a048926dca876d731d72604df32e8bcac055af32866b50bc2cc8`](https://dl.google.com/developers/android/sc/non-sdk/hiddenapi-flags.csv). Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded or implicitly used) @@ -283,8 +283,8 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (since API 30) `Landroid/net/wifi/SoftApInfo;->CHANNEL_WIDTH_*:I,sdk,system-api,test-api` * (on API 30) `Landroid/net/wifi/SoftApInfo;->CHANNEL_WIDTH_INVALID:I,sdk,system-api,test-api` * (since API 31) `Landroid/net/wifi/SoftApInfo;->getAutoShutdownTimeoutMillis()J,sdk,system-api,test-api` -* (since API 31) `Landroid/net/wifi/SoftApInfo;->getBssid()Landroid/net/MacAddress;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApInfo;->getBandwidth()I,sdk,system-api,test-api` +* (since API 31) `Landroid/net/wifi/SoftApInfo;->getBssid()Landroid/net/MacAddress;,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApInfo;->getFrequency()I,sdk,system-api,test-api` * (since API 31) `Landroid/net/wifi/SoftApInfo;->getWifiStandard()I,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/WifiClient;->getMacAddress()Landroid/net/MacAddress;,sdk,system-api,test-api` From 37eeeb43248045e83fec0511ef01132579e2a326 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 24 Jul 2021 20:26:51 -0400 Subject: [PATCH 093/112] Update dependencies --- build.gradle.kts | 2 +- mobile/build.gradle.kts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index eb5f95ad..5a06bcdf 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -10,7 +10,7 @@ buildscript { dependencies { classpath(kotlin("gradle-plugin", "1.5.21")) - classpath("com.android.tools.build:gradle:7.0.0-beta05") + classpath("com.android.tools.build:gradle:7.0.0-rc01") classpath("com.google.firebase:firebase-crashlytics-gradle:2.7.1") classpath("com.google.android.gms:oss-licenses-plugin:0.10.4") classpath("com.google.gms:google-services:4.3.8") diff --git a/mobile/build.gradle.kts b/mobile/build.gradle.kts index ca30bc95..80bc2356 100644 --- a/mobile/build.gradle.kts +++ b/mobile/build.gradle.kts @@ -70,11 +70,11 @@ dependencies { coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.5") kapt("androidx.room:room-compiler:$roomVersion") implementation(kotlin("stdlib-jdk8")) - implementation("androidx.appcompat:appcompat:1.3.0") // https://issuetracker.google.com/issues/151603528 + implementation("androidx.appcompat:appcompat:1.3.1") // https://issuetracker.google.com/issues/151603528 implementation("androidx.browser:browser:1.3.0") implementation("androidx.core:core-ktx:1.6.0") implementation("androidx.emoji:emoji:1.1.0") - implementation("androidx.fragment:fragment-ktx:1.3.5") + implementation("androidx.fragment:fragment-ktx:1.3.6") implementation("androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion") implementation("androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion") implementation("androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion") @@ -85,7 +85,7 @@ dependencies { implementation("com.google.android.gms:play-services-oss-licenses:17.0.0") implementation("com.google.android.material:material:1.5.0-alpha01") implementation("com.google.firebase:firebase-analytics-ktx:19.0.0") - implementation("com.google.firebase:firebase-crashlytics:18.1.0") + implementation("com.google.firebase:firebase-crashlytics:18.2.0") implementation("com.google.zxing:core:3.4.1") implementation("com.jakewharton.timber:timber:4.7.1") implementation("com.linkedin.dexmaker:dexmaker:2.28.1") From b03303c73ac3f77191a79a56979ddaa4236d9d32 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 24 Jul 2021 20:33:30 -0400 Subject: [PATCH 094/112] Drop support for old Android 12 betas --- .../vpnhotspot/client/ClientViewModel.kt | 6 +-- .../vpnhotspot/manage/BluetoothTethering.kt | 3 +- .../mygod/vpnhotspot/manage/TetherManager.kt | 11 ++--- .../net/wifi/SoftApConfigurationCompat.kt | 46 +++++++++---------- .../net/wifi/WifiApDialogFragment.kt | 11 ++--- .../vpnhotspot/net/wifi/WifiApManager.kt | 2 +- .../mygod/vpnhotspot/root/WifiApCommands.kt | 4 +- 7 files changed, 40 insertions(+), 43 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientViewModel.kt b/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientViewModel.kt index 53a848dd..69d08aad 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientViewModel.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientViewModel.kt @@ -4,10 +4,10 @@ import android.content.ComponentName import android.content.IntentFilter import android.content.ServiceConnection import android.net.wifi.p2p.WifiP2pDevice +import android.os.Build import android.os.IBinder import android.os.Parcelable import androidx.annotation.RequiresApi -import androidx.core.os.BuildCompat import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.MutableLiveData @@ -87,10 +87,10 @@ class ClientViewModel : ViewModel(), ServiceConnection, IpNeighbourMonitor.Callb override fun onStart(owner: LifecycleOwner) { app.registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED)) IpNeighbourMonitor.registerCallback(this, false) - if (BuildCompat.isAtLeastS()) WifiApCommands.registerSoftApCallback(this) + if (Build.VERSION.SDK_INT >= 31) WifiApCommands.registerSoftApCallback(this) } override fun onStop(owner: LifecycleOwner) { - if (BuildCompat.isAtLeastS()) WifiApCommands.unregisterSoftApCallback(this) + if (Build.VERSION.SDK_INT >= 31) WifiApCommands.unregisterSoftApCallback(this) IpNeighbourMonitor.unregisterCallback(this) app.unregisterReceiver(receiver) } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt index 1dce98e2..bbb9e823 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt @@ -12,7 +12,6 @@ import android.content.IntentFilter import android.os.Build import androidx.annotation.RequiresApi import androidx.core.content.getSystemService -import androidx.core.os.BuildCompat import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.net.TetheringManager import be.mygod.vpnhotspot.util.broadcastReceiver @@ -91,7 +90,7 @@ class BluetoothTethering(context: Context, val stateListener: () -> Unit) : check(adapter.getProfileProxy(context, this, PAN)) proxyCreated = true } catch (e: SecurityException) { - if (BuildCompat.isAtLeastS()) Timber.d(e.readableMessage) else Timber.w(e) + if (Build.VERSION.SDK_INT >= 31) Timber.d(e.readableMessage) else Timber.w(e) activeFailureCause = e } } 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 93caf1c3..2b9ee1ff 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt @@ -15,7 +15,6 @@ import android.view.View import android.widget.Toast import androidx.annotation.RequiresApi import androidx.core.net.toUri -import androidx.core.os.BuildCompat import androidx.core.view.updatePaddingRelative import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner @@ -198,7 +197,7 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), val numClients = numClients val maxClients = capability.maxSupportedClients var features = capability.supportedFeatures - if (BuildCompat.isAtLeastS()) for ((flag, band) in arrayOf( + if (Build.VERSION.SDK_INT >= 31) for ((flag, band) in arrayOf( SoftApCapability.SOFTAP_FEATURE_BAND_24G_SUPPORTED to SoftApConfigurationCompat.BAND_2GHZ, SoftApCapability.SOFTAP_FEATURE_BAND_5G_SUPPORTED to SoftApConfigurationCompat.BAND_5GHZ, SoftApCapability.SOFTAP_FEATURE_BAND_6G_SUPPORTED to SoftApConfigurationCompat.BAND_6GHZ, @@ -214,7 +213,7 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), R.string.tethering_manage_wifi_feature_ap_mac_randomization)) if (Services.wifi.isStaApConcurrencySupported) yield(parent.getText( R.string.tethering_manage_wifi_feature_sta_ap_concurrency)) - if (BuildCompat.isAtLeastS()) { + if (Build.VERSION.SDK_INT >= 31) { if (Services.wifi.isBridgedApConcurrencySupported) yield(parent.getText( R.string.tethering_manage_wifi_feature_bridged_ap_concurrency)) if (Services.wifi.isStaBridgedApConcurrencySupported) yield(parent.getText( @@ -228,7 +227,7 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), }.joinToSpanned().let { if (it.isEmpty()) parent.getText(R.string.tethering_manage_wifi_no_features) else it }) - if (BuildCompat.isAtLeastS()) { + if (Build.VERSION.SDK_INT >= 31) { val list = SoftApConfigurationCompat.BAND_TYPES.map { band -> val channels = capability.getSupportedChannelList(band) if (channels.isNotEmpty()) StringBuilder().apply { @@ -272,7 +271,7 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), val frequency = info.frequency val channel = SoftApConfigurationCompat.frequencyToChannel(frequency) val bandwidth = SoftApInfo.channelWidthLookup(info.bandwidth, true) - if (BuildCompat.isAtLeastS()) { + if (Build.VERSION.SDK_INT >= 31) { var bssid = makeMacSpan(info.bssid.toString()) info.apInstanceIdentifier?.let { // take the fast route if possible bssid = if (bssid is String) "$bssid%$it" else SpannableStringBuilder(bssid).append("%$it") @@ -315,7 +314,7 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), onTetheringStarted() // force flush } override fun onResume(owner: LifecycleOwner) { - if (!BuildCompat.isAtLeastS() || parent.requireContext().checkSelfPermission( + if (Build.VERSION.SDK_INT < 31 || parent.requireContext().checkSelfPermission( Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED) { tethering.ensureInit(parent.requireContext()) } else if (parent.shouldShowRequestPermissionRationale(Manifest.permission.BLUETOOTH_CONNECT)) { diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt index 97b18594..fc4ee751 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt @@ -8,7 +8,6 @@ import android.os.Build import android.os.Parcelable import android.util.SparseIntArray import androidx.annotation.RequiresApi -import androidx.core.os.BuildCompat import be.mygod.vpnhotspot.net.MacAddressCompat import be.mygod.vpnhotspot.net.MacAddressCompat.Companion.toCompat import be.mygod.vpnhotspot.net.monitor.TetherTimeoutMonitor @@ -64,7 +63,7 @@ data class SoftApConfigurationCompat( const val BAND_60GHZ = 8 const val BAND_LEGACY = BAND_2GHZ or BAND_5GHZ val BAND_TYPES by lazy { - if (BuildCompat.isAtLeastS()) try { + if (Build.VERSION.SDK_INT >= 31) try { return@lazy UnblockCentral.SoftApConfiguration_BAND_TYPES } catch (e: ReflectiveOperationException) { Timber.w(e) @@ -314,25 +313,26 @@ data class SoftApConfigurationCompat( @RequiresApi(30) @Suppress("UNCHECKED_CAST") fun SoftApConfiguration.toCompat() = SoftApConfigurationCompat( - ssid, - bssid?.toCompat()?.addr, - passphrase, - isHiddenSsid, - if (BuildCompat.isAtLeastS()) getChannels(this) as SparseIntArray else SparseIntArray(1).also { - it.append(getBand(this) as Int, getChannel(this) as Int) - }, - securityType, - getMaxNumberOfClients(this) as Int, - isAutoShutdownEnabled(this) as Boolean, - getShutdownTimeoutMillis(this) as Long, - isClientControlByUserEnabled(this) as Boolean, - getBlockedClientList(this) as List, - getAllowedClientList(this) as List, - if (BuildCompat.isAtLeastS()) getMacRandomizationSetting(this) as Int else RANDOMIZATION_PERSISTENT, - !BuildCompat.isAtLeastS() || isBridgedModeOpportunisticShutdownEnabled(this) as Boolean, - !BuildCompat.isAtLeastS() || isIeee80211axEnabled(this) as Boolean, - !BuildCompat.isAtLeastS() || isUserConfiguration(this) as Boolean, - this) + ssid, + bssid?.toCompat()?.addr, + passphrase, + isHiddenSsid, + if (Build.VERSION.SDK_INT >= 31) getChannels(this) as SparseIntArray else SparseIntArray(1).also { + it.append(getBand(this) as Int, getChannel(this) as Int) + }, + securityType, + getMaxNumberOfClients(this) as Int, + isAutoShutdownEnabled(this) as Boolean, + getShutdownTimeoutMillis(this) as Long, + isClientControlByUserEnabled(this) as Boolean, + getBlockedClientList(this) as List, + getAllowedClientList(this) as List, + if (Build.VERSION.SDK_INT >= 31) getMacRandomizationSetting(this) as Int else RANDOMIZATION_PERSISTENT, + Build.VERSION.SDK_INT < 31 || isBridgedModeOpportunisticShutdownEnabled(this) as Boolean, + Build.VERSION.SDK_INT < 31 || isIeee80211axEnabled(this) as Boolean, + Build.VERSION.SDK_INT < 31 || isUserConfiguration(this) as Boolean, + this, + ) } @Suppress("DEPRECATION") @@ -438,7 +438,7 @@ data class SoftApConfigurationCompat( setSsid(builder, ssid) setPassphrase(builder, if (securityType == SoftApConfiguration.SECURITY_TYPE_OPEN) null else passphrase, securityType) - if (BuildCompat.isAtLeastS()) setChannels(builder, channels) else { + if (Build.VERSION.SDK_INT >= 31) setChannels(builder, channels) else { val (band, channel) = requireSingleBand() if (channel == 0) setBand(builder, band) else setChannel(builder, channel, band) } @@ -450,7 +450,7 @@ data class SoftApConfigurationCompat( setHiddenSsid(builder, isHiddenSsid) setAllowedClientList(builder, allowedClientList) setBlockedClientList(builder, blockedClientList) - if (BuildCompat.isAtLeastS()) { + if (Build.VERSION.SDK_INT >= 31) { setMacRandomizationSetting(builder, macRandomizationSetting) setBridgedModeOpportunisticShutdownEnabled(builder, isBridgedModeOpportunisticShutdownEnabled) setIeee80211axEnabled(builder, isIeee80211axEnabled) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt index 4a280766..0f68d590 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt @@ -20,7 +20,6 @@ import android.widget.Toast import androidx.annotation.RequiresApi import androidx.appcompat.app.AlertDialog import androidx.appcompat.widget.Toolbar -import androidx.core.os.BuildCompat import androidx.core.view.isGone import be.mygod.librootkotlinx.toByteArray import be.mygod.librootkotlinx.toParcelable @@ -117,7 +116,7 @@ class WifiApDialogFragment : AlertDialogFragment= 0) channels.append(band, channel) } - if (!arg.p2pMode && BuildCompat.isAtLeastS() && dialogView.bridgedMode.isChecked) { + if (!arg.p2pMode && Build.VERSION.SDK_INT >= 31 && dialogView.bridgedMode.isChecked) { this.channels = channels } else optimizeChannels(channels) } @@ -179,7 +178,7 @@ class WifiApDialogFragment : AlertDialogFragment= 30 && !arg.p2pMode) dialogView.band6G.configure(channels6G) else dialogView.bandWrapper6G.isGone = true - if (BuildCompat.isAtLeastS() && !arg.p2pMode) dialogView.band60G.configure(channels60G) else { + if (Build.VERSION.SDK_INT >= 31 && !arg.p2pMode) dialogView.band60G.configure(channels60G) else { dialogView.bandWrapper60G.isGone = true dialogView.bridgedMode.isGone = true dialogView.bridgedModeOpportunisticShutdown.isGone = true @@ -193,8 +192,8 @@ class WifiApDialogFragment : AlertDialogFragment= 29) dialogView.macRandomization.isEnabled = false - else if (arg.p2pMode || !BuildCompat.isAtLeastS()) dialogView.macRandomization.isGone = true - if (arg.p2pMode || !BuildCompat.isAtLeastS()) { + else if (arg.p2pMode || Build.VERSION.SDK_INT < 31) dialogView.macRandomization.isGone = true + if (arg.p2pMode || Build.VERSION.SDK_INT < 31) { dialogView.ieee80211ax.isGone = true dialogView.userConfig.isGone = true } @@ -302,7 +301,7 @@ class WifiApDialogFragment : AlertDialogFragment option5G is ChannelOption.Disabled } } - Build.VERSION.SDK_INT == 30 && !BuildCompat.isAtLeastS() -> { + Build.VERSION.SDK_INT == 30 -> { var expected = 1 var set = 0 for (s in arrayOf(dialogView.band2G, dialogView.band5G, dialogView.band6G)) when (s.selectedItem) { diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt index 5bef6e2d..0a83397f 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt @@ -287,7 +287,7 @@ object WifiApManager { if (noArgs != 1) Timber.w("Unexpected args for $name: ${args?.contentToString()}") val arg = args!![0] if (arg is List<*>) { - if (!BuildCompat.isAtLeastS()) Timber.w(Exception("Unexpected onInfoChanged API 31+")) + if (Build.VERSION.SDK_INT < 31) Timber.w(Exception("Unexpected onInfoChanged API 31+")) @Suppress("UNCHECKED_CAST") callback.onInfoChanged(arg as List) } else { diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt b/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt index e05e7f10..e7db4c6d 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/root/WifiApCommands.kt @@ -2,9 +2,9 @@ package be.mygod.vpnhotspot.root import android.annotation.TargetApi import android.content.ClipData +import android.os.Build import android.os.Parcelable import androidx.annotation.RequiresApi -import androidx.core.os.BuildCompat import be.mygod.librootkotlinx.ParcelableBoolean import be.mygod.librootkotlinx.RootCommand import be.mygod.librootkotlinx.RootCommandChannel @@ -129,7 +129,7 @@ object WifiApCommands { val client = WifiClient(parcel.client) val macAddress = client.macAddress var name = macAddress.toString() - if (BuildCompat.isAtLeastS()) client.apInstanceIdentifier?.let { name += "%$it" } + if (Build.VERSION.SDK_INT >= 31) client.apInstanceIdentifier?.let { name += "%$it" } val reason = WifiApManager.clientBlockLookup(parcel.blockedReason, true) Timber.i("$name blocked from connecting: $reason (${parcel.blockedReason})") SmartSnackbar.make(app.getString(R.string.tethering_manage_wifi_client_blocked, name, reason)).apply { From 403f7c0631b25ea2b66f012256fa3a562b676cd7 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 24 Jul 2021 20:36:02 -0400 Subject: [PATCH 095/112] Make sure the notification is cancelled --- .../main/java/be/mygod/vpnhotspot/ServiceNotification.kt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/ServiceNotification.kt b/mobile/src/main/java/be/mygod/vpnhotspot/ServiceNotification.kt index 587a0160..cabea3aa 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/ServiceNotification.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/ServiceNotification.kt @@ -62,11 +62,9 @@ object ServiceNotification { } } fun stopForeground(service: Service) = synchronized(this) { - deviceCountsMap.remove(service) - if (deviceCountsMap.isEmpty()) service.stopForeground(true) else { - service.stopForeground(false) - manager.notify(NOTIFICATION_ID, buildNotification(service)) - } + val shutdown = deviceCountsMap.remove(service) != null && deviceCountsMap.isEmpty() + service.stopForeground(shutdown) + if (shutdown) manager.cancel(NOTIFICATION_ID) else manager.notify(NOTIFICATION_ID, buildNotification(service)) } fun updateNotificationChannels() { From dd1aced71c4381630cc9e08689f942424457170e Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 24 Jul 2021 21:18:27 -0400 Subject: [PATCH 096/112] setIncludeOtherUidNetworks --- .../vpnhotspot/net/monitor/DefaultNetworkMonitor.kt | 9 +++++---- .../mygod/vpnhotspot/net/monitor/InterfaceMonitor.kt | 3 ++- .../mygod/vpnhotspot/net/monitor/UpstreamMonitor.kt | 10 ---------- .../be/mygod/vpnhotspot/net/monitor/VpnMonitor.kt | 9 +++++---- .../src/main/java/be/mygod/vpnhotspot/util/Utils.kt | 12 +++++++++--- 5 files changed, 21 insertions(+), 22 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/DefaultNetworkMonitor.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/DefaultNetworkMonitor.kt index 9f0739fd..6cbe6661 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/DefaultNetworkMonitor.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/DefaultNetworkMonitor.kt @@ -9,6 +9,7 @@ import android.os.Build import android.os.Handler import android.os.Looper import be.mygod.vpnhotspot.util.Services +import be.mygod.vpnhotspot.util.globalNetworkRequestBuilder import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch @@ -20,10 +21,10 @@ object DefaultNetworkMonitor : UpstreamMonitor() { * Unfortunately registerDefaultNetworkCallback is going to return VPN interface since Android P DP1: * https://android.googlesource.com/platform/frameworks/base/+/dda156ab0c5d66ad82bdcf76cda07cbc0a9c8a2e */ - private val networkRequest = networkRequestBuilder() - .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) - .build() + private val networkRequest = globalNetworkRequestBuilder().apply { + addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) + }.build() private val networkCallback = object : ConnectivityManager.NetworkCallback() { override fun onAvailable(network: Network) { val properties = Services.connectivity.getLinkProperties(network) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/InterfaceMonitor.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/InterfaceMonitor.kt index 41b17b2b..e9c9913a 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/InterfaceMonitor.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/InterfaceMonitor.kt @@ -6,6 +6,7 @@ import android.net.Network import android.net.NetworkCapabilities import be.mygod.vpnhotspot.util.Services import be.mygod.vpnhotspot.util.allInterfaceNames +import be.mygod.vpnhotspot.util.globalNetworkRequestBuilder import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import timber.log.Timber @@ -18,7 +19,7 @@ class InterfaceMonitor(private val ifaceRegex: String) : UpstreamMonitor() { Timber.d(e); { it == ifaceRegex } } - private val request = networkRequestBuilder().apply { + private val request = globalNetworkRequestBuilder().apply { removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/UpstreamMonitor.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/UpstreamMonitor.kt index c8629496..9f872c43 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/UpstreamMonitor.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/UpstreamMonitor.kt @@ -2,9 +2,6 @@ package be.mygod.vpnhotspot.net.monitor import android.content.SharedPreferences import android.net.LinkProperties -import android.net.NetworkCapabilities -import android.net.NetworkRequest -import android.os.Build import be.mygod.vpnhotspot.App.Companion.app import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch @@ -23,13 +20,6 @@ abstract class UpstreamMonitor { } private var monitor = generateMonitor() - fun networkRequestBuilder() = NetworkRequest.Builder().apply { - if (Build.VERSION.SDK_INT == 23) { // workarounds for OEM bugs - removeCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) - removeCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL) - } - } - fun registerCallback(callback: Callback) = synchronized(this) { monitor.registerCallback(callback) } fun unregisterCallback(callback: Callback) = synchronized(this) { monitor.unregisterCallback(callback) } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/VpnMonitor.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/VpnMonitor.kt index 03ef2575..880ba10b 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/VpnMonitor.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/VpnMonitor.kt @@ -5,15 +5,16 @@ import android.net.LinkProperties import android.net.Network import android.net.NetworkCapabilities import be.mygod.vpnhotspot.util.Services +import be.mygod.vpnhotspot.util.globalNetworkRequestBuilder import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import timber.log.Timber object VpnMonitor : UpstreamMonitor() { - private val request = networkRequestBuilder() - .addTransportType(NetworkCapabilities.TRANSPORT_VPN) - .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) - .build() + private val request = globalNetworkRequestBuilder().apply { + addTransportType(NetworkCapabilities.TRANSPORT_VPN) + removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) + }.build() private var registered = false private val available = HashMap() diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt b/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt index 5b6d4049..6d24d6b2 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt @@ -4,9 +4,7 @@ import android.annotation.SuppressLint import android.annotation.TargetApi import android.content.* import android.content.res.Resources -import android.net.InetAddresses -import android.net.LinkProperties -import android.net.RouteInfo +import android.net.* import android.os.Build import android.os.RemoteException import android.system.ErrnoException @@ -238,6 +236,14 @@ fun InvocationHandler.callSuper(interfaceClass: Class<*>, proxy: Any, method: Me } } +fun globalNetworkRequestBuilder() = NetworkRequest.Builder().apply { + if (Build.VERSION.SDK_INT >= 31) setIncludeOtherUidNetworks(true) else if (Build.VERSION.SDK_INT == 23) { + // workarounds for OEM bugs + removeCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) + removeCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL) + } +} + @Suppress("FunctionName") fun if_nametoindex(ifname: String) = if (Build.VERSION.SDK_INT >= 26) { Os.if_nametoindex(ifname) From cb417c8c2340a6d9b63a7a4d32059d39f8956850 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 24 Jul 2021 21:35:44 -0400 Subject: [PATCH 097/112] Monitor network list instead --- .../vpnhotspot/SettingsPreferenceFragment.kt | 21 ++--- ...ompleteEditTextPreferenceDialogFragment.kt | 42 ---------- ...CompleteNetworkPreferenceDialogFragment.kt | 78 +++++++++++++++++++ 3 files changed, 86 insertions(+), 55 deletions(-) delete mode 100644 mobile/src/main/java/be/mygod/vpnhotspot/preference/AlwaysAutoCompleteEditTextPreferenceDialogFragment.kt create mode 100644 mobile/src/main/java/be/mygod/vpnhotspot/preference/AutoCompleteNetworkPreferenceDialogFragment.kt diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/SettingsPreferenceFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/SettingsPreferenceFragment.kt index 5d3372b0..cef0e46a 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/SettingsPreferenceFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/SettingsPreferenceFragment.kt @@ -14,13 +14,12 @@ import be.mygod.vpnhotspot.net.monitor.FallbackUpstreamMonitor import be.mygod.vpnhotspot.net.monitor.IpMonitor import be.mygod.vpnhotspot.net.monitor.UpstreamMonitor import be.mygod.vpnhotspot.net.wifi.WifiDoubleLock -import be.mygod.vpnhotspot.preference.AlwaysAutoCompleteEditTextPreferenceDialogFragment +import be.mygod.vpnhotspot.preference.AutoCompleteNetworkPreferenceDialogFragment import be.mygod.vpnhotspot.preference.SharedPreferenceDataStore import be.mygod.vpnhotspot.preference.SummaryFallbackProvider import be.mygod.vpnhotspot.root.Dump import be.mygod.vpnhotspot.root.RootManager import be.mygod.vpnhotspot.util.Services -import be.mygod.vpnhotspot.util.allInterfaceNames import be.mygod.vpnhotspot.util.launchUrl import be.mygod.vpnhotspot.util.showAllowingStateLoss import be.mygod.vpnhotspot.widget.SmartSnackbar @@ -144,16 +143,12 @@ class SettingsPreferenceFragment : PreferenceFragmentCompat() { } } - override fun onDisplayPreferenceDialog(preference: Preference) { - when (preference.key) { - UpstreamMonitor.KEY, FallbackUpstreamMonitor.KEY -> - AlwaysAutoCompleteEditTextPreferenceDialogFragment().apply { - setArguments(preference.key, Services.connectivity.allNetworks.mapNotNull { - Services.connectivity.getLinkProperties(it)?.allInterfaceNames - }.flatten().toTypedArray()) - setTargetFragment(this@SettingsPreferenceFragment, 0) - }.showAllowingStateLoss(parentFragmentManager, preference.key) - else -> super.onDisplayPreferenceDialog(preference) - } + override fun onDisplayPreferenceDialog(preference: Preference) = when (preference.key) { + UpstreamMonitor.KEY, FallbackUpstreamMonitor.KEY -> + AutoCompleteNetworkPreferenceDialogFragment().apply { + setArguments(preference.key) + setTargetFragment(this@SettingsPreferenceFragment, 0) + }.showAllowingStateLoss(parentFragmentManager, preference.key) + else -> super.onDisplayPreferenceDialog(preference) } } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/preference/AlwaysAutoCompleteEditTextPreferenceDialogFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/preference/AlwaysAutoCompleteEditTextPreferenceDialogFragment.kt deleted file mode 100644 index c1fce475..00000000 --- a/mobile/src/main/java/be/mygod/vpnhotspot/preference/AlwaysAutoCompleteEditTextPreferenceDialogFragment.kt +++ /dev/null @@ -1,42 +0,0 @@ -package be.mygod.vpnhotspot.preference - -import android.content.Context -import android.view.View -import android.view.ViewGroup -import android.widget.ArrayAdapter -import androidx.core.os.bundleOf -import androidx.preference.EditTextPreferenceDialogFragmentCompat -import be.mygod.vpnhotspot.R -import be.mygod.vpnhotspot.widget.AlwaysAutoCompleteEditText - -class AlwaysAutoCompleteEditTextPreferenceDialogFragment : EditTextPreferenceDialogFragmentCompat() { - companion object { - private const val ARG_SUGGESTIONS = "suggestions" - } - - fun setArguments(key: String, suggestions: Array) { - arguments = bundleOf(ARG_KEY to key, ARG_SUGGESTIONS to suggestions) - } - - private lateinit var editText: AlwaysAutoCompleteEditText - - override fun onCreateDialogView(context: Context) = super.onCreateDialogView(context).apply { - editText = AlwaysAutoCompleteEditText(context).apply { - id = android.R.id.edit - minHeight = resources.getDimensionPixelSize(R.dimen.touch_target_min) - } - val oldEditText = findViewById(android.R.id.edit)!! - val container = oldEditText.parent as ViewGroup - container.removeView(oldEditText) - container.addView(editText, oldEditText.layoutParams) - } - - override fun onBindDialogView(view: View) { - super.onBindDialogView(view) - editText.hint = (preference.summaryProvider as SummaryFallbackProvider).fallback - arguments?.getStringArray(ARG_SUGGESTIONS)?.let { suggestions -> - editText.setAdapter(ArrayAdapter(view.context, android.R.layout.select_dialog_item, suggestions)) - } - editText.clearFocus() // having focus is buggy currently - } -} diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/preference/AutoCompleteNetworkPreferenceDialogFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/preference/AutoCompleteNetworkPreferenceDialogFragment.kt new file mode 100644 index 00000000..a2effe05 --- /dev/null +++ b/mobile/src/main/java/be/mygod/vpnhotspot/preference/AutoCompleteNetworkPreferenceDialogFragment.kt @@ -0,0 +1,78 @@ +package be.mygod.vpnhotspot.preference + +import android.content.Context +import android.net.ConnectivityManager +import android.net.LinkProperties +import android.net.Network +import android.net.NetworkCapabilities +import android.view.View +import android.view.ViewGroup +import android.widget.ArrayAdapter +import androidx.core.os.bundleOf +import androidx.preference.EditTextPreferenceDialogFragmentCompat +import be.mygod.vpnhotspot.R +import be.mygod.vpnhotspot.util.Services +import be.mygod.vpnhotspot.util.allInterfaceNames +import be.mygod.vpnhotspot.util.globalNetworkRequestBuilder +import be.mygod.vpnhotspot.widget.AlwaysAutoCompleteEditText + +class AutoCompleteNetworkPreferenceDialogFragment : EditTextPreferenceDialogFragmentCompat() { + fun setArguments(key: String) { + arguments = bundleOf(ARG_KEY to key) + } + + private lateinit var editText: AlwaysAutoCompleteEditText + private lateinit var adapter: ArrayAdapter + private fun updateAdapter() { + adapter.clear() + adapter.addAll(interfaceNames.flatMap { it.value }) + } + + private val interfaceNames = mutableMapOf>() + private val callback = object : ConnectivityManager.NetworkCallback() { + override fun onLinkPropertiesChanged(network: Network, properties: LinkProperties) { + interfaceNames[network] = properties.allInterfaceNames + updateAdapter() + } + + override fun onLost(network: Network) { + interfaceNames.remove(network) + updateAdapter() + } + } + + override fun onCreateDialogView(context: Context) = super.onCreateDialogView(context).apply { + editText = AlwaysAutoCompleteEditText(context).apply { + id = android.R.id.edit + minHeight = resources.getDimensionPixelSize(R.dimen.touch_target_min) + } + val oldEditText = findViewById(android.R.id.edit)!! + val container = oldEditText.parent as ViewGroup + container.removeView(oldEditText) + container.addView(editText, oldEditText.layoutParams) + } + + override fun onBindDialogView(view: View) { + super.onBindDialogView(view) + editText.hint = (preference.summaryProvider as SummaryFallbackProvider).fallback + adapter = ArrayAdapter(view.context, android.R.layout.select_dialog_item) + editText.setAdapter(adapter) + editText.clearFocus() // having focus is buggy currently + } + + override fun onStart() { + super.onStart() + Services.connectivity.registerNetworkCallback(globalNetworkRequestBuilder().apply { + removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) + removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) + removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) + }.build(), callback) + } + + override fun onStop() { + Services.connectivity.unregisterNetworkCallback(callback) + interfaceNames.clear() + updateAdapter() + super.onStop() + } +} From ad2a45597bf264f639d7db0ae14a0ef936c274e6 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 24 Jul 2021 21:42:42 -0400 Subject: [PATCH 098/112] Band needs to be set --- .../be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt index 0f68d590..07588eed 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt @@ -210,7 +210,7 @@ class WifiApDialogFragment : AlertDialogFragment 0 } private fun populateFromConfiguration() { dialogView.ssid.setText(base.ssid) @@ -310,10 +311,7 @@ class WifiApDialogFragment : AlertDialogFragment { - setBridgedMode() - true - } + else -> setBridgedMode() } dialogView.bssidWrapper.error = null val bssidValid = dialogView.bssid.length() == 0 || try { From c7d53db7e3fc8b1ebd16af9b49e8e3cb76fbeb0f Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 24 Jul 2021 21:43:54 -0400 Subject: [PATCH 099/112] Fix missing 60G --- .../java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt index 07588eed..8b4b412c 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApDialogFragment.kt @@ -213,7 +213,8 @@ class WifiApDialogFragment : AlertDialogFragment auto = 1 !is ChannelOption.Disabled -> ++set } From 9652804f9a5d1a21c113fe53b359e0ed39d5cd16 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 24 Jul 2021 22:50:01 -0400 Subject: [PATCH 100/112] Fix bssid might be null --- .../be/mygod/vpnhotspot/manage/TetherManager.kt | 14 +++++++++----- .../be/mygod/vpnhotspot/net/wifi/SoftApInfo.kt | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) 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 2b9ee1ff..4cb41497 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt @@ -272,15 +272,19 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), val channel = SoftApConfigurationCompat.frequencyToChannel(frequency) val bandwidth = SoftApInfo.channelWidthLookup(info.bandwidth, true) if (Build.VERSION.SDK_INT >= 31) { - var bssid = makeMacSpan(info.bssid.toString()) - info.apInstanceIdentifier?.let { // take the fast route if possible - bssid = if (bssid is String) "$bssid%$it" else SpannableStringBuilder(bssid).append("%$it") - } + val bssid = info.bssid.let { if (it == null) null else makeMacSpan(it.toString()) } + val bssidAp = info.apInstanceIdentifier?.let { + when (bssid) { + null -> it + is String -> "$bssid%$it" // take the fast route if possible + else -> SpannableStringBuilder(bssid).append("%$it") + } + } ?: bssid ?: "?" val timeout = info.autoShutdownTimeoutMillis parent.getText(if (timeout == 0L) { R.string.tethering_manage_wifi_info_timeout_disabled } else R.string.tethering_manage_wifi_info_timeout_enabled).format(locale, - frequency, channel, bandwidth, bssid, info.wifiStandard, + frequency, channel, bandwidth, bssidAp, info.wifiStandard, // http://unicode.org/cldr/trac/ticket/3407 DateUtils.formatElapsedTime(timeout / 1000)) } else parent.getText(R.string.tethering_manage_wifi_info).format(locale, diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApInfo.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApInfo.kt index ccec77cf..c218f0bf 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApInfo.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApInfo.kt @@ -30,7 +30,7 @@ value class SoftApInfo(val inner: Parcelable) { val frequency get() = getFrequency(inner) as Int val bandwidth get() = getBandwidth(inner) as Int @get:RequiresApi(31) - val bssid get() = getBssid(inner) as MacAddress + val bssid get() = getBssid(inner) as MacAddress? @get:RequiresApi(31) val wifiStandard get() = getWifiStandard(inner) as Int @get:RequiresApi(31) From 37df32880dbb6712aecd4082e81e639e9ba504c2 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 24 Jul 2021 22:58:04 -0400 Subject: [PATCH 101/112] More descriptive errors in p2p supplicant --- .../vpnhotspot/net/wifi/P2pSupplicantConfiguration.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/P2pSupplicantConfiguration.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/P2pSupplicantConfiguration.kt index 00f22e56..d291ac87 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/P2pSupplicantConfiguration.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/P2pSupplicantConfiguration.kt @@ -75,7 +75,13 @@ class P2pSupplicantConfiguration(private val group: WifiP2pGroup? = null) { if (matchedBssid.isEmpty()) { check(block.pskLine == null && block.psk == null) if (match.groups[5] != null) { - block.psk = match.groupValues[5].apply { check(length in 8..63) } + block.psk = match.groupValues[5].apply { + when (length) { + in 8..63 -> { } + 64 -> error("WPA-PSK hex not supported") + else -> error("Unknown length $length") + } + } } block.pskLine = block.size } else if (bssids.any { matchedBssid.equals(it, true) }) { From 0c36bcbade50ccc3a14ad26a76460da08d07305b Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 24 Jul 2021 23:29:50 -0400 Subject: [PATCH 102/112] Prevent Binder stub swallowing Exceptions --- .../main/java/be/mygod/vpnhotspot/net/TetheringManager.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 56ac2303..932eecbd 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/TetheringManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/TetheringManager.kt @@ -66,7 +66,11 @@ object TetheringManager { } private object InPlaceExecutor : Executor { - override fun execute(command: Runnable) = command.run() + override fun execute(command: Runnable) = try { + command.run() + } catch (e: Exception) { + Timber.w(e) // prevent Binder stub swallowing the exception + } } /** From 1d6fe3def10cff25b5a63ca58864309f9805c1fa Mon Sep 17 00:00:00 2001 From: Mygod Date: Sun, 25 Jul 2021 00:27:50 -0400 Subject: [PATCH 103/112] Make InvocationHandler more robust --- .../be/mygod/vpnhotspot/net/TetherType.kt | 4 +- .../mygod/vpnhotspot/net/TetheringManager.kt | 53 ++++++---------- .../vpnhotspot/net/wifi/SoftApCapability.kt | 2 +- .../mygod/vpnhotspot/net/wifi/SoftApInfo.kt | 2 +- .../vpnhotspot/net/wifi/WifiApManager.kt | 62 +++++++------------ .../mygod/vpnhotspot/net/wifi/WifiClient.kt | 2 +- .../net/wifi/WifiP2pManagerHelper.kt | 7 +-- .../java/be/mygod/vpnhotspot/util/Utils.kt | 8 +++ 8 files changed, 58 insertions(+), 82 deletions(-) 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 18b6ef72..aad28004 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/TetherType.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/TetherType.kt @@ -71,9 +71,9 @@ enum class TetherType(@DrawableRes val icon: Int) { } @RequiresApi(30) - override fun onTetherableInterfaceRegexpsChanged(args: Array?) = synchronized(this) { + override fun onTetherableInterfaceRegexpsChanged(reg: Any?) = synchronized(this) { if (requiresUpdate) return@synchronized - Timber.i("onTetherableInterfaceRegexpsChanged: ${args?.contentDeepToString()}") + Timber.i("onTetherableInterfaceRegexpsChanged: $reg") TetheringManager.unregisterTetheringEventCallback(this) requiresUpdate = true listener() 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 932eecbd..dd9da97c 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/TetheringManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/TetheringManager.kt @@ -251,13 +251,12 @@ object TetheringManager { val proxy = ProxyBuilder.forClass(classOnStartTetheringCallback).apply { dexCache(cacheDir) handler { proxy, method, args -> - if (args.isNotEmpty()) Timber.w("Unexpected args for ${method.name}: $args") @Suppress("NAME_SHADOWING") val callback = reference.get() - when (method.name) { - "onTetheringStarted" -> callback?.onTetheringStarted() - "onTetheringFailed" -> callback?.onTetheringFailed() - else -> ProxyBuilder.callSuper(proxy, method, args) + if (args.isEmpty()) when (method.name) { + "onTetheringStarted" -> return@handler callback?.onTetheringStarted() + "onTetheringFailed" -> return@handler callback?.onTetheringFailed() } + ProxyBuilder.callSuper(proxy, method, args) } }.build() startTetheringLegacy(Services.connectivity, type, showProvisioningUi, proxy, handler) @@ -279,15 +278,9 @@ object TetheringManager { arrayOf(interfaceStartTetheringCallback), object : InvocationHandler { override fun invoke(proxy: Any, method: Method, args: Array?): Any? { @Suppress("NAME_SHADOWING") val callback = reference.get() - return when (val name = method.name) { - "onTetheringStarted" -> { - if (!args.isNullOrEmpty()) Timber.w("Unexpected args for $name: $args") - callback?.onTetheringStarted() - } - "onTetheringFailed" -> { - if (args?.size != 1) Timber.w("Unexpected args for $name: $args") - callback?.onTetheringFailed(args?.get(0) as Int) - } + return when { + method.matches("onTetheringStarted") -> callback?.onTetheringStarted() + method.matches1("onTetheringFailed") -> callback?.onTetheringFailed(args?.get(0) as Int) else -> callSuper(interfaceStartTetheringCallback, proxy, method, args) } } @@ -449,7 +442,7 @@ object TetheringManager { * *@param reg The new regular expressions. * @hide */ - fun onTetherableInterfaceRegexpsChanged(args: Array?) {} + fun onTetherableInterfaceRegexpsChanged(reg: Any?) {} /** * Called when there was a change in the list of tetherable interfaces. Tetherable @@ -545,40 +538,34 @@ object TetheringManager { override fun invoke(proxy: Any, method: Method, args: Array?): Any? { @Suppress("NAME_SHADOWING") val callback = reference.get() - val noArgs = args?.size ?: 0 - return when (val name = method.name) { - "onTetheringSupported" -> { - if (noArgs != 1) Timber.w("Unexpected args for $name: $args") + return when { + method.matches1("onTetheringSupported") -> { callback?.onTetheringSupported(args!![0] as Boolean) } - "onUpstreamChanged" -> { - if (noArgs != 1) Timber.w("Unexpected args for $name: $args") + method.matches1("onUpstreamChanged") -> { callback?.onUpstreamChanged(args!![0] as Network?) } - "onTetherableInterfaceRegexpsChanged" -> { - if (regexpsSent) callback?.onTetherableInterfaceRegexpsChanged(args) + method.name == "onTetherableInterfaceRegexpsChanged" && + method.parameters.singleOrNull()?.type?.name == + "android.net.TetheringManager\$TetheringInterfaceRegexps" -> { + if (regexpsSent) callback?.onTetherableInterfaceRegexpsChanged(args!!.single()) regexpsSent = true } - "onTetherableInterfacesChanged" -> { - if (noArgs != 1) Timber.w("Unexpected args for $name: $args") + method.matches1>("onTetherableInterfacesChanged") -> { @Suppress("UNCHECKED_CAST") callback?.onTetherableInterfacesChanged(args!![0] as List) } - "onTetheredInterfacesChanged" -> { - if (noArgs != 1) Timber.w("Unexpected args for $name: $args") + method.matches1>("onTetheredInterfacesChanged") -> { @Suppress("UNCHECKED_CAST") callback?.onTetheredInterfacesChanged(args!![0] as List) } - "onError" -> { - if (noArgs != 2) Timber.w("Unexpected args for $name: $args") + method.matches2("onError") -> { callback?.onError(args!![0] as String, args[1] as Int) } - "onClientsChanged" -> { - if (noArgs != 1) Timber.w("Unexpected args for $name: $args") + method.matches1>("onClientsChanged") -> { callback?.onClientsChanged(args!![0] as Collection<*>) } - "onOffloadStatusChanged" -> { - if (noArgs != 1) Timber.w("Unexpected args for $name: $args") + method.matches1("onOffloadStatusChanged") -> { callback?.onOffloadStatusChanged(args!![0] as Int) } else -> callSuper(interfaceTetheringEventCallback, proxy, method, args) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApCapability.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApCapability.kt index 24432405..dbed4cc0 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApCapability.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApCapability.kt @@ -8,7 +8,7 @@ import be.mygod.vpnhotspot.util.LongConstantLookup @RequiresApi(30) value class SoftApCapability(val inner: Parcelable) { companion object { - private val clazz by lazy { Class.forName("android.net.wifi.SoftApCapability") } + val clazz by lazy { Class.forName("android.net.wifi.SoftApCapability") } private val getMaxSupportedClients by lazy { clazz.getDeclaredMethod("getMaxSupportedClients") } private val areFeaturesSupported by lazy { clazz.getDeclaredMethod("areFeaturesSupported", Long::class.java) } @get:RequiresApi(31) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApInfo.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApInfo.kt index c218f0bf..167534d5 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApInfo.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApInfo.kt @@ -12,7 +12,7 @@ import timber.log.Timber @RequiresApi(30) value class SoftApInfo(val inner: Parcelable) { companion object { - private val clazz by lazy { Class.forName("android.net.wifi.SoftApInfo") } + val clazz by lazy { Class.forName("android.net.wifi.SoftApInfo") } private val getFrequency by lazy { clazz.getDeclaredMethod("getFrequency") } private val getBandwidth by lazy { clazz.getDeclaredMethod("getBandwidth") } @get:RequiresApi(31) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt index 0a83397f..46438958 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt @@ -10,13 +10,9 @@ import android.os.Build import android.os.Handler import android.os.Parcelable import androidx.annotation.RequiresApi -import androidx.core.os.BuildCompat import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.net.wifi.SoftApConfigurationCompat.Companion.toCompat -import be.mygod.vpnhotspot.util.ConstantLookup -import be.mygod.vpnhotspot.util.Services -import be.mygod.vpnhotspot.util.callSuper -import be.mygod.vpnhotspot.util.findIdentifier +import be.mygod.vpnhotspot.util.* import timber.log.Timber import java.lang.reflect.InvocationHandler import java.lang.reflect.Method @@ -260,55 +256,41 @@ object WifiApManager { } else invokeActual(proxy, method, args) private fun invokeActual(proxy: Any, method: Method, args: Array?): Any? { - val noArgs = args?.size ?: 0 - return when (val name = method.name) { - "onStateChanged" -> { - if (noArgs != 2) Timber.w("Unexpected args for $name: ${args?.contentToString()}") + return when { + method.matches2("onStateChanged") -> { callback.onStateChanged(args!![0] as Int, args[1] as Int) } - "onNumClientsChanged" -> @Suppress("DEPRECATION") { + method.matches1("onNumClientsChanged") -> @Suppress("DEPRECATION") { if (Build.VERSION.SDK_INT >= 30) Timber.w(Exception("Unexpected onNumClientsChanged")) - if (noArgs != 1) Timber.w("Unexpected args for $name: ${args?.contentToString()}") callback.onNumClientsChanged(args!![0] as Int) } - "onConnectedClientsChanged" -> @TargetApi(30) { + method.matches1>("onConnectedClientsChanged") -> @TargetApi(30) { if (Build.VERSION.SDK_INT < 30) Timber.w(Exception("Unexpected onConnectedClientsChanged")) @Suppress("UNCHECKED_CAST") - when (noArgs) { - 1 -> callback.onConnectedClientsChanged(args!![0] as List) - 2 -> null // we use the old method which returns all clients in one call - else -> { - Timber.w("Unexpected args for $name: ${args?.contentToString()}") - null - } - } + callback.onConnectedClientsChanged(args!![0] as List) } - "onInfoChanged" -> @TargetApi(30) { - if (noArgs != 1) Timber.w("Unexpected args for $name: ${args?.contentToString()}") + method.matches1>("onInfoChanged") -> @TargetApi(31) { + if (Build.VERSION.SDK_INT < 31) Timber.w(Exception("Unexpected onInfoChanged API 31+")) + @Suppress("UNCHECKED_CAST") + callback.onInfoChanged(args!![0] as List) + } + method.matches("onInfoChanged", SoftApInfo.clazz) -> @TargetApi(30) { + when (Build.VERSION.SDK_INT) { + 30 -> { } + in 31..Int.MAX_VALUE -> return null // ignore old version calls + else -> Timber.w(Exception("Unexpected onInfoChanged API 30")) + } val arg = args!![0] - if (arg is List<*>) { - if (Build.VERSION.SDK_INT < 31) Timber.w(Exception("Unexpected onInfoChanged API 31+")) - @Suppress("UNCHECKED_CAST") - callback.onInfoChanged(arg as List) - } else { - when (Build.VERSION.SDK_INT) { - 30 -> { } - in 31..Int.MAX_VALUE -> return null // ignore old version calls - else -> Timber.w(Exception("Unexpected onInfoChanged API 30")) - } - val info = SoftApInfo(arg as Parcelable) - callback.onInfoChanged( // check for legacy empty info with CHANNEL_WIDTH_INVALID - if (info.frequency == 0 && info.bandwidth == 0) emptyList() else listOf(arg)) - } + val info = SoftApInfo(arg as Parcelable) + callback.onInfoChanged( // check for legacy empty info with CHANNEL_WIDTH_INVALID + if (info.frequency == 0 && info.bandwidth == 0) emptyList() else listOf(arg)) } - "onCapabilityChanged" -> @TargetApi(30) { + method.matches("onCapabilityChanged", SoftApCapability.clazz) -> @TargetApi(30) { if (Build.VERSION.SDK_INT < 30) Timber.w(Exception("Unexpected onCapabilityChanged")) - if (noArgs != 1) Timber.w("Unexpected args for $name: ${args?.contentToString()}") callback.onCapabilityChanged(args!![0] as Parcelable) } - "onBlockedClientConnecting" -> @TargetApi(30) { + method.matches("onBlockedClientConnecting", WifiClient.clazz, Int::class.java) -> @TargetApi(30) { if (Build.VERSION.SDK_INT < 30) Timber.w(Exception("Unexpected onBlockedClientConnecting")) - if (noArgs != 2) Timber.w("Unexpected args for $name: ${args?.contentToString()}") callback.onBlockedClientConnecting(args!![0] as Parcelable, args[1] as Int) } else -> callSuper(interfaceSoftApCallback, proxy, method, args) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiClient.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiClient.kt index 57edc1a3..b106db9d 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiClient.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiClient.kt @@ -11,7 +11,7 @@ import timber.log.Timber @RequiresApi(30) value class WifiClient(val inner: Parcelable) { companion object { - private val clazz by lazy { Class.forName("android.net.wifi.WifiClient") } + val clazz by lazy { Class.forName("android.net.wifi.WifiClient") } private val getMacAddress by lazy { clazz.getDeclaredMethod("getMacAddress") } @get:RequiresApi(31) private val getApInstanceIdentifier by lazy @TargetApi(31) { UnblockCentral.getApInstanceIdentifier(clazz) } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiP2pManagerHelper.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiP2pManagerHelper.kt index 2f26d7a3..f95c87d9 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiP2pManagerHelper.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiP2pManagerHelper.kt @@ -9,8 +9,8 @@ import androidx.annotation.RequiresApi import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.net.MacAddressCompat import be.mygod.vpnhotspot.util.callSuper +import be.mygod.vpnhotspot.util.matches1 import kotlinx.coroutines.CompletableDeferred -import timber.log.Timber import java.lang.reflect.InvocationHandler import java.lang.reflect.Method import java.lang.reflect.Proxy @@ -117,9 +117,8 @@ object WifiP2pManagerHelper { val result = CompletableDeferred>() requestPersistentGroupInfo(this, c, Proxy.newProxyInstance(interfacePersistentGroupInfoListener.classLoader, arrayOf(interfacePersistentGroupInfoListener), object : InvocationHandler { - override fun invoke(proxy: Any, method: Method, args: Array?): Any? = when (method.name) { - "onPersistentGroupInfoAvailable" -> { - if (args?.size != 1) Timber.w(IllegalArgumentException("Unexpected args: $args")) + override fun invoke(proxy: Any, method: Method, args: Array?): Any? = when { + method.matches1>("onPersistentGroupInfoAvailable") -> { @Suppress("UNCHECKED_CAST") result.complete(getGroupList(args!![0]) as Collection) } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt b/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt index 6d24d6b2..49cb547f 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt @@ -53,6 +53,14 @@ fun Long.toPluralInt(): Int { return (this % 1000000000).toInt() + 1000000000 } +@RequiresApi(26) +fun Method.matches(name: String, vararg classes: Class<*>) = this.name == name && parameterCount == classes.size && + (0 until parameterCount).all { i -> parameters[i].type == classes[i] } +@RequiresApi(26) +inline fun Method.matches1(name: String) = matches(name, T::class.java) +@RequiresApi(26) +inline fun Method.matches2(name: String) = matches(name, T0::class.java, T1::class.java) + fun Context.ensureReceiverUnregistered(receiver: BroadcastReceiver) { try { unregisterReceiver(receiver) From b2a65915fd44f23db946c5edb89f56d911078871 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sun, 25 Jul 2021 00:37:05 -0400 Subject: [PATCH 104/112] Revert hide notification explicitly --- mobile/src/main/java/be/mygod/vpnhotspot/ServiceNotification.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/ServiceNotification.kt b/mobile/src/main/java/be/mygod/vpnhotspot/ServiceNotification.kt index cabea3aa..caacd93b 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/ServiceNotification.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/ServiceNotification.kt @@ -64,7 +64,7 @@ object ServiceNotification { fun stopForeground(service: Service) = synchronized(this) { val shutdown = deviceCountsMap.remove(service) != null && deviceCountsMap.isEmpty() service.stopForeground(shutdown) - if (shutdown) manager.cancel(NOTIFICATION_ID) else manager.notify(NOTIFICATION_ID, buildNotification(service)) + if (!shutdown) manager.notify(NOTIFICATION_ID, buildNotification(service)) } fun updateNotificationChannels() { From 00b869397dc2d028706a1a6fe5d323302a1e868b Mon Sep 17 00:00:00 2001 From: Mygod Date: Sun, 25 Jul 2021 01:25:30 -0400 Subject: [PATCH 105/112] Fix Kotlin stupid bug (KT-47881) --- .../java/be/mygod/vpnhotspot/net/TetheringManager.kt | 10 ++++++---- .../java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt | 4 ++-- mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt | 2 -- 3 files changed, 8 insertions(+), 8 deletions(-) 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 dd9da97c..8fc28bec 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/TetheringManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/TetheringManager.kt @@ -280,7 +280,9 @@ object TetheringManager { @Suppress("NAME_SHADOWING") val callback = reference.get() return when { method.matches("onTetheringStarted") -> callback?.onTetheringStarted() - method.matches1("onTetheringFailed") -> callback?.onTetheringFailed(args?.get(0) as Int) + method.matches("onTetheringFailed", Integer.TYPE) -> { + callback?.onTetheringFailed(args?.get(0) as Int) + } else -> callSuper(interfaceStartTetheringCallback, proxy, method, args) } } @@ -539,7 +541,7 @@ object TetheringManager { @Suppress("NAME_SHADOWING") val callback = reference.get() return when { - method.matches1("onTetheringSupported") -> { + method.matches("onTetheringSupported", Boolean::class.java) -> { callback?.onTetheringSupported(args!![0] as Boolean) } method.matches1("onUpstreamChanged") -> { @@ -559,13 +561,13 @@ object TetheringManager { @Suppress("UNCHECKED_CAST") callback?.onTetheredInterfacesChanged(args!![0] as List) } - method.matches2("onError") -> { + method.matches("onError", String::class.java, Integer.TYPE) -> { callback?.onError(args!![0] as String, args[1] as Int) } method.matches1>("onClientsChanged") -> { callback?.onClientsChanged(args!![0] as Collection<*>) } - method.matches1("onOffloadStatusChanged") -> { + method.matches("onOffloadStatusChanged", Integer.TYPE) -> { callback?.onOffloadStatusChanged(args!![0] as Int) } else -> callSuper(interfaceTetheringEventCallback, proxy, method, args) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt index 46438958..2afad6a0 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt @@ -257,10 +257,10 @@ object WifiApManager { private fun invokeActual(proxy: Any, method: Method, args: Array?): Any? { return when { - method.matches2("onStateChanged") -> { + method.matches("onStateChanged", Integer.TYPE, Integer.TYPE) -> { callback.onStateChanged(args!![0] as Int, args[1] as Int) } - method.matches1("onNumClientsChanged") -> @Suppress("DEPRECATION") { + method.matches("onNumClientsChanged", Integer.TYPE) -> @Suppress("DEPRECATION") { if (Build.VERSION.SDK_INT >= 30) Timber.w(Exception("Unexpected onNumClientsChanged")) callback.onNumClientsChanged(args!![0] as Int) } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt b/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt index 49cb547f..51aad9ee 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt @@ -58,8 +58,6 @@ fun Method.matches(name: String, vararg classes: Class<*>) = this.name == name & (0 until parameterCount).all { i -> parameters[i].type == classes[i] } @RequiresApi(26) inline fun Method.matches1(name: String) = matches(name, T::class.java) -@RequiresApi(26) -inline fun Method.matches2(name: String) = matches(name, T0::class.java, T1::class.java) fun Context.ensureReceiverUnregistered(receiver: BroadcastReceiver) { try { From 6ed3044dfcb884d71f88b6f33c3b01a2bebebe62 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sun, 25 Jul 2021 04:13:10 -0400 Subject: [PATCH 106/112] Fix class not found on lower Android versions --- .../be/mygod/vpnhotspot/net/wifi/WifiApManager.kt | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt index 2afad6a0..d99e13f8 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt @@ -274,23 +274,18 @@ object WifiApManager { @Suppress("UNCHECKED_CAST") callback.onInfoChanged(args!![0] as List) } - method.matches("onInfoChanged", SoftApInfo.clazz) -> @TargetApi(30) { - when (Build.VERSION.SDK_INT) { - 30 -> { } - in 31..Int.MAX_VALUE -> return null // ignore old version calls - else -> Timber.w(Exception("Unexpected onInfoChanged API 30")) - } + Build.VERSION.SDK_INT >= 30 && method.matches("onInfoChanged", SoftApInfo.clazz) -> { + if (Build.VERSION.SDK_INT >= 31) return null // ignore old version calls val arg = args!![0] val info = SoftApInfo(arg as Parcelable) callback.onInfoChanged( // check for legacy empty info with CHANNEL_WIDTH_INVALID if (info.frequency == 0 && info.bandwidth == 0) emptyList() else listOf(arg)) } - method.matches("onCapabilityChanged", SoftApCapability.clazz) -> @TargetApi(30) { - if (Build.VERSION.SDK_INT < 30) Timber.w(Exception("Unexpected onCapabilityChanged")) + Build.VERSION.SDK_INT >= 30 && method.matches("onCapabilityChanged", SoftApCapability.clazz) -> { callback.onCapabilityChanged(args!![0] as Parcelable) } - method.matches("onBlockedClientConnecting", WifiClient.clazz, Int::class.java) -> @TargetApi(30) { - if (Build.VERSION.SDK_INT < 30) Timber.w(Exception("Unexpected onBlockedClientConnecting")) + Build.VERSION.SDK_INT >= 30 && method.matches("onBlockedClientConnecting", WifiClient.clazz, + Int::class.java) -> { callback.onBlockedClientConnecting(args!![0] as Parcelable, args[1] as Int) } else -> callSuper(interfaceSoftApCallback, proxy, method, args) From bdaa8c3f51b5092a65bd4add3c4e0315cce3023b Mon Sep 17 00:00:00 2001 From: Mygod Date: Sun, 25 Jul 2021 14:20:21 -0400 Subject: [PATCH 107/112] v2.12.5 --- mobile/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/build.gradle.kts b/mobile/build.gradle.kts index 80bc2356..73d36734 100644 --- a/mobile/build.gradle.kts +++ b/mobile/build.gradle.kts @@ -24,8 +24,8 @@ android { minSdk = 21 this.targetSdk = targetSdk resourceConfigurations.addAll(arrayOf("it", "ru", "zh-rCN", "zh-rTW")) - versionCode = 275 - versionName = "2.12.4" + versionCode = 276 + versionName = "2.12.5" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" javaCompileOptions.annotationProcessorOptions.arguments.apply { put("room.expandProjection", "true") From ed09f747cea2a3294edb79ec1320f95aebbb918e Mon Sep 17 00:00:00 2001 From: Mygod Date: Sun, 25 Jul 2021 22:38:17 -0400 Subject: [PATCH 108/112] Fix PersistentGroupInfoListener --- .../be/mygod/vpnhotspot/net/wifi/WifiP2pManagerHelper.kt | 9 ++++----- mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt | 9 ++++++++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiP2pManagerHelper.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiP2pManagerHelper.kt index f95c87d9..d4749c8e 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiP2pManagerHelper.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiP2pManagerHelper.kt @@ -9,7 +9,7 @@ import androidx.annotation.RequiresApi import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.net.MacAddressCompat import be.mygod.vpnhotspot.util.callSuper -import be.mygod.vpnhotspot.util.matches1 +import be.mygod.vpnhotspot.util.matchesCompat import kotlinx.coroutines.CompletableDeferred import java.lang.reflect.InvocationHandler import java.lang.reflect.Method @@ -99,9 +99,8 @@ object WifiP2pManagerHelper { private val interfacePersistentGroupInfoListener by lazy { Class.forName("android.net.wifi.p2p.WifiP2pManager\$PersistentGroupInfoListener") } - private val getGroupList by lazy { - Class.forName("android.net.wifi.p2p.WifiP2pGroupList").getDeclaredMethod("getGroupList") - } + private val classWifiP2pGroupList by lazy { Class.forName("android.net.wifi.p2p.WifiP2pGroupList") } + private val getGroupList by lazy { classWifiP2pGroupList.getDeclaredMethod("getGroupList") } private val requestPersistentGroupInfo by lazy { WifiP2pManager::class.java.getDeclaredMethod("requestPersistentGroupInfo", WifiP2pManager.Channel::class.java, interfacePersistentGroupInfoListener) @@ -118,7 +117,7 @@ object WifiP2pManagerHelper { requestPersistentGroupInfo(this, c, Proxy.newProxyInstance(interfacePersistentGroupInfoListener.classLoader, arrayOf(interfacePersistentGroupInfoListener), object : InvocationHandler { override fun invoke(proxy: Any, method: Method, args: Array?): Any? = when { - method.matches1>("onPersistentGroupInfoAvailable") -> { + method.matchesCompat("onPersistentGroupInfoAvailable", args, classWifiP2pGroupList) -> { @Suppress("UNCHECKED_CAST") result.complete(getGroupList(args!![0]) as Collection) } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt b/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt index 51aad9ee..75d27f73 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt @@ -55,10 +55,17 @@ fun Long.toPluralInt(): Int { @RequiresApi(26) fun Method.matches(name: String, vararg classes: Class<*>) = this.name == name && parameterCount == classes.size && - (0 until parameterCount).all { i -> parameters[i].type == classes[i] } + classes.indices.all { i -> parameters[i].type == classes[i] } @RequiresApi(26) inline fun Method.matches1(name: String) = matches(name, T::class.java) +fun Method.matchesCompat(name: String, args: Array?, vararg classes: Class<*>) = + if (Build.VERSION.SDK_INT < 26) { + this.name == name && args?.size ?: 0 == classes.size && classes.indices.all { i -> + args!![i]?.let { classes[i].isInstance(it) } != false + } + } else matches(name, *classes) + fun Context.ensureReceiverUnregistered(receiver: BroadcastReceiver) { try { unregisterReceiver(receiver) From 5f6e33c85280e35cea46aa1e0a6a5d8ead0638e3 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sun, 25 Jul 2021 22:51:50 -0400 Subject: [PATCH 109/112] Fix notification again --- .../src/main/java/be/mygod/vpnhotspot/ServiceNotification.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/ServiceNotification.kt b/mobile/src/main/java/be/mygod/vpnhotspot/ServiceNotification.kt index caacd93b..740b170e 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/ServiceNotification.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/ServiceNotification.kt @@ -62,7 +62,8 @@ object ServiceNotification { } } fun stopForeground(service: Service) = synchronized(this) { - val shutdown = deviceCountsMap.remove(service) != null && deviceCountsMap.isEmpty() + deviceCountsMap.remove(service) ?: return@synchronized + val shutdown = deviceCountsMap.isEmpty() service.stopForeground(shutdown) if (!shutdown) manager.notify(NOTIFICATION_ID, buildNotification(service)) } From 018682e2f5d38be507083ad8afd7310a8c10d3d8 Mon Sep 17 00:00:00 2001 From: Mygod Date: Tue, 27 Jul 2021 14:53:23 -0400 Subject: [PATCH 110/112] Remove Bluetooth tethering if unsupported --- .../vpnhotspot/manage/BluetoothTethering.kt | 12 ++++-------- .../mygod/vpnhotspot/manage/TetherManager.kt | 6 ++++-- .../vpnhotspot/manage/TetheringFragment.kt | 18 +++++++++++++----- .../vpnhotspot/manage/TetheringTileService.kt | 6 +++++- 4 files changed, 26 insertions(+), 16 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt index bbb9e823..ac87da09 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/BluetoothTethering.kt @@ -3,7 +3,6 @@ package be.mygod.vpnhotspot.manage import android.annotation.SuppressLint import android.annotation.TargetApi import android.bluetooth.BluetoothAdapter -import android.bluetooth.BluetoothManager import android.bluetooth.BluetoothProfile import android.content.BroadcastReceiver import android.content.Context @@ -11,7 +10,6 @@ import android.content.Intent import android.content.IntentFilter import android.os.Build import androidx.annotation.RequiresApi -import androidx.core.content.getSystemService import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.net.TetheringManager import be.mygod.vpnhotspot.util.broadcastReceiver @@ -20,7 +18,7 @@ import be.mygod.vpnhotspot.widget.SmartSnackbar import timber.log.Timber import java.lang.reflect.InvocationTargetException -class BluetoothTethering(context: Context, val stateListener: () -> Unit) : +class BluetoothTethering(context: Context, private val adapter: BluetoothAdapter, val stateListener: () -> Unit) : BluetoothProfile.ServiceListener, AutoCloseable { companion object : BroadcastReceiver() { /** @@ -30,7 +28,6 @@ class BluetoothTethering(context: Context, val stateListener: () -> Unit) : private val clazz by lazy { Class.forName("android.bluetooth.BluetoothPan") } private val isTetheringOn by lazy { clazz.getDeclaredMethod("isTetheringOn") } - private val adapter = app.getSystemService()?.adapter private val BluetoothProfile.isTetheringOn get() = isTetheringOn(this) as Boolean private fun registerBluetoothStateListener(receiver: BroadcastReceiver) = @@ -67,7 +64,7 @@ class BluetoothTethering(context: Context, val stateListener: () -> Unit) : val pan = pan ?: return null if (!connected) return null activeFailureCause = null - val on = adapter?.state == BluetoothAdapter.STATE_ON && try { + val on = adapter.state == BluetoothAdapter.STATE_ON && try { pan.isTetheringOn } catch (e: InvocationTargetException) { activeFailureCause = e.cause ?: e @@ -84,7 +81,6 @@ class BluetoothTethering(context: Context, val stateListener: () -> Unit) : private val receiver = broadcastReceiver { _, _ -> stateListener() } fun ensureInit(context: Context) { - val adapter = adapter ?: return activeFailureCause = null if (!proxyCreated) try { check(adapter.getProfileProxy(context, this, PAN)) @@ -116,7 +112,7 @@ class BluetoothTethering(context: Context, val stateListener: () -> Unit) : @RequiresApi(24) fun start(callback: TetheringManager.StartTetheringCallback) { if (pendingCallback == null) try { - if (adapter?.state == BluetoothAdapter.STATE_OFF) { + if (adapter.state == BluetoothAdapter.STATE_OFF) { registerBluetoothStateListener(BluetoothTethering) pendingCallback = callback adapter.enable() @@ -134,6 +130,6 @@ class BluetoothTethering(context: Context, val stateListener: () -> Unit) : override fun close() { app.unregisterReceiver(receiver) - adapter!!.closeProfileProxy(PAN, pan) + adapter.closeProfileProxy(PAN, pan) } } 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 4cb41497..685ee034 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt @@ -2,6 +2,7 @@ package be.mygod.vpnhotspot.manage import android.Manifest import android.annotation.TargetApi +import android.bluetooth.BluetoothAdapter import android.content.Context import android.content.Intent import android.content.IntentFilter @@ -306,8 +307,9 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), override fun stop() = TetheringManager.stopTethering(TetheringManager.TETHERING_USB, this::onException) } @RequiresApi(24) - class Bluetooth(parent: TetheringFragment) : TetherManager(parent), DefaultLifecycleObserver { - private val tethering = BluetoothTethering(parent.requireContext()) { data.notifyChange() } + class Bluetooth(parent: TetheringFragment, adapter: BluetoothAdapter) : + TetherManager(parent), DefaultLifecycleObserver { + private val tethering = BluetoothTethering(parent.requireContext(), adapter) { data.notifyChange() } init { parent.viewLifecycleOwner.lifecycle.addObserver(this) 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 b799f9f8..6253db9f 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt @@ -3,6 +3,7 @@ package be.mygod.vpnhotspot.manage import android.annotation.TargetApi +import android.bluetooth.BluetoothManager import android.content.* import android.os.Build import android.os.Bundle @@ -15,6 +16,7 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.RequiresApi import androidx.appcompat.widget.Toolbar import androidx.core.content.ContextCompat +import androidx.core.content.getSystemService import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.DefaultItemAnimator @@ -49,12 +51,18 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick @get:RequiresApi(26) internal val localOnlyHotspotManager by lazy @TargetApi(26) { LocalOnlyHotspotManager(this@TetheringFragment) } @get:RequiresApi(24) - internal val bluetoothManager by lazy @TargetApi(24) { TetherManager.Bluetooth(this@TetheringFragment) } + internal val bluetoothManager by lazy { + if (Build.VERSION.SDK_INT >= 24) requireContext().getSystemService()?.adapter?.let { + TetherManager.Bluetooth(this@TetheringFragment, it) + } else null + } @get:RequiresApi(24) private val tetherManagers by lazy @TargetApi(24) { - listOf(TetherManager.Wifi(this@TetheringFragment), - TetherManager.Usb(this@TetheringFragment), - bluetoothManager) + listOfNotNull( + TetherManager.Wifi(this@TetheringFragment), + TetherManager.Usb(this@TetheringFragment), + bluetoothManager, + ) } @get:RequiresApi(30) private val tetherManagers30 by lazy @TargetApi(30) { @@ -139,7 +147,7 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick } @RequiresApi(31) val requestBluetooth = registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted -> - if (granted) adapter.bluetoothManager.ensureInit(requireContext()) + if (granted) adapter.bluetoothManager!!.ensureInit(requireContext()) } var ifaceLookup: Map = emptyMap() 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 4efd8174..18442497 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringTileService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringTileService.kt @@ -1,5 +1,6 @@ package be.mygod.vpnhotspot.manage +import android.bluetooth.BluetoothManager import android.content.ComponentName import android.content.Context import android.content.Intent @@ -11,6 +12,7 @@ import android.service.quicksettings.Tile import android.widget.Toast import androidx.annotation.RequiresApi import androidx.core.content.ContextCompat +import androidx.core.content.getSystemService import be.mygod.vpnhotspot.R import be.mygod.vpnhotspot.TetheringService import be.mygod.vpnhotspot.net.TetherType @@ -158,7 +160,9 @@ sealed class TetheringTileService : IpNeighbourMonitoringTileService(), Tetherin } override fun onStartListening() { - tethering = BluetoothTethering(this) { updateTile() } + tethering = getSystemService()?.adapter?.let { + BluetoothTethering(this, it) { updateTile() } + } super.onStartListening() } override fun onStopListening() { From b9b26aa81bff42c261add5537ee6613641ded95a Mon Sep 17 00:00:00 2001 From: Mygod Date: Wed, 28 Jul 2021 16:50:05 -0400 Subject: [PATCH 111/112] v2.12.6 --- build.gradle.kts | 2 +- mobile/build.gradle.kts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 5a06bcdf..f80fd139 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -10,7 +10,7 @@ buildscript { dependencies { classpath(kotlin("gradle-plugin", "1.5.21")) - classpath("com.android.tools.build:gradle:7.0.0-rc01") + classpath("com.android.tools.build:gradle:7.0.0") classpath("com.google.firebase:firebase-crashlytics-gradle:2.7.1") classpath("com.google.android.gms:oss-licenses-plugin:0.10.4") classpath("com.google.gms:google-services:4.3.8") diff --git a/mobile/build.gradle.kts b/mobile/build.gradle.kts index 73d36734..57c710cd 100644 --- a/mobile/build.gradle.kts +++ b/mobile/build.gradle.kts @@ -24,8 +24,8 @@ android { minSdk = 21 this.targetSdk = targetSdk resourceConfigurations.addAll(arrayOf("it", "ru", "zh-rCN", "zh-rTW")) - versionCode = 276 - versionName = "2.12.5" + versionCode = 277 + versionName = "2.12.6" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" javaCompileOptions.annotationProcessorOptions.arguments.apply { put("room.expandProjection", "true") From 19eac93bc9b337831bf23d1d8e16087d1b3df364 Mon Sep 17 00:00:00 2001 From: Mygod Date: Thu, 29 Jul 2021 11:11:03 +0800 Subject: [PATCH 112/112] Update to Java 11 --- mobile/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/build.gradle.kts b/mobile/build.gradle.kts index 57c710cd..005b6a3f 100644 --- a/mobile/build.gradle.kts +++ b/mobile/build.gradle.kts @@ -9,7 +9,7 @@ plugins { } android { - val javaVersion = JavaVersion.VERSION_1_8 + val javaVersion = JavaVersion.VERSION_11 val targetSdk = 29 buildToolsVersion = "31.0.0" compileOptions {