diff --git a/mobile/build.gradle.kts b/mobile/build.gradle.kts index bdfab3a9..e4d8c7fb 100644 --- a/mobile/build.gradle.kts +++ b/mobile/build.gradle.kts @@ -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) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt index 94c25b6f..ca04d317 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt @@ -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 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 171d7c3b..ce9bc2f6 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/client/Client.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/client/Client.kt @@ -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() { 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(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 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 312193c9..0cad57f7 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientViewModel.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientViewModel.kt @@ -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>() private fun populateClients() { - val clients = HashMap, Client>() + val clients = HashMap, 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 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 a75487c8..24f44575 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientsFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientsFragment.kt @@ -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() { 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, TrafficRate>() + private var rates = mutableMapOf, TrafficRate>() override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { binding = FragmentClientsBinding.inflate(inflater, container, false) 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 a1c5e785..4e89f9e4 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/client/MacLookup.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/client/MacLookup.kt @@ -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>() + private val macLookupBusy = mutableMapOf>() 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 diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/client/WifiP2pClient.kt b/mobile/src/main/java/be/mygod/vpnhotspot/client/WifiP2pClient.kt index ea5027f3..fa805954 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/client/WifiP2pClient.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/client/WifiP2pClient.kt @@ -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 } 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 603aeecd..54592f7d 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/IpNeighbour.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/IpNeighbour.kt @@ -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) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/MacAddressCompat.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/MacAddressCompat.kt new file mode 100644 index 00000000..14a6ec1b --- /dev/null +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/MacAddressCompat.kt @@ -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): 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)) + } +} 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 ac36b12e..d3ebc721 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/Routing.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/Routing.kt @@ -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") diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/TrafficRecorder.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/TrafficRecorder.kt index bfecd37e..eb60b046 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/TrafficRecorder.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/TrafficRecorder.kt @@ -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, TrafficRecord>() val foregroundListeners = Event2, LongSparseArray>() - 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 } } 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 8b495e55..64dd9750 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/room/ClientRecord.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/room/ClientRecord.kt @@ -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 - 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) } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/room/Converters.kt b/mobile/src/main/java/be/mygod/vpnhotspot/room/Converters.kt index d6992b52..fed46b49 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/room/Converters.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/room/Converters.kt @@ -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.macToString() = joinToString(":") { "%02x".format(it) } -fun Long.macToString(): String = ByteBuffer.allocate(8).run { - order(ByteOrder.LITTLE_ENDIAN) - putLong(this@macToString) - array().take(6).macToString() -} 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 02b3f21f..967ba260 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt @@ -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)) diff --git a/mobile/src/test/java/be/mygod/vpnhotspot/room/ConvertersTest.kt b/mobile/src/test/java/be/mygod/vpnhotspot/net/MacAddressCompatTest.kt similarity index 62% rename from mobile/src/test/java/be/mygod/vpnhotspot/room/ConvertersTest.kt rename to mobile/src/test/java/be/mygod/vpnhotspot/net/MacAddressCompatTest.kt index 97f52587..f65ee1f6 100644 --- a/mobile/src/test/java/be/mygod/vpnhotspot/room/ConvertersTest.kt +++ b/mobile/src/test/java/be/mygod/vpnhotspot/net/MacAddressCompatTest.kt @@ -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)) } } }