From f75b522d82d9fc819d8de0cf7fe76f3377041404 Mon Sep 17 00:00:00 2001 From: Mygod Date: Thu, 14 Mar 2019 19:15:33 +0800 Subject: [PATCH 01/35] Fix temp hotspot on Android Q beta --- mobile/src/main/AndroidManifest.xml | 2 ++ .../vpnhotspot/manage/LocalOnlyHotspotManager.kt | 12 ++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/mobile/src/main/AndroidManifest.xml b/mobile/src/main/AndroidManifest.xml index 293052ae..76d87ee1 100644 --- a/mobile/src/main/AndroidManifest.xml +++ b/mobile/src/main/AndroidManifest.xml @@ -39,6 +39,8 @@ + + Date: Fri, 15 Mar 2019 17:59:30 +0800 Subject: [PATCH 02/35] Update parseNumericAddress on Android Q --- mobile/build.gradle | 3 +-- mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/mobile/build.gradle b/mobile/build.gradle index c5b65bd0..355c8930 100644 --- a/mobile/build.gradle +++ b/mobile/build.gradle @@ -9,8 +9,7 @@ if (!getGradle().getStartParameter().getTaskRequests().toString().contains("Fdro } android { - buildToolsVersion "28.0.3" - compileSdkVersion 28 + compileSdkVersion 'android-Q' compileOptions { sourceCompatibility 1.8 targetCompatibility 1.8 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 b3365485..563b4adf 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt @@ -1,6 +1,7 @@ package be.mygod.vpnhotspot.util import android.content.* +import android.net.InetAddresses import android.os.Build import android.text.Spannable import android.text.SpannableString @@ -9,6 +10,7 @@ import android.view.View import android.widget.ImageView import androidx.annotation.DrawableRes import androidx.core.net.toUri +import androidx.core.os.BuildCompat import androidx.core.view.isVisible import androidx.databinding.BindingAdapter import be.mygod.vpnhotspot.App.Companion.app @@ -76,7 +78,8 @@ private val parseNumericAddress by lazy { isAccessible = true } } -fun parseNumericAddress(address: String) = parseNumericAddress.invoke(null, address) as InetAddress +fun parseNumericAddress(address: String) = if (BuildCompat.isAtLeastQ()) + InetAddresses.parseNumericAddress(address) else parseNumericAddress.invoke(null, address) as InetAddress fun Context.launchUrl(url: String) { if (app.hasTouch) try { From da9d64733903fdd9e6324fa1a840fa1dc2fa1513 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 16 Mar 2019 16:30:31 +0800 Subject: [PATCH 03/35] Fix WifiP2p permissions --- .../vpnhotspot/manage/RepeaterManager.kt | 9 +++++++ .../vpnhotspot/manage/TetheringFragment.kt | 26 ++++++++++++++----- 2 files changed, 29 insertions(+), 6 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 00050db7..3501a041 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt @@ -1,9 +1,11 @@ package be.mygod.vpnhotspot.manage +import android.Manifest 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.WifiConfiguration import android.net.wifi.p2p.WifiP2pGroup import android.os.Bundle @@ -14,6 +16,7 @@ import android.view.WindowManager import android.widget.EditText import androidx.appcompat.app.AlertDialog import androidx.core.content.ContextCompat +import androidx.core.os.BuildCompat import androidx.databinding.BaseObservable import androidx.databinding.Bindable import androidx.lifecycle.ViewModel @@ -82,6 +85,12 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic when (binder?.service?.status) { RepeaterService.Status.IDLE -> { val context = parent.requireContext() + if (BuildCompat.isAtLeastQ() && context.checkSelfPermission( + Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + parent.requestPermissions(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), + TetheringFragment.START_REPEATER) + return + } ContextCompat.startForegroundService(context, Intent(context, RepeaterService::class.java)) } RepeaterService.Status.ACTIVE -> binder.shutdown() 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 9f859cd7..e2b22373 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt @@ -9,7 +9,10 @@ import android.content.pm.PackageManager import android.os.Build import android.os.Bundle import android.os.IBinder -import android.view.* +import android.view.LayoutInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup import androidx.core.content.ContextCompat import androidx.databinding.DataBindingUtil import androidx.fragment.app.Fragment @@ -17,7 +20,10 @@ import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView -import be.mygod.vpnhotspot.* +import be.mygod.vpnhotspot.LocalOnlyHotspotService +import be.mygod.vpnhotspot.R +import be.mygod.vpnhotspot.RepeaterService +import be.mygod.vpnhotspot.TetheringService import be.mygod.vpnhotspot.databinding.FragmentTetheringBinding import be.mygod.vpnhotspot.net.TetherType import be.mygod.vpnhotspot.net.TetheringManager @@ -32,6 +38,7 @@ import java.net.SocketException class TetheringFragment : Fragment(), ServiceConnection, MenuItem.OnMenuItemClickListener { companion object { + const val START_REPEATER = 4 const val START_LOCAL_ONLY_HOTSPOT = 1 const val REPEATER_EDIT_CONFIGURATION = 2 const val REPEATER_WPS = 3 @@ -138,12 +145,19 @@ class TetheringFragment : Fragment(), ServiceConnection, MenuItem.OnMenuItemClic } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { - if (requestCode == START_LOCAL_ONLY_HOTSPOT) @TargetApi(26) { - if (grantResults.firstOrNull() == PackageManager.PERMISSION_GRANTED) { + when (requestCode) { + START_REPEATER -> if (grantResults.firstOrNull() == PackageManager.PERMISSION_GRANTED) @TargetApi(29) { val context = requireContext() - context.startForegroundService(Intent(context, LocalOnlyHotspotService::class.java)) + context.startForegroundService(Intent(context, RepeaterService::class.java)) } - } else super.onRequestPermissionsResult(requestCode, permissions, grantResults) + START_LOCAL_ONLY_HOTSPOT -> { + if (grantResults.firstOrNull() == PackageManager.PERMISSION_GRANTED) @TargetApi(26) { + val context = requireContext() + context.startForegroundService(Intent(context, LocalOnlyHotspotService::class.java)) + } + } + else -> super.onRequestPermissionsResult(requestCode, permissions, grantResults) + } } override fun onServiceConnected(name: ComponentName?, service: IBinder?) { From 841ee5ce9ddf810d5cc8df2b267bc837402c0f44 Mon Sep 17 00:00:00 2001 From: Mygod Date: Wed, 20 Mar 2019 11:42:05 +0800 Subject: [PATCH 04/35] Support new Wi-Fi locks in Android Q --- README.md | 16 +++++- .../java/be/mygod/vpnhotspot/MainActivity.kt | 2 + .../vpnhotspot/SettingsPreferenceFragment.kt | 2 + .../vpnhotspot/net/wifi/WifiDoubleLock.kt | 55 +++++++++++++++++-- mobile/src/main/res/values-v29/arrays.xml | 13 +++++ mobile/src/main/res/values/strings.xml | 2 + mobile/src/main/res/xml/pref_settings.xml | 1 - 7 files changed, 82 insertions(+), 9 deletions(-) create mode 100644 mobile/src/main/res/values-v29/arrays.xml diff --git a/README.md b/README.md index c4e45603..7aadba8f 100644 --- a/README.md +++ b/README.md @@ -65,8 +65,20 @@ Default settings are picked to suit general use cases and maximize compatibility * Keep Wi-Fi alive: Acquire Wi-Fi locks when repeater, temporary hotspot or system VPN hotspot is activated. - Choose "System default" to save battery life; - - Choose "On" (default) if repeater/hotspot turns itself off automatically or stops working after a while; - - Choose "High Performance Mode" to minimize packet loss and latency (will consume more power). + - (up to Android 9) Choose "On" (default) if repeater/hotspot turns itself off automatically or stops working after a while; + - (up to Android 9) Choose "High Performance Mode" to minimize packet loss and latency (will consume more power); + - (since Android Q) Choose "Disable power save" to decrease packet latency. + An example use case is when a voice connection needs to be kept active even after the device screen goes off. + Using this mode may improve the call quality. + Requires support from the hardware. + - (since Android Q) Choose "Low latency mode" to optimize for reduced packet latency, and this might result in: + 1. Reduced battery life. + 2. Reduced throughput. + 3. Reduced frequency of Wi-Fi scanning. + This may cause the device not roaming or switching to the AP with highest signal quality, and location accuracy may be reduced. + Example use cases are real time gaming or virtual reality applications where low latency is a key factor for user experience. + Requires support from the hardware. + Note: Requires this app running in foreground with screen on. * Start repeater on boot: Self explanatory. * Network status monitor mode: This option controls how the app monitors connected devices as well as interface changes (when custom upstream is used). diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/MainActivity.kt b/mobile/src/main/java/be/mygod/vpnhotspot/MainActivity.kt index 4b859996..4914c620 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/MainActivity.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/MainActivity.kt @@ -15,6 +15,7 @@ import be.mygod.vpnhotspot.client.ClientViewModel import be.mygod.vpnhotspot.client.ClientsFragment import be.mygod.vpnhotspot.databinding.ActivityMainBinding import be.mygod.vpnhotspot.manage.TetheringFragment +import be.mygod.vpnhotspot.net.wifi.WifiDoubleLock import be.mygod.vpnhotspot.util.ServiceForegroundConnector import be.mygod.vpnhotspot.widget.SmartSnackbar import com.google.android.material.bottomnavigation.BottomNavigationMenuView @@ -41,6 +42,7 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS if (RepeaterService.supported) ServiceForegroundConnector(this, model, RepeaterService::class) model.clients.observe(this, Observer { badge.badgeNumber = it.size }) SmartSnackbar.Register(lifecycle, binding.fragmentHolder) + WifiDoubleLock.ActivityListener(this) } override fun onNavigationItemSelected(item: MenuItem) = when (item.itemId) { diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/SettingsPreferenceFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/SettingsPreferenceFragment.kt index 5a4faf26..e1b5986f 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/SettingsPreferenceFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/SettingsPreferenceFragment.kt @@ -12,6 +12,7 @@ import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.net.Routing.Companion.IPTABLES 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.AlwaysAutoCompleteEditTextPreferenceDialogFragmentCompat import be.mygod.vpnhotspot.preference.SharedPreferenceDataStore import be.mygod.vpnhotspot.util.RootSession @@ -26,6 +27,7 @@ import java.net.SocketException class SettingsPreferenceFragment : PreferenceFragmentCompat() { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + WifiDoubleLock.mode = WifiDoubleLock.mode // handle complicated default value and possible system upgrades preferenceManager.preferenceDataStore = SharedPreferenceDataStore(app.pref) RoutingManager.masqueradeMode = RoutingManager.masqueradeMode // flush default value addPreferencesFromResource(R.xml.pref_settings) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiDoubleLock.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiDoubleLock.kt index 45261e1b..7032d710 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiDoubleLock.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiDoubleLock.kt @@ -4,7 +4,15 @@ import android.annotation.SuppressLint import android.content.SharedPreferences import android.net.wifi.WifiManager import android.os.PowerManager +import android.view.WindowManager +import androidx.activity.ComponentActivity +import androidx.annotation.RequiresApi +import androidx.core.content.edit import androidx.core.content.getSystemService +import androidx.core.os.BuildCompat +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleObserver +import androidx.lifecycle.OnLifecycleEvent import be.mygod.vpnhotspot.App.Companion.app /** @@ -13,8 +21,12 @@ import be.mygod.vpnhotspot.App.Companion.app class WifiDoubleLock(lockType: Int) : AutoCloseable { companion object : SharedPreferences.OnSharedPreferenceChangeListener { private const val KEY = "service.wifiLock" - private val lockType get() = - WifiDoubleLock.Mode.valueOf(app.pref.getString(KEY, WifiDoubleLock.Mode.Full.toString()) ?: "").lockType + var mode: Mode + @Suppress("DEPRECATION") + get() = Mode.valueOf(app.pref.getString(KEY, Mode.Full.toString()) ?: "").let { + if (it == Mode.Full && BuildCompat.isAtLeastQ()) Mode.None else it + } + set(value) = app.pref.edit { putString(KEY, value.toString()) } private val service by lazy { app.getSystemService()!! } private var holders = mutableSetOf() @@ -23,7 +35,7 @@ class WifiDoubleLock(lockType: Int) : AutoCloseable { fun acquire(holder: Any) = synchronized(this) { if (holders.isEmpty()) { app.pref.registerOnSharedPreferenceChangeListener(this) - val lockType = lockType + val lockType = mode.lockType if (lockType != null) lock = WifiDoubleLock(lockType) } check(holders.add(holder)) @@ -40,14 +52,45 @@ class WifiDoubleLock(lockType: Int) : AutoCloseable { override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { if (key == KEY) synchronized(this) { lock?.close() - val lockType = lockType + val lockType = mode.lockType lock = if (lockType == null) null else WifiDoubleLock(lockType) } } } - enum class Mode(val lockType: Int? = null) { - None, Full(WifiManager.WIFI_MODE_FULL), HighPerf(WifiManager.WIFI_MODE_FULL_HIGH_PERF) + enum class Mode(val lockType: Int? = null, val keepScreenOn: Boolean = false) { + None, + @Suppress("DEPRECATION") + @Deprecated("This constant was deprecated in API level Q.\n" + + "This API is non-functional and will have no impact.") + Full(WifiManager.WIFI_MODE_FULL), + HighPerf(WifiManager.WIFI_MODE_FULL_HIGH_PERF), + @RequiresApi(29) + LowLatency(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, true), + } + + class ActivityListener(val activity: ComponentActivity) : + LifecycleObserver, SharedPreferences.OnSharedPreferenceChangeListener { + private var keepScreenOn: Boolean = false + set(value) { + if (field == value) return + field = value + if (value) activity.window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + else activity.window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + } + + init { + activity.lifecycle.addObserver(this) + app.pref.registerOnSharedPreferenceChangeListener(this) + keepScreenOn = mode.keepScreenOn + } + + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { + if (key == KEY) keepScreenOn = mode.keepScreenOn + } + + @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) + fun shutdown() = app.pref.unregisterOnSharedPreferenceChangeListener(this) } private val wifi = app.wifi.createWifiLock(lockType, "vpnhotspot:wifi").apply { acquire() } diff --git a/mobile/src/main/res/values-v29/arrays.xml b/mobile/src/main/res/values-v29/arrays.xml new file mode 100644 index 00000000..30824b47 --- /dev/null +++ b/mobile/src/main/res/values-v29/arrays.xml @@ -0,0 +1,13 @@ + + + + @string/settings_service_wifi_lock_none + @string/settings_service_wifi_lock_high_perf_v29 + @string/settings_service_wifi_lock_low_latency + + + None + HighPerf + LowLatency + + diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml index 70098cbb..6b8a73b8 100644 --- a/mobile/src/main/res/values/strings.xml +++ b/mobile/src/main/res/values/strings.xml @@ -101,6 +101,8 @@ System default On High Performance Mode + Disable power save + Low latency mode Network status monitor mode Netlink monitor Netlink monitor with root diff --git a/mobile/src/main/res/xml/pref_settings.xml b/mobile/src/main/res/xml/pref_settings.xml index d78bea81..1edeb6b2 100644 --- a/mobile/src/main/res/xml/pref_settings.xml +++ b/mobile/src/main/res/xml/pref_settings.xml @@ -41,7 +41,6 @@ app:icon="@drawable/ic_device_wifi_lock" app:entries="@array/settings_service_wifi_lock" app:entryValues="@array/settings_service_wifi_lock_values" - app:defaultValue="Full" app:title="@string/settings_service_wifi_lock" app:useSimpleSummaryProvider="true"/> Date: Wed, 20 Mar 2019 11:53:04 +0800 Subject: [PATCH 05/35] Handle API changes --- mobile/src/main/java/be/mygod/vpnhotspot/App.kt | 7 ++++--- .../src/main/java/be/mygod/vpnhotspot/RepeaterService.kt | 4 ++-- .../src/main/java/be/mygod/vpnhotspot/TetheringService.kt | 4 ++-- .../src/main/java/be/mygod/vpnhotspot/client/MacLookup.kt | 4 ++-- .../java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt | 2 ++ .../main/java/be/mygod/vpnhotspot/util/SpanFormatter.kt | 2 +- 6 files changed, 13 insertions(+), 10 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/App.kt b/mobile/src/main/java/be/mygod/vpnhotspot/App.kt index ba743039..0e0eaa55 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/App.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/App.kt @@ -7,13 +7,13 @@ import android.content.res.Configuration import android.net.ConnectivityManager import android.net.wifi.WifiManager import android.os.Build -import android.preference.PreferenceManager import androidx.browser.customtabs.CustomTabsIntent import androidx.core.content.ContextCompat import androidx.core.content.getSystemService import androidx.core.provider.FontRequest import androidx.emoji.text.EmojiCompat import androidx.emoji.text.FontRequestEmojiCompatConfig +import androidx.preference.PreferenceManager import be.mygod.vpnhotspot.net.DhcpWorkaround import be.mygod.vpnhotspot.room.AppDatabase import be.mygod.vpnhotspot.util.DeviceStorageApp @@ -32,7 +32,8 @@ class App : Application() { app = this if (Build.VERSION.SDK_INT >= 24) { deviceStorage = DeviceStorageApp(this) - deviceStorage.moveSharedPreferencesFrom(this, PreferenceManager.getDefaultSharedPreferencesName(this)) + // alternative to PreferenceManager.getDefaultSharedPreferencesName(this) + deviceStorage.moveSharedPreferencesFrom(this, PreferenceManager(this).sharedPreferencesName) deviceStorage.moveDatabaseFrom(this, AppDatabase.DB_NAME) } else deviceStorage = this DebugHelper.init() @@ -51,7 +52,7 @@ class App : Application() { if (DhcpWorkaround.shouldEnable) DhcpWorkaround.enable(true) } - override fun onConfigurationChanged(newConfig: Configuration?) { + override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) ServiceNotification.updateNotificationChannels() } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt index 69fb257d..9d614aa6 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt @@ -120,9 +120,9 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere if (intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, 0) == WifiP2pManager.WIFI_P2P_STATE_DISABLED) clean() // ignore P2P enabled WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION -> onP2pConnectionChanged( - intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO), + intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO)!!, intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO), - intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP)) + intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP)!!) } } private val deviceListener = broadcastReceiver { _, intent -> diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/TetheringService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/TetheringService.kt index 79af6088..33bbffcc 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/TetheringService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/TetheringService.kt @@ -90,7 +90,7 @@ class TetheringService : IpNeighbourMonitoringService() { if (start()) check(downstreams.put(iface, this) == null) else destroy() } } - intent.getStringExtra(EXTRA_ADD_INTERFACE_MONITOR)?.let { iface -> + intent.getStringExtra(EXTRA_ADD_INTERFACE_MONITOR)?.also { iface -> val downstream = downstreams[iface] if (downstream == null) Downstream(this, iface, true).apply { start() @@ -98,7 +98,7 @@ class TetheringService : IpNeighbourMonitoringService() { downstreams[iface] = this } else downstream.monitor = true } - downstreams.remove(intent.getStringExtra(EXTRA_REMOVE_INTERFACE))?.destroy() + intent.getStringExtra(EXTRA_REMOVE_INTERFACE)?.also { downstreams.remove(it)?.destroy() } updateNotification() // call this first just in case we are shutting down immediately onDownstreamsChangedLocked() } else if (downstreams.isEmpty()) stopSelf(startId) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/client/MacLookup.kt b/mobile/src/main/java/be/mygod/vpnhotspot/client/MacLookup.kt index 5f7ed771..4a89e2b0 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/client/MacLookup.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/client/MacLookup.kt @@ -47,7 +47,7 @@ object MacLookup { try { val response = conn.inputStream.bufferedReader().readText() val obj = JSONObject(response).getJSONObject("result") - obj.optString("error", null)?.also { throw UnexpectedError(mac, it) } + obj.opt("error")?.also { throw UnexpectedError(mac, it.toString()) } val company = obj.getString("company") val match = extractCountry(mac, response, obj) val result = if (match != null) { @@ -71,7 +71,7 @@ object MacLookup { } private fun extractCountry(mac: Long, response: String, obj: JSONObject): MatchResult? { - obj.optString("country")?.let { countryCodeRegex.matchEntire(it) }?.also { return it } + countryCodeRegex.matchEntire(obj.optString("country"))?.also { return it } val address = obj.optString("address") if (address.isNullOrBlank()) return null countryCodeRegex.find(address)?.also { return it } 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 653d3f78..c4fbd5ec 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 @@ -25,11 +25,13 @@ object WifiApManager { private fun WifiManager.setWifiApEnabled(wifiConfig: WifiConfiguration?, enabled: Boolean) = setWifiApEnabled.invoke(this, wifiConfig, enabled) as Boolean + @Suppress("DEPRECATION") @Deprecated("Not usable since API 26, malfunctioning on API 25") fun start(wifiConfig: WifiConfiguration? = null) { app.wifi.isWifiEnabled = false app.wifi.setWifiApEnabled(wifiConfig, true) } + @Suppress("DEPRECATION") @Deprecated("Not usable since API 26") fun stop() { app.wifi.setWifiApEnabled(null, false) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/util/SpanFormatter.kt b/mobile/src/main/java/be/mygod/vpnhotspot/util/SpanFormatter.kt index 8ed8a93a..e9fa604f 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/util/SpanFormatter.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/util/SpanFormatter.kt @@ -57,7 +57,7 @@ object SpanFormatter { i = m.start() val exprEnd = m.end() - val argTerm = m.group(1) + val argTerm = m.group(1)!! val modTerm = m.group(2) val typeTerm = m.group(3) From ef32866c6624e53684bb81335e871e18ad48472c Mon Sep 17 00:00:00 2001 From: Mygod Date: Wed, 20 Mar 2019 12:03:04 +0800 Subject: [PATCH 06/35] Update Gradle for Android Q --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 2ce3be2c..ac2db8c8 100644 --- a/build.gradle +++ b/build.gradle @@ -12,7 +12,7 @@ buildscript { } } dependencies { - classpath "com.android.tools.build:gradle:3.3.2" + classpath "com.android.tools.build:gradle:3.4.0-rc02" classpath 'com.github.ben-manes:gradle-versions-plugin:0.21.0' classpath 'com.google.gms:google-services:4.2.0' classpath 'io.fabric.tools:gradle:1.28.0' From 673ff9800aab7b761cdd05e480787c313c10db6a Mon Sep 17 00:00:00 2001 From: Mygod Date: Wed, 20 Mar 2019 12:21:22 +0800 Subject: [PATCH 07/35] Refine code style --- detekt.yml | 2 +- .../be/mygod/vpnhotspot/RepeaterService.kt | 47 +++++++++++-------- .../be/mygod/vpnhotspot/manage/Manager.kt | 4 +- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/detekt.yml b/detekt.yml index 305b961d..7c7e4559 100644 --- a/detekt.yml +++ b/detekt.yml @@ -363,7 +363,7 @@ style: OptionalUnit: active: true OptionalWhenBraces: - active: true + active: false PreferToOverPairSyntax: active: false ProtectedMemberInFinalClass: diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt index 9d614aa6..bf3787d0 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt @@ -22,10 +22,7 @@ import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.netId import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.requestPersistentGroupInfo import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.setWifiP2pChannels import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.startWps -import be.mygod.vpnhotspot.util.StickyEvent0 -import be.mygod.vpnhotspot.util.StickyEvent1 -import be.mygod.vpnhotspot.util.broadcastReceiver -import be.mygod.vpnhotspot.util.intentFilter +import be.mygod.vpnhotspot.util.* import be.mygod.vpnhotspot.widget.SmartSnackbar import timber.log.Timber import java.lang.reflect.InvocationTargetException @@ -233,29 +230,41 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere registerReceiver(receiver, intentFilter(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION, WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)) receiverRegistered = true - p2pManager.requestGroupInfo(channel) { - when { - it == null -> doStart() - it.isGroupOwner -> if (routingManager == null) doStart(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)) - }) + try { + p2pManager.requestGroupInfo(channel) { + when { + it == null -> doStart() + it.isGroupOwner -> if (routingManager == null) doStart(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)) + }) + } } } + } catch (e: SecurityException) { + Timber.w(e) + startFailure(e.readableMessage) } return START_NOT_STICKY } /** * startService Step 2 (if a group isn't already available) */ - private fun doStart() = p2pManager.createGroup(channel, object : WifiP2pManager.ActionListener { - override fun onFailure(reason: Int) = startFailure(formatReason(R.string.repeater_create_group_failure, reason)) - override fun onSuccess() { } // wait for WIFI_P2P_CONNECTION_CHANGED_ACTION to fire to go to step 3 - }) + private fun doStart() = try { + p2pManager.createGroup(channel, object : WifiP2pManager.ActionListener { + override fun onFailure(reason: Int) { + startFailure(formatReason(R.string.repeater_create_group_failure, reason)) + } + override fun onSuccess() { } // wait for WIFI_P2P_CONNECTION_CHANGED_ACTION to fire to go to step 3 + }) + } catch (e: SecurityException) { + Timber.w(e) + startFailure(e.readableMessage) + } /** * Used during step 2, also called when connection changed */ diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/Manager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/Manager.kt index 495d44d4..6dc70cc8 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/Manager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/Manager.kt @@ -1,5 +1,6 @@ package be.mygod.vpnhotspot.manage +import android.annotation.SuppressLint import android.annotation.TargetApi import android.view.LayoutInflater import android.view.ViewGroup @@ -21,7 +22,8 @@ abstract class Manager { const val VIEW_TYPE_REPEATER = 7 override fun areItemsTheSame(oldItem: Manager, newItem: Manager) = oldItem.isSameItemAs(newItem) - override fun areContentsTheSame(oldItem: Manager, newItem: Manager) = oldItem == newItem + @SuppressLint("DiffUtilEquals") + override fun areContentsTheSame(oldItem: Manager, newItem: Manager) = oldItem === newItem fun createViewHolder(inflater: LayoutInflater, parent: ViewGroup, type: Int) = when (type) { VIEW_TYPE_INTERFACE -> From bb41536eb12ede451857bc0d47a0943e24e440be Mon Sep 17 00:00:00 2001 From: Mygod Date: Thu, 4 Apr 2019 11:31:27 +0800 Subject: [PATCH 08/35] Update to Android Q beta 2 APIs --- build.gradle | 2 +- .../java/be/mygod/vpnhotspot/LocalOnlyHotspotService.kt | 2 +- .../src/main/java/be/mygod/vpnhotspot/TetheringService.kt | 2 +- .../java/be/mygod/vpnhotspot/client/ClientViewModel.kt | 3 ++- .../mygod/vpnhotspot/manage/TetherListeningTileService.kt | 7 ++++--- .../java/be/mygod/vpnhotspot/manage/TetheringFragment.kt | 5 +++-- 6 files changed, 12 insertions(+), 9 deletions(-) diff --git a/build.gradle b/build.gradle index e3cfcaba..2b2cacd9 100644 --- a/build.gradle +++ b/build.gradle @@ -12,7 +12,7 @@ buildscript { } } dependencies { - classpath "com.android.tools.build:gradle:3.4.0-rc02" + classpath 'com.android.tools.build:gradle:3.4.0-rc03' classpath 'com.github.ben-manes:gradle-versions-plugin:0.21.0' classpath 'com.google.gms:google-services:4.2.0' classpath 'io.fabric.tools:gradle:1.28.1' diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyHotspotService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyHotspotService.kt index fe7cb571..2ec37e78 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyHotspotService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyHotspotService.kt @@ -41,7 +41,7 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService() { private var routingManager: RoutingManager? = null private var receiverRegistered = false private val receiver = broadcastReceiver { _, intent -> - val ifaces = intent.localOnlyTetheredIfaces + val ifaces = intent.localOnlyTetheredIfaces ?: return@broadcastReceiver DebugHelper.log(TAG, "onTetherStateChangedLocked: $ifaces") check(ifaces.size <= 1) val iface = ifaces.singleOrNull() diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/TetheringService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/TetheringService.kt index 33bbffcc..b803cd4f 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/TetheringService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/TetheringService.kt @@ -48,7 +48,7 @@ class TetheringService : IpNeighbourMonitoringService() { private val receiver = broadcastReceiver { _, intent -> synchronized(downstreams) { val toRemove = downstreams.toMutableMap() // make a copy - for (iface in intent.tetheredIfaces) { + for (iface in intent.tetheredIfaces ?: return@synchronized) { val downstream = toRemove.remove(iface) ?: continue if (downstream.monitor) downstream.start() } 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 7983ea25..09c7c479 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientViewModel.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientViewModel.kt @@ -20,7 +20,8 @@ import be.mygod.vpnhotspot.util.broadcastReceiver class ClientViewModel : ViewModel(), ServiceConnection, IpNeighbourMonitor.Callback { private var tetheredInterfaces = emptySet() private val receiver = broadcastReceiver { _, intent -> - tetheredInterfaces = intent.tetheredIfaces.toSet() + intent.localOnlyTetheredIfaces + tetheredInterfaces = (intent.tetheredIfaces ?: return@broadcastReceiver).toSet() + + (intent.localOnlyTetheredIfaces ?: return@broadcastReceiver) populateClients() } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherListeningTileService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherListeningTileService.kt index 0917b7e0..7c29933f 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherListeningTileService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherListeningTileService.kt @@ -12,14 +12,15 @@ abstract class TetherListeningTileService : KillableTileService() { protected var tethered: List = emptyList() private val receiver = broadcastReceiver { _, intent -> - tethered = intent.tetheredIfaces + tethered = intent.tetheredIfaces ?: return@broadcastReceiver updateTile() } override fun onStartListening() { super.onStartListening() - val intent = registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED)) - if (intent != null) tethered = intent.tetheredIfaces + registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED))?.tetheredIfaces?.let { + tethered = it + } } override fun onStopListening() { 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 13ca8d05..1b616cea 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt @@ -98,8 +98,9 @@ class TetheringFragment : Fragment(), ServiceConnection, MenuItem.OnMenuItemClic var binder: TetheringService.Binder? = null private val adapter = ManagerAdapter() private val receiver = broadcastReceiver { _, intent -> - adapter.update(intent.tetheredIfaces, intent.localOnlyTetheredIfaces, - intent.getStringArrayListExtra(TetheringManager.EXTRA_ERRORED_TETHER)) + adapter.update(intent.tetheredIfaces ?: return@broadcastReceiver, + intent.localOnlyTetheredIfaces ?: return@broadcastReceiver, + intent.getStringArrayListExtra(TetheringManager.EXTRA_ERRORED_TETHER) ?: return@broadcastReceiver) } private fun updateMonitorList(canMonitor: List = emptyList()) { From e91abe07382300ce141ebf00ab31892cf1d4e3c3 Mon Sep 17 00:00:00 2001 From: Mygod Date: Thu, 4 Apr 2019 21:10:38 +0800 Subject: [PATCH 09/35] Support custom repeater SSID without root --- .../be/mygod/vpnhotspot/RepeaterService.kt | 47 +++++++++++++--- .../vpnhotspot/manage/RepeaterManager.kt | 53 +++++++++++++++---- .../P2pSupplicantConfiguration.kt | 1 + 3 files changed, 82 insertions(+), 19 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt index db464636..9b3218e0 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt @@ -5,14 +5,12 @@ import android.content.Intent import android.content.SharedPreferences import android.content.res.Configuration import android.net.NetworkInfo -import android.net.wifi.p2p.WifiP2pDevice -import android.net.wifi.p2p.WifiP2pGroup -import android.net.wifi.p2p.WifiP2pInfo -import android.net.wifi.p2p.WifiP2pManager +import android.net.wifi.p2p.* import android.os.Build import android.os.Handler import android.os.Looper import androidx.annotation.StringRes +import androidx.core.content.edit import androidx.core.content.getSystemService import androidx.core.os.BuildCompat import be.mygod.vpnhotspot.App.Companion.app @@ -33,7 +31,10 @@ import java.lang.reflect.InvocationTargetException class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPreferences.OnSharedPreferenceChangeListener { companion object { private const val TAG = "RepeaterService" - const val KEY_OPERATING_CHANNEL = "service.repeater.oc" + private const val KEY_NETWORK_NAME = "service.repeater.networkName" + private const val KEY_PASSPHRASE = "service.repeater.passphrase" + private const val KEY_OPERATING_BAND = "service.repeater.band" + private const val KEY_OPERATING_CHANNEL = "service.repeater.oc" /** * This is only a "ServiceConnection" to system service and its impact on system is minimal. @@ -49,12 +50,23 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere val supported get() = p2pManager != null var persistentSupported = false + var networkName: String? + get() = app.pref.getString(KEY_NETWORK_NAME, null) + set(value) = app.pref.edit { putString(KEY_NETWORK_NAME, value) } + var passphrase: String? + get() = app.pref.getString(KEY_PASSPHRASE, null) + set(value) = app.pref.edit { putString(KEY_PASSPHRASE, value) } + var operatingBand: Int + get() = app.pref.getInt(KEY_OPERATING_BAND, 0) + set(value) = app.pref.edit { putInt(KEY_OPERATING_BAND, value) } var operatingChannel: Int get() { val result = app.pref.getString(KEY_OPERATING_CHANNEL, null)?.toIntOrNull() ?: 0 return if (result in 1..165) result else 0 } - set(value) = app.pref.edit().putString(RepeaterService.KEY_OPERATING_CHANNEL, value.toString()).apply() + set(value) = app.pref.edit { putString(RepeaterService.KEY_OPERATING_CHANNEL, value.toString()) } + + private val networkNameField by lazy { WifiP2pConfig::class.java.getDeclaredField("networkName") } } enum class Status { @@ -120,6 +132,7 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere } } private var routingManager: RoutingManager? = null + private var persistNextGroup = false var status = Status.IDLE private set(value) { @@ -245,12 +258,25 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere * startService Step 2 (if a group isn't already available) */ private fun doStart() = try { - p2pManager.createGroup(channel, object : WifiP2pManager.ActionListener { + val listener = object : WifiP2pManager.ActionListener { override fun onFailure(reason: Int) { startFailure(formatReason(R.string.repeater_create_group_failure, reason)) } override fun onSuccess() { } // wait for WIFI_P2P_CONNECTION_CHANGED_ACTION to fire to go to step 3 - }) + } + val networkName = networkName + val passphrase = passphrase + if (!BuildCompat.isAtLeastQ() || networkName == null || passphrase == null) { + persistNextGroup = true + p2pManager.createGroup(channel, listener) + } else p2pManager.createGroup(channel, WifiP2pConfig.Builder().apply { + setNetworkName("DIRECT-00-VPNHotspot") // placeholder for bypassing networkName check + setPassphrase(passphrase) + val frequency = operatingChannel + if (frequency == 0) setGroupOperatingBand(operatingBand) else setGroupOperatingFrequency(frequency) + }.build().apply { + networkNameField.set(this, networkName) + }, listener) } catch (e: SecurityException) { Timber.w(e) startFailure(e.readableMessage) @@ -276,6 +302,11 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere */ private fun doStart(group: WifiP2pGroup) { binder.group = group + if (persistNextGroup) { + networkName = group.networkName + passphrase = group.passphrase + persistNextGroup = false + } check(routingManager == null) routingManager = RoutingManager.LocalOnly(this, group.`interface`!!).apply { start() } status = Status.ACTIVE 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 f49bdb05..b7add94c 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt @@ -7,6 +7,7 @@ import android.content.Intent import android.content.ServiceConnection import android.content.pm.PackageManager import android.net.wifi.WifiConfiguration +import android.net.wifi.p2p.WifiP2pConfig import android.net.wifi.p2p.WifiP2pGroup import android.os.Build import android.os.Bundle @@ -32,6 +33,7 @@ import be.mygod.vpnhotspot.util.formatAddresses import be.mygod.vpnhotspot.widget.SmartSnackbar import kotlinx.android.parcel.Parcelize import timber.log.Timber +import java.lang.IllegalArgumentException import java.net.NetworkInterface import java.net.SocketException @@ -112,6 +114,8 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic } } + @Deprecated("No longer used since Android Q") + @Suppress("DEPRECATION") class ConfigHolder : ViewModel() { var config: P2pSupplicantConfiguration? = null } @@ -124,6 +128,7 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic private val data = Data() internal var binder: RepeaterService.Binder? = null private var p2pInterface: String? = null + @Suppress("DEPRECATION") private val holder = ViewModelProviders.of(parent).get() override fun bindTo(viewHolder: RecyclerView.ViewHolder) { @@ -153,25 +158,51 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic } val configuration: WifiConfiguration? get() { - val group = binder?.group - if (group != null) try { - val config = P2pSupplicantConfiguration(group, binder?.thisDevice?.deviceAddress) - holder.config = config - return newWifiApConfiguration(group.networkName, config.psk).apply { - allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK) // is not actually used - if (Build.VERSION.SDK_INT >= 23) { - apBand = AP_BAND_ANY + if (BuildCompat.isAtLeastQ()) { + val networkName = RepeaterService.networkName + val passphrase = RepeaterService.passphrase + if (networkName != null && passphrase != null) { + return newWifiApConfiguration(networkName, passphrase).apply { + allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK) // is not actually used + apBand = when (RepeaterService.operatingBand) { + WifiP2pConfig.GROUP_OWNER_BAND_AUTO -> AP_BAND_ANY + WifiP2pConfig.GROUP_OWNER_BAND_2GHZ -> AP_BAND_2GHZ + WifiP2pConfig.GROUP_OWNER_BAND_5GHZ -> AP_BAND_5GHZ + else -> throw IllegalArgumentException("Unknown operatingBand") + } apChannel = RepeaterService.operatingChannel } } - } catch (e: RuntimeException) { - Timber.w(e) + } else @Suppress("DEPRECATION") { + val group = binder?.group + if (group != null) try { + val config = P2pSupplicantConfiguration(group, binder?.thisDevice?.deviceAddress) + holder.config = config + return newWifiApConfiguration(group.networkName, config.psk).apply { + allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK) // is not actually used + if (Build.VERSION.SDK_INT >= 23) { + apBand = AP_BAND_ANY + apChannel = RepeaterService.operatingChannel + } + } + } catch (e: RuntimeException) { + Timber.w(e) + } } SmartSnackbar.make(R.string.repeater_configure_failure).show() return null } fun updateConfiguration(config: WifiConfiguration) { - holder.config?.let { master -> + if (BuildCompat.isAtLeastQ()) { + RepeaterService.networkName = config.SSID + RepeaterService.passphrase = config.preSharedKey + RepeaterService.operatingBand = when (config.apBand) { + AP_BAND_ANY -> WifiP2pConfig.GROUP_OWNER_BAND_AUTO + AP_BAND_2GHZ -> WifiP2pConfig.GROUP_OWNER_BAND_2GHZ + AP_BAND_5GHZ -> WifiP2pConfig.GROUP_OWNER_BAND_5GHZ + else -> throw IllegalArgumentException("Unknown apBand") + } + } else @Suppress("DEPRECATION") holder.config?.let { master -> if (binder?.group?.networkName != config.SSID || master.psk != config.preSharedKey) try { master.update(config.SSID, config.preSharedKey) binder!!.group = null diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/P2pSupplicantConfiguration.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/P2pSupplicantConfiguration.kt index 396086b4..17323224 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/P2pSupplicantConfiguration.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/P2pSupplicantConfiguration.kt @@ -14,6 +14,7 @@ import java.lang.IllegalStateException * https://android.googlesource.com/platform/external/wpa_supplicant_8/+/d2986c2/wpa_supplicant/config.c#488 * https://android.googlesource.com/platform/external/wpa_supplicant_8/+/6fa46df/wpa_supplicant/config_file.c#182 */ +@Deprecated("No longer used since Android Q") class P2pSupplicantConfiguration(private val group: WifiP2pGroup, ownerAddress: String?) { companion object { private const val TAG = "P2pSupplicantConfiguration" From 617a2dda9564ac7cabfa28cd240f965b0ce2299d Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 5 Apr 2019 09:24:53 +0800 Subject: [PATCH 10/35] Change networkName without resorting to blacklisted API --- .../be/mygod/vpnhotspot/RepeaterService.kt | 37 ++++++++++++++++--- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt index 9b3218e0..a47f6928 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt @@ -5,6 +5,7 @@ import android.content.Intent import android.content.SharedPreferences import android.content.res.Configuration import android.net.NetworkInfo +import android.net.wifi.WpsInfo import android.net.wifi.p2p.* import android.os.Build import android.os.Handler @@ -35,6 +36,10 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere private const val KEY_PASSPHRASE = "service.repeater.passphrase" private const val KEY_OPERATING_BAND = "service.repeater.band" private const val KEY_OPERATING_CHANNEL = "service.repeater.oc" + /** + * Placeholder for bypassing networkName check. + */ + private const val PLACEHOLDER_NETWORK_NAME = "DIRECT-00-VPNHotspot" /** * This is only a "ServiceConnection" to system service and its impact on system is minimal. @@ -57,7 +62,7 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere get() = app.pref.getString(KEY_PASSPHRASE, null) set(value) = app.pref.edit { putString(KEY_PASSPHRASE, value) } var operatingBand: Int - get() = app.pref.getInt(KEY_OPERATING_BAND, 0) + get() = app.pref.getInt(KEY_OPERATING_BAND, WifiP2pConfig.GROUP_OWNER_BAND_AUTO) set(value) = app.pref.edit { putInt(KEY_OPERATING_BAND, value) } var operatingChannel: Int get() { @@ -65,8 +70,6 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere return if (result in 1..165) result else 0 } set(value) = app.pref.edit { putString(RepeaterService.KEY_OPERATING_CHANNEL, value.toString()) } - - private val networkNameField by lazy { WifiP2pConfig::class.java.getDeclaredField("networkName") } } enum class Status { @@ -270,12 +273,34 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere persistNextGroup = true p2pManager.createGroup(channel, listener) } else p2pManager.createGroup(channel, WifiP2pConfig.Builder().apply { - setNetworkName("DIRECT-00-VPNHotspot") // placeholder for bypassing networkName check + setNetworkName(PLACEHOLDER_NETWORK_NAME) setPassphrase(passphrase) val frequency = operatingChannel if (frequency == 0) setGroupOperatingBand(operatingBand) else setGroupOperatingFrequency(frequency) - }.build().apply { - networkNameField.set(this, networkName) + }.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 int = p.readInt() + check(p.dataPosition() == end) + p.setDataPosition(0) + p.writeString(creator) + p.writeString(deviceAddress) + p.writeParcelable(wps, 0) + p.writeLong(long) + p.writeString(networkName) + p.writeString(passphrase) + p.writeInt(int) + p.setDataPosition(0) + p.readParcelable(javaClass.classLoader) + } }, listener) } catch (e: SecurityException) { Timber.w(e) From e799fcf9ffa9999447d9c94aa6771e9f99ef8f26 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 5 Apr 2019 09:34:23 +0800 Subject: [PATCH 11/35] Prevent tempering with persistent groups in Android Q This seems to cause improper persistent group deletions, but either way, I cannot seem to find where persistent groups are persisted now on Android Q. --- README.md | 4 ++-- .../main/java/be/mygod/vpnhotspot/RepeaterService.kt | 10 ++++++++-- .../mygod/vpnhotspot/net/wifi/WifiP2pManagerHelper.kt | 3 +++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6b54d09a..493fd150 100644 --- a/README.md +++ b/README.md @@ -133,8 +133,8 @@ Undocumented API list: * (deprecated since API 26) `Landroid/net/wifi/WifiManager;->setWifiApEnabled(Landroid/net/wifi/WifiConfiguration;Z)Z` * [`Landroid/net/wifi/p2p/WifiP2pGroup;->getNetworkId()I,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#123194) * [`Landroid/net/wifi/p2p/WifiP2pGroupList;->getGroupList()Ljava/util/Collection;,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#123239) -* [`Landroid/net/wifi/p2p/WifiP2pManager;->deletePersistentGroup(Landroid/net/wifi/p2p/WifiP2pManager$Channel;ILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#123431) -* [`Landroid/net/wifi/p2p/WifiP2pManager;->requestPersistentGroupInfo(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Landroid/net/wifi/p2p/WifiP2pManager$PersistentGroupInfoListener;)V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#123450) +* (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pManager;->deletePersistentGroup(Landroid/net/wifi/p2p/WifiP2pManager$Channel;ILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#123431) +* (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pManager;->requestPersistentGroupInfo(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Landroid/net/wifi/p2p/WifiP2pManager$PersistentGroupInfoListener;)V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#123450) * [`Landroid/net/wifi/p2p/WifiP2pManager;->setWifiP2pChannels(Landroid/net/wifi/p2p/WifiP2pManager$Channel;IILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#123458) * [`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/+/aa21a6e/appcompat/hiddenapi-flags.csv#123459) * [`Ljava/net/InetAddress;->parseNumericAddress(Ljava/lang/String;)Ljava/net/InetAddress;,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#299587) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt index a47f6928..01bb10e4 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt @@ -86,6 +86,7 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere groupChanged(value) } val groupChanged = StickyEvent1 { group } + @Deprecated("Not initialized and no use at all since Android Q") var thisDevice: WifiP2pDevice? = null @Deprecated("WPS was deprecated RIP") @@ -127,6 +128,8 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP)!!) } } + @Deprecated("No longer used since Android Q") + @Suppress("DEPRECATION") private val deviceListener = broadcastReceiver { _, intent -> when (intent.action) { WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION -> binder.thisDevice = @@ -158,7 +161,8 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere override fun onCreate() { super.onCreate() onChannelDisconnected() - registerReceiver(deviceListener, intentFilter(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION, + if (!BuildCompat.isAtLeastQ()) @Suppress("DEPRECATION") registerReceiver(deviceListener, intentFilter( + WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION, WifiP2pManagerHelper.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION)) app.pref.registerOnSharedPreferenceChangeListener(this) } @@ -198,6 +202,8 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere if (key == KEY_OPERATING_CHANNEL) setOperatingChannel() } + @Deprecated("No longer used since Android Q") + @Suppress("DEPRECATION") private fun onPersistentGroupsChanged() { val channel = channel ?: return val device = binder.thisDevice ?: return @@ -377,7 +383,7 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere if (status != Status.IDLE) binder.shutdown() clean() // force clean to prevent leakage app.pref.unregisterOnSharedPreferenceChangeListener(this) - unregisterReceiver(deviceListener) + if (!BuildCompat.isAtLeastQ()) @Suppress("DEPRECATION") unregisterReceiver(deviceListener) status = Status.DESTROYED if (Build.VERSION.SDK_INT >= 27) channel?.close() super.onDestroy() 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 794ec602..23657929 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 @@ -12,6 +12,7 @@ import java.lang.reflect.Proxy object WifiP2pManagerHelper { const val UNSUPPORTED = -2 + @Deprecated("No longer used since Android Q") const val WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION = "android.net.wifi.p2p.PERSISTENT_GROUPS_CHANGED" /** @@ -61,6 +62,7 @@ object WifiP2pManagerHelper { WifiP2pManager::class.java.getDeclaredMethod("deletePersistentGroup", WifiP2pManager.Channel::class.java, Int::class.java, WifiP2pManager.ActionListener::class.java) } + @Deprecated("No longer used since Android Q") fun WifiP2pManager.deletePersistentGroup(c: WifiP2pManager.Channel, netId: Int, listener: WifiP2pManager.ActionListener) { try { @@ -87,6 +89,7 @@ object WifiP2pManagerHelper { * @param c is the channel created at {@link #initialize} * @param listener for callback when persistent group info list is available. Can be null. */ + @Deprecated("No longer used since Android Q") fun WifiP2pManager.requestPersistentGroupInfo(c: WifiP2pManager.Channel, listener: (Collection) -> Unit) { val proxy = Proxy.newProxyInstance(interfacePersistentGroupInfoListener.classLoader, From 11d5b7786179a87ade388728fccd9f32b2bb1666 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 5 Apr 2019 09:45:28 +0800 Subject: [PATCH 12/35] Handle deprecation changes --- .../java/be/mygod/vpnhotspot/RepeaterService.kt | 16 +++++++--------- 1 file 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 01bb10e4..b71f1c7d 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt @@ -1,10 +1,10 @@ package be.mygod.vpnhotspot +import android.annotation.SuppressLint import android.app.Service import android.content.Intent import android.content.SharedPreferences import android.content.res.Configuration -import android.net.NetworkInfo import android.net.wifi.WpsInfo import android.net.wifi.p2p.* import android.os.Build @@ -62,7 +62,7 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere get() = app.pref.getString(KEY_PASSPHRASE, null) set(value) = app.pref.edit { putString(KEY_PASSPHRASE, value) } var operatingBand: Int - get() = app.pref.getInt(KEY_OPERATING_BAND, WifiP2pConfig.GROUP_OWNER_BAND_AUTO) + @SuppressLint("InlinedApi") get() = app.pref.getInt(KEY_OPERATING_BAND, WifiP2pConfig.GROUP_OWNER_BAND_AUTO) set(value) = app.pref.edit { putInt(KEY_OPERATING_BAND, value) } var operatingChannel: Int get() { @@ -89,14 +89,13 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere @Deprecated("Not initialized and no use at all since Android Q") var thisDevice: WifiP2pDevice? = null - @Deprecated("WPS was deprecated RIP") fun startWps(pin: String? = null) { val channel = channel if (channel == null) SmartSnackbar.make(R.string.repeater_failure_disconnected).show() - else @Suppress("DEPRECATION") if (active) p2pManager.startWps(channel, android.net.wifi.WpsInfo().apply { - setup = if (pin == null) android.net.wifi.WpsInfo.PBC else { + else if (active) p2pManager.startWps(channel, WpsInfo().apply { + setup = if (pin == null) WpsInfo.PBC else { this.pin = pin - android.net.wifi.WpsInfo.KEYPAD + WpsInfo.KEYPAD } }, object : WifiP2pManager.ActionListener { override fun onSuccess() = SmartSnackbar.make( @@ -124,7 +123,6 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere WifiP2pManager.WIFI_P2P_STATE_DISABLED) clean() // ignore P2P enabled WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION -> onP2pConnectionChanged( intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO)!!, - intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO), intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP)!!) } } @@ -315,8 +313,8 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere /** * Used during step 2, also called when connection changed */ - private fun onP2pConnectionChanged(info: WifiP2pInfo, net: NetworkInfo?, group: WifiP2pGroup) { - DebugHelper.log(TAG, "P2P connection changed: $info\n$net\n$group") + private fun onP2pConnectionChanged(info: WifiP2pInfo, group: WifiP2pGroup) { + DebugHelper.log(TAG, "P2P connection changed: $info\n$group") when { !info.groupFormed || !info.isGroupOwner || !group.isGroupOwner -> { if (routingManager != null) clean() // P2P shutdown, else other groups changing before start, ignore From 458dfaee2f986683260a102cfea1427888032c0c Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 5 Apr 2019 09:49:30 +0800 Subject: [PATCH 13/35] Update README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 493fd150..e5fe8bc7 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,7 @@ Undocumented system configurations: Other: +* (since API 29) `android.net.wifi.p2p.WifiP2pConfig` needs to be parcelized in a very specific order. * (since API 27) [`Landroid/provider/Settings$Global;->TETHER_OFFLOAD_DISABLED:Ljava/lang/String;,greylist-max-o`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#144760) is assumed to be `"tether_offload_disabled"`. * (since API 27) `com.android.server.connectivity.tethering.OffloadHardwareInterface.DEFAULT_TETHER_OFFLOAD_DISABLED` is assumed to be 0. * Several constants in `ConnectivityManager` is assumed to be defined as in `TetheringManager.kt`; From e73d89b5b8cdcd06b39fdfdedcb0073f79fc4ad0 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 5 Apr 2019 10:03:51 +0800 Subject: [PATCH 14/35] Support showing repeater frequency in Android Q --- .../src/main/java/be/mygod/vpnhotspot/RepeaterService.kt | 6 ++++-- .../java/be/mygod/vpnhotspot/manage/RepeaterManager.kt | 7 +++++++ .../net/wifi/configuration/WifiApDialogFragment.kt | 9 ++++++++- .../net/wifi/configuration/WifiConfiguration.kt | 5 +++++ mobile/src/main/res/layout/listitem_repeater.xml | 2 +- mobile/src/main/res/values-zh-rCN/strings.xml | 1 + mobile/src/main/res/values/strings.xml | 1 + 7 files changed, 27 insertions(+), 4 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt index b71f1c7d..ba62bb9b 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt @@ -21,6 +21,7 @@ import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.netId import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.requestPersistentGroupInfo import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.setWifiP2pChannels import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.startWps +import be.mygod.vpnhotspot.net.wifi.configuration.channelToFrequency import be.mygod.vpnhotspot.util.* import be.mygod.vpnhotspot.widget.SmartSnackbar import timber.log.Timber @@ -279,8 +280,9 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere } else p2pManager.createGroup(channel, WifiP2pConfig.Builder().apply { setNetworkName(PLACEHOLDER_NETWORK_NAME) setPassphrase(passphrase) - val frequency = operatingChannel - if (frequency == 0) setGroupOperatingBand(operatingBand) else setGroupOperatingFrequency(frequency) + val channel = operatingChannel + if (channel == 0) setGroupOperatingBand(operatingBand) + else setGroupOperatingFrequency(channelToFrequency(channel)) }.build().run { useParcel { p -> p.writeParcelable(this, 0) 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 b7add94c..f8c38527 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt @@ -55,6 +55,12 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic else -> false } + val title: CharSequence @Bindable get() { + if (BuildCompat.isAtLeastQ()) binder?.group?.frequency?.let { + return parent.getString(R.string.repeater_channel, it, frequencyToChannel(it)) + } + return parent.getString(R.string.title_repeater) + } val addresses: CharSequence @Bindable get() { return try { NetworkInterface.getByName(p2pInterface ?: return "")?.formatAddresses() ?: "" @@ -70,6 +76,7 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic } fun onGroupChanged(group: WifiP2pGroup? = null) { p2pInterface = group?.`interface` + if (BuildCompat.isAtLeastQ()) notifyPropertyChanged(BR.title) notifyPropertyChanged(BR.addresses) } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/WifiApDialogFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/WifiApDialogFragment.kt index ed61142b..7a28b7f1 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/WifiApDialogFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/WifiApDialogFragment.kt @@ -16,6 +16,7 @@ import android.widget.AdapterView import android.widget.ArrayAdapter import androidx.appcompat.app.AlertDialog import androidx.appcompat.widget.Toolbar +import androidx.core.os.BuildCompat import androidx.core.view.isGone import be.mygod.vpnhotspot.AlertDialogFragment import be.mygod.vpnhotspot.App.Companion.app @@ -112,7 +113,13 @@ class WifiApDialogFragment : AlertDialogFragment= 23) { bandOptions = mutableListOf().apply { - if (arg.p2pMode) add(BandOption.BandAny) else { + if (arg.p2pMode) { + add(BandOption.BandAny) + if (BuildCompat.isAtLeastQ()) { + add(BandOption.Band2GHz) + add(BandOption.Band5GHz) + } + } else { if (Build.VERSION.SDK_INT >= 28) add(BandOption.BandAny) add(BandOption.Band2GHz) add(BandOption.Band5GHz) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/WifiConfiguration.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/WifiConfiguration.kt index 661be180..5f290a14 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/WifiConfiguration.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/WifiConfiguration.kt @@ -64,6 +64,11 @@ fun channelToFrequency(channel: Int) = when (channel) { in 15..165 -> 5000 + 5 * channel else -> throw IllegalArgumentException("Invalid channel $channel") } +fun frequencyToChannel(frequency: Int) = when (frequency % 5) { + 2 -> ((frequency - 2407) / 5).also { check(it in 1..14) { "Invalid 2.4 GHz frequency $frequency" } } + 0 -> ((frequency - 5000) / 5).also { check(it in 15..165) { "Invalid 5 GHz frequency $frequency" } } + else -> throw IllegalArgumentException("Invalid frequency $frequency") +} val WifiConfiguration.apKeyManagement get() = allowedKeyManagement.nextSetBit(0).also { selected -> check(selected >= 0) { "No key management selected" } diff --git a/mobile/src/main/res/layout/listitem_repeater.xml b/mobile/src/main/res/layout/listitem_repeater.xml index d8723abb..2e6c09c0 100644 --- a/mobile/src/main/res/layout/listitem_repeater.xml +++ b/mobile/src/main/res/layout/listitem_repeater.xml @@ -40,7 +40,7 @@ 已连设备 设置选项 + 无线中继 (%1$d MHz, 频道 %2$d) WPS(不安全) 输入 PIN 一键加密 diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml index 97f3aa67..60f79d31 100644 --- a/mobile/src/main/res/values/strings.xml +++ b/mobile/src/main/res/values/strings.xml @@ -14,6 +14,7 @@ Clients Settings + Repeater (%1$d MHz, channel %2$d) WPS (insecure) Enter PIN Push Button From 0979f6e06a615121fd85de57579a8e423faf7bad Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 5 Apr 2019 10:07:49 +0800 Subject: [PATCH 15/35] Update README for more consistent style --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e5fe8bc7..c8c20601 100644 --- a/README.md +++ b/README.md @@ -65,8 +65,8 @@ Default settings are picked to suit general use cases and maximize compatibility * Keep Wi-Fi alive: Acquire Wi-Fi locks when repeater, temporary hotspot or system VPN hotspot is activated. - Choose "System default" to save battery life; - - (up to Android 9) Choose "On" (default) if repeater/hotspot turns itself off automatically or stops working after a while; - - (up to Android 9) Choose "High Performance Mode" to minimize packet loss and latency (will consume more power); + - (prior to Android Q) Choose "On" (default) if repeater/hotspot turns itself off automatically or stops working after a while; + - (prior to Android Q) Choose "High Performance Mode" to minimize packet loss and latency (will consume more power); - (since Android Q) Choose "Disable power save" to decrease packet latency. An example use case is when a voice connection needs to be kept active even after the device screen goes off. Using this mode may improve the call quality. From bb4bf783f25b7303d29895af4bb9ae81dfe609e0 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 5 Apr 2019 10:12:37 +0800 Subject: [PATCH 16/35] Prevent calling private API to set channel on Q --- README.md | 8 ++++---- .../java/be/mygod/vpnhotspot/RepeaterService.kt | 16 +++++++++++----- .../vpnhotspot/net/wifi/WifiP2pManagerHelper.kt | 2 ++ 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index c8c20601..a57e72cd 100644 --- a/README.md +++ b/README.md @@ -131,13 +131,13 @@ Undocumented API list: * [`Landroid/net/wifi/WifiManager;->getWifiApConfiguration()Landroid/net/wifi/WifiConfiguration;,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#121357) * [`Landroid/net/wifi/WifiManager;->setWifiApConfiguration(Landroid/net/wifi/WifiConfiguration;)Z,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#121416) * (deprecated since API 26) `Landroid/net/wifi/WifiManager;->setWifiApEnabled(Landroid/net/wifi/WifiConfiguration;Z)Z` -* [`Landroid/net/wifi/p2p/WifiP2pGroup;->getNetworkId()I,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#123194) -* [`Landroid/net/wifi/p2p/WifiP2pGroupList;->getGroupList()Ljava/util/Collection;,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#123239) +* (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pGroup;->getNetworkId()I,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#123194) +* (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pGroupList;->getGroupList()Ljava/util/Collection;,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#123239) * (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pManager;->deletePersistentGroup(Landroid/net/wifi/p2p/WifiP2pManager$Channel;ILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#123431) * (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pManager;->requestPersistentGroupInfo(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Landroid/net/wifi/p2p/WifiP2pManager$PersistentGroupInfoListener;)V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#123450) -* [`Landroid/net/wifi/p2p/WifiP2pManager;->setWifiP2pChannels(Landroid/net/wifi/p2p/WifiP2pManager$Channel;IILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#123458) +* (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pManager;->setWifiP2pChannels(Landroid/net/wifi/p2p/WifiP2pManager$Channel;IILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#123458) * [`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/+/aa21a6e/appcompat/hiddenapi-flags.csv#123459) -* [`Ljava/net/InetAddress;->parseNumericAddress(Ljava/lang/String;)Ljava/net/InetAddress;,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#299587) +* (prior to API Q) [`Ljava/net/InetAddress;->parseNumericAddress(Ljava/lang/String;)Ljava/net/InetAddress;,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#299587) Undocumented system configurations: diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt index ba62bb9b..b29f4c47 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt @@ -160,14 +160,18 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere override fun onCreate() { super.onCreate() onChannelDisconnected() - if (!BuildCompat.isAtLeastQ()) @Suppress("DEPRECATION") registerReceiver(deviceListener, intentFilter( - WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION, - WifiP2pManagerHelper.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION)) - app.pref.registerOnSharedPreferenceChangeListener(this) + if (!BuildCompat.isAtLeastQ()) @Suppress("DEPRECATION") { + registerReceiver(deviceListener, intentFilter( + WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION, + WifiP2pManagerHelper.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION)) + app.pref.registerOnSharedPreferenceChangeListener(this) + } } override fun onBind(intent: Intent) = binder + @Deprecated("No longer used since Android Q") + @Suppress("DEPRECATION") private fun setOperatingChannel(oc: Int = operatingChannel) = try { val channel = channel if (channel == null) SmartSnackbar.make(R.string.repeater_failure_disconnected).show() @@ -190,13 +194,15 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere channel = null if (status != Status.DESTROYED) try { channel = p2pManager.initialize(this, Looper.getMainLooper(), this) - setOperatingChannel() + if (!BuildCompat.isAtLeastQ()) @Suppress("DEPRECATION") setOperatingChannel() } catch (e: RuntimeException) { Timber.w(e) handler.postDelayed(this::onChannelDisconnected, 1000) } } + @Deprecated("No longer used since Android Q") + @Suppress("DEPRECATION") override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { if (key == KEY_OPERATING_CHANNEL) setOperatingChannel() } 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 23657929..91b561aa 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 @@ -25,6 +25,7 @@ object WifiP2pManagerHelper { WifiP2pManager::class.java.getDeclaredMethod("setWifiP2pChannels", WifiP2pManager.Channel::class.java, Int::class.java, Int::class.java, WifiP2pManager.ActionListener::class.java) } + @Deprecated("No longer used since Android Q") fun WifiP2pManager.setWifiP2pChannels(c: WifiP2pManager.Channel, lc: Int, oc: Int, listener: WifiP2pManager.ActionListener) { try { @@ -112,5 +113,6 @@ object WifiP2pManagerHelper { * Source: https://android.googlesource.com/platform/frameworks/base/+/android-4.2_r1/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java#253 */ private val getNetworkId by lazy { WifiP2pGroup::class.java.getDeclaredMethod("getNetworkId") } + @Deprecated("No longer used since Android Q") val WifiP2pGroup.netId get() = getNetworkId.invoke(this) as Int } From ddd9ae147067da4188f4c70a5595d537f2a0a64e Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 5 Apr 2019 10:29:45 +0800 Subject: [PATCH 17/35] Remove redundant unregister --- .../src/main/java/be/mygod/vpnhotspot/RepeaterService.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt index b29f4c47..4fc54e9a 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt @@ -161,8 +161,7 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere super.onCreate() onChannelDisconnected() if (!BuildCompat.isAtLeastQ()) @Suppress("DEPRECATION") { - registerReceiver(deviceListener, intentFilter( - WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION, + registerReceiver(deviceListener, intentFilter(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION, WifiP2pManagerHelper.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION)) app.pref.registerOnSharedPreferenceChangeListener(this) } @@ -388,8 +387,10 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere handler.removeCallbacksAndMessages(null) if (status != Status.IDLE) binder.shutdown() clean() // force clean to prevent leakage - app.pref.unregisterOnSharedPreferenceChangeListener(this) - if (!BuildCompat.isAtLeastQ()) @Suppress("DEPRECATION") unregisterReceiver(deviceListener) + if (!BuildCompat.isAtLeastQ()) @Suppress("DEPRECATION") { + app.pref.unregisterOnSharedPreferenceChangeListener(this) + unregisterReceiver(deviceListener) + } status = Status.DESTROYED if (Build.VERSION.SDK_INT >= 27) channel?.close() super.onDestroy() From 59984f1e7a17b3be34a17138562409385003a954 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 5 Apr 2019 10:48:18 +0800 Subject: [PATCH 18/35] Update undocumented API list to Q beta 2 --- README.md | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index a57e72cd..bb1ce8d7 100644 --- a/README.md +++ b/README.md @@ -121,23 +121,23 @@ This is only meant to be an index. You can read more in the source code. Undocumented API list: -* (since API 24) [`Landroid/bluetooth/BluetoothPan;->isTetheringOn()Z,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#28703) -* (since API 24) [`Landroid/net/ConnectivityManager$OnStartTetheringCallback;->()V,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#112695) -* (since API 24) [`Landroid/net/ConnectivityManager$OnStartTetheringCallback;->onTetheringFailed()V,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#112696) -* (since API 24) [`Landroid/net/ConnectivityManager$OnStartTetheringCallback;->onTetheringStarted()V,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#112697) -* (since API 24) [`Landroid/net/ConnectivityManager;->getLastTetherError(Ljava/lang/String;)I,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#112882) -* (since API 24) [`Landroid/net/ConnectivityManager;->startTethering(IZLandroid/net/ConnectivityManager$OnStartTetheringCallback;Landroid/os/Handler;)V,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#112972) -* (since API 24) [`Landroid/net/ConnectivityManager;->stopTethering(I)V,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#112974) -* [`Landroid/net/wifi/WifiManager;->getWifiApConfiguration()Landroid/net/wifi/WifiConfiguration;,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#121357) -* [`Landroid/net/wifi/WifiManager;->setWifiApConfiguration(Landroid/net/wifi/WifiConfiguration;)Z,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#121416) +* (since API 24) [`Landroid/bluetooth/BluetoothPan;->isTetheringOn()Z,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#31689) +* (since API 24) [`Landroid/net/ConnectivityManager$OnStartTetheringCallback;->()V,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#122391) +* (since API 24) [`Landroid/net/ConnectivityManager$OnStartTetheringCallback;->onTetheringFailed()V,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#122392) +* (since API 24) [`Landroid/net/ConnectivityManager$OnStartTetheringCallback;->onTetheringStarted()V,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#122393) +* (since API 24) [`Landroid/net/ConnectivityManager;->getLastTetherError(Ljava/lang/String;)I,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#122588) +* (since API 24) [`Landroid/net/ConnectivityManager;->startTethering(IZLandroid/net/ConnectivityManager$OnStartTetheringCallback;Landroid/os/Handler;)V,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#122683) +* (since API 24) [`Landroid/net/ConnectivityManager;->stopTethering(I)V,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#122685) +* [`Landroid/net/wifi/WifiManager;->getWifiApConfiguration()Landroid/net/wifi/WifiConfiguration;,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#131756) +* [`Landroid/net/wifi/WifiManager;->setWifiApConfiguration(Landroid/net/wifi/WifiConfiguration;)Z,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#131825) * (deprecated since API 26) `Landroid/net/wifi/WifiManager;->setWifiApEnabled(Landroid/net/wifi/WifiConfiguration;Z)Z` -* (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pGroup;->getNetworkId()I,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#123194) -* (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pGroupList;->getGroupList()Ljava/util/Collection;,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#123239) -* (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pManager;->deletePersistentGroup(Landroid/net/wifi/p2p/WifiP2pManager$Channel;ILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#123431) -* (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pManager;->requestPersistentGroupInfo(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Landroid/net/wifi/p2p/WifiP2pManager$PersistentGroupInfoListener;)V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#123450) -* (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pManager;->setWifiP2pChannels(Landroid/net/wifi/p2p/WifiP2pManager$Channel;IILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#123458) -* [`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/+/aa21a6e/appcompat/hiddenapi-flags.csv#123459) -* (prior to API Q) [`Ljava/net/InetAddress;->parseNumericAddress(Ljava/lang/String;)Ljava/net/InetAddress;,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#299587) +* (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pGroup;->getNetworkId()I,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#133878) +* (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pGroupList;->getGroupList()Ljava/util/Collection;,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#133925) +* (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pManager;->deletePersistentGroup(Landroid/net/wifi/p2p/WifiP2pManager$Channel;ILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#134141) +* (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pManager;->requestPersistentGroupInfo(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Landroid/net/wifi/p2p/WifiP2pManager$PersistentGroupInfoListener;)V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#134166) +* (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pManager;->setWifiP2pChannels(Landroid/net/wifi/p2p/WifiP2pManager$Channel;IILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#134175) +* [`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/+/7cb2ccf/appcompat/hiddenapi-flags.csv#134176) +* (prior to API Q) [`Ljava/net/InetAddress;->parseNumericAddress(Ljava/lang/String;)Ljava/net/InetAddress;,core-platform-api,greylist-max-p`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#332821) Undocumented system configurations: @@ -149,21 +149,21 @@ Undocumented system configurations: Other: * (since API 29) `android.net.wifi.p2p.WifiP2pConfig` needs to be parcelized in a very specific order. -* (since API 27) [`Landroid/provider/Settings$Global;->TETHER_OFFLOAD_DISABLED:Ljava/lang/String;,greylist-max-o`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#144760) is assumed to be `"tether_offload_disabled"`. +* (since API 27) [`Landroid/provider/Settings$Global;->TETHER_OFFLOAD_DISABLED:Ljava/lang/String;,greylist-max-o`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#157569) is assumed to be `"tether_offload_disabled"`. * (since API 27) `com.android.server.connectivity.tethering.OffloadHardwareInterface.DEFAULT_TETHER_OFFLOAD_DISABLED` is assumed to be 0. * Several constants in `ConnectivityManager` is assumed to be defined as in `TetheringManager.kt`; * Following broadcasts are assumed to be sticky: - - [`Landroid/net/ConnectivityManager;->ACTION_TETHER_STATE_CHANGED:Ljava/lang/String;,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#112743) is assumed to be `android.net.conn.TETHER_STATE_CHANGED`. - - [`Landroid/net/wifi/p2p/WifiP2pManager;->WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION:Ljava/lang/String;,greylist-max-o`](https://android.googlesource.com/platform/prebuilts/runtime/+/aa21a6e/appcompat/hiddenapi-flags.csv#123415) is assumed to be `android.net.wifi.p2p.PERSISTENT_GROUPS_CHANGED`; + - [`Landroid/net/ConnectivityManager;->ACTION_TETHER_STATE_CHANGED:Ljava/lang/String;,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#122441) is assumed to be `android.net.conn.TETHER_STATE_CHANGED`. + - (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pManager;->WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION:Ljava/lang/String;,greylist-max-o`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#134124) is assumed to be `android.net.wifi.p2p.PERSISTENT_GROUPS_CHANGED`; * 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; `RULE_PRIORITY_DEFAULT_NETWORK` is assumed to be 22000 (or at least > 18000) for API 27-. +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). DHCP server like `dnsmasq` is assumed to run and send DHCP packets as root. Undocumented system binaries are all bundled and executable: -* Since API 24: `iptables-save`; +* (since API 24) `iptables-save`; * `echo`; * `ip` (`link monitor neigh rule` with proper output format); * `ndc` (`ipfwd` with proper output format since API 23, `nat`); @@ -172,7 +172,7 @@ Undocumented system binaries are all bundled and executable: If some of these are unavailable, you can alternatively install a recent version (v1.28.1 or higher) of Busybox. -Wi-Fi driver `wpa_supplicant`: +(prior to API Q) Wi-Fi driver `wpa_supplicant`: * P2P configuration file is assumed to be saved to [`/data/vendor/wifi/wpa/p2p_supplicant.conf` or `/data/misc/wifi/p2p_supplicant.conf`](https://android.googlesource.com/platform/external/wpa_supplicant_8/+/0b4856b6dc451e290f1f64f6af17e010be78c073/wpa_supplicant/hidl/1.1/supplicant.cpp#26) and have reasonable format; * Android system is expected to restart `wpa_supplicant` after it crashes. From 3f0d1407be884379b11747bffa742eead4c825f1 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 5 Apr 2019 11:19:12 +0800 Subject: [PATCH 19/35] Add missing deprecation --- 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 4fc54e9a..a0dee289 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt @@ -54,6 +54,7 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere } } val supported get() = p2pManager != null + @Deprecated("Not initialized and no use at all since Android Q") var persistentSupported = false var networkName: String? From 722a8e616ee336411285cc1bbd28ff793a968896 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 5 Apr 2019 12:15:21 +0800 Subject: [PATCH 20/35] Add missing import removed by merge --- 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 410b6a00..8484de90 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt @@ -13,6 +13,7 @@ import android.os.Looper import androidx.annotation.StringRes import androidx.core.content.edit import androidx.core.content.getSystemService +import androidx.core.os.BuildCompat import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.deletePersistentGroup From 5cbf65b0969d2ad74fe9c6a0eaff47edf1abf350 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 5 Apr 2019 12:40:24 +0800 Subject: [PATCH 21/35] Update gradle.properties --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 5066c426..64666031 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,8 +10,8 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. android.enableJetifier=true -android.enableR8=true android.enableR8.fullMode=true +android.injected.testOnly=false android.useAndroidX=true org.gradle.jvmargs=-Xmx1536m From 313f0da7f950917fc76aeb0425f82800061ae365 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sun, 7 Apr 2019 12:56:24 +0800 Subject: [PATCH 22/35] Add missing translations --- mobile/src/main/res/values-zh-rCN/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mobile/src/main/res/values-zh-rCN/strings.xml b/mobile/src/main/res/values-zh-rCN/strings.xml index 7402edda..eab8ea62 100644 --- a/mobile/src/main/res/values-zh-rCN/strings.xml +++ b/mobile/src/main/res/values-zh-rCN/strings.xml @@ -90,6 +90,8 @@ 系统默认 高性能模式 + 禁用省电 + 低延迟模式 网络状态监听模式 Netlink 监听 Netlink 监听 (root) From 219d868089593a1179af2837f7b5632f797e59fb Mon Sep 17 00:00:00 2001 From: Mygod Date: Sun, 14 Apr 2019 12:24:17 +0800 Subject: [PATCH 23/35] Handle new nullability issues --- .../be/mygod/vpnhotspot/RepeaterService.kt | 80 ++++++++++--------- .../configuration/WifiApDialogFragment.kt | 4 +- 2 files changed, 44 insertions(+), 40 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt index 8484de90..5a083229 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt @@ -271,52 +271,56 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere /** * startService Step 2 (if a group isn't already available) */ - private fun doStart() = try { + private fun doStart() { val listener = object : WifiP2pManager.ActionListener { override fun onFailure(reason: Int) { startFailure(formatReason(R.string.repeater_create_group_failure, reason)) } override fun onSuccess() { } // wait for WIFI_P2P_CONNECTION_CHANGED_ACTION to fire to go to step 3 } + val channel = channel ?: return listener.onFailure(WifiP2pManager.BUSY) val networkName = networkName val passphrase = passphrase - if (!BuildCompat.isAtLeastQ() || networkName == null || passphrase == null) { - persistNextGroup = true - p2pManager.createGroup(channel, listener) - } else p2pManager.createGroup(channel, WifiP2pConfig.Builder().apply { - setNetworkName(PLACEHOLDER_NETWORK_NAME) - setPassphrase(passphrase) - val channel = operatingChannel - if (channel == 0) setGroupOperatingBand(operatingBand) - else setGroupOperatingFrequency(channelToFrequency(channel)) - }.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 int = p.readInt() - check(p.dataPosition() == end) - p.setDataPosition(0) - p.writeString(creator) - p.writeString(deviceAddress) - p.writeParcelable(wps, 0) - p.writeLong(long) - p.writeString(networkName) - p.writeString(passphrase) - p.writeInt(int) - p.setDataPosition(0) - p.readParcelable(javaClass.classLoader) - } - }, listener) - } catch (e: SecurityException) { - Timber.w(e) - startFailure(e.readableMessage) + try { + if (!BuildCompat.isAtLeastQ() || networkName == null || passphrase == null) { + persistNextGroup = true + p2pManager.createGroup(channel, listener) + } else p2pManager.createGroup(channel, WifiP2pConfig.Builder().apply { + setNetworkName(PLACEHOLDER_NETWORK_NAME) + setPassphrase(passphrase) + operatingChannel.let { oc -> + if (oc == 0) setGroupOperatingBand(operatingBand) + else setGroupOperatingFrequency(channelToFrequency(oc)) + } + }.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 int = p.readInt() + check(p.dataPosition() == end) + p.setDataPosition(0) + p.writeString(creator) + p.writeString(deviceAddress) + p.writeParcelable(wps, 0) + p.writeLong(long) + p.writeString(networkName) + p.writeString(passphrase) + p.writeInt(int) + p.setDataPosition(0) + p.readParcelable(javaClass.classLoader) + } + }, listener) + } catch (e: SecurityException) { + Timber.w(e) + startFailure(e.readableMessage) + } } /** * Used during step 2, also called when connection changed diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/WifiApDialogFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/WifiApDialogFragment.kt index 58e21a67..f0af651c 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/WifiApDialogFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/WifiApDialogFragment.kt @@ -177,8 +177,8 @@ class WifiApDialogFragment : AlertDialogFragment { - app.clipboard.primaryClip = ClipData.newPlainText(null, - Base64.encodeToString(ret.configuration.toByteArray(), BASE64_FLAGS)) + app.clipboard.setPrimaryClip(ClipData.newPlainText(null, + Base64.encodeToString(ret.configuration.toByteArray(), BASE64_FLAGS))) true } android.R.id.paste -> { From 8bfbe8ce53a4821d6a7074bdd58f1135928b3332 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sun, 14 Apr 2019 13:36:59 +0800 Subject: [PATCH 24/35] Refine code style --- mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt | 2 +- mobile/src/main/java/be/mygod/vpnhotspot/client/MacLookup.kt | 2 +- .../main/java/be/mygod/vpnhotspot/net/wifi/WifiDoubleLock.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt index 5a083229..1ae1e42d 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(), WifiP2pManager.ChannelListener, SharedPrefere val result = app.pref.getString(KEY_OPERATING_CHANNEL, null)?.toIntOrNull() ?: 0 return if (result in 1..165) result else 0 } - set(value) = app.pref.edit { putString(RepeaterService.KEY_OPERATING_CHANNEL, value.toString()) } + set(value) = app.pref.edit { putString(KEY_OPERATING_CHANNEL, value.toString()) } } enum class Status { diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/client/MacLookup.kt b/mobile/src/main/java/be/mygod/vpnhotspot/client/MacLookup.kt index 4a89e2b0..cbcbcf0c 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/client/MacLookup.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/client/MacLookup.kt @@ -73,7 +73,7 @@ object MacLookup { private fun extractCountry(mac: Long, response: String, obj: JSONObject): MatchResult? { countryCodeRegex.matchEntire(obj.optString("country"))?.also { return it } val address = obj.optString("address") - if (address.isNullOrBlank()) return null + if (address.isBlank()) return null countryCodeRegex.find(address)?.also { return it } Timber.w(UnexpectedError(mac, response)) return null diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiDoubleLock.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiDoubleLock.kt index 7032d710..64078939 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiDoubleLock.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiDoubleLock.kt @@ -69,7 +69,7 @@ class WifiDoubleLock(lockType: Int) : AutoCloseable { LowLatency(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, true), } - class ActivityListener(val activity: ComponentActivity) : + class ActivityListener(private val activity: ComponentActivity) : LifecycleObserver, SharedPreferences.OnSharedPreferenceChangeListener { private var keepScreenOn: Boolean = false set(value) { From 34d232afd68824bc11b3637ae14a263e0ecb3d75 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 10 May 2019 13:44:43 +0800 Subject: [PATCH 25/35] Update beta dependencies because it is called beta branch --- build.gradle | 2 +- mobile/build.gradle | 12 +++++------ .../java/be/mygod/vpnhotspot/MainActivity.kt | 6 +++--- .../java/be/mygod/vpnhotspot/client/Client.kt | 20 +++++++++---------- .../vpnhotspot/client/ClientsFragment.kt | 7 ++++--- 5 files changed, 23 insertions(+), 24 deletions(-) diff --git a/build.gradle b/build.gradle index b6668a65..a5a469d7 100644 --- a/build.gradle +++ b/build.gradle @@ -12,7 +12,7 @@ buildscript { } } dependencies { - classpath 'com.android.tools.build:gradle:3.4.0' + classpath 'com.android.tools.build:gradle:3.5.0-beta01' classpath 'com.github.ben-manes:gradle-versions-plugin:0.21.0' classpath 'com.google.gms:google-services:4.2.0' classpath 'io.fabric.tools:gradle:1.28.1' diff --git a/mobile/build.gradle b/mobile/build.gradle index 654cb35d..703c1039 100644 --- a/mobile/build.gradle +++ b/mobile/build.gradle @@ -67,16 +67,17 @@ def aux = [ 'com.crashlytics.sdk.android:crashlytics:2.10.0', 'com.google.firebase:firebase-core:16.0.9', ] -def lifecycleVersion = '2.0.0' +def lifecycleVersion = '2.1.0-beta01' def roomVersion = '2.1.0-beta01' dependencies { kapt "androidx.room:room-compiler:$roomVersion" implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.browser:browser:1.0.0' - implementation 'androidx.core:core-ktx:1.0.2' + implementation 'androidx.core:core-ktx:1.1.0-beta01' implementation 'androidx.emoji:emoji:1.0.0' implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion" implementation "androidx.lifecycle:lifecycle-extensions:$lifecycleVersion" + implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion" implementation 'androidx.preference:preference:1.1.0-alpha05' implementation "androidx.room:room-ktx:$roomVersion" @@ -93,10 +94,9 @@ dependencies { freedomImplementation dep googleImplementation dep } - testImplementation "androidx.arch.core:core-testing:$lifecycleVersion" testImplementation 'junit:junit:4.12' androidTestImplementation "androidx.room:room-testing:$roomVersion" - androidTestImplementation 'androidx.test:runner:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' - androidTestImplementation 'androidx.test.ext:junit-ktx:1.1.0' + androidTestImplementation 'androidx.test:runner:1.2.0-beta01' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0-beta01' + androidTestImplementation 'androidx.test.ext:junit-ktx:1.1.1-beta01' } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/MainActivity.kt b/mobile/src/main/java/be/mygod/vpnhotspot/MainActivity.kt index 3e48b877..ff2328c1 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/MainActivity.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/MainActivity.kt @@ -7,9 +7,9 @@ import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import androidx.databinding.DataBindingUtil import androidx.fragment.app.Fragment -import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProviders import androidx.lifecycle.get +import androidx.lifecycle.observe import be.mygod.vpnhotspot.client.ClientViewModel import be.mygod.vpnhotspot.client.ClientsFragment import be.mygod.vpnhotspot.databinding.ActivityMainBinding @@ -30,13 +30,13 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS if (savedInstanceState == null) displayFragment(TetheringFragment()) val model = ViewModelProviders.of(this).get() if (RepeaterService.supported) ServiceForegroundConnector(this, model, RepeaterService::class) - model.clients.observe(this, Observer { + model.clients.observe(this) { if (it.isNotEmpty()) binding.navigation.showBadge(R.id.navigation_clients).apply { backgroundColor = ContextCompat.getColor(this@MainActivity, R.color.colorSecondary) badgeTextColor = ContextCompat.getColor(this@MainActivity, R.color.primary_text_default_material_light) number = it.size } else binding.navigation.removeBadge(R.id.navigation_clients) - }) + } SmartSnackbar.Register(lifecycle, binding.fragmentHolder) WifiDoubleLock.ActivityListener(this) } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/client/Client.kt b/mobile/src/main/java/be/mygod/vpnhotspot/client/Client.kt index 783682dc..2631e70f 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/client/Client.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/client/Client.kt @@ -3,7 +3,7 @@ package be.mygod.vpnhotspot.client import android.text.SpannableStringBuilder import android.text.Spanned import android.text.style.StrikethroughSpan -import androidx.lifecycle.Transformations +import androidx.lifecycle.map import androidx.recyclerview.widget.DiffUtil import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.R @@ -37,24 +37,22 @@ open class Client(val mac: Long, val iface: String) { val blocked get() = record.value?.blocked == true open val icon get() = TetherType.ofInterface(iface).icon - val title = Transformations.map(record) { record -> + val title = record.map { record -> /** * we hijack the get title process to check if we need to perform MacLookup, * as record might not be initialized in other more appropriate places */ - SpannableStringBuilder(if (record?.nickname.isNullOrEmpty()) { - if (record?.macLookupPending != false) MacLookup.perform(mac) + SpannableStringBuilder(if (record.nickname.isEmpty()) { + if (record.macLookupPending) MacLookup.perform(mac) macIface - } else emojize(record?.nickname)).apply { - if (record?.blocked == true) { - setSpan(StrikethroughSpan(), 0, length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) - } + } else emojize(record.nickname)).apply { + if (record.blocked) setSpan(StrikethroughSpan(), 0, length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) } } - val titleSelectable = Transformations.map(record) { it?.nickname.isNullOrEmpty() } - val description = Transformations.map(record) { record -> + val titleSelectable = record.map { it.nickname.isEmpty() } + val description = record.map { record -> SpannableStringBuilder().apply { - if (!record?.nickname.isNullOrEmpty()) appendln(macIface) + if (!record.nickname.isEmpty()) appendln(macIface) ip.entries.forEach { (ip, state) -> append(makeIpSpan(ip)) appendln(app.getText(when (state) { 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 de9d3b51..b9549ab6 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientsFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientsFragment.kt @@ -17,9 +17,9 @@ import androidx.appcompat.widget.PopupMenu import androidx.databinding.BaseObservable import androidx.databinding.DataBindingUtil import androidx.fragment.app.Fragment -import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProviders import androidx.lifecycle.get +import androidx.lifecycle.observe import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.ListAdapter @@ -219,8 +219,9 @@ class ClientsFragment : Fragment() { binding.swipeRefresher.setOnRefreshListener { IpNeighbourMonitor.instance?.flush() } - ViewModelProviders.of(requireActivity()).get().clients.observe(this, - Observer { adapter.submitList(it.toMutableList()) }) + ViewModelProviders.of(requireActivity()).get().clients.observe(this) { + adapter.submitList(it.toMutableList()) + } return binding.root } From 965bd13eac67f04f6fb7dde27c395b194836b7e5 Mon Sep 17 00:00:00 2001 From: Mygod Date: Mon, 13 May 2019 18:41:58 +0800 Subject: [PATCH 26/35] Fix nullability issues --- mobile/src/main/java/be/mygod/vpnhotspot/client/Client.kt | 2 +- mobile/src/main/java/be/mygod/vpnhotspot/net/Routing.kt | 6 +++--- .../src/main/java/be/mygod/vpnhotspot/room/ClientRecord.kt | 7 ++++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/client/Client.kt b/mobile/src/main/java/be/mygod/vpnhotspot/client/Client.kt index 2631e70f..9c905a21 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/client/Client.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/client/Client.kt @@ -27,7 +27,7 @@ open class Client(val mac: Long, val iface: String) { val ip = TreeMap(InetAddressComparator) val macString by lazy { mac.macToString() } - private val record = AppDatabase.instance.clientRecordDao.lookupSync(mac) + private val record = AppDatabase.instance.clientRecordDao.lookupOrDefaultSync(mac) private val macIface get() = SpannableStringBuilder(makeMacSpan(macString)).apply { append('%') append(iface) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/Routing.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/Routing.kt index 51ccf72c..73979e1b 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/Routing.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/Routing.kt @@ -200,9 +200,9 @@ class Routing(private val caller: Any, private val downstream: String) : IpNeigh override fun onIpNeighbourAvailable(neighbours: List) = synchronized(this) { val toRemove = HashSet(clients.keys) for (neighbour in neighbours) { - if (neighbour.dev != downstream || neighbour.ip !is Inet4Address || - runBlocking { AppDatabase.instance.clientRecordDao.lookup(neighbour.lladdr) } - ?.blocked == true) continue + if (neighbour.dev != downstream || neighbour.ip !is Inet4Address || runBlocking { + AppDatabase.instance.clientRecordDao.lookupOrDefault(neighbour.lladdr) + }.blocked) continue toRemove.remove(neighbour.ip) try { clients.computeIfAbsentCompat(neighbour.ip) { Client(neighbour.ip, neighbour.lladdr) } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/room/ClientRecord.kt b/mobile/src/main/java/be/mygod/vpnhotspot/room/ClientRecord.kt index 786e6dc6..4504eca1 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/room/ClientRecord.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/room/ClientRecord.kt @@ -1,6 +1,7 @@ package be.mygod.vpnhotspot.room import androidx.lifecycle.LiveData +import androidx.lifecycle.map import androidx.room.* @Entity @@ -12,12 +13,12 @@ data class ClientRecord(@PrimaryKey @androidx.room.Dao abstract class Dao { @Query("SELECT * FROM `ClientRecord` WHERE `mac` = :mac") - abstract suspend fun lookup(mac: Long): ClientRecord? - + protected abstract suspend fun lookup(mac: Long): ClientRecord? suspend fun lookupOrDefault(mac: Long) = lookup(mac) ?: ClientRecord(mac) @Query("SELECT * FROM `ClientRecord` WHERE `mac` = :mac") - abstract fun lookupSync(mac: Long): LiveData + protected abstract fun lookupSync(mac: Long): LiveData + fun lookupOrDefaultSync(mac: Long) = lookupSync(mac).map { it ?: ClientRecord(mac) } @Insert(onConflict = OnConflictStrategy.REPLACE) protected abstract suspend fun updateInternal(value: ClientRecord): Long From b5be56608e341cc3976855419a5c6afaf8f624a8 Mon Sep 17 00:00:00 2001 From: Mygod Date: Wed, 22 May 2019 18:01:30 +0800 Subject: [PATCH 27/35] Update doc to Q beta 3 --- README.md | 44 ++++++++++++++++++++++---------------------- build.gradle | 2 +- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 9fc189ea..5d334f27 100644 --- a/README.md +++ b/README.md @@ -119,27 +119,27 @@ _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. -Undocumented API list: +Non-public API list: -* (since API 24) [`Landroid/bluetooth/BluetoothPan;->isTetheringOn()Z,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#31689) -* (since API 24) [`Landroid/net/ConnectivityManager$OnStartTetheringCallback;->()V,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#122391) -* (since API 24) [`Landroid/net/ConnectivityManager$OnStartTetheringCallback;->onTetheringFailed()V,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#122392) -* (since API 24) [`Landroid/net/ConnectivityManager$OnStartTetheringCallback;->onTetheringStarted()V,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#122393) -* (since API 24) [`Landroid/net/ConnectivityManager;->getLastTetherError(Ljava/lang/String;)I,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#122588) -* (since API 24) [`Landroid/net/ConnectivityManager;->startTethering(IZLandroid/net/ConnectivityManager$OnStartTetheringCallback;Landroid/os/Handler;)V,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#122683) -* (since API 24) [`Landroid/net/ConnectivityManager;->stopTethering(I)V,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#122685) -* (since API 23) [`Landroid/net/wifi/WifiConfiguration;->apBand:I,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#131003) -* (since API 23) [`Landroid/net/wifi/WifiConfiguration;->apChannel:I,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#131004) -* [`Landroid/net/wifi/WifiManager;->getWifiApConfiguration()Landroid/net/wifi/WifiConfiguration;,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#131756) -* [`Landroid/net/wifi/WifiManager;->setWifiApConfiguration(Landroid/net/wifi/WifiConfiguration;)Z,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#131825) +* (since API 24) [`Landroid/bluetooth/BluetoothPan;->isTetheringOn()Z,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#32103) +* (since API 24) [`Landroid/net/ConnectivityManager$OnStartTetheringCallback;->()V,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#123103) +* (since API 24) [`Landroid/net/ConnectivityManager$OnStartTetheringCallback;->onTetheringFailed()V,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#123104) +* (since API 24) [`Landroid/net/ConnectivityManager$OnStartTetheringCallback;->onTetheringStarted()V,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#123105) +* (since API 24) [`Landroid/net/ConnectivityManager;->getLastTetherError(Ljava/lang/String;)I,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#123309) +* (since API 24) [`Landroid/net/ConnectivityManager;->startTethering(IZLandroid/net/ConnectivityManager$OnStartTetheringCallback;Landroid/os/Handler;)V,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#123408) +* (since API 24) [`Landroid/net/ConnectivityManager;->stopTethering(I)V,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#123410) +* (since API 23) [`Landroid/net/wifi/WifiConfiguration;->apBand:I,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#131529) +* (since API 23) [`Landroid/net/wifi/WifiConfiguration;->apChannel:I,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#131530) +* [`Landroid/net/wifi/WifiManager;->getWifiApConfiguration()Landroid/net/wifi/WifiConfiguration;,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#132289) +* [`Landroid/net/wifi/WifiManager;->setWifiApConfiguration(Landroid/net/wifi/WifiConfiguration;)Z,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#132358) * (deprecated since API 26) `Landroid/net/wifi/WifiManager;->setWifiApEnabled(Landroid/net/wifi/WifiConfiguration;Z)Z` -* (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pGroup;->getNetworkId()I,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#133878) -* (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pGroupList;->getGroupList()Ljava/util/Collection;,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#133925) -* (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pManager;->deletePersistentGroup(Landroid/net/wifi/p2p/WifiP2pManager$Channel;ILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#134141) -* (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pManager;->requestPersistentGroupInfo(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Landroid/net/wifi/p2p/WifiP2pManager$PersistentGroupInfoListener;)V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#134166) -* (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pManager;->setWifiP2pChannels(Landroid/net/wifi/p2p/WifiP2pManager$Channel;IILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#134175) -* [`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/+/7cb2ccf/appcompat/hiddenapi-flags.csv#134176) -* (prior to API Q) [`Ljava/net/InetAddress;->parseNumericAddress(Ljava/lang/String;)Ljava/net/InetAddress;,core-platform-api,greylist-max-p`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#332821) +* (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pGroup;->getNetworkId()I,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#134440) +* (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pGroupList;->getGroupList()Ljava/util/Collection;,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#134487) +* (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pManager;->deletePersistentGroup(Landroid/net/wifi/p2p/WifiP2pManager$Channel;ILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#134703) +* (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pManager;->requestPersistentGroupInfo(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Landroid/net/wifi/p2p/WifiP2pManager$PersistentGroupInfoListener;)V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#134728) +* (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pManager;->setWifiP2pChannels(Landroid/net/wifi/p2p/WifiP2pManager$Channel;IILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#134737) +* [`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/+/3d07e5c/appcompat/hiddenapi-flags.csv#134738) +* (prior to API Q) [`Ljava/net/InetAddress;->parseNumericAddress(Ljava/lang/String;)Ljava/net/InetAddress;,core-platform-api,greylist-max-p`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#335306) Undocumented system configurations: @@ -151,12 +151,12 @@ Undocumented system configurations: Other: * (since API 29) `android.net.wifi.p2p.WifiP2pConfig` needs to be parcelized in a very specific order. -* (since API 27) [`Landroid/provider/Settings$Global;->TETHER_OFFLOAD_DISABLED:Ljava/lang/String;,greylist-max-o`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#157569) is assumed to be `"tether_offload_disabled"`. +* (since API 27) [`Landroid/provider/Settings$Global;->TETHER_OFFLOAD_DISABLED:Ljava/lang/String;,greylist-max-o`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#158331) is assumed to be `"tether_offload_disabled"`. * (since API 27) `com.android.server.connectivity.tethering.OffloadHardwareInterface.DEFAULT_TETHER_OFFLOAD_DISABLED` is assumed to be 0. * Several constants in `ConnectivityManager` is assumed to be defined as in `TetheringManager.kt`; * Following broadcasts are assumed to be sticky: - - [`Landroid/net/ConnectivityManager;->ACTION_TETHER_STATE_CHANGED:Ljava/lang/String;,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#122441) is assumed to be `android.net.conn.TETHER_STATE_CHANGED`. - - (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pManager;->WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION:Ljava/lang/String;,greylist-max-o`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#134124) is assumed to be `android.net.wifi.p2p.PERSISTENT_GROUPS_CHANGED`; + - [`Landroid/net/ConnectivityManager;->ACTION_TETHER_STATE_CHANGED:Ljava/lang/String;,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#123163) is assumed to be `android.net.conn.TETHER_STATE_CHANGED`. + - (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pManager;->WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION:Ljava/lang/String;,greylist-max-o`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#134686) is assumed to be `android.net.wifi.p2p.PERSISTENT_GROUPS_CHANGED`; * 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; diff --git a/build.gradle b/build.gradle index 7b66043c..69aa8504 100644 --- a/build.gradle +++ b/build.gradle @@ -12,7 +12,7 @@ buildscript { } } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0-beta01' + classpath 'com.android.tools.build:gradle:3.5.0-beta02' classpath 'com.github.ben-manes:gradle-versions-plugin:0.21.0' classpath 'com.google.gms:google-services:4.2.0' classpath 'io.fabric.tools:gradle:1.29.0' From c0e40436df650195a82616ec1351fd27ea76099b Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 1 Jun 2019 07:33:23 +0800 Subject: [PATCH 28/35] Update dependencies --- build.gradle | 2 +- mobile/build.gradle | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index 5160a8b0..c3d52971 100644 --- a/build.gradle +++ b/build.gradle @@ -12,7 +12,7 @@ buildscript { } } dependencies { - classpath 'com.android.tools.build:gradle:3.4.0' + classpath 'com.android.tools.build:gradle:3.4.1' classpath 'com.github.ben-manes:gradle-versions-plugin:0.21.0' classpath 'com.google.gms:google-services:4.2.0' classpath 'io.fabric.tools:gradle:1.29.0' diff --git a/mobile/build.gradle b/mobile/build.gradle index 9acc422f..ffd72465 100644 --- a/mobile/build.gradle +++ b/mobile/build.gradle @@ -65,11 +65,11 @@ androidExtensions { } def aux = [ - 'com.crashlytics.sdk.android:crashlytics:2.10.0', + 'com.crashlytics.sdk.android:crashlytics:2.10.1', 'com.google.firebase:firebase-core:16.0.9', ] def lifecycleVersion = '2.0.0' -def roomVersion = '2.1.0-beta01' +def roomVersion = '2.1.0-rc01' dependencies { kapt "androidx.room:room-compiler:$roomVersion" implementation fileTree(dir: 'libs', include: ['*.jar']) @@ -83,7 +83,7 @@ dependencies { implementation "androidx.room:room-ktx:$roomVersion" implementation 'com.android.billingclient:billing:2.0.0' implementation 'com.github.topjohnwu.libsu:core:2.5.0' - implementation 'com.google.android.material:material:1.1.0-alpha06' + implementation 'com.google.android.material:material:1.1.0-alpha07' implementation 'com.jakewharton.timber:timber:4.7.1' implementation 'com.linkedin.dexmaker:dexmaker:2.25.0' implementation 'com.takisoft.preferencex:preferencex-simplemenu:1.0.0' @@ -97,7 +97,7 @@ dependencies { testImplementation "androidx.arch.core:core-testing:$lifecycleVersion" testImplementation 'junit:junit:4.12' androidTestImplementation "androidx.room:room-testing:$roomVersion" - androidTestImplementation 'androidx.test:runner:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' androidTestImplementation 'androidx.test.ext:junit-ktx:1.1.0' } From d168dea613673b4bb8361f1f7f1d17e064a905a4 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 1 Jun 2019 07:39:23 +0800 Subject: [PATCH 29/35] Refine apKeyManagement --- .../vpnhotspot/net/wifi/configuration/WifiConfiguration.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/WifiConfiguration.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/WifiConfiguration.kt index 661be180..33dfd469 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/WifiConfiguration.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/WifiConfiguration.kt @@ -65,9 +65,9 @@ fun channelToFrequency(channel: Int) = when (channel) { else -> throw IllegalArgumentException("Invalid channel $channel") } -val WifiConfiguration.apKeyManagement get() = allowedKeyManagement.nextSetBit(0).also { selected -> - check(selected >= 0) { "No key management selected" } +val WifiConfiguration.apKeyManagement get() = allowedKeyManagement.nextSetBit(0).let { selected -> check(allowedKeyManagement.nextSetBit(selected + 1) < 0) { "More than 1 key managements supplied" } + if (selected < 0) WifiConfiguration.KeyMgmt.NONE else selected // getAuthType returns NONE if nothing is selected } private val qrSanitizer = Regex("([\\\\\":;,])") From 8795941bab019cd975d3dd97de733ce1505a7426 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 1 Jun 2019 07:42:45 +0800 Subject: [PATCH 30/35] Prevent crash when sharing invalid Wi-Fi config --- .../net/wifi/configuration/WifiApDialogFragment.kt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/WifiApDialogFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/WifiApDialogFragment.kt index 1e8a292c..d0fef699 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/WifiApDialogFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/WifiApDialogFragment.kt @@ -187,8 +187,13 @@ class WifiApDialogFragment : AlertDialogFragment { - QRCodeDialog().withArg(ret.configuration.toQRString()) - .show(fragmentManager ?: return false, "QRCodeDialog") + val qrString = try { + ret.configuration.toQRString() + } catch (e: IllegalArgumentException) { + SmartSnackbar.make(e).show() + return false + } + QRCodeDialog().withArg(qrString).show(fragmentManager ?: return false, "QRCodeDialog") true } else -> false From b7b4298fb4b576d1d790fd56cdc71ec898ff9f3c Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 1 Jun 2019 07:48:26 +0800 Subject: [PATCH 31/35] Catch remote exceptions while setting wifi config --- .../main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt | 2 ++ 1 file changed, 2 insertions(+) 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 870bf533..e5bc35eb 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt @@ -189,6 +189,8 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick WifiApManager.configuration = configuration } catch (e: IllegalArgumentException) { SmartSnackbar.make(R.string.configuration_rejected).show() + } catch (e: InvocationTargetException) { + SmartSnackbar.make(e.targetException).show() } else -> super.onActivityResult(requestCode, resultCode, data) } From 0cb28477e473da6acd1119da725870225a2f4332 Mon Sep 17 00:00:00 2001 From: Mygod Date: Mon, 10 Jun 2019 21:34:37 +0800 Subject: [PATCH 32/35] Android Q beta 4 support --- build.gradle | 2 +- mobile/build.gradle | 10 +++++----- .../main/java/be/mygod/vpnhotspot/RepeaterService.kt | 9 ++++----- .../vpnhotspot/manage/LocalOnlyHotspotManager.kt | 3 +-- .../be/mygod/vpnhotspot/manage/RepeaterManager.kt | 12 +++++------- .../main/java/be/mygod/vpnhotspot/net/IpNeighbour.kt | 5 ++--- .../be/mygod/vpnhotspot/net/wifi/WifiDoubleLock.kt | 4 ++-- .../net/wifi/configuration/WifiApDialogFragment.kt | 4 +--- .../src/main/java/be/mygod/vpnhotspot/util/Utils.kt | 5 ++--- 9 files changed, 23 insertions(+), 31 deletions(-) diff --git a/build.gradle b/build.gradle index 69aa8504..fe803682 100644 --- a/build.gradle +++ b/build.gradle @@ -12,7 +12,7 @@ buildscript { } } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0-beta02' + classpath 'com.android.tools.build:gradle:3.5.0-beta04' classpath 'com.github.ben-manes:gradle-versions-plugin:0.21.0' classpath 'com.google.gms:google-services:4.2.0' classpath 'io.fabric.tools:gradle:1.29.0' diff --git a/mobile/build.gradle b/mobile/build.gradle index 78ed3dfb..4e2843eb 100644 --- a/mobile/build.gradle +++ b/mobile/build.gradle @@ -9,7 +9,7 @@ if (!getGradle().getStartParameter().getTaskRequests().toString().contains("Fdro } android { - compileSdkVersion 'android-Q' + compileSdkVersion 29 compileOptions { sourceCompatibility 1.8 targetCompatibility 1.8 @@ -17,7 +17,7 @@ android { defaultConfig { applicationId "be.mygod.vpnhotspot" minSdkVersion 21 - targetSdkVersion 28 + targetSdkVersion 29 resConfigs "ru", "zh-rCN" versionCode 204 versionName '2.4.4' @@ -73,15 +73,15 @@ dependencies { kapt "androidx.room:room-compiler:$roomVersion" implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.browser:browser:1.0.0' - implementation 'androidx.core:core-ktx:1.1.0-beta01' + implementation 'androidx.core:core-ktx:1.1.0-rc01' implementation 'androidx.emoji:emoji:1.0.0' implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion" implementation "androidx.lifecycle:lifecycle-extensions:$lifecycleVersion" implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion" - implementation 'androidx.preference:preference:1.1.0-alpha05' + implementation 'androidx.preference:preference:1.1.0-beta01' implementation "androidx.room:room-ktx:$roomVersion" - implementation 'com.android.billingclient:billing:2.0.0' + implementation 'com.android.billingclient:billing:2.0.1' implementation 'com.github.topjohnwu.libsu:core:2.5.0' implementation 'com.google.android.material:material:1.1.0-alpha07' implementation 'com.jakewharton.timber:timber:4.7.1' diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt index 1ae1e42d..19f882f8 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 androidx.annotation.StringRes import androidx.core.content.edit import androidx.core.content.getSystemService -import androidx.core.os.BuildCompat import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.deletePersistentGroup @@ -161,7 +160,7 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere override fun onCreate() { super.onCreate() onChannelDisconnected() - if (!BuildCompat.isAtLeastQ()) @Suppress("DEPRECATION") { + if (Build.VERSION.SDK_INT < 29) @Suppress("DEPRECATION") { registerReceiver(deviceListener, intentFilter(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION, WifiP2pManagerHelper.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION)) app.pref.registerOnSharedPreferenceChangeListener(this) @@ -194,7 +193,7 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere channel = null if (status != Status.DESTROYED) try { channel = p2pManager.initialize(this, Looper.getMainLooper(), this) - if (!BuildCompat.isAtLeastQ()) @Suppress("DEPRECATION") setOperatingChannel() + if (Build.VERSION.SDK_INT < 29) @Suppress("DEPRECATION") setOperatingChannel() } catch (e: RuntimeException) { Timber.w(e) handler.postDelayed(this::onChannelDisconnected, 1000) @@ -282,7 +281,7 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere val networkName = networkName val passphrase = passphrase try { - if (!BuildCompat.isAtLeastQ() || networkName == null || passphrase == null) { + if (Build.VERSION.SDK_INT < 29 || networkName == null || passphrase == null) { persistNextGroup = true p2pManager.createGroup(channel, listener) } else p2pManager.createGroup(channel, WifiP2pConfig.Builder().apply { @@ -392,7 +391,7 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere handler.removeCallbacksAndMessages(null) if (status != Status.IDLE) binder.shutdown() clean() // force clean to prevent leakage - if (!BuildCompat.isAtLeastQ()) @Suppress("DEPRECATION") { + if (Build.VERSION.SDK_INT < 29) @Suppress("DEPRECATION") { app.pref.unregisterOnSharedPreferenceChangeListener(this) unregisterReceiver(deviceListener) } 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 2b0de909..db30b982 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/LocalOnlyHotspotManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/LocalOnlyHotspotManager.kt @@ -14,7 +14,6 @@ import android.provider.Settings import android.view.View import android.widget.Toast import androidx.core.content.getSystemService -import androidx.core.os.BuildCompat import androidx.core.os.bundleOf import androidx.recyclerview.widget.RecyclerView import be.mygod.vpnhotspot.DebugHelper @@ -29,7 +28,7 @@ import java.net.NetworkInterface @TargetApi(26) class LocalOnlyHotspotManager(private val parent: TetheringFragment) : Manager(), ServiceConnection { companion object { - val permission = if (BuildCompat.isAtLeastQ()) + val permission = if (Build.VERSION.SDK_INT >= 29) Manifest.permission.ACCESS_FINE_LOCATION else Manifest.permission.ACCESS_COARSE_LOCATION } 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 f23bb7fd..6716b8a7 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt @@ -18,7 +18,6 @@ import android.view.WindowManager import android.widget.EditText import androidx.appcompat.app.AlertDialog import androidx.core.content.ContextCompat -import androidx.core.os.BuildCompat import androidx.databinding.BaseObservable import androidx.databinding.Bindable import androidx.lifecycle.ViewModel @@ -33,7 +32,6 @@ import be.mygod.vpnhotspot.util.formatAddresses import be.mygod.vpnhotspot.widget.SmartSnackbar import kotlinx.android.parcel.Parcelize import timber.log.Timber -import java.lang.IllegalArgumentException import java.net.NetworkInterface import java.net.SocketException @@ -56,7 +54,7 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic } val title: CharSequence @Bindable get() { - if (BuildCompat.isAtLeastQ()) binder?.group?.frequency?.let { + if (Build.VERSION.SDK_INT >= 29) binder?.group?.frequency?.let { return parent.getString(R.string.repeater_channel, it, frequencyToChannel(it)) } return parent.getString(R.string.title_repeater) @@ -76,7 +74,7 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic } fun onGroupChanged(group: WifiP2pGroup? = null) { p2pInterface = group?.`interface` - if (BuildCompat.isAtLeastQ()) notifyPropertyChanged(BR.title) + if (Build.VERSION.SDK_INT >= 29) notifyPropertyChanged(BR.title) notifyPropertyChanged(BR.addresses) } @@ -85,7 +83,7 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic when (binder?.service?.status) { RepeaterService.Status.IDLE -> { val context = parent.requireContext() - if (BuildCompat.isAtLeastQ() && context.checkSelfPermission( + if (Build.VERSION.SDK_INT >= 29 && context.checkSelfPermission( Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { parent.requestPermissions(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), TetheringFragment.START_REPEATER) @@ -165,7 +163,7 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic } val configuration: WifiConfiguration? get() { - if (BuildCompat.isAtLeastQ()) { + if (Build.VERSION.SDK_INT >= 29) { val networkName = RepeaterService.networkName val passphrase = RepeaterService.passphrase if (networkName != null && passphrase != null) { @@ -200,7 +198,7 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic return null } fun updateConfiguration(config: WifiConfiguration) { - if (BuildCompat.isAtLeastQ()) { + if (Build.VERSION.SDK_INT >= 29) { RepeaterService.networkName = config.SSID RepeaterService.passphrase = config.preSharedKey RepeaterService.operatingBand = when (config.apBand) { diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/IpNeighbour.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/IpNeighbour.kt index 4a4a88ed..603aeecd 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/IpNeighbour.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/IpNeighbour.kt @@ -1,15 +1,14 @@ package be.mygod.vpnhotspot.net +import android.os.Build import android.system.ErrnoException import android.system.OsConstants -import androidx.core.os.BuildCompat import be.mygod.vpnhotspot.room.macToLong import be.mygod.vpnhotspot.util.parseNumericAddress import timber.log.Timber import java.io.File import java.io.FileNotFoundException import java.io.IOException -import java.lang.NumberFormatException import java.net.InetAddress import java.net.NetworkInterface import java.net.SocketException @@ -98,7 +97,7 @@ data class IpNeighbour(val ip: InetAddress, val dev: String, val lladdr: Long, v .filter { it.size >= 6 && mac.matcher(it[ARP_HW_ADDRESS]).matches() } .toList() } catch (e: IOException) { - if (e !is FileNotFoundException || !BuildCompat.isAtLeastQ() || + if (e !is FileNotFoundException || Build.VERSION.SDK_INT < 29 || (e.cause as? ErrnoException)?.errno != OsConstants.EACCES) Timber.w(e) } return arpCache diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiDoubleLock.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiDoubleLock.kt index 3bb1061e..4ce1be0c 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiDoubleLock.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiDoubleLock.kt @@ -3,13 +3,13 @@ package be.mygod.vpnhotspot.net.wifi import android.annotation.SuppressLint import android.content.SharedPreferences import android.net.wifi.WifiManager +import android.os.Build import android.os.PowerManager import android.view.WindowManager import androidx.activity.ComponentActivity import androidx.annotation.RequiresApi import androidx.core.content.edit import androidx.core.content.getSystemService -import androidx.core.os.BuildCompat import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import be.mygod.vpnhotspot.App.Companion.app @@ -23,7 +23,7 @@ class WifiDoubleLock(lockType: Int) : AutoCloseable { var mode: Mode @Suppress("DEPRECATION") get() = Mode.valueOf(app.pref.getString(KEY, Mode.Full.toString()) ?: "").let { - if (it == Mode.Full && BuildCompat.isAtLeastQ()) Mode.None else it + if (it == Mode.Full && Build.VERSION.SDK_INT >= 29) Mode.None else it } set(value) = app.pref.edit { putString(KEY, value.toString()) } private val service by lazy { app.getSystemService()!! } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/WifiApDialogFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/WifiApDialogFragment.kt index 4315d655..54eb2d60 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/WifiApDialogFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/WifiApDialogFragment.kt @@ -15,7 +15,6 @@ import android.widget.AdapterView import android.widget.ArrayAdapter import androidx.appcompat.app.AlertDialog import androidx.appcompat.widget.Toolbar -import androidx.core.os.BuildCompat import androidx.core.view.isGone import be.mygod.vpnhotspot.AlertDialogFragment import be.mygod.vpnhotspot.App.Companion.app @@ -27,7 +26,6 @@ import be.mygod.vpnhotspot.util.toParcelable import be.mygod.vpnhotspot.widget.SmartSnackbar import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.dialog_wifi_ap.view.* -import java.lang.IllegalStateException import java.nio.charset.Charset /** @@ -117,7 +115,7 @@ class WifiApDialogFragment : AlertDialogFragment().apply { if (arg.p2pMode) { add(BandOption.BandAny) - if (BuildCompat.isAtLeastQ()) { + if (Build.VERSION.SDK_INT >= 29) { add(BandOption.Band2GHz) add(BandOption.Band5GHz) } 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 45037d7e..02dd7b96 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt @@ -14,7 +14,6 @@ import android.view.View import android.widget.ImageView import androidx.annotation.DrawableRes import androidx.core.net.toUri -import androidx.core.os.BuildCompat import androidx.core.view.isVisible import androidx.databinding.BindingAdapter import be.mygod.vpnhotspot.App.Companion.app @@ -95,12 +94,12 @@ fun NetworkInterface.formatAddresses(macOnly: Boolean = false) = SpannableString } }.trimEnd() -private val parseNumericAddress by lazy { +private val parseNumericAddress by lazy @SuppressLint("SoonBlockedPrivateApi") { InetAddress::class.java.getDeclaredMethod("parseNumericAddress", String::class.java).apply { isAccessible = true } } -fun parseNumericAddress(address: String) = if (BuildCompat.isAtLeastQ()) +fun parseNumericAddress(address: String) = if (Build.VERSION.SDK_INT >= 29) InetAddresses.parseNumericAddress(address) else parseNumericAddress.invoke(null, address) as InetAddress fun Context.launchUrl(url: String) { From e560572a6122206f0e4b804e4351307094c68fc9 Mon Sep 17 00:00:00 2001 From: Mygod Date: Mon, 10 Jun 2019 22:03:55 +0800 Subject: [PATCH 33/35] Update doc for Android Q --- README.md | 24 +++++++++---------- mobile/src/main/AndroidManifest.xml | 2 +- .../be/mygod/vpnhotspot/RepeaterService.kt | 12 +++++----- .../vpnhotspot/manage/RepeaterManager.kt | 2 +- .../net/wifi/WifiP2pManagerHelper.kt | 10 ++++---- .../P2pSupplicantConfiguration.kt | 2 +- 6 files changed, 26 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 5d334f27..75601883 100644 --- a/README.md +++ b/README.md @@ -65,13 +65,13 @@ Default settings are picked to suit general use cases and maximize compatibility * Keep Wi-Fi alive: Acquire Wi-Fi locks when repeater, temporary hotspot or system VPN hotspot is activated. - Choose "System default" to save battery life; - - (prior to Android Q) Choose "On" (default) if repeater/hotspot turns itself off automatically or stops working after a while; - - (prior to Android Q) Choose "High Performance Mode" to minimize packet loss and latency (will consume more power); - - (since Android Q) Choose "Disable power save" to decrease packet latency. + - (prior to Android 10) Choose "On" (default) if repeater/hotspot turns itself off automatically or stops working after a while; + - (prior to Android 10) Choose "High Performance Mode" to minimize packet loss and latency (will consume more power); + - (since Android 10) Choose "Disable power save" to decrease packet latency. An example use case is when a voice connection needs to be kept active even after the device screen goes off. Using this mode may improve the call quality. Requires support from the hardware. - - (since Android Q) Choose "Low latency mode" to optimize for reduced packet latency, and this might result in: + - (since Android 10) Choose "Low latency mode" to optimize for reduced packet latency, and this might result in: 1. Reduced battery life. 2. Reduced throughput. 3. Reduced frequency of Wi-Fi scanning. @@ -133,13 +133,13 @@ Non-public API list: * [`Landroid/net/wifi/WifiManager;->getWifiApConfiguration()Landroid/net/wifi/WifiConfiguration;,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#132289) * [`Landroid/net/wifi/WifiManager;->setWifiApConfiguration(Landroid/net/wifi/WifiConfiguration;)Z,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#132358) * (deprecated since API 26) `Landroid/net/wifi/WifiManager;->setWifiApEnabled(Landroid/net/wifi/WifiConfiguration;Z)Z` -* (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pGroup;->getNetworkId()I,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#134440) -* (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pGroupList;->getGroupList()Ljava/util/Collection;,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#134487) -* (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pManager;->deletePersistentGroup(Landroid/net/wifi/p2p/WifiP2pManager$Channel;ILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#134703) -* (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pManager;->requestPersistentGroupInfo(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Landroid/net/wifi/p2p/WifiP2pManager$PersistentGroupInfoListener;)V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#134728) -* (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pManager;->setWifiP2pChannels(Landroid/net/wifi/p2p/WifiP2pManager$Channel;IILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#134737) +* (prior to API 29) [`Landroid/net/wifi/p2p/WifiP2pGroup;->getNetworkId()I,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#134440) +* (prior to API 29) [`Landroid/net/wifi/p2p/WifiP2pGroupList;->getGroupList()Ljava/util/Collection;,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#134487) +* (prior to API 29) [`Landroid/net/wifi/p2p/WifiP2pManager;->deletePersistentGroup(Landroid/net/wifi/p2p/WifiP2pManager$Channel;ILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#134703) +* (prior to API 29) [`Landroid/net/wifi/p2p/WifiP2pManager;->requestPersistentGroupInfo(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Landroid/net/wifi/p2p/WifiP2pManager$PersistentGroupInfoListener;)V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#134728) +* (prior to API 29) [`Landroid/net/wifi/p2p/WifiP2pManager;->setWifiP2pChannels(Landroid/net/wifi/p2p/WifiP2pManager$Channel;IILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#134737) * [`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/+/3d07e5c/appcompat/hiddenapi-flags.csv#134738) -* (prior to API Q) [`Ljava/net/InetAddress;->parseNumericAddress(Ljava/lang/String;)Ljava/net/InetAddress;,core-platform-api,greylist-max-p`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#335306) +* (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/+/3d07e5c/appcompat/hiddenapi-flags.csv#335306) Undocumented system configurations: @@ -156,7 +156,7 @@ Other: * Several constants in `ConnectivityManager` is assumed to be defined as in `TetheringManager.kt`; * Following broadcasts are assumed to be sticky: - [`Landroid/net/ConnectivityManager;->ACTION_TETHER_STATE_CHANGED:Ljava/lang/String;,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#123163) is assumed to be `android.net.conn.TETHER_STATE_CHANGED`. - - (prior to API Q) [`Landroid/net/wifi/p2p/WifiP2pManager;->WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION:Ljava/lang/String;,greylist-max-o`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#134686) is assumed to be `android.net.wifi.p2p.PERSISTENT_GROUPS_CHANGED`; + - (prior to API 29) [`Landroid/net/wifi/p2p/WifiP2pManager;->WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION:Ljava/lang/String;,greylist-max-o`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#134686) is assumed to be `android.net.wifi.p2p.PERSISTENT_GROUPS_CHANGED`; * 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; @@ -174,7 +174,7 @@ Undocumented system binaries are all bundled and executable: If some of these are unavailable, you can alternatively install a recent version (v1.28.1 or higher) of Busybox. -(prior to API Q) Wi-Fi driver `wpa_supplicant`: +(prior to API 29) Wi-Fi driver `wpa_supplicant`: * P2P configuration file is assumed to be saved to [`/data/vendor/wifi/wpa/p2p_supplicant.conf` or `/data/misc/wifi/p2p_supplicant.conf`](https://android.googlesource.com/platform/external/wpa_supplicant_8/+/0b4856b6dc451e290f1f64f6af17e010be78c073/wpa_supplicant/hidl/1.1/supplicant.cpp#26) and have reasonable format; * Android system is expected to restart `wpa_supplicant` after it crashes. diff --git a/mobile/src/main/AndroidManifest.xml b/mobile/src/main/AndroidManifest.xml index 3cd0dc8b..23b1a406 100644 --- a/mobile/src/main/AndroidManifest.xml +++ b/mobile/src/main/AndroidManifest.xml @@ -41,7 +41,7 @@ - + when (intent.action) { @@ -169,7 +169,7 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere override fun onBind(intent: Intent) = binder - @Deprecated("No longer used since Android Q") + @Deprecated("No longer used since API 29") @Suppress("DEPRECATION") private fun setOperatingChannel(oc: Int = operatingChannel) = try { val channel = channel @@ -200,13 +200,13 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere } } - @Deprecated("No longer used since Android Q") + @Deprecated("No longer used since API 29") @Suppress("DEPRECATION") override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { if (key == KEY_OPERATING_CHANNEL) setOperatingChannel() } - @Deprecated("No longer used since Android Q") + @Deprecated("No longer used since API 29") @Suppress("DEPRECATION") private fun onPersistentGroupsChanged() { val channel = channel ?: return 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 6716b8a7..24121e71 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt @@ -119,7 +119,7 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic } } - @Deprecated("No longer used since Android Q") + @Deprecated("No longer used since API 29") @Suppress("DEPRECATION") class ConfigHolder : ViewModel() { var config: P2pSupplicantConfiguration? = null 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 2a59a387..abb6a235 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 @@ -12,7 +12,7 @@ import java.lang.reflect.Proxy object WifiP2pManagerHelper { const val UNSUPPORTED = -2 - @Deprecated("No longer used since Android Q") + @Deprecated("No longer used since API 29") const val WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION = "android.net.wifi.p2p.PERSISTENT_GROUPS_CHANGED" /** @@ -25,7 +25,7 @@ object WifiP2pManagerHelper { WifiP2pManager::class.java.getDeclaredMethod("setWifiP2pChannels", WifiP2pManager.Channel::class.java, Int::class.java, Int::class.java, WifiP2pManager.ActionListener::class.java) } - @Deprecated("No longer used since Android Q") + @Deprecated("No longer used since API 29") fun WifiP2pManager.setWifiP2pChannels(c: WifiP2pManager.Channel, lc: Int, oc: Int, listener: WifiP2pManager.ActionListener) { try { @@ -64,7 +64,7 @@ object WifiP2pManagerHelper { WifiP2pManager::class.java.getDeclaredMethod("deletePersistentGroup", WifiP2pManager.Channel::class.java, Int::class.java, WifiP2pManager.ActionListener::class.java) } - @Deprecated("No longer used since Android Q") + @Deprecated("No longer used since API 29") fun WifiP2pManager.deletePersistentGroup(c: WifiP2pManager.Channel, netId: Int, listener: WifiP2pManager.ActionListener) { try { @@ -91,7 +91,7 @@ object WifiP2pManagerHelper { * @param c is the channel created at {@link #initialize} * @param listener for callback when persistent group info list is available. Can be null. */ - @Deprecated("No longer used since Android Q") + @Deprecated("No longer used since API 29") fun WifiP2pManager.requestPersistentGroupInfo(c: WifiP2pManager.Channel, listener: (Collection) -> Unit) { val proxy = Proxy.newProxyInstance(interfacePersistentGroupInfoListener.classLoader, @@ -114,6 +114,6 @@ object WifiP2pManagerHelper { * Source: https://android.googlesource.com/platform/frameworks/base/+/android-4.2_r1/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java#253 */ private val getNetworkId by lazy { WifiP2pGroup::class.java.getDeclaredMethod("getNetworkId") } - @Deprecated("No longer used since Android Q") + @Deprecated("No longer used since API 29") val WifiP2pGroup.netId get() = getNetworkId.invoke(this) as Int } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/P2pSupplicantConfiguration.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/P2pSupplicantConfiguration.kt index 17323224..4e4279dc 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/P2pSupplicantConfiguration.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/P2pSupplicantConfiguration.kt @@ -14,7 +14,7 @@ import java.lang.IllegalStateException * https://android.googlesource.com/platform/external/wpa_supplicant_8/+/d2986c2/wpa_supplicant/config.c#488 * https://android.googlesource.com/platform/external/wpa_supplicant_8/+/6fa46df/wpa_supplicant/config_file.c#182 */ -@Deprecated("No longer used since Android Q") +@Deprecated("No longer used since API 29") class P2pSupplicantConfiguration(private val group: WifiP2pGroup, ownerAddress: String?) { companion object { private const val TAG = "P2pSupplicantConfiguration" From 7b9502be8abbbe81d0c0a7bee6d99381a09ebfcb Mon Sep 17 00:00:00 2001 From: Mygod Date: Mon, 10 Jun 2019 22:12:31 +0800 Subject: [PATCH 34/35] Refine code style --- mobile/src/main/java/be/mygod/vpnhotspot/client/Client.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/client/Client.kt b/mobile/src/main/java/be/mygod/vpnhotspot/client/Client.kt index 9c905a21..499c4134 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/client/Client.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/client/Client.kt @@ -52,7 +52,7 @@ open class Client(val mac: Long, val iface: String) { val titleSelectable = record.map { it.nickname.isEmpty() } val description = record.map { record -> SpannableStringBuilder().apply { - if (!record.nickname.isEmpty()) appendln(macIface) + if (record.nickname.isNotEmpty()) appendln(macIface) ip.entries.forEach { (ip, state) -> append(makeIpSpan(ip)) appendln(app.getText(when (state) { From 5bb122df42abf510042d4ed5e5a3378dab327e78 Mon Sep 17 00:00:00 2001 From: Mygod Date: Mon, 10 Jun 2019 22:13:01 +0800 Subject: [PATCH 35/35] Remove testOnly --- gradle.properties | 1 - 1 file changed, 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 64666031..d08d04cc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,7 +11,6 @@ # The setting is particularly useful for tweaking memory settings. android.enableJetifier=true android.enableR8.fullMode=true -android.injected.testOnly=false android.useAndroidX=true org.gradle.jvmargs=-Xmx1536m