Move MAC utils to MacAddressCompat
This commit is contained in:
@@ -16,7 +16,10 @@ android {
|
||||
targetCompatibility = javaVersion
|
||||
}
|
||||
compileSdkVersion("android-R")
|
||||
kotlinOptions.jvmTarget = javaVersion.toString()
|
||||
kotlinOptions {
|
||||
freeCompilerArgs = listOf("-XXLanguage:+InlineClasses")
|
||||
jvmTarget = javaVersion.toString()
|
||||
}
|
||||
defaultConfig {
|
||||
applicationId = "be.mygod.vpnhotspot"
|
||||
minSdkVersion(21)
|
||||
|
||||
@@ -16,6 +16,7 @@ import androidx.annotation.StringRes
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.content.getSystemService
|
||||
import be.mygod.vpnhotspot.App.Companion.app
|
||||
import be.mygod.vpnhotspot.net.MacAddressCompat
|
||||
import be.mygod.vpnhotspot.net.monitor.TetherTimeoutMonitor
|
||||
import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper
|
||||
import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.deletePersistentGroup
|
||||
@@ -23,7 +24,6 @@ import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.requestPersistentGroupI
|
||||
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.room.macToString
|
||||
import be.mygod.vpnhotspot.util.*
|
||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||
import kotlinx.coroutines.*
|
||||
@@ -381,7 +381,7 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene
|
||||
check(routingManager == null)
|
||||
routingManager = object : RoutingManager.LocalOnly(this, group.`interface`!!) {
|
||||
override fun ifaceHandler(iface: NetworkInterface) {
|
||||
iface.hardwareAddress?.asIterable()?.macToString()?.let { lastMac = it }
|
||||
iface.hardwareAddress?.let { lastMac = MacAddressCompat.bytesToString(it) }
|
||||
}
|
||||
}.apply { start() }
|
||||
status = Status.ACTIVE
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package be.mygod.vpnhotspot.net
|
||||
import android.os.Build
|
||||
import android.system.ErrnoException
|
||||
import android.system.OsConstants
|
||||
import be.mygod.vpnhotspot.room.macToLong
|
||||
import be.mygod.vpnhotspot.util.parseNumericAddress
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
@@ -13,7 +12,7 @@ import java.net.InetAddress
|
||||
import java.net.NetworkInterface
|
||||
import java.net.SocketException
|
||||
|
||||
data class IpNeighbour(val ip: InetAddress, val dev: String, val lladdr: Long, val state: State) {
|
||||
data class IpNeighbour(val ip: InetAddress, val dev: String, val lladdr: MacAddressCompat, val state: State) {
|
||||
enum class State {
|
||||
INCOMPLETE, VALID, FAILED, DELETING
|
||||
}
|
||||
@@ -56,12 +55,12 @@ data class IpNeighbour(val ip: InetAddress, val dev: String, val lladdr: Long, v
|
||||
else -> throw IllegalArgumentException("Unknown state encountered: ${match.groupValues[7]}")
|
||||
}
|
||||
val mac = try {
|
||||
lladdr.macToLong()
|
||||
} catch (e: NumberFormatException) {
|
||||
MacAddressCompat.fromString(lladdr)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
if (match.groups[4] == null) return emptyList()
|
||||
// for DELETING, we only care about IP address and do not care if MAC is not present
|
||||
if (state != State.DELETING) Timber.w(IOException("Failed to find MAC address for $line"))
|
||||
0L
|
||||
MacAddressCompat.ALL_ZEROS_ADDRESS
|
||||
}
|
||||
val result = IpNeighbour(ip, dev, mac, state)
|
||||
val devParser = devFallback.matchEntire(dev)
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package be.mygod.vpnhotspot.net
|
||||
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
|
||||
inline class MacAddressCompat(val addr: Long) {
|
||||
companion object {
|
||||
private const val ETHER_ADDR_LEN = 6
|
||||
/**
|
||||
* The MacAddress zero MAC address.
|
||||
*
|
||||
* Not publicly exposed or treated specially since the OUI 00:00:00 is registered.
|
||||
* @hide
|
||||
*/
|
||||
val ALL_ZEROS_ADDRESS = MacAddressCompat(0)
|
||||
|
||||
fun bytesToString(addr: ByteArray): String {
|
||||
require(addr.size == ETHER_ADDR_LEN) { addr.contentToString() + " was not a valid MAC address" }
|
||||
return addr.joinToString(":") { "%02x".format(it) }
|
||||
}
|
||||
fun bytesToString(addr: Collection<Byte>): String {
|
||||
require(addr.size == ETHER_ADDR_LEN) { addr.joinToString() + " was not a valid MAC address" }
|
||||
return addr.joinToString(":") { "%02x".format(it) }
|
||||
}
|
||||
|
||||
@Throws(IllegalArgumentException::class)
|
||||
fun fromString(addr: String) = MacAddressCompat(ByteBuffer.allocate(8).run {
|
||||
order(ByteOrder.LITTLE_ENDIAN)
|
||||
mark()
|
||||
try {
|
||||
put(addr.split(':').map { Integer.parseInt(it, 16).toByte() }.toByteArray())
|
||||
} catch (e: NumberFormatException) {
|
||||
throw IllegalArgumentException(e)
|
||||
}
|
||||
reset()
|
||||
long
|
||||
})
|
||||
}
|
||||
|
||||
override fun toString() = ByteBuffer.allocate(8).run {
|
||||
order(ByteOrder.LITTLE_ENDIAN)
|
||||
putLong(addr)
|
||||
bytesToString(array().take(6))
|
||||
}
|
||||
}
|
||||
@@ -208,7 +208,7 @@ class Routing(private val caller: Any, private val downstream: String,
|
||||
private val upstream = Upstream(RULE_PRIORITY_UPSTREAM)
|
||||
private var disableSystem: RootSession.Transaction? = null
|
||||
|
||||
private inner class Client(private val ip: Inet4Address, mac: Long) : AutoCloseable {
|
||||
private inner class Client(private val ip: Inet4Address, mac: MacAddressCompat) : AutoCloseable {
|
||||
private val transaction = RootSession.beginTransaction().safeguard {
|
||||
val address = ip.hostAddress
|
||||
iptablesInsert("vpnhotspot_acl -i $downstream -s $address -j ACCEPT")
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package be.mygod.vpnhotspot.net.monitor
|
||||
|
||||
import android.util.LongSparseArray
|
||||
import be.mygod.vpnhotspot.net.MacAddressCompat
|
||||
import be.mygod.vpnhotspot.net.Routing.Companion.IPTABLES
|
||||
import be.mygod.vpnhotspot.room.AppDatabase
|
||||
import be.mygod.vpnhotspot.room.TrafficRecord
|
||||
@@ -20,8 +21,8 @@ object TrafficRecorder {
|
||||
private val records = mutableMapOf<Pair<InetAddress, String>, TrafficRecord>()
|
||||
val foregroundListeners = Event2<Collection<TrafficRecord>, LongSparseArray<TrafficRecord>>()
|
||||
|
||||
fun register(ip: InetAddress, downstream: String, mac: Long) {
|
||||
val record = TrafficRecord(mac = mac, ip = ip, downstream = downstream)
|
||||
fun register(ip: InetAddress, downstream: String, mac: MacAddressCompat) {
|
||||
val record = TrafficRecord(mac = mac.addr, ip = ip, downstream = downstream)
|
||||
AppDatabase.instance.trafficRecordDao.insert(record)
|
||||
synchronized(this) {
|
||||
Timber.d("Registering $ip%$downstream")
|
||||
@@ -149,5 +150,5 @@ object TrafficRecorder {
|
||||
/**
|
||||
* Possibly inefficient. Don't call this too often.
|
||||
*/
|
||||
fun isWorking(mac: Long) = records.values.any { it.mac == mac }
|
||||
fun isWorking(mac: MacAddressCompat) = records.values.any { it.mac == mac.addr }
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package be.mygod.vpnhotspot.room
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.map
|
||||
import androidx.room.*
|
||||
import be.mygod.vpnhotspot.net.MacAddressCompat
|
||||
|
||||
@Entity
|
||||
data class ClientRecord(@PrimaryKey
|
||||
@@ -14,7 +15,7 @@ data class ClientRecord(@PrimaryKey
|
||||
abstract class Dao {
|
||||
@Query("SELECT * FROM `ClientRecord` WHERE `mac` = :mac")
|
||||
protected abstract fun lookupBlocking(mac: Long): ClientRecord?
|
||||
fun lookupOrDefaultBlocking(mac: Long) = lookupBlocking(mac) ?: ClientRecord(mac)
|
||||
fun lookupOrDefaultBlocking(mac: MacAddressCompat) = lookupBlocking(mac.addr) ?: ClientRecord(mac.addr)
|
||||
|
||||
@Query("SELECT * FROM `ClientRecord` WHERE `mac` = :mac")
|
||||
protected abstract suspend fun lookup(mac: Long): ClientRecord?
|
||||
@@ -22,14 +23,15 @@ data class ClientRecord(@PrimaryKey
|
||||
|
||||
@Query("SELECT * FROM `ClientRecord` WHERE `mac` = :mac")
|
||||
protected abstract fun lookupSync(mac: Long): LiveData<ClientRecord?>
|
||||
fun lookupOrDefaultSync(mac: Long) = lookupSync(mac).map { it ?: ClientRecord(mac) }
|
||||
fun lookupOrDefaultSync(mac: MacAddressCompat) = lookupSync(mac.addr).map { it ?: ClientRecord(mac.addr) }
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
protected abstract suspend fun updateInternal(value: ClientRecord): Long
|
||||
suspend fun update(value: ClientRecord) = check(updateInternal(value) == value.mac)
|
||||
|
||||
@Transaction
|
||||
open suspend fun upsert(mac: Long, operation: suspend ClientRecord.() -> Unit) = lookupOrDefault(mac).apply {
|
||||
open suspend fun upsert(mac: MacAddressCompat, operation: suspend ClientRecord.() -> Unit) = lookupOrDefault(
|
||||
mac.addr).apply {
|
||||
operation()
|
||||
update(this)
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ import androidx.room.TypeConverter
|
||||
import be.mygod.vpnhotspot.util.useParcel
|
||||
import timber.log.Timber
|
||||
import java.net.InetAddress
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
|
||||
object Converters {
|
||||
@JvmStatic
|
||||
@@ -37,18 +35,3 @@ object Converters {
|
||||
@TypeConverter
|
||||
fun unpersistInetAddress(data: ByteArray): InetAddress = InetAddress.getByAddress(data)
|
||||
}
|
||||
|
||||
fun String.macToLong(): Long = ByteBuffer.allocate(8).run {
|
||||
order(ByteOrder.LITTLE_ENDIAN)
|
||||
mark()
|
||||
put(split(':').map { Integer.parseInt(it, 16).toByte() }.toByteArray())
|
||||
reset()
|
||||
long
|
||||
}
|
||||
|
||||
fun Iterable<Byte>.macToString() = joinToString(":") { "%02x".format(it) }
|
||||
fun Long.macToString(): String = ByteBuffer.allocate(8).run {
|
||||
order(ByteOrder.LITTLE_ENDIAN)
|
||||
putLong(this@macToString)
|
||||
array().take(6).macToString()
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import androidx.databinding.BindingAdapter
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import be.mygod.vpnhotspot.App.Companion.app
|
||||
import be.mygod.vpnhotspot.room.macToString
|
||||
import be.mygod.vpnhotspot.net.MacAddressCompat
|
||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||
import java.net.InetAddress
|
||||
import java.net.NetworkInterface
|
||||
@@ -94,7 +94,7 @@ fun makeMacSpan(mac: String) = if (app.hasTouch) SpannableString(mac).apply {
|
||||
|
||||
fun NetworkInterface.formatAddresses(macOnly: Boolean = false) = SpannableStringBuilder().apply {
|
||||
try {
|
||||
hardwareAddress?.apply { appendln(makeMacSpan(asIterable().macToString())) }
|
||||
hardwareAddress?.let { appendln(makeMacSpan(MacAddressCompat.bytesToString(it))) }
|
||||
} catch (_: SocketException) { }
|
||||
if (!macOnly) for (address in interfaceAddresses) {
|
||||
append(makeIpSpan(address.address))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package be.mygod.vpnhotspot.room
|
||||
package be.mygod.vpnhotspot.net
|
||||
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Test
|
||||
@@ -7,7 +7,7 @@ class ConvertersTest {
|
||||
@Test
|
||||
fun macSerialization() {
|
||||
for (test in listOf("01:23:45:67:89:ab", "DE:AD:88:88:BE:EF")) {
|
||||
assertTrue(test.equals(test.macToLong().macToString(), true))
|
||||
assertTrue(test.equals(MacAddressCompat.fromString(test).toString(), true))
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user