mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
* Expose wantAck in Mesh Packet * Update DataPacket.kt * Update DataPacket.kt * Update DataPacket.kt * Update DataPacket.kt * Refactor: Change wantAck field type in DataPacket - Changed the `wantAck` field in `DataPacket` from `Int` to `Boolean`. - Updated read/write and equals/hashcode functions accordingly. * Update MeshService.kt * Update MeshService.kt --------- Co-authored-by: Ben Meadors <benmmeadors@gmail.com> Co-authored-by: James Rich <2199651+jamesarich@users.noreply.github.com>
216 lines
7.1 KiB
Kotlin
216 lines
7.1 KiB
Kotlin
/*
|
|
* Copyright (c) 2025 Meshtastic LLC
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package com.geeksville.mesh
|
|
|
|
import android.os.Parcel
|
|
import android.os.Parcelable
|
|
import kotlinx.parcelize.Parcelize
|
|
import kotlinx.serialization.Serializable
|
|
|
|
/**
|
|
* Generic [Parcel.readParcelable] Android 13 compatibility extension.
|
|
*/
|
|
private inline fun <reified T : Parcelable> Parcel.readParcelableCompat(loader: ClassLoader?): T? {
|
|
return if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.TIRAMISU) {
|
|
@Suppress("DEPRECATION")
|
|
readParcelable(loader)
|
|
} else {
|
|
readParcelable(loader, T::class.java)
|
|
}
|
|
}
|
|
|
|
@Parcelize
|
|
enum class MessageStatus : Parcelable {
|
|
UNKNOWN, // Not set for this message
|
|
RECEIVED, // Came in from the mesh
|
|
QUEUED, // Waiting to send to the mesh as soon as we connect to the device
|
|
ENROUTE, // Delivered to the radio, but no ACK or NAK received
|
|
DELIVERED, // We received an ack
|
|
ERROR // We received back a nak, message not delivered
|
|
}
|
|
|
|
/**
|
|
* A parcelable version of the protobuf MeshPacket + Data subpacket.
|
|
*/
|
|
@Serializable
|
|
data class DataPacket(
|
|
var to: String? = ID_BROADCAST, // a nodeID string, or ID_BROADCAST for broadcast
|
|
val bytes: ByteArray?,
|
|
val dataType: Int, // A port number for this packet (formerly called DataType, see portnums.proto for new usage instructions)
|
|
var from: String? = ID_LOCAL, // a nodeID string, or ID_LOCAL for localhost
|
|
var time: Long = System.currentTimeMillis(), // msecs since 1970
|
|
var id: Int = 0, // 0 means unassigned
|
|
var status: MessageStatus? = MessageStatus.UNKNOWN,
|
|
var hopLimit: Int = 0,
|
|
var channel: Int = 0, // channel index
|
|
var wantAck: Boolean = true, // If true, the receiver should send an ack back
|
|
) : Parcelable {
|
|
|
|
/**
|
|
* If there was an error with this message, this string describes what was wrong.
|
|
*/
|
|
var errorMessage: String? = null
|
|
|
|
/**
|
|
* Syntactic sugar to make it easy to create text messages
|
|
*/
|
|
constructor(to: String?, channel: Int, text: String) : this(
|
|
to = to,
|
|
bytes = text.encodeToByteArray(),
|
|
dataType = Portnums.PortNum.TEXT_MESSAGE_APP_VALUE,
|
|
channel = channel
|
|
)
|
|
|
|
/**
|
|
* If this is a text message, return the string, otherwise null
|
|
*/
|
|
val text: String?
|
|
get() = if (dataType == Portnums.PortNum.TEXT_MESSAGE_APP_VALUE) {
|
|
bytes?.decodeToString()
|
|
} else {
|
|
null
|
|
}
|
|
|
|
val alert: String?
|
|
get() = if (dataType == Portnums.PortNum.ALERT_APP_VALUE) {
|
|
bytes?.decodeToString()
|
|
} else {
|
|
null
|
|
}
|
|
|
|
constructor(to: String?, channel: Int, waypoint: MeshProtos.Waypoint) : this(
|
|
to = to,
|
|
bytes = waypoint.toByteArray(),
|
|
dataType = Portnums.PortNum.WAYPOINT_APP_VALUE,
|
|
channel = channel
|
|
)
|
|
|
|
val waypoint: MeshProtos.Waypoint?
|
|
get() = if (dataType == Portnums.PortNum.WAYPOINT_APP_VALUE) {
|
|
MeshProtos.Waypoint.parseFrom(bytes)
|
|
} else {
|
|
null
|
|
}
|
|
|
|
// Autogenerated comparision, because we have a byte array
|
|
|
|
constructor(parcel: Parcel) : this(
|
|
parcel.readString(),
|
|
parcel.createByteArray(),
|
|
parcel.readInt(),
|
|
parcel.readString(),
|
|
parcel.readLong(),
|
|
parcel.readInt(),
|
|
parcel.readParcelableCompat(MessageStatus::class.java.classLoader),
|
|
parcel.readInt(),
|
|
parcel.readInt(),
|
|
parcel.readInt() == 1,
|
|
)
|
|
|
|
override fun equals(other: Any?): Boolean {
|
|
if (this === other) return true
|
|
if (javaClass != other?.javaClass) return false
|
|
|
|
other as DataPacket
|
|
|
|
if (from != other.from) return false
|
|
if (to != other.to) return false
|
|
if (channel != other.channel) return false
|
|
if (time != other.time) return false
|
|
if (id != other.id) return false
|
|
if (dataType != other.dataType) return false
|
|
if (!bytes!!.contentEquals(other.bytes!!)) return false
|
|
if (status != other.status) return false
|
|
if (hopLimit != other.hopLimit) return false
|
|
if (wantAck != other.wantAck) return false
|
|
|
|
return true
|
|
}
|
|
|
|
override fun hashCode(): Int {
|
|
var result = from.hashCode()
|
|
result = 31 * result + to.hashCode()
|
|
result = 31 * result + time.hashCode()
|
|
result = 31 * result + id
|
|
result = 31 * result + dataType
|
|
result = 31 * result + bytes!!.contentHashCode()
|
|
result = 31 * result + status.hashCode()
|
|
result = 31 * result + hopLimit
|
|
result = 31 * result + channel
|
|
result = 31 * result + wantAck.hashCode()
|
|
return result
|
|
}
|
|
|
|
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
|
parcel.writeString(to)
|
|
parcel.writeByteArray(bytes)
|
|
parcel.writeInt(dataType)
|
|
parcel.writeString(from)
|
|
parcel.writeLong(time)
|
|
parcel.writeInt(id)
|
|
parcel.writeParcelable(status, flags)
|
|
parcel.writeInt(hopLimit)
|
|
parcel.writeInt(channel)
|
|
parcel.writeInt(if (wantAck) 1 else 0)
|
|
}
|
|
|
|
override fun describeContents(): Int {
|
|
return 0
|
|
}
|
|
|
|
// Update our object from our parcel (used for inout parameters
|
|
fun readFromParcel(parcel: Parcel) {
|
|
to = parcel.readString()
|
|
parcel.createByteArray()
|
|
parcel.readInt()
|
|
from = parcel.readString()
|
|
time = parcel.readLong()
|
|
id = parcel.readInt()
|
|
status = parcel.readParcelableCompat(MessageStatus::class.java.classLoader)
|
|
hopLimit = parcel.readInt()
|
|
channel = parcel.readInt()
|
|
wantAck = parcel.readInt() == 1
|
|
}
|
|
|
|
companion object CREATOR : Parcelable.Creator<DataPacket> {
|
|
// Special node IDs that can be used for sending messages
|
|
|
|
/** the Node ID for broadcast destinations */
|
|
const val ID_BROADCAST = "^all"
|
|
|
|
/** The Node ID for the local node - used for from when sender doesn't know our local node ID */
|
|
const val ID_LOCAL = "^local"
|
|
|
|
// special broadcast address
|
|
const val NODENUM_BROADCAST = (0xffffffff).toInt()
|
|
|
|
// Public-key cryptography (PKC) channel index
|
|
const val PKC_CHANNEL_INDEX = 8
|
|
|
|
fun nodeNumToDefaultId(n: Int): String = "!%08x".format(n)
|
|
fun idToDefaultNodeNum(id: String?): Int? = runCatching { id?.toLong(16)?.toInt() }.getOrNull()
|
|
|
|
override fun createFromParcel(parcel: Parcel): DataPacket {
|
|
return DataPacket(parcel)
|
|
}
|
|
|
|
override fun newArray(size: Int): Array<DataPacket?> {
|
|
return arrayOfNulls(size)
|
|
}
|
|
}
|
|
}
|