Add support for requestPersistentGroupInfo

This commit is contained in:
Mygod
2018-04-21 17:22:53 -07:00
parent 8ead85b75c
commit e3a1003c80
4 changed files with 149 additions and 80 deletions

View File

@@ -59,8 +59,8 @@ class RepeaterFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClickL
} }
} }
val ssid @Bindable get() = binder?.service?.ssid ?: getText(R.string.repeater_inactive) val ssid @Bindable get() = binder?.ssid ?: getText(R.string.repeater_inactive)
val password @Bindable get() = binder?.service?.password ?: "" val password @Bindable get() = binder?.password ?: ""
val addresses @Bindable get(): String { val addresses @Bindable get(): String {
return try { return try {
NetworkInterface.getByName(p2pInterface ?: return "")?.formatAddresses() ?: "" NetworkInterface.getByName(p2pInterface ?: return "")?.formatAddresses() ?: ""

View File

@@ -18,80 +18,20 @@ import android.widget.Toast
import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.App.Companion.app
import be.mygod.vpnhotspot.net.Routing import be.mygod.vpnhotspot.net.Routing
import be.mygod.vpnhotspot.net.VpnMonitor import be.mygod.vpnhotspot.net.VpnMonitor
import be.mygod.vpnhotspot.net.WifiP2pManagerHelper
import be.mygod.vpnhotspot.net.WifiP2pManagerHelper.deletePersistentGroup
import be.mygod.vpnhotspot.net.WifiP2pManagerHelper.netId
import be.mygod.vpnhotspot.net.WifiP2pManagerHelper.requestPersistentGroupInfo
import be.mygod.vpnhotspot.net.WifiP2pManagerHelper.setWifiP2pChannels
import be.mygod.vpnhotspot.net.WifiP2pManagerHelper.startWps
import java.net.InetAddress import java.net.InetAddress
import java.net.SocketException import java.net.SocketException
import java.util.regex.Pattern
class RepeaterService : Service(), WifiP2pManager.ChannelListener, VpnMonitor.Callback, class RepeaterService : Service(), WifiP2pManager.ChannelListener, VpnMonitor.Callback,
SharedPreferences.OnSharedPreferenceChangeListener { SharedPreferences.OnSharedPreferenceChangeListener {
companion object { companion object {
const val ACTION_STATUS_CHANGED = "be.mygod.vpnhotspot.RepeaterService.STATUS_CHANGED" const val ACTION_STATUS_CHANGED = "be.mygod.vpnhotspot.RepeaterService.STATUS_CHANGED"
const val KEY_NET_ID = "netId"
private const val TAG = "RepeaterService" private const val TAG = "RepeaterService"
private const val TEMPORARY_NET_ID = -1
/**
* Matches the output of dumpsys wifip2p. This part is available since Android 4.2.
*
* Related sources:
* https://android.googlesource.com/platform/frameworks/base/+/f0afe4144d09aa9b980cffd444911ab118fa9cbe%5E%21/wifi/java/android/net/wifi/p2p/WifiP2pService.java
* https://android.googlesource.com/platform/frameworks/opt/net/wifi/+/a8d5e40/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java#639
*
* https://android.googlesource.com/platform/frameworks/base.git/+/android-5.0.0_r1/core/java/android/net/NetworkInfo.java#433
* https://android.googlesource.com/platform/frameworks/base.git/+/220871a/core/java/android/net/NetworkInfo.java#415
*/
private val patternNetworkInfo = "^mNetworkInfo .* (isA|a)vailable: (true|false)".toPattern(Pattern.MULTILINE)
/**
* Available since Android 4.4.
*
* Source: https://android.googlesource.com/platform/frameworks/base/+/android-4.4_r1/wifi/java/android/net/wifi/p2p/WifiP2pManager.java#994
* Implementation: https://android.googlesource.com/platform/frameworks/opt/net/wifi/+/d72d2f4/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceHal.java#1159
*/
private val setWifiP2pChannels by lazy {
WifiP2pManager::class.java.getDeclaredMethod("setWifiP2pChannels", WifiP2pManager.Channel::class.java,
Int::class.java, Int::class.java, WifiP2pManager.ActionListener::class.java)
}
private fun WifiP2pManager.setWifiP2pChannels(c: WifiP2pManager.Channel, lc: Int, oc: Int,
listener: WifiP2pManager.ActionListener) {
setWifiP2pChannels.invoke(this, c, lc, oc, listener)
}
/**
* Available since Android 4.3.
*
* Source: https://android.googlesource.com/platform/frameworks/base/+/android-4.3_r0.9/wifi/java/android/net/wifi/p2p/WifiP2pManager.java#958
*/
private val startWps by lazy {
WifiP2pManager::class.java.getDeclaredMethod("startWps",
WifiP2pManager.Channel::class.java, WpsInfo::class.java, WifiP2pManager.ActionListener::class.java)
}
private fun WifiP2pManager.startWps(c: WifiP2pManager.Channel, wps: WpsInfo,
listener: WifiP2pManager.ActionListener) {
startWps.invoke(this, c, wps, listener)
}
/**
* Available since Android 4.2.
*
* Source: https://android.googlesource.com/platform/frameworks/base/+/android-4.2_r1/wifi/java/android/net/wifi/p2p/WifiP2pManager.java#1353
*/
private val deletePersistentGroup by lazy {
WifiP2pManager::class.java.getDeclaredMethod("deletePersistentGroup",
WifiP2pManager.Channel::class.java, Int::class.java, WifiP2pManager.ActionListener::class.java)
}
private fun WifiP2pManager.deletePersistentGroup(c: WifiP2pManager.Channel, netId: Int,
listener: WifiP2pManager.ActionListener) {
deletePersistentGroup.invoke(this, c, netId, listener)
}
/**
* Available since Android 4.2.
*
* 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") }
private val WifiP2pGroup.netId get() = getNetworkId.invoke(this) as Int
} }
enum class Status { enum class Status {
@@ -103,8 +43,11 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, VpnMonitor.Ca
var data: RepeaterFragment.Data? = null var data: RepeaterFragment.Data? = null
val active get() = status == Status.ACTIVE val active get() = status == Status.ACTIVE
val ssid get() = group?.networkName
val password get() = group?.passphrase
fun startWps(pin: String? = null) { fun startWps(pin: String? = null) {
if (status != Status.ACTIVE) return if (!active) return
val wps = WpsInfo() val wps = WpsInfo()
if (pin == null) wps.setup = WpsInfo.PBC else { if (pin == null) wps.setup = WpsInfo.PBC else {
wps.setup = WpsInfo.KEYPAD wps.setup = WpsInfo.KEYPAD
@@ -120,13 +63,11 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, VpnMonitor.Ca
} }
fun shutdown() { fun shutdown() {
if (status == Status.ACTIVE) removeGroup() if (active) removeGroup()
} }
fun resetCredentials() { fun resetCredentials() {
val netId = app.pref.getInt(KEY_NET_ID, TEMPORARY_NET_ID) p2pManager.deletePersistentGroup(channel, (group ?: return).netId, object : WifiP2pManager.ActionListener {
if (netId == TEMPORARY_NET_ID) return
p2pManager.deletePersistentGroup(channel, netId, object : WifiP2pManager.ActionListener {
override fun onSuccess() = Toast.makeText(this@RepeaterService, override fun onSuccess() = Toast.makeText(this@RepeaterService,
R.string.repeater_reset_credentials_success, Toast.LENGTH_SHORT).show() R.string.repeater_reset_credentials_success, Toast.LENGTH_SHORT).show()
override fun onFailure(reason: Int) = Toast.makeText(this@RepeaterService, override fun onFailure(reason: Int) = Toast.makeText(this@RepeaterService,
@@ -140,7 +81,7 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, VpnMonitor.Ca
var group: WifiP2pGroup? = null var group: WifiP2pGroup? = null
private set(value) { private set(value) {
field = value field = value
if (value != null) app.pref.edit().putInt(KEY_NET_ID, value.netId).apply() binder.data?.onGroupChanged(group)
} }
private val binder = RepeaterBinder() private val binder = RepeaterBinder()
private var receiverRegistered = false private var receiverRegistered = false
@@ -161,9 +102,6 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, VpnMonitor.Ca
} }
} }
val ssid get() = if (status == Status.ACTIVE) group?.networkName else null
val password get() = if (status == Status.ACTIVE) group?.passphrase else null
private var upstream: String? = null private var upstream: String? = null
private var dns: List<InetAddress> = emptyList() private var dns: List<InetAddress> = emptyList()
private var routing: Routing? = null private var routing: Routing? = null
@@ -214,6 +152,18 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, VpnMonitor.Ca
override fun onChannelDisconnected() { override fun onChannelDisconnected() {
channel = p2pManager.initialize(this, Looper.getMainLooper(), this) channel = p2pManager.initialize(this, Looper.getMainLooper(), this)
setOperatingChannel(true) setOperatingChannel(true)
try {
p2pManager.requestPersistentGroupInfo(channel, {
when (it.size) {
0 -> { }
1 -> group = it.single()
else -> Log.w(TAG, "Unexpected groups: $it")
}
})
} catch (e: Exception) {
e.printStackTrace()
Toast.makeText(this, e.message, Toast.LENGTH_LONG).show()
}
} }
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
@@ -239,7 +189,8 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, VpnMonitor.Ca
* startService 2nd stop * startService 2nd stop
*/ */
private fun setup(ifname: String? = null, dns: List<InetAddress> = emptyList()) { private fun setup(ifname: String? = null, dns: List<InetAddress> = emptyList()) {
val matcher = patternNetworkInfo.matcher(loggerSu("dumpsys ${Context.WIFI_P2P_SERVICE}") ?: "") val matcher = WifiP2pManagerHelper.patternNetworkInfo.matcher(
loggerSu("dumpsys ${Context.WIFI_P2P_SERVICE}") ?: "")
when { when {
!matcher.find() -> startFailure(getString(R.string.root_unavailable)) !matcher.find() -> startFailure(getString(R.string.root_unavailable))
matcher.group(2) == "true" -> { matcher.group(2) == "true" -> {
@@ -323,7 +274,6 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, VpnMonitor.Ca
return return
} else showNotification(group) } else showNotification(group)
this.group = group this.group = group
binder.data?.onGroupChanged(group)
} }
private fun initRouting(upstream: String?, downstream: String, private fun initRouting(upstream: String?, downstream: String,
owner: InetAddress, dns: List<InetAddress>): Boolean { owner: InetAddress, dns: List<InetAddress>): Boolean {

View File

@@ -68,7 +68,7 @@ class RepeaterTileService : TileService(), ServiceConnection {
RepeaterService.Status.ACTIVE -> { RepeaterService.Status.ACTIVE -> {
qsTile.state = Tile.STATE_ACTIVE qsTile.state = Tile.STATE_ACTIVE
qsTile.icon = tileOn qsTile.icon = tileOn
qsTile.label = binder?.service?.ssid qsTile.label = binder?.ssid
} }
null -> { null -> {
qsTile.state = Tile.STATE_UNAVAILABLE qsTile.state = Tile.STATE_UNAVAILABLE

View File

@@ -0,0 +1,119 @@
package be.mygod.vpnhotspot.net
import android.annotation.SuppressLint
import android.net.wifi.WpsInfo
import android.net.wifi.p2p.WifiP2pGroup
import android.net.wifi.p2p.WifiP2pManager
import android.util.Log
import com.android.dx.stock.ProxyBuilder
import java.lang.reflect.Proxy
import java.util.regex.Pattern
object WifiP2pManagerHelper {
/** Interface for callback invocation when stored group info list is available {@hide} */
interface PersistentGroupInfoListener {
/**
* The requested stored p2p group info list is available
* @param groups Wi-Fi p2p group info list
*/
fun onPersistentGroupInfoAvailable(groups: Collection<WifiP2pGroup>)
}
const val TAG = "WifiP2pManagerHelper"
const val TEMPORARY_NET_ID = -1
/**
* Matches the output of dumpsys wifip2p. This part is available since Android 4.2.
*
* Related sources:
* https://android.googlesource.com/platform/frameworks/base/+/f0afe4144d09aa9b980cffd444911ab118fa9cbe%5E%21/wifi/java/android/net/wifi/p2p/WifiP2pService.java
* https://android.googlesource.com/platform/frameworks/opt/net/wifi/+/a8d5e40/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java#639
*
* https://android.googlesource.com/platform/frameworks/base.git/+/android-5.0.0_r1/core/java/android/net/NetworkInfo.java#433
* https://android.googlesource.com/platform/frameworks/base.git/+/220871a/core/java/android/net/NetworkInfo.java#415
*/
val patternNetworkInfo = "^mNetworkInfo .* (isA|a)vailable: (true|false)".toPattern(Pattern.MULTILINE)
/**
* Available since Android 4.4.
*
* Source: https://android.googlesource.com/platform/frameworks/base/+/android-4.4_r1/wifi/java/android/net/wifi/p2p/WifiP2pManager.java#994
* Implementation: https://android.googlesource.com/platform/frameworks/opt/net/wifi/+/d72d2f4/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceHal.java#1159
*/
private val setWifiP2pChannels by lazy {
WifiP2pManager::class.java.getDeclaredMethod("setWifiP2pChannels", WifiP2pManager.Channel::class.java,
Int::class.java, Int::class.java, WifiP2pManager.ActionListener::class.java)
}
fun WifiP2pManager.setWifiP2pChannels(c: WifiP2pManager.Channel, lc: Int, oc: Int,
listener: WifiP2pManager.ActionListener) {
setWifiP2pChannels.invoke(this, c, lc, oc, listener)
}
/**
* Available since Android 4.3.
*
* Source: https://android.googlesource.com/platform/frameworks/base/+/android-4.3_r0.9/wifi/java/android/net/wifi/p2p/WifiP2pManager.java#958
*/
private val startWps by lazy {
WifiP2pManager::class.java.getDeclaredMethod("startWps",
WifiP2pManager.Channel::class.java, WpsInfo::class.java, WifiP2pManager.ActionListener::class.java)
}
fun WifiP2pManager.startWps(c: WifiP2pManager.Channel, wps: WpsInfo,
listener: WifiP2pManager.ActionListener) {
startWps.invoke(this, c, wps, listener)
}
/**
* Available since Android 4.2.
*
* Source: https://android.googlesource.com/platform/frameworks/base/+/android-4.2_r1/wifi/java/android/net/wifi/p2p/WifiP2pManager.java#1353
*/
private val deletePersistentGroup by lazy {
WifiP2pManager::class.java.getDeclaredMethod("deletePersistentGroup",
WifiP2pManager.Channel::class.java, Int::class.java, WifiP2pManager.ActionListener::class.java)
}
fun WifiP2pManager.deletePersistentGroup(c: WifiP2pManager.Channel, netId: Int,
listener: WifiP2pManager.ActionListener) {
deletePersistentGroup.invoke(this, c, netId, listener)
}
private val interfacePersistentGroupInfoListener by lazy @SuppressLint("PrivateApi") {
Class.forName("android.net.wifi.p2p.WifiP2pManager\$PersistentGroupInfoListener")
}
private val getGroupList by lazy @SuppressLint("PrivateApi") {
Class.forName("android.net.wifi.p2p.WifiP2pGroupList").getDeclaredMethod("getGroupList")
}
private val requestPersistentGroupInfo by lazy {
WifiP2pManager::class.java.getDeclaredMethod("requestPersistentGroupInfo",
WifiP2pManager.Channel::class.java, interfacePersistentGroupInfoListener)
}
/**
* Request a list of all the persistent p2p groups stored in system.
*
* @param c is the channel created at {@link #initialize}
* @param listener for callback when persistent group info list is available. Can be null.
*/
fun WifiP2pManager.requestPersistentGroupInfo(c: WifiP2pManager.Channel,
listener: (Collection<WifiP2pGroup>) -> Unit) {
val proxy = Proxy.newProxyInstance(interfacePersistentGroupInfoListener.classLoader,
arrayOf(interfacePersistentGroupInfoListener), { proxy, method, args ->
if (method.name == "onPersistentGroupInfoAvailable") {
if (args.size != 1) Log.w(TAG, "Unexpected args: $args")
listener(getGroupList.invoke(args[0]) as Collection<WifiP2pGroup>)
null
} else {
Log.w(TAG, "Unexpected method, calling super: $method")
ProxyBuilder.callSuper(proxy, method, args)
}
})
requestPersistentGroupInfo.invoke(this, c, proxy)
}
/**
* Available since Android 4.2.
*
* 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") }
val WifiP2pGroup.netId get() = getNetworkId.invoke(this) as Int
}