mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
#2013 Enhance SUID extension message handling and correct encryption sync when the algorithm is detected as ACCORDION and the key ID and MI are both zero, indicating a bad algorithm ID value. (#2014)
Co-authored-by: Dennis Sheirer <dsheirer@github.com>
This commit is contained in:
@@ -31,8 +31,10 @@ import io.github.dsheirer.identifier.IdentifierCollection;
|
||||
import io.github.dsheirer.identifier.MutableIdentifierCollection;
|
||||
import io.github.dsheirer.identifier.alias.TalkerAliasManager;
|
||||
import io.github.dsheirer.identifier.encryption.EncryptionKeyIdentifier;
|
||||
import io.github.dsheirer.identifier.patch.PatchGroupIdentifier;
|
||||
import io.github.dsheirer.identifier.patch.PatchGroupPreLoadDataContent;
|
||||
import io.github.dsheirer.identifier.scramble.ScrambleParameterIdentifier;
|
||||
import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier;
|
||||
import io.github.dsheirer.log.LoggingSuppressor;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.message.IMessageListener;
|
||||
@@ -709,6 +711,78 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a Phase 1 HDU indicating a call start.
|
||||
*
|
||||
* @param frequency for the call event
|
||||
* @param talkgroup to update within the event.
|
||||
* @param eki for encryption settings.
|
||||
* @param timestamp for the update
|
||||
*/
|
||||
public void processP1TrafficCallStart(long frequency, Identifier<?> talkgroup, Identifier<?> radio,
|
||||
EncryptionKeyIdentifier eki, ServiceOptions serviceOptions,
|
||||
IChannelDescriptor channelDescriptor, long timestamp)
|
||||
{
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
P25TrafficChannelEventTracker tracker = getTracker(frequency, P25P1Message.TIMESLOT_1);
|
||||
|
||||
if(tracker != null)
|
||||
{
|
||||
removeTracker(frequency, P25P1Message.TIMESLOT_1);
|
||||
}
|
||||
|
||||
DecodeEventType decodeEventType = getDecodeEventType(talkgroup, eki);
|
||||
|
||||
MutableIdentifierCollection mic = new MutableIdentifierCollection();
|
||||
mic.update(talkgroup);
|
||||
mic.update(radio);
|
||||
mic.update(eki);
|
||||
|
||||
//Create a new event for the current call.
|
||||
P25ChannelGrantEvent callEvent = P25ChannelGrantEvent.builder(decodeEventType, timestamp, serviceOptions)
|
||||
.channelDescriptor(channelDescriptor)
|
||||
.details("PHASE 1 CALL " + (serviceOptions != null ? serviceOptions : ""))
|
||||
.identifiers(mic)
|
||||
.build();
|
||||
|
||||
tracker = new P25TrafficChannelEventTracker(callEvent);
|
||||
addTracker(tracker, frequency, P25P1Message.TIMESLOT_1);
|
||||
broadcast(tracker);
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the HDU message call decode event type from the talkgroup identifier and encryption key
|
||||
* @param talkgroup to inspect
|
||||
* @param eki to inspect
|
||||
* @return decode event type.
|
||||
*/
|
||||
private static DecodeEventType getDecodeEventType(Identifier talkgroup, EncryptionKeyIdentifier eki)
|
||||
{
|
||||
DecodeEventType decodeEventType = null;
|
||||
|
||||
if(talkgroup instanceof PatchGroupIdentifier)
|
||||
{
|
||||
decodeEventType = eki.isEncrypted() ? DecodeEventType.CALL_PATCH_GROUP_ENCRYPTED : DecodeEventType.CALL_PATCH_GROUP;
|
||||
}
|
||||
else if(talkgroup instanceof TalkgroupIdentifier ti && ti.getValue() == 0) //Unit-to-Unit private call
|
||||
{
|
||||
decodeEventType = eki.isEncrypted() ? DecodeEventType.CALL_UNIT_TO_UNIT_ENCRYPTED : DecodeEventType.CALL_UNIT_TO_UNIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
decodeEventType = eki.isEncrypted() ? DecodeEventType.CALL_GROUP_ENCRYPTED : DecodeEventType.CALL_GROUP;
|
||||
}
|
||||
return decodeEventType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an identifier for an ongoing call event on the frequency and updates the event duration timestamp.
|
||||
*
|
||||
@@ -755,6 +829,55 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates all current identifiers for an ongoing call event on the frequency and updates the event duration timestamp.
|
||||
*
|
||||
* Note: if this manager does not have an existing call event for the frequency, the update is ignored
|
||||
* because we don't have enough detail to create a call event.
|
||||
*
|
||||
* This is used primarily to add encryption, GPS, talker alias, etc. but can be used for any identifier update.
|
||||
*
|
||||
* @param frequency for the call event
|
||||
* @param identifier to update within the event.
|
||||
* @param timestamp for the update
|
||||
*/
|
||||
public void processP1TrafficCurrentUserIdentifiers(long frequency, List<Identifier> identifiers, long timestamp, String context)
|
||||
{
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
P25TrafficChannelEventTracker tracker = getTracker(frequency, P25P1Message.TIMESLOT_1);
|
||||
|
||||
if(tracker != null && tracker.isComplete())
|
||||
{
|
||||
removeTracker(frequency, P25P1Message.TIMESLOT_1);
|
||||
tracker = null;
|
||||
}
|
||||
|
||||
if(tracker != null)
|
||||
{
|
||||
for(Identifier identifier : identifiers)
|
||||
{
|
||||
tracker.addIdentifierIfMissing(identifier);
|
||||
|
||||
//Add the encryption key to the call event details.
|
||||
if(identifier instanceof EncryptionKeyIdentifier eki && eki.isEncrypted())
|
||||
{
|
||||
tracker.addDetailsIfMissing(eki.toString());
|
||||
}
|
||||
}
|
||||
|
||||
tracker.updateDurationTraffic(timestamp);
|
||||
broadcast(tracker);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes traffic channel announced current user information.
|
||||
* @param frequency of the traffic channel
|
||||
|
||||
@@ -39,7 +39,7 @@ import io.github.dsheirer.module.decode.p25.phase1.message.ldu.EncryptionSyncPar
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDU1Message;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDU2Message;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDUMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULinkControlMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULCMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDUMessage;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import java.util.List;
|
||||
@@ -119,9 +119,9 @@ public class P25P1CallSequenceRecorder extends MBECallSequenceRecorder
|
||||
{
|
||||
process((LDUMessage)message);
|
||||
}
|
||||
else if(message instanceof TDULinkControlMessage)
|
||||
else if(message instanceof TDULCMessage)
|
||||
{
|
||||
process((TDULinkControlMessage)message);
|
||||
process((TDULCMessage)message);
|
||||
}
|
||||
else if(message instanceof TDUMessage)
|
||||
{
|
||||
@@ -133,7 +133,7 @@ public class P25P1CallSequenceRecorder extends MBECallSequenceRecorder
|
||||
}
|
||||
}
|
||||
|
||||
private void process(TDULinkControlMessage tdulc)
|
||||
private void process(TDULCMessage tdulc)
|
||||
{
|
||||
process(tdulc.getLinkControlWord());
|
||||
}
|
||||
|
||||
@@ -61,7 +61,6 @@ import io.github.dsheirer.module.decode.p25.phase1.message.IFrequencyBand;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.P25P1Message;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.hdu.HDUMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.hdu.HeaderData;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.IExtendedSourceMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.LinkControlWord;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris.LCHarrisReturnToControlChannel;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris.LCHarrisTalkerAliasComplete;
|
||||
@@ -113,7 +112,7 @@ import io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.PacketMess
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.sndcp.SNDCPPacketMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.response.ResponseMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.umbtc.isp.UMBTCTelephoneInterconnectRequestExplicitDialing;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULinkControlMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULCMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.Opcode;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.TSBKMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.harris.osp.L3HarrisGroupRegroupExplicitEncryptionCommand;
|
||||
@@ -190,6 +189,7 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
private final P25P1NetworkConfigurationMonitor mNetworkConfigurationMonitor;
|
||||
private final Listener<ChannelEvent> mChannelEventListener;
|
||||
private P25TrafficChannelManager mTrafficChannelManager;
|
||||
private ServiceOptions mCurrentServiceOptions;
|
||||
|
||||
/**
|
||||
* Constructs an APCO-25 decoder state with an optional traffic channel manager.
|
||||
@@ -316,10 +316,6 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if(iMessage instanceof IExtendedSourceMessage esm && iMessage instanceof LinkControlWord lcw)
|
||||
{
|
||||
processLC(lcw, esm.getTimestamp(), esm.isTerminator());
|
||||
}
|
||||
else if(iMessage instanceof MotorolaTalkerAliasComplete tac)
|
||||
{
|
||||
mTrafficChannelManager.getTalkerAliasManager().update(tac.getRadio(), tac.getAlias());
|
||||
@@ -413,7 +409,6 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
getIdentifierCollection().update(updated);
|
||||
DecodeEventType decodeEventType = getLCDecodeEventType(lcw);
|
||||
|
||||
|
||||
ServiceOptions serviceOptions = null;
|
||||
|
||||
if(lcw instanceof IServiceOptionsProvider sop)
|
||||
@@ -831,17 +826,31 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
if(message.isValid() && message instanceof HDUMessage hdu)
|
||||
{
|
||||
HeaderData headerData = hdu.getHeaderData();
|
||||
//Run the talkgroup through the patch group manager so we don't get a plain talkgroup in addition to the patch
|
||||
Identifier talkgroup = mPatchGroupManager.update(headerData.getTalkgroup(), message.getTimestamp());
|
||||
mTrafficChannelManager.processP1TrafficCurrentUser(getCurrentFrequency(), talkgroup, hdu.getTimestamp(), message.toString());
|
||||
|
||||
if(headerData.isEncryptedAudio())
|
||||
if(headerData.isValid())
|
||||
{
|
||||
broadcast(new DecoderStateEvent(this, Event.START, State.ENCRYPTED));
|
||||
}
|
||||
else
|
||||
{
|
||||
broadcast(new DecoderStateEvent(this, Event.START, State.CALL));
|
||||
Identifier talkgroup = headerData.getTalkgroup();
|
||||
|
||||
//Run the talkgroup through the patch group manager so we don't get a plain talkgroup in addition to the patch
|
||||
//Talkgroup value of zero indicates a unit-to-unit call, so don't attempt to update it as a patch group
|
||||
if(headerData.getTalkgroup().getValue() > 0)
|
||||
{
|
||||
talkgroup = mPatchGroupManager.update(talkgroup, message.getTimestamp());
|
||||
}
|
||||
|
||||
Identifier<?> radio = getIdentifierCollection().getFromIdentifier();
|
||||
|
||||
mTrafficChannelManager.processP1TrafficCallStart(getCurrentFrequency(), talkgroup, radio,
|
||||
headerData.getEncryptionKey(), mCurrentServiceOptions, getCurrentChannel(), message.getTimestamp());
|
||||
|
||||
if(headerData.isEncryptedAudio())
|
||||
{
|
||||
broadcast(new DecoderStateEvent(this, Event.START, State.ENCRYPTED));
|
||||
}
|
||||
else
|
||||
{
|
||||
broadcast(new DecoderStateEvent(this, Event.START, State.CALL));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -862,6 +871,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
if(lcw != null && lcw.isValid())
|
||||
{
|
||||
processLC(lcw, message.getTimestamp(), false);
|
||||
mTrafficChannelManager.processP1TrafficCurrentUserIdentifiers(getCurrentFrequency(),
|
||||
getIdentifierCollection().getIdentifiers(), message.getTimestamp(), ldu1.toString());
|
||||
}
|
||||
}
|
||||
else if(message instanceof LDU2Message ldu2)
|
||||
@@ -917,7 +928,7 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
getIdentifierCollection().remove(IdentifierClass.USER, Role.FROM);
|
||||
}
|
||||
|
||||
if(message instanceof TDULinkControlMessage tdulc)
|
||||
if(message instanceof TDULCMessage tdulc)
|
||||
{
|
||||
LinkControlWord lcw = tdulc.getLinkControlWord();
|
||||
|
||||
@@ -1831,7 +1842,10 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
{
|
||||
switch(lcw.getOpcode())
|
||||
{
|
||||
//Calls in-progress on this channel
|
||||
case SOURCE_ID_EXTENSION:
|
||||
//Ignore - handled elsewhere
|
||||
break;
|
||||
//Calls getting ready to start or in-progress on this channel
|
||||
case GROUP_VOICE_CHANNEL_USER:
|
||||
case MOTOROLA_GROUP_REGROUP_VOICE_CHANNEL_USER:
|
||||
case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_USER:
|
||||
@@ -1839,7 +1853,9 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
case UNIT_TO_UNIT_VOICE_CHANNEL_USER_EXTENDED:
|
||||
if(isTerminator)
|
||||
{
|
||||
closeCurrentCallEvent(timestamp, lcw.toString());
|
||||
mTrafficChannelManager.processP1TrafficCallEnd(getCurrentFrequency(), timestamp, lcw.toString());
|
||||
List<Identifier> updated = mPatchGroupManager.update(lcw.getIdentifiers(), timestamp);
|
||||
getIdentifierCollection().update(updated);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1941,11 +1957,6 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
getIdentifierCollection().update(frequencyID);
|
||||
}
|
||||
|
||||
if(isTerminator)
|
||||
{
|
||||
closeCurrentCallEvent(timestamp, lcw.toString());
|
||||
}
|
||||
|
||||
mNetworkConfigurationMonitor.process(lcw);
|
||||
break;
|
||||
case NETWORK_STATUS_BROADCAST_EXPLICIT:
|
||||
@@ -2162,9 +2173,6 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
closeCurrentCallEvent(timestamp, lcw.toString());
|
||||
}
|
||||
break;
|
||||
case SOURCE_ID_EXTENSION:
|
||||
//Ignore - handled elsewhere
|
||||
break;
|
||||
default:
|
||||
if(isTerminator)
|
||||
{
|
||||
|
||||
@@ -19,10 +19,12 @@
|
||||
package io.github.dsheirer.module.decode.p25.phase1;
|
||||
|
||||
import io.github.dsheirer.channel.IChannelDescriptor;
|
||||
import io.github.dsheirer.message.AbstractMessage;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.p25.P25FrequencyBandPreloadDataContent;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.IFrequencyBand;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.IFrequencyBandReceiver;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.hdu.HDUMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.IExtendedSourceMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.LinkControlWord;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris.HarrisTalkerAliasAssembler;
|
||||
@@ -30,7 +32,8 @@ import io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris.LCHarrisT
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.motorola.LCMotorolaTalkerAliasAssembler;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.standard.LCSourceIDExtension;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDU1Message;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULinkControlMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDU2Message;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULCMessage;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -59,9 +62,12 @@ public class P25P1MessageProcessor implements Listener<IMessage>
|
||||
private Map<Integer,IFrequencyBand> mFrequencyBandMap = new TreeMap<Integer,IFrequencyBand>();
|
||||
|
||||
/**
|
||||
* Temporary holding of an extended source link control message while it awaits the extension message to arrive.
|
||||
* Temporary holding for an extended source link control message while it awaits the extension message to arrive.
|
||||
*/
|
||||
private IExtendedSourceMessage mExtendedSourceMessage;
|
||||
private TDULCMessage mHeldTDULCMessage;
|
||||
private HDUMessage mHeldHDUMessage;
|
||||
private LDU1Message mHeldLDU1Message;
|
||||
private LDU2Message mHeldLDU2Message;
|
||||
|
||||
/**
|
||||
* Motorola talker alias assembler for link control header and data blocks.
|
||||
@@ -103,120 +109,200 @@ public class P25P1MessageProcessor implements Listener<IMessage>
|
||||
@Override
|
||||
public void receive(IMessage message)
|
||||
{
|
||||
//Optional message created during processing that should be sent after the current argument is sent.
|
||||
IMessage additionalMessageToSend = null;
|
||||
|
||||
if(message.isValid())
|
||||
{
|
||||
//Reassemble extended source link control messages.
|
||||
if(message instanceof LDU1Message ldu)
|
||||
if(message instanceof LDU1Message ldu1)
|
||||
{
|
||||
if(ldu.getLinkControlWord() instanceof IExtendedSourceMessage extendedSourceMessage)
|
||||
LinkControlWord lcw = ldu1.getLinkControlWord();;
|
||||
|
||||
if(lcw instanceof IExtendedSourceMessage esm)
|
||||
{
|
||||
additionalMessageToSend = reassembleLC(extendedSourceMessage);
|
||||
if(lcw instanceof LCSourceIDExtension extension)
|
||||
{
|
||||
processSourceIDExtension(extension);
|
||||
}
|
||||
else if(esm.isExtensionRequired())
|
||||
{
|
||||
processSourceIDExtension(null);
|
||||
mHeldLDU1Message = ldu1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if(ldu.getLinkControlWord() instanceof LCHarrisTalkerAliasBase harrisTalkerAlias)
|
||||
else if(lcw instanceof LCHarrisTalkerAliasBase harrisTalkerAlias)
|
||||
{
|
||||
//Send the LCW to the harris talker alias assembler
|
||||
additionalMessageToSend = mHarrisTalkerAliasAssembler.process(harrisTalkerAlias, ldu.getTimestamp());
|
||||
dispatch(mHarrisTalkerAliasAssembler.process(harrisTalkerAlias, ldu1.getTimestamp()));
|
||||
}
|
||||
|
||||
//Flush any held messages
|
||||
processSourceIDExtension(null);
|
||||
|
||||
dispatch(ldu1);
|
||||
}
|
||||
else if(message instanceof LDU2Message ldu2)
|
||||
{
|
||||
//If we held onto an LDU1 awaiting a source ID extension, then also hold onto this LDU2 and flush them in sequence.
|
||||
if(mHeldLDU1Message != null)
|
||||
{
|
||||
mHeldLDU2Message = ldu2;
|
||||
}
|
||||
else
|
||||
{
|
||||
dispatch(ldu2);
|
||||
}
|
||||
}
|
||||
else if(message instanceof TDULinkControlMessage tdu)
|
||||
else if(message instanceof TDULCMessage tdulc)
|
||||
{
|
||||
LinkControlWord lcw = tdu.getLinkControlWord();
|
||||
LinkControlWord lcw = tdulc.getLinkControlWord();
|
||||
|
||||
if(lcw instanceof IExtendedSourceMessage extendedSourceMessage)
|
||||
if(lcw instanceof IExtendedSourceMessage esm)
|
||||
{
|
||||
additionalMessageToSend = reassembleLC(extendedSourceMessage);
|
||||
if(lcw instanceof LCSourceIDExtension sourceIDExtension)
|
||||
{
|
||||
processSourceIDExtension(sourceIDExtension);
|
||||
}
|
||||
else if(esm.isExtensionRequired())
|
||||
{
|
||||
processSourceIDExtension(null);
|
||||
mHeldTDULCMessage = tdulc;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//Motorola carries the talker alias in the TDULC
|
||||
else if(mMotorolaTalkerAliasAssembler.add(lcw, message.getTimestamp()))
|
||||
{
|
||||
additionalMessageToSend = mMotorolaTalkerAliasAssembler.assemble();
|
||||
dispatch(mMotorolaTalkerAliasAssembler.assemble());
|
||||
}
|
||||
|
||||
//Harris carries the talker alias in the LDU voice messages, so reset the assembler on TDULC.
|
||||
//Harris carries the talker alias in the LDU1 link control messages, so reset the assembler on TDULC.
|
||||
mHarrisTalkerAliasAssembler.reset();
|
||||
|
||||
dispatch(tdulc);
|
||||
}
|
||||
|
||||
//Insert frequency band identifier update messages into channel-type messages */
|
||||
if(message instanceof IFrequencyBandReceiver)
|
||||
else if(message instanceof HDUMessage hdu && mHeldTDULCMessage != null)
|
||||
{
|
||||
IFrequencyBandReceiver receiver = (IFrequencyBandReceiver)message;
|
||||
|
||||
List<IChannelDescriptor> channels = receiver.getChannels();
|
||||
|
||||
for(IChannelDescriptor channel : channels)
|
||||
{
|
||||
int[] frequencyBandIdentifiers = channel.getFrequencyBandIdentifiers();
|
||||
|
||||
for(int id : frequencyBandIdentifiers)
|
||||
{
|
||||
if(mFrequencyBandMap.containsKey(id))
|
||||
{
|
||||
channel.setFrequencyBand(mFrequencyBandMap.get(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
//If the last TDULC message was held because it needs an extension that can arrive in the first LDU1,
|
||||
//hold onto the intermediate HDU message and send everything (TDU, HDU, LDU) in correct sequence.
|
||||
mHeldHDUMessage = hdu;
|
||||
}
|
||||
|
||||
//Store band identifiers so that they can be injected into channel type messages
|
||||
if(message instanceof IFrequencyBand)
|
||||
else
|
||||
{
|
||||
IFrequencyBand bandIdentifier = (IFrequencyBand)message;
|
||||
|
||||
//Only store the frequency band if it's new so we don't hold on to more than one instance of the
|
||||
//frequency band message. Otherwise, we'll hold on to several instances of each message as they get
|
||||
//injected into other messages with channel information.
|
||||
if(!mFrequencyBandMap.containsKey(bandIdentifier.getIdentifier()))
|
||||
{
|
||||
mFrequencyBandMap.put(bandIdentifier.getIdentifier(), bandIdentifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(mMessageListener != null)
|
||||
{
|
||||
if(message != null)
|
||||
{
|
||||
mMessageListener.receive(message);
|
||||
}
|
||||
|
||||
if(additionalMessageToSend != null)
|
||||
{
|
||||
mMessageListener.receive(additionalMessageToSend);
|
||||
//flush any held messages
|
||||
processSourceIDExtension(null);
|
||||
dispatch(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes link control words to reassemble source ID extension messages.
|
||||
* @param lcw to process
|
||||
* @return reassembled link control or null if the original lcw is a receiver (which we hold onto and send later)
|
||||
* Processes the source extension message and attaches it to any held TDULC or LDU1 with LC messages and flushes any
|
||||
* held messages.
|
||||
* @param extension to attach to held message(s) or null extension to flush all held messages.
|
||||
*/
|
||||
private IMessage reassembleLC(IExtendedSourceMessage lcw)
|
||||
private void processSourceIDExtension(LCSourceIDExtension extension)
|
||||
{
|
||||
IMessage toReturn = null;
|
||||
|
||||
if(lcw instanceof IExtendedSourceMessage extendedSourceMessage && extendedSourceMessage.isExtensionRequired())
|
||||
if(mHeldTDULCMessage != null && mHeldTDULCMessage.getLinkControlWord() instanceof IExtendedSourceMessage esm)
|
||||
{
|
||||
mExtendedSourceMessage = extendedSourceMessage;
|
||||
}
|
||||
else if(lcw instanceof LCSourceIDExtension sie && mExtendedSourceMessage != null)
|
||||
{
|
||||
mExtendedSourceMessage.setSourceIDExtension(sie);
|
||||
toReturn = mExtendedSourceMessage;
|
||||
mExtendedSourceMessage = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
//The source extension message should always immediately follow the message that is being extended, so if
|
||||
//we get a message that is not an extended message or the extension, then we've missed the extension and we
|
||||
//should nullify any extended message that's waiting for the extension.
|
||||
mExtendedSourceMessage = null;
|
||||
esm.setSourceIDExtension(extension);
|
||||
dispatch(mHeldTDULCMessage);
|
||||
mHeldTDULCMessage = null;
|
||||
dispatch(mHeldHDUMessage);
|
||||
mHeldHDUMessage = null;
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
if(mHeldLDU1Message != null && mHeldLDU1Message.getLinkControlWord() instanceof IExtendedSourceMessage esm)
|
||||
{
|
||||
esm.setSourceIDExtension(extension);
|
||||
dispatch(mHeldLDU1Message);
|
||||
mHeldLDU1Message = null;
|
||||
dispatch(mHeldLDU2Message);
|
||||
mHeldLDU2Message = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-process the message for frequency band details.
|
||||
* @param message to post process and dispatch
|
||||
*/
|
||||
private void dispatch(IMessage message)
|
||||
{
|
||||
if(message == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
processForFrequencyBands(message);
|
||||
|
||||
//Also process the link control messages for frequency bands.
|
||||
if(message instanceof LDU1Message ldu1)
|
||||
{
|
||||
processForFrequencyBands(ldu1.getLinkControlWord());
|
||||
}
|
||||
else if(message instanceof TDULCMessage tdulc)
|
||||
{
|
||||
processForFrequencyBands(tdulc.getLinkControlWord());
|
||||
}
|
||||
|
||||
if(mMessageListener != null)
|
||||
{
|
||||
mMessageListener.receive(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures IDEN_UPDATE messages and attaches them to messages with channel descriptors.
|
||||
* @param message to process for frequency band assembly.
|
||||
*/
|
||||
private void processForFrequencyBands(IMessage message)
|
||||
{
|
||||
if(message instanceof AbstractMessage am)
|
||||
{
|
||||
processForFrequencyBands(am);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures IDEN_UPDATE messages and attaches them to messages with channel descriptors.
|
||||
* @param message to process for frequency band assembly.
|
||||
*/
|
||||
private void processForFrequencyBands(AbstractMessage message)
|
||||
{
|
||||
//Insert frequency band identifier update messages into channel-type messages */
|
||||
if(message instanceof IFrequencyBandReceiver)
|
||||
{
|
||||
IFrequencyBandReceiver receiver = (IFrequencyBandReceiver)message;
|
||||
|
||||
List<IChannelDescriptor> channels = receiver.getChannels();
|
||||
|
||||
for(IChannelDescriptor channel : channels)
|
||||
{
|
||||
int[] frequencyBandIdentifiers = channel.getFrequencyBandIdentifiers();
|
||||
|
||||
for(int id : frequencyBandIdentifiers)
|
||||
{
|
||||
if(mFrequencyBandMap.containsKey(id))
|
||||
{
|
||||
channel.setFrequencyBand(mFrequencyBandMap.get(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Store band identifiers so that they can be injected into channel type messages
|
||||
if(message instanceof IFrequencyBand)
|
||||
{
|
||||
IFrequencyBand bandIdentifier = (IFrequencyBand)message;
|
||||
|
||||
//Only store the frequency band if it's new so we don't hold on to more than one instance of the
|
||||
//frequency band message. Otherwise, we'll hold on to several instances of each message as they get
|
||||
//injected into other messages with channel information.
|
||||
if(!mFrequencyBandMap.containsKey(bandIdentifier.getIdentifier()))
|
||||
{
|
||||
mFrequencyBandMap.put(bandIdentifier.getIdentifier(), bandIdentifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,7 +23,7 @@ import io.github.dsheirer.module.decode.p25.phase1.P25P1DataUnitID;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.hdu.HDUMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDU1Message;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDU2Message;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULinkControlMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULCMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDUMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.vselp.VSELP1Message;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.vselp.VSELP2Message;
|
||||
@@ -65,7 +65,7 @@ public class P25MessageFactory
|
||||
case TERMINATOR_DATA_UNIT:
|
||||
return new TDUMessage(message, nac, timestamp);
|
||||
case TERMINATOR_DATA_UNIT_LINK_CONTROL:
|
||||
return new TDULinkControlMessage(message, nac, timestamp);
|
||||
return new TDULCMessage(message, nac, timestamp);
|
||||
case TRUNKING_SIGNALING_BLOCK_1:
|
||||
case TRUNKING_SIGNALING_BLOCK_2:
|
||||
case TRUNKING_SIGNALING_BLOCK_3:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
* Copyright (C) 2014-2024 Dennis Sheirer
|
||||
*
|
||||
* 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
|
||||
@@ -23,7 +23,7 @@ import io.github.dsheirer.filter.Filter;
|
||||
import io.github.dsheirer.filter.FilterElement;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.LinkControlOpcode;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULinkControlMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULCMessage;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Function;
|
||||
|
||||
@@ -63,7 +63,7 @@ public class TerminatorMessageFilter extends Filter<IMessage, LinkControlOpcode>
|
||||
@Override
|
||||
public LinkControlOpcode apply(IMessage message)
|
||||
{
|
||||
if(message instanceof TDULinkControlMessage tdulc)
|
||||
if(message instanceof TDULCMessage tdulc)
|
||||
{
|
||||
return tdulc.getLinkControlWord().getOpcode();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
* Copyright (C) 2014-2024 Dennis Sheirer
|
||||
*
|
||||
* 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
|
||||
@@ -22,7 +22,7 @@ package io.github.dsheirer.module.decode.p25.phase1.message.filter;
|
||||
import io.github.dsheirer.filter.FilterSet;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.LinkControlOpcode;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULinkControlMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULCMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDUMessage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -59,6 +59,6 @@ public class TerminatorMessageFilterSet extends FilterSet<IMessage>
|
||||
@Override
|
||||
public boolean canProcess(IMessage message)
|
||||
{
|
||||
return (message instanceof TDUMessage || message instanceof TDULinkControlMessage) && super.canProcess(message);
|
||||
return (message instanceof TDUMessage || message instanceof TDULCMessage) && super.canProcess(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2024 Dennis Sheirer
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 Dennis Sheirer
|
||||
* *
|
||||
* * 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 <http://www.gnu.org/licenses/>
|
||||
* * *****************************************************************************
|
||||
* 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 <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.p25.phase1.message.hdu;
|
||||
@@ -25,11 +22,11 @@ package io.github.dsheirer.module.decode.p25.phase1.message.hdu;
|
||||
import io.github.dsheirer.bits.BinaryMessage;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.identifier.encryption.EncryptionKeyIdentifier;
|
||||
import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier;
|
||||
import io.github.dsheirer.module.decode.p25.identifier.encryption.APCO25EncryptionKey;
|
||||
import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25Talkgroup;
|
||||
import io.github.dsheirer.module.decode.p25.reference.Encryption;
|
||||
import io.github.dsheirer.module.decode.p25.reference.Vendor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -49,8 +46,8 @@ public class HeaderData
|
||||
|
||||
private boolean mValid = true;
|
||||
private BinaryMessage mMessage;
|
||||
private Identifier mEncryptionKey;
|
||||
private Identifier mTalkgroup;
|
||||
private EncryptionKeyIdentifier mEncryptionKey;
|
||||
private TalkgroupIdentifier mTalkgroup;
|
||||
private List<Identifier> mIdentifiers;
|
||||
|
||||
public HeaderData(BinaryMessage message)
|
||||
@@ -129,9 +126,9 @@ public class HeaderData
|
||||
return Vendor.fromValue(getMessage().getInt(VENDOR_ID));
|
||||
}
|
||||
|
||||
public Identifier getEncryptionKey()
|
||||
public EncryptionKeyIdentifier getEncryptionKey()
|
||||
{
|
||||
if(mEncryptionKey == null && isEncryptedAudio())
|
||||
if(mEncryptionKey == null)
|
||||
{
|
||||
mEncryptionKey = EncryptionKeyIdentifier.create(APCO25EncryptionKey.create(getMessage().getInt(ALGORITHM_ID),
|
||||
getMessage().getInt(KEY_ID)));
|
||||
@@ -150,7 +147,7 @@ public class HeaderData
|
||||
return getEncryption() != Encryption.UNENCRYPTED;
|
||||
}
|
||||
|
||||
public Identifier getTalkgroup()
|
||||
public TalkgroupIdentifier getTalkgroup()
|
||||
{
|
||||
if(mTalkgroup == null)
|
||||
{
|
||||
|
||||
@@ -32,7 +32,8 @@ import java.util.List;
|
||||
*/
|
||||
public class LCCallTermination extends LinkControlWord
|
||||
{
|
||||
private static final int MOTOROLA_SYSTEM_CONTROLLER = 0xFFFFFD;
|
||||
private static final int MOTOROLA_SYSTEM_CONTROLLER_1 = 0xFFFFFD;
|
||||
private static final int MOTOROLA_SYSTEM_CONTROLLER_2 = 0xFFFFFF;
|
||||
private static final int HARRIS_SYSTEM_CONTROLLER = 0;
|
||||
private static final IntField ADDRESS = IntField.length24(OCTET_6_BIT_48);
|
||||
private Identifier mAddress;
|
||||
@@ -75,7 +76,7 @@ public class LCCallTermination extends LinkControlWord
|
||||
public boolean isNetworkCommandedTeardown()
|
||||
{
|
||||
int address = getInt(ADDRESS);
|
||||
return address == MOTOROLA_SYSTEM_CONTROLLER || address == HARRIS_SYSTEM_CONTROLLER;
|
||||
return address == MOTOROLA_SYSTEM_CONTROLLER_1 || address == MOTOROLA_SYSTEM_CONTROLLER_2 || address == HARRIS_SYSTEM_CONTROLLER;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2024 Dennis Sheirer
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 Dennis Sheirer
|
||||
* *
|
||||
* * 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 <http://www.gnu.org/licenses/>
|
||||
* * *****************************************************************************
|
||||
* 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 <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.p25.phase1.message.ldu;
|
||||
@@ -27,7 +24,7 @@ import io.github.dsheirer.bits.BinaryMessage;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.identifier.encryption.EncryptionKeyIdentifier;
|
||||
import io.github.dsheirer.module.decode.p25.identifier.encryption.APCO25EncryptionKey;
|
||||
|
||||
import io.github.dsheirer.module.decode.p25.reference.Encryption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -36,6 +33,7 @@ import java.util.List;
|
||||
*/
|
||||
public class EncryptionSyncParameters implements IEncryptionSyncParameters
|
||||
{
|
||||
private static final String EMPTY_MESSAGE_INDICATOR = "000000000000000000";
|
||||
private static final int[] MESSAGE_INDICATOR_1 = {0, 1, 2, 3, 4, 5, 6, 7};
|
||||
private static final int[] MESSAGE_INDICATOR_2 = {8, 9, 10, 11, 12, 13, 14, 15};
|
||||
private static final int[] MESSAGE_INDICATOR_3 = {16, 17, 18, 19, 20, 21, 22, 23};
|
||||
@@ -134,8 +132,16 @@ public class EncryptionSyncParameters implements IEncryptionSyncParameters
|
||||
{
|
||||
if(mEncryptionKey == null)
|
||||
{
|
||||
mEncryptionKey = EncryptionKeyIdentifier.create(APCO25EncryptionKey.create(getMessage().getInt(ALGORITHM_ID),
|
||||
getMessage().getInt(KEY_ID)));
|
||||
int algorithm = getMessage().getInt(ALGORITHM_ID);
|
||||
int key = getMessage().getInt(KEY_ID);
|
||||
|
||||
//Detect when algorithm, key and MI are all zeros and override algorithm to set as unencrypted.
|
||||
if(algorithm == 0 && key == 0 && getMessageIndicator().contains(EMPTY_MESSAGE_INDICATOR))
|
||||
{
|
||||
algorithm = Encryption.UNENCRYPTED.getValue(); //0x80
|
||||
}
|
||||
|
||||
mEncryptionKey = EncryptionKeyIdentifier.create(APCO25EncryptionKey.create(algorithm, key));
|
||||
}
|
||||
|
||||
return mEncryptionKey;
|
||||
|
||||
@@ -31,7 +31,7 @@ import io.github.dsheirer.module.decode.p25.phase1.message.lc.LinkControlWordFac
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class TDULinkControlMessage extends P25P1Message implements IFrequencyBandReceiver
|
||||
public class TDULCMessage extends P25P1Message implements IFrequencyBandReceiver
|
||||
{
|
||||
public static final int[] LC_HEX_0 = {0, 1, 2, 3, 4, 5};
|
||||
public static final int[] LC_HEX_1 = {6, 7, 8, 9, 10, 11};
|
||||
@@ -63,7 +63,7 @@ public class TDULinkControlMessage extends P25P1Message implements IFrequencyBan
|
||||
|
||||
private LinkControlWord mLinkControlWord;
|
||||
|
||||
public TDULinkControlMessage(CorrectedBinaryMessage message, int nac, long timestamp)
|
||||
public TDULCMessage(CorrectedBinaryMessage message, int nac, long timestamp)
|
||||
{
|
||||
super(message, nac, timestamp);
|
||||
}
|
||||
Reference in New Issue
Block a user