From 5b5cef9f7fb28cc68c6cd211943888d3ed2c246b Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 4 Jun 2021 02:26:33 -0400 Subject: [PATCH 1/8] Init current upstream always --- .../java/be/mygod/vpnhotspot/preference/UpstreamsPreference.kt | 1 + 1 file changed, 1 insertion(+) 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..5ab3df9b 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/preference/UpstreamsPreference.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/preference/UpstreamsPreference.kt @@ -60,6 +60,7 @@ class UpstreamsPreference(context: Context, attrs: AttributeSet) : Preference(co init { (context as LifecycleOwner).lifecycle.addObserver(this) + onUpdate() } override fun onStart(owner: LifecycleOwner) { From 4b19dc37c218bf94254b2b5a21079e771fd7b06f Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 5 Jun 2021 02:13:51 -0400 Subject: [PATCH 2/8] Resume Google Play beta track --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f02531c8..3a6eaa96 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,8 @@ Connecting things to your VPN made simple. Share your VPN connection over hotspot or repeater. (**root required**) , -sign up for beta +sign up for beta at Google Play +or Firebase This app is useful for: From dfcc860adbcddc2b21642e1dbca043968521318e Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 4 Jun 2021 01:25:07 -0400 Subject: [PATCH 3/8] 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 9a17ac79..78b808f3 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 @@ -123,6 +124,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 21fed36f..b1456137 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 @@ -215,6 +219,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 -> @@ -311,6 +318,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 */ @@ -321,29 +352,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 @@ -351,15 +378,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() @@ -487,6 +518,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 c2d7d51e..29f47448 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 bbbc216e..1dd6bf2b 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 @@ -129,7 +130,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() @@ -139,4 +142,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 37095766..d3927cf8 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 f3ce86b8..f2b020a9 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 c0fdd5ef04d4d3578694727c0ce2a41a26ffe6c9 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 5 Jun 2021 14:09:42 +0800 Subject: [PATCH 4/8] 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 29f47448..994d474a 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/LocalOnlyHotspotManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/LocalOnlyHotspotManager.kt @@ -3,13 +3,11 @@ package be.mygod.vpnhotspot.manage import android.Manifest import android.annotation.TargetApi 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.core.content.getSystemService import androidx.recyclerview.widget.RecyclerView import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.LocalOnlyHotspotService From aa1813687cec702159d46ca5aa0de515fdd7fd3e Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 5 Jun 2021 14:42:36 +0800 Subject: [PATCH 5/8] v2.11.8 --- 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 773ad12d..91c74d2e 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 = 260 - versionName = "2.11.7" + versionCode = 261 + versionName = "2.11.8" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" javaCompileOptions.annotationProcessorOptions.arguments.apply { put("room.expandProjection", "true") From ffb7ec1017e1c29a5951ef17777da30d337d7e68 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 5 Jun 2021 14:47:32 +0800 Subject: [PATCH 6/8] Fix missing import --- mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt index b1456137..624b9870 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt @@ -12,6 +12,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.librootkotlinx.useParcel From 2fdb0ee909ee55b89d6eaddd4112461bcdc6e16b Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 5 Jun 2021 15:07:51 -0400 Subject: [PATCH 7/8] Refine accessibility --- mobile/src/main/res/layout/fragment_ebeg.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/mobile/src/main/res/layout/fragment_ebeg.xml b/mobile/src/main/res/layout/fragment_ebeg.xml index deb5090a..1a632d07 100644 --- a/mobile/src/main/res/layout/fragment_ebeg.xml +++ b/mobile/src/main/res/layout/fragment_ebeg.xml @@ -65,6 +65,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" + android:minHeight="@dimen/touch_target_min" android:text="@string/donations__google_android_market_donate_button" /> From b887569cee31a959c229fe4cf12b254aa350a95a Mon Sep 17 00:00:00 2001 From: Mygod Date: Tue, 8 Jun 2021 14:03:09 -0400 Subject: [PATCH 8/8] Fix more stream closed detections --- mobile/src/main/java/be/mygod/librootkotlinx/Utils.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mobile/src/main/java/be/mygod/librootkotlinx/Utils.kt b/mobile/src/main/java/be/mygod/librootkotlinx/Utils.kt index 290043d2..ee8c1a60 100644 --- a/mobile/src/main/java/be/mygod/librootkotlinx/Utils.kt +++ b/mobile/src/main/java/be/mygod/librootkotlinx/Utils.kt @@ -12,6 +12,7 @@ import android.util.* import androidx.annotation.RequiresApi import kotlinx.parcelize.Parcelize import java.io.IOException +import java.util.* class NoShellException(cause: Throwable) : Exception("Root missing", cause) @@ -252,4 +253,5 @@ inline fun ByteArray.toParcelable(classLoader: ClassLoa } // Stream closed caused in NullOutputStream -val IOException.isEBADF get() = message == "Stream closed" || (cause as? ErrnoException)?.errno == OsConstants.EBADF +val IOException.isEBADF get() = (cause as? ErrnoException)?.errno == OsConstants.EBADF || + message?.lowercase(Locale.ENGLISH) == "stream closed"