Skip to content

Commit 9968b94

Browse files
committed
1.0.31
removed libcotshrink and just used EXI, not as good but no more dealing with unhandled xml elements that TPC keeps adding also reworked the sending of chunked messages, we now wait for ACK for each chunk just like how data packages are sent.
1 parent e509f74 commit 9968b94

File tree

3 files changed

+146
-84
lines changed

3 files changed

+146
-84
lines changed

app/build.gradle

+5-4
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
buildscript {
1010

1111

12-
ext.PLUGIN_VERSION = "1.0.29"
12+
ext.PLUGIN_VERSION = "1.0.31"
1313
ext.ATAK_VERSION = "4.10.0"
1414

1515
def takdevVersion = '2.+'
@@ -219,9 +219,10 @@ android {
219219
dependencies {
220220
implementation fileTree(dir: 'libs', include: '*.jar')
221221

222-
implementation ('com.paulmandal.atak:libcotshrink:1.0.4') {
223-
exclude group: 'com.google.protobuf', module: 'protobuf-javalite'
224-
}
222+
// https://mvnrepository.com/artifact/com.siemens.ct.exi/exificient
223+
implementation 'com.siemens.ct.exi:exificient:1.0.7'
224+
225+
225226
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:1.0-M1-1.4.0-rc"
226227
implementation 'com.google.protobuf:protobuf-kotlin:3.21.12'
227228
implementation 'com.google.ar:core:1.43.0'

app/src/main/java/com/atakmap/android/meshtastic/MeshtasticMapComponent.java

+107-63
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import android.os.RemoteException;
2020
import android.preference.PreferenceManager;
2121
import android.widget.Toast;
22-
import android.app.NotificationChannel;
2322

2423
import androidx.core.app.NotificationCompat;
2524

@@ -29,7 +28,6 @@
2928
import com.atakmap.android.ipc.AtakBroadcast;
3029
import com.atakmap.android.maps.MapView;
3130
import com.atakmap.android.meshtastic.plugin.R;
32-
import com.atakmap.android.util.NotificationUtil;
3331
import com.atakmap.app.preferences.ToolsPreferenceFragment;
3432
import com.atakmap.comms.CotServiceRemote;
3533
import com.atakmap.coremap.filesystem.FileSystemUtils;
@@ -49,13 +47,17 @@
4947
import com.geeksville.mesh.IMeshService;
5048

5149
import com.google.protobuf.ByteString;
52-
import com.paulmandal.atak.libcotshrink.pub.api.CotShrinker;
53-
import com.paulmandal.atak.libcotshrink.pub.api.CotShrinkerFactory;
50+
import com.siemens.ct.exi.core.EXIFactory;
51+
import com.siemens.ct.exi.core.helpers.DefaultEXIFactory;
52+
import com.siemens.ct.exi.main.api.sax.EXIResult;
5453

54+
import org.xml.sax.InputSource;
55+
import org.xml.sax.XMLReader;
5556
import org.xmlpull.v1.XmlPullParser;
5657
import org.xmlpull.v1.XmlPullParserException;
5758
import org.xmlpull.v1.XmlPullParserFactory;
5859

60+
import java.io.ByteArrayOutputStream;
5961
import java.io.File;
6062
import java.io.IOException;
6163
import java.io.StringReader;
@@ -65,7 +67,10 @@
6567
import java.util.HashMap;
6668
import java.util.List;
6769
import java.util.Locale;
68-
import java.util.concurrent.ThreadLocalRandom;
70+
import java.util.concurrent.atomic.AtomicInteger;
71+
72+
import javax.xml.parsers.SAXParser;
73+
import javax.xml.parsers.SAXParserFactory;
6974

7075

7176
public class MeshtasticMapComponent extends DropDownMapComponent
@@ -74,9 +79,6 @@ public class MeshtasticMapComponent extends DropDownMapComponent
7479
CotServiceRemote.ConnectionListener {
7580
private static final String TAG = "MeshtasticMapComponent";
7681
private Context pluginContext;
77-
public static CotShrinkerFactory cotShrinkerFactory = new CotShrinkerFactory();
78-
public static CotShrinker cotShrinker = cotShrinkerFactory.createCotShrinker();
79-
8082
@Override
8183
public void onCotServiceConnected(Bundle bundle) {
8284

@@ -189,7 +191,7 @@ public static boolean sendFile(File f) {
189191
try {
190192
// send out 1 chunk
191193
DataPacket dp;
192-
i = ThreadLocalRandom.current().nextInt(0x10000000, 0x7fffff00);
194+
i = mMeshService.getPacketId();
193195
Log.d(TAG, "Chunk ID: " + i);
194196
chunkMap.put(String.valueOf(i), combined);
195197
editor.putInt("plugin_meshtastic_chunk_id", i);
@@ -202,7 +204,7 @@ public static boolean sendFile(File f) {
202204
mMeshService.send(dp);
203205
while (prefs.getBoolean("plugin_meshtastic_chunk_ACK", false)) {
204206
try {
205-
Thread.sleep(500);
207+
Thread.sleep(250);
206208
if (prefs.getBoolean("plugin_meshtastic_chunk_ERR", false)) {
207209
Log.d(TAG, "Chunk ERR received, retransmitting message ID: " + i);
208210
j=0;
@@ -342,7 +344,7 @@ public void processCotEvent(CotEvent cotEvent, String[] strings) {
342344
return;
343345
}
344346

345-
DataPacket dp;
347+
final DataPacket[] dp = new DataPacket[1];
346348

347349
int hopLimit = mr.getHopLimit();
348350

@@ -475,10 +477,10 @@ else if (xpp.getAttributeName(i).equalsIgnoreCase("speed"))
475477
Log.d(TAG, "Total wire size for TAKPacket: " + tak_packet.build().toByteArray().length);
476478
Log.d(TAG, "Sending: " + tak_packet.build().toString());
477479

478-
dp = new DataPacket(DataPacket.ID_BROADCAST, tak_packet.build().toByteArray(), Portnums.PortNum.ATAK_PLUGIN_VALUE, DataPacket.ID_LOCAL, System.currentTimeMillis(), 0, MessageStatus.UNKNOWN, hopLimit, channel);
480+
dp[0] = new DataPacket(DataPacket.ID_BROADCAST, tak_packet.build().toByteArray(), Portnums.PortNum.ATAK_PLUGIN_VALUE, DataPacket.ID_LOCAL, System.currentTimeMillis(), 0, MessageStatus.UNKNOWN, hopLimit, channel);
479481
try {
480482
if (mMeshService != null)
481-
mMeshService.send(dp);
483+
mMeshService.send(dp[0]);
482484
} catch (RemoteException e) {
483485
throw new RuntimeException(e);
484486
}
@@ -545,10 +547,10 @@ else if (xpp.getAttributeName(i).equalsIgnoreCase("speed"))
545547
Log.d(TAG, "Total wire size for TAKPacket: " + tak_packet.build().toByteArray().length);
546548
Log.d(TAG, "Sending: " + tak_packet.build().toString());
547549

548-
dp = new DataPacket(DataPacket.ID_BROADCAST, tak_packet.build().toByteArray(), Portnums.PortNum.ATAK_PLUGIN_VALUE, DataPacket.ID_LOCAL, System.currentTimeMillis(), 0, MessageStatus.UNKNOWN, hopLimit, channel);
550+
dp[0] = new DataPacket(DataPacket.ID_BROADCAST, tak_packet.build().toByteArray(), Portnums.PortNum.ATAK_PLUGIN_VALUE, DataPacket.ID_LOCAL, System.currentTimeMillis(), 0, MessageStatus.UNKNOWN, hopLimit, channel);
549551
try {
550552
if (mMeshService != null)
551-
mMeshService.send(dp);
553+
mMeshService.send(dp[0]);
552554
} catch (RemoteException e) {
553555
throw new RuntimeException(e);
554556
}
@@ -623,14 +625,14 @@ else if (xpp.getAttributeName(i).equalsIgnoreCase("speed"))
623625
// if "to" starts with !, its probably a meshtastic ID, so don't send it to ^all but the actual ID
624626
if (to.startsWith("!")) {
625627
Log.d(TAG, "Sending to Meshtastic ID: " + to);
626-
dp = new DataPacket(to, MeshProtos.Data.newBuilder().setPayload(ByteString.copyFrom(message.getBytes(StandardCharsets.UTF_8))).build().toByteArray(),Portnums.PortNum.TEXT_MESSAGE_APP_VALUE, DataPacket.ID_LOCAL, System.currentTimeMillis(), 0, MessageStatus.UNKNOWN, hopLimit, channel);
628+
dp[0] = new DataPacket(to, MeshProtos.Data.newBuilder().setPayload(ByteString.copyFrom(message.getBytes(StandardCharsets.UTF_8))).build().toByteArray(),Portnums.PortNum.TEXT_MESSAGE_APP_VALUE, DataPacket.ID_LOCAL, System.currentTimeMillis(), 0, MessageStatus.UNKNOWN, hopLimit, channel);
627629
} else {
628630
Log.d(TAG, "Sending to ^all");
629-
dp = new DataPacket(DataPacket.ID_BROADCAST, tak_packet.build().toByteArray(), Portnums.PortNum.ATAK_PLUGIN_VALUE, DataPacket.ID_LOCAL, System.currentTimeMillis(), 0, MessageStatus.UNKNOWN, hopLimit, channel);
631+
dp[0] = new DataPacket(DataPacket.ID_BROADCAST, tak_packet.build().toByteArray(), Portnums.PortNum.ATAK_PLUGIN_VALUE, DataPacket.ID_LOCAL, System.currentTimeMillis(), 0, MessageStatus.UNKNOWN, hopLimit, channel);
630632
}
631633
try {
632634
if (mMeshService != null)
633-
mMeshService.send(dp);
635+
mMeshService.send(dp[0]);
634636
} catch (RemoteException e) {
635637
throw new RuntimeException(e);
636638
}
@@ -640,62 +642,104 @@ else if (xpp.getAttributeName(i).equalsIgnoreCase("speed"))
640642
Log.d(TAG, "PLI/Chat Only");
641643
return;
642644
}
645+
new Thread(() -> {
646+
Log.d(TAG, "Using libcotshrink");
643647

644-
Log.d(TAG, "Using libcotshrink");
645-
646-
byte[] cotAsBytes = cotShrinker.toByteArrayLossy(cotEvent);
647-
648-
Log.d(TAG, "Size: " + cotAsBytes.length);
648+
byte[] cotAsBytes;
649649

650-
if (cotAsBytes.length < 236) {
651-
Log.d(TAG, "Small send");
652-
dp = new DataPacket(DataPacket.ID_BROADCAST, cotAsBytes, Portnums.PortNum.ATAK_FORWARDER_VALUE, DataPacket.ID_LOCAL, System.currentTimeMillis(), 0, MessageStatus.UNKNOWN, hopLimit, channel);
653650
try {
654-
if (mMeshService != null)
655-
mMeshService.send(dp);
656-
} catch (RemoteException e) {
651+
EXIFactory exiFactory = DefaultEXIFactory.newInstance();
652+
ByteArrayOutputStream osEXI = new ByteArrayOutputStream();
653+
EXIResult exiResult = new EXIResult(exiFactory);
654+
exiResult.setOutputStream(osEXI);
655+
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
656+
SAXParser newSAXParser = saxParserFactory.newSAXParser();
657+
XMLReader xmlReader = newSAXParser.getXMLReader();
658+
xmlReader.setContentHandler(exiResult.getHandler());
659+
InputSource stream = new InputSource(new StringReader(cotEvent.toString()));
660+
xmlReader.parse(stream); // parse XML input
661+
cotAsBytes = osEXI.toByteArray();
662+
osEXI.close();
663+
} catch (Exception e) {
657664
e.printStackTrace();
665+
return;
658666
}
659-
return;
660-
}
661667

662-
List<byte[]> chunkList = divideArray(cotAsBytes, 200);
668+
Log.d(TAG, "Size: " + cotAsBytes.length);
663669

664-
int chunks = (int) Math.floor(cotAsBytes.length / 200);
665-
chunks++;
666-
Log.d(TAG, "Sending " + chunks);
670+
if (cotAsBytes.length < 236) {
671+
Log.d(TAG, "Small send");
672+
dp[0] = new DataPacket(DataPacket.ID_BROADCAST, cotAsBytes, Portnums.PortNum.ATAK_FORWARDER_VALUE, DataPacket.ID_LOCAL, System.currentTimeMillis(), 0, MessageStatus.UNKNOWN, hopLimit, channel);
673+
try {
674+
if (mMeshService != null)
675+
mMeshService.send(dp[0]);
676+
} catch (RemoteException e) {
677+
e.printStackTrace();
678+
}
679+
return;
680+
}
667681

668-
byte[] chunk_hdr = String.format(Locale.US, "CHK_%d_", cotAsBytes.length).getBytes();
682+
int chunkSize = 220;
683+
List<byte[]> chunkList = divideArray(cotAsBytes, chunkSize);
684+
final AtomicInteger[] i = {new AtomicInteger()};
685+
int chunks = (int) Math.floor(cotAsBytes.length / chunkSize);
686+
chunks++;
687+
Log.d(TAG, "Sending " + chunks);
688+
689+
byte[] chunk_hdr = String.format(Locale.US, "CHK_%d_", cotAsBytes.length).getBytes();
690+
HashMap<String, byte[]> chunkMap = new HashMap<>();
691+
692+
for (byte[] c : chunkList) {
693+
byte[] combined = new byte[chunk_hdr.length + c.length];
694+
try {
695+
System.arraycopy(chunk_hdr, 0, combined, 0, chunk_hdr.length);
696+
System.arraycopy(c, 0, combined, chunk_hdr.length, c.length);
697+
} catch (Exception e) {
698+
e.printStackTrace();
699+
return;
700+
}
669701

670-
for (byte[] c : chunkList) {
671-
byte[] combined = new byte[chunk_hdr.length + c.length];
672-
try {
673-
System.arraycopy(chunk_hdr, 0, combined, 0, chunk_hdr.length);
674-
System.arraycopy(c, 0, combined, chunk_hdr.length, c.length);
675-
} catch (Exception e) {
676-
e.printStackTrace();
677-
return;
702+
try {
703+
// send out 1 chunk
704+
i[0].set(mMeshService.getPacketId());
705+
Log.d(TAG, "Chunk ID: " + i[0]);
706+
chunkMap.put(String.valueOf(i[0].get()), combined);
707+
editor.putInt("plugin_meshtastic_chunk_id", i[0].get());
708+
editor.putBoolean("plugin_meshtastic_chunk_ACK", true);
709+
editor.apply();
710+
711+
INNER:
712+
for (int j = 0; j < 1; j++) {
713+
dp[0] = new DataPacket(DataPacket.ID_BROADCAST, combined, Portnums.PortNum.ATAK_FORWARDER_VALUE, DataPacket.ID_LOCAL, System.currentTimeMillis(), i[0].get(), MessageStatus.UNKNOWN, hopLimit, channel);
714+
mMeshService.send(dp[0]);
715+
while (prefs.getBoolean("plugin_meshtastic_chunk_ACK", false)) {
716+
try {
717+
Thread.sleep(500);
718+
if (prefs.getBoolean("plugin_meshtastic_chunk_ERR", false)) {
719+
Log.d(TAG, "Chunk ERR received, retransmitting message ID: " + i[0]);
720+
j = 0;
721+
break INNER;
722+
}
723+
} catch (Exception e) {
724+
e.printStackTrace();
725+
}
726+
}
727+
}
728+
} catch (RemoteException e) {
729+
e.printStackTrace();
730+
return;
731+
}
678732
}
679-
// send out 1 chunk
680-
dp = new DataPacket(DataPacket.ID_BROADCAST, combined, Portnums.PortNum.ATAK_FORWARDER_VALUE, DataPacket.ID_LOCAL, System.currentTimeMillis(), 0, MessageStatus.UNKNOWN, hopLimit, channel);
681-
try {
682-
if (mMeshService != null)
683-
mMeshService.send(dp);
684-
} catch (RemoteException e) {
685-
e.printStackTrace();
686-
return;
733+
// We're done chunking
734+
dp[0] = new DataPacket(DataPacket.ID_BROADCAST, new byte[]{'E', 'N', 'D'}, Portnums.PortNum.ATAK_FORWARDER_VALUE, DataPacket.ID_LOCAL, System.currentTimeMillis(), 0, MessageStatus.UNKNOWN, 3, prefs.getInt("meshtastic_channel", 0));
735+
if (mMeshService != null) {
736+
try {
737+
mMeshService.send(dp[0]);
738+
} catch (RemoteException e) {
739+
e.printStackTrace();
740+
}
687741
}
688-
}
689-
690-
// We're done chunking
691-
dp = new DataPacket(DataPacket.ID_BROADCAST, new byte[]{'E', 'N', 'D'}, Portnums.PortNum.ATAK_FORWARDER_VALUE, DataPacket.ID_LOCAL, System.currentTimeMillis(), 0, MessageStatus.UNKNOWN, hopLimit, channel);
692-
try {
693-
if (mMeshService != null)
694-
mMeshService.send(dp);
695-
} catch (RemoteException e) {
696-
e.printStackTrace();
697-
return;
698-
}
742+
}).start();
699743
}
700744
}
701745
public void onCreate(final Context context, Intent intent, MapView view) {

0 commit comments

Comments
 (0)