Move MAC utils to MacAddressCompat

This commit is contained in:
Mygod
2020-05-30 02:06:41 -04:00
parent d328215764
commit e8fb62a0b3
15 changed files with 97 additions and 61 deletions

View File

@@ -9,16 +9,16 @@ import be.mygod.vpnhotspot.App.Companion.app
import be.mygod.vpnhotspot.R
import be.mygod.vpnhotspot.net.InetAddressComparator
import be.mygod.vpnhotspot.net.IpNeighbour
import be.mygod.vpnhotspot.net.MacAddressCompat
import be.mygod.vpnhotspot.net.TetherType
import be.mygod.vpnhotspot.room.AppDatabase
import be.mygod.vpnhotspot.room.ClientRecord
import be.mygod.vpnhotspot.room.macToString
import be.mygod.vpnhotspot.util.makeIpSpan
import be.mygod.vpnhotspot.util.makeMacSpan
import java.net.InetAddress
import java.util.*
open class Client(val mac: Long, val iface: String) {
open class Client(val mac: MacAddressCompat, val iface: String) {
companion object DiffCallback : DiffUtil.ItemCallback<Client>() {
override fun areItemsTheSame(oldItem: Client, newItem: Client) =
oldItem.iface == newItem.iface && oldItem.mac == newItem.mac
@@ -26,7 +26,7 @@ open class Client(val mac: Long, val iface: String) {
}
val ip = TreeMap<InetAddress, IpNeighbour.State>(InetAddressComparator)
val macString by lazy { mac.macToString() }
val macString by lazy { mac.toString() }
private val record = AppDatabase.instance.clientRecordDao.lookupOrDefaultSync(mac)
private val macIface get() = SpannableStringBuilder(makeMacSpan(macString)).apply {
append('%')
@@ -65,7 +65,7 @@ open class Client(val mac: Long, val iface: String) {
}.trimEnd()
}
fun obtainRecord() = record.value ?: ClientRecord(mac)
fun obtainRecord() = record.value ?: ClientRecord(mac.addr)
override fun equals(other: Any?): Boolean {
if (this === other) return true

View File

@@ -10,11 +10,11 @@ import androidx.lifecycle.ViewModel
import be.mygod.vpnhotspot.App.Companion.app
import be.mygod.vpnhotspot.RepeaterService
import be.mygod.vpnhotspot.net.IpNeighbour
import be.mygod.vpnhotspot.net.MacAddressCompat
import be.mygod.vpnhotspot.net.TetheringManager
import be.mygod.vpnhotspot.net.TetheringManager.localOnlyTetheredIfaces
import be.mygod.vpnhotspot.net.TetheringManager.tetheredIfaces
import be.mygod.vpnhotspot.net.monitor.IpNeighbourMonitor
import be.mygod.vpnhotspot.room.macToLong
import be.mygod.vpnhotspot.util.broadcastReceiver
class ClientViewModel : ViewModel(), ServiceConnection, IpNeighbourMonitor.Callback {
@@ -31,15 +31,15 @@ class ClientViewModel : ViewModel(), ServiceConnection, IpNeighbourMonitor.Callb
val clients = MutableLiveData<List<Client>>()
private fun populateClients() {
val clients = HashMap<Pair<String, Long>, Client>()
val clients = HashMap<Pair<String, MacAddressCompat>, Client>()
val group = repeater?.group
val p2pInterface = group?.`interface`
if (p2pInterface != null) {
for (client in p2p) clients[Pair(p2pInterface, client.deviceAddress.macToLong())] =
for (client in p2p) clients[p2pInterface to MacAddressCompat.fromString(client.deviceAddress)] =
WifiP2pClient(p2pInterface, client)
}
for (neighbour in neighbours) {
val key = Pair(neighbour.dev, neighbour.lladdr)
val key = neighbour.dev to neighbour.lladdr
var client = clients[key]
if (client == null) {
if (!tetheredInterfaces.contains(neighbour.dev)) continue

View File

@@ -30,13 +30,13 @@ import be.mygod.vpnhotspot.Empty
import be.mygod.vpnhotspot.R
import be.mygod.vpnhotspot.databinding.FragmentClientsBinding
import be.mygod.vpnhotspot.databinding.ListitemClientBinding
import be.mygod.vpnhotspot.net.MacAddressCompat
import be.mygod.vpnhotspot.net.TetherType
import be.mygod.vpnhotspot.net.monitor.IpNeighbourMonitor
import be.mygod.vpnhotspot.net.monitor.TrafficRecorder
import be.mygod.vpnhotspot.room.AppDatabase
import be.mygod.vpnhotspot.room.ClientStats
import be.mygod.vpnhotspot.room.TrafficRecord
import be.mygod.vpnhotspot.room.macToString
import be.mygod.vpnhotspot.util.SpanFormatter
import be.mygod.vpnhotspot.util.toPluralInt
import be.mygod.vpnhotspot.widget.SmartSnackbar
@@ -46,11 +46,11 @@ import java.text.NumberFormat
class ClientsFragment : Fragment() {
@Parcelize
data class NicknameArg(val mac: Long, val nickname: CharSequence) : Parcelable
data class NicknameArg(val mac: MacAddressCompat, val nickname: CharSequence) : Parcelable
class NicknameDialogFragment : AlertDialogFragment<NicknameArg, Empty>() {
override fun AlertDialog.Builder.prepare(listener: DialogInterface.OnClickListener) {
setView(R.layout.dialog_nickname)
setTitle(getString(R.string.clients_nickname_title, arg.mac.macToString()))
setTitle(getString(R.string.clients_nickname_title, arg.mac.toString()))
setPositiveButton(android.R.string.ok, listener)
setNegativeButton(android.R.string.cancel, null)
setNeutralButton(emojize(getText(R.string.clients_nickname_set_to_vendor)), listener)
@@ -153,7 +153,7 @@ class ClientsFragment : Fragment() {
withContext(Dispatchers.Unconfined) {
StatsDialogFragment().withArg(StatsArg(
client.title.value ?: return@withContext,
AppDatabase.instance.trafficRecordDao.queryStats(client.mac)
AppDatabase.instance.trafficRecordDao.queryStats(client.mac.addr)
)).show(this@ClientsFragment)
}
}
@@ -181,7 +181,7 @@ class ClientsFragment : Fragment() {
override fun onBindViewHolder(holder: ClientViewHolder, position: Int) {
val client = getItem(position)
holder.binding.client = client
holder.binding.rate = rates.computeIfAbsent(Pair(client.iface, client.mac)) { TrafficRate() }
holder.binding.rate = rates.computeIfAbsent(client.iface to client.mac) { TrafficRate() }
holder.binding.executePendingBindings()
}
@@ -196,7 +196,9 @@ class ClientsFragment : Fragment() {
check(newRecord.receivedPackets == oldRecord.receivedPackets)
check(newRecord.receivedBytes == oldRecord.receivedBytes)
} else {
val rate = rates.computeIfAbsent(Pair(newRecord.downstream, newRecord.mac)) { TrafficRate() }
val rate = rates.computeIfAbsent(newRecord.downstream to MacAddressCompat(newRecord.mac)) {
TrafficRate()
}
if (rate.send < 0 || rate.receive < 0) {
rate.send = 0
rate.receive = 0
@@ -211,7 +213,7 @@ class ClientsFragment : Fragment() {
private lateinit var binding: FragmentClientsBinding
private val adapter = ClientAdapter()
private var rates = mutableMapOf<Pair<String, Long>, TrafficRate>()
private var rates = mutableMapOf<Pair<String, MacAddressCompat>, TrafficRate>()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = FragmentClientsBinding.inflate(inflater, container, false)

View File

@@ -5,8 +5,8 @@ import android.os.Build
import androidx.annotation.MainThread
import be.mygod.vpnhotspot.App.Companion.app
import be.mygod.vpnhotspot.R
import be.mygod.vpnhotspot.net.MacAddressCompat
import be.mygod.vpnhotspot.room.AppDatabase
import be.mygod.vpnhotspot.room.macToString
import be.mygod.vpnhotspot.widget.SmartSnackbar
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
@@ -22,26 +22,26 @@ import java.net.URL
* This class generates a default nickname for new clients.
*/
object MacLookup {
class UnexpectedError(val mac: Long, val error: String) : JSONException("") {
class UnexpectedError(val mac: MacAddressCompat, val error: String) : JSONException("") {
private fun formatMessage(context: Context) =
context.getString(R.string.clients_mac_lookup_unexpected_error, mac.macToString(), error)
context.getString(R.string.clients_mac_lookup_unexpected_error, mac.toString(), error)
override val message get() = formatMessage(app.english)
override fun getLocalizedMessage() = formatMessage(app)
}
private val macLookupBusy = mutableMapOf<Long, Pair<HttpURLConnection, Job>>()
private val macLookupBusy = mutableMapOf<MacAddressCompat, Pair<HttpURLConnection, Job>>()
private val countryCodeRegex = "([A-Z]{2})\\s*\$".toRegex() // http://en.wikipedia.org/wiki/ISO_3166-1
@MainThread
fun abort(mac: Long) = macLookupBusy.remove(mac)?.let { (conn, job) ->
fun abort(mac: MacAddressCompat) = macLookupBusy.remove(mac)?.let { (conn, job) ->
job.cancel()
if (Build.VERSION.SDK_INT >= 26) conn.disconnect() else GlobalScope.launch(Dispatchers.IO) { conn.disconnect() }
}
@MainThread
fun perform(mac: Long, explicit: Boolean = false) {
fun perform(mac: MacAddressCompat, explicit: Boolean = false) {
abort(mac)
val conn = URL("https://macvendors.co/api/" + mac.macToString()).openConnection() as HttpURLConnection
val conn = URL("https://macvendors.co/api/$mac").openConnection() as HttpURLConnection
macLookupBusy[mac] = conn to GlobalScope.launch(Dispatchers.IO) {
try {
val response = conn.inputStream.bufferedReader().readText()
@@ -69,7 +69,7 @@ object MacLookup {
}
}
private fun extractCountry(mac: Long, response: String, obj: JSONObject): MatchResult? {
private fun extractCountry(mac: MacAddressCompat, response: String, obj: JSONObject): MatchResult? {
countryCodeRegex.matchEntire(obj.optString("country"))?.also { return it }
val address = obj.optString("address")
if (address.isBlank()) return null

View File

@@ -1,9 +1,10 @@
package be.mygod.vpnhotspot.client
import android.net.wifi.p2p.WifiP2pDevice
import be.mygod.vpnhotspot.net.MacAddressCompat
import be.mygod.vpnhotspot.net.TetherType
import be.mygod.vpnhotspot.room.macToLong
class WifiP2pClient(p2pInterface: String, p2p: WifiP2pDevice) : Client(p2p.deviceAddress!!.macToLong(), p2pInterface) {
class WifiP2pClient(p2pInterface: String, p2p: WifiP2pDevice) :
Client(MacAddressCompat.fromString(p2p.deviceAddress!!), p2pInterface) {
override val icon: Int get() = TetherType.WIFI_P2P.icon
}