#2018 P25 Phase 1 resolves issue where NowPlaying identifiers were flickering as they are removed and readded with each TDULC terminator message. (#2019)

Co-authored-by: Dennis Sheirer <dsheirer@github.com>
This commit is contained in:
Denny Sheirer
2024-10-15 09:53:52 -04:00
committed by GitHub
parent 9a02416403
commit 024a624083
3 changed files with 80 additions and 155 deletions

View File

@@ -38,6 +38,7 @@ import io.github.dsheirer.identifier.integer.IntegerIdentifier;
import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier;
import io.github.dsheirer.log.LoggingSuppressor;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.message.TimeslotMessage;
import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.dmr.channel.DMRAbsoluteChannel;
import io.github.dsheirer.module.decode.dmr.channel.DMRChannel;
@@ -1459,14 +1460,22 @@ public class DMRDecoderState extends TimeslotDecoderState
{
StringBuilder sb = new StringBuilder();
boolean networkAdded = false;
if(mNetworkConfigurationMonitor != null)
{
sb.append(mNetworkConfigurationMonitor.getActivitySummary());
sb.append("\n\n");
sb.append(mTrafficChannelManager.getTalkerAliasManager().getAliasSummary());
networkAdded = true;
}
else
//Only add the talker alias summary to timeslot 1 activity summary since we aggregate across both timeslots.
if(getTimeslot() == TimeslotMessage.TIMESLOT_1)
{
if(networkAdded)
{
sb.append("\n\n");
}
sb.append(mTrafficChannelManager.getTalkerAliasManager().getAliasSummary());
}

View File

@@ -59,6 +59,7 @@ import io.github.dsheirer.module.decode.p25.phase2.DecodeConfigP25Phase2;
import io.github.dsheirer.module.decode.p25.phase2.enumeration.ScrambleParameters;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.MacOpcode;
import io.github.dsheirer.module.decode.p25.reference.ServiceOptions;
import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions;
import io.github.dsheirer.module.decode.traffic.TrafficChannelManager;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.source.config.SourceConfigTuner;
@@ -729,27 +730,36 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
{
P25TrafficChannelEventTracker tracker = getTracker(frequency, P25P1Message.TIMESLOT_1);
if(tracker != null)
//If the tracker is already started, it was for another call. Close it and recreate the event.
if(tracker != null && tracker.isStarted())
{
removeTracker(frequency, P25P1Message.TIMESLOT_1);
tracker = null;
}
DecodeEventType decodeEventType = getDecodeEventType(talkgroup, eki);
if(tracker != null)
{
tracker.addIdentifierIfMissing(talkgroup);
}
else
{
DecodeEventType decodeEventType = getDecodeEventType(talkgroup, eki);
MutableIdentifierCollection mic = new MutableIdentifierCollection();
mic.update(talkgroup);
mic.update(radio);
mic.update(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();
//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);
}
tracker = new P25TrafficChannelEventTracker(callEvent);
addTracker(tracker, frequency, P25P1Message.TIMESLOT_1);
broadcast(tracker);
}
finally
@@ -838,19 +848,22 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
* 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 identifiers to update within the event.
* @param timestamp for the update
*/
public void processP1TrafficCurrentUserIdentifiers(long frequency, List<Identifier> identifiers, long timestamp, String context)
public void processP1TrafficLDU1(long frequency, List<Identifier> identifiers, long timestamp, String context)
{
mLock.lock();
try
{
IChannelDescriptor channelDescriptor = null;
P25TrafficChannelEventTracker tracker = getTracker(frequency, P25P1Message.TIMESLOT_1);
if(tracker != null && tracker.isComplete())
{
channelDescriptor = tracker.getEvent().getChannelDescriptor();;
removeTracker(frequency, P25P1Message.TIMESLOT_1);
tracker = null;
}
@@ -871,6 +884,29 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
tracker.updateDurationTraffic(timestamp);
broadcast(tracker);
}
else
{
MutableIdentifierCollection mic = new MutableIdentifierCollection(identifiers);
Identifier talkgroup = mic.getToIdentifier();
Identifier encryption = mic.getEncryptionIdentifier();
if(talkgroup != null && encryption instanceof EncryptionKeyIdentifier eki)
{
DecodeEventType decodeEventType = getDecodeEventType(talkgroup, eki);
//Create a new event for the current call.
ServiceOptions serviceOptions = (eki.isEncrypted() ? VoiceServiceOptions.createEncrypted() :
VoiceServiceOptions.createUnencrypted());
P25ChannelGrantEvent callEvent = P25ChannelGrantEvent.builder(decodeEventType, timestamp, serviceOptions)
.channelDescriptor(channelDescriptor)
.details("PHASE 1 CALL " + (eki.isEncrypted() ? eki.toString() : ""))
.identifiers(mic)
.build();
tracker = new P25TrafficChannelEventTracker(callEvent);
addTracker(tracker, frequency, P25P1Message.TIMESLOT_1);
broadcast(tracker);
}
}
}
finally
{

View File

@@ -405,8 +405,7 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
*/
private void processLCChannelUser(LinkControlWord lcw, long timestamp)
{
List<Identifier> updated = mPatchGroupManager.update(lcw.getIdentifiers(), timestamp);
getIdentifierCollection().update(updated);
getIdentifierCollection().update(mPatchGroupManager.update(lcw.getIdentifiers(), timestamp));
DecodeEventType decodeEventType = getLCDecodeEventType(lcw);
ServiceOptions serviceOptions = null;
@@ -871,7 +870,7 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
if(lcw != null && lcw.isValid())
{
processLC(lcw, message.getTimestamp(), false);
mTrafficChannelManager.processP1TrafficCurrentUserIdentifiers(getCurrentFrequency(),
mTrafficChannelManager.processP1TrafficLDU1(getCurrentFrequency(),
getIdentifierCollection().getIdentifiers(), message.getTimestamp(), ldu1.toString());
}
}
@@ -881,9 +880,10 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
if(esp != null && esp.isValid())
{
getIdentifierCollection().update(esp.getIdentifiers());
if(esp.isEncryptedAudio())
{
getIdentifierCollection().update(esp.getIdentifiers());
mTrafficChannelManager.processP1TrafficCurrentUser(getCurrentFrequency(), esp.getEncryptionKey(),
message.getTimestamp(), ldu2.toString());
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.ENCRYPTED));
@@ -904,12 +904,7 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
*/
private void processTDU(P25P1Message message)
{
//If the TCM indicates that it closed the traffic call event, then remove the FROM users
if(mTrafficChannelManager.processP1TrafficCallEnd(getCurrentFrequency(), message.getTimestamp(), "TDU:" + message))
{
getIdentifierCollection().remove(IdentifierClass.USER, Role.FROM);
}
mTrafficChannelManager.processP1TrafficCallEnd(getCurrentFrequency(), message.getTimestamp(), "TDU:" + message);
broadcast(new DecoderStateEvent(this, Event.DECODE, State.ACTIVE));
}
@@ -921,23 +916,14 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
*/
private void processTDULC(P25P1Message message)
{
boolean completed = mTrafficChannelManager.processP1TrafficCallEnd(getCurrentFrequency(), message.getTimestamp(), "TDULC:" + message);
if(completed)
{
getIdentifierCollection().remove(IdentifierClass.USER, Role.FROM);
}
if(message instanceof TDULCMessage tdulc)
{
LinkControlWord lcw = tdulc.getLinkControlWord();
if(lcw != null && lcw.isValid())
{
//Set the state to ACTIVE while the call continues in hangtime. The processLC() method will signal
// the channel teardown when that happens.
mTrafficChannelManager.processP1TrafficCallEnd(getCurrentFrequency(), message.getTimestamp(), "TDULC:" + message);
broadcast(new DecoderStateEvent(this, Event.DECODE, State.ACTIVE));
//Don't signal call end to the TCM or remove FROM identifiers unless the call has started on this traffic channel.
processLC(lcw, message.getTimestamp(), true);
}
}
@@ -950,6 +936,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
*/
private void processPDU(P25P1Message message)
{
getIdentifierCollection().remove(IdentifierClass.USER);
if(message.isValid() && message instanceof PDUMessage pdu)
{
broadcastEvent(pdu.getIdentifiers(), message.getTimestamp(), DecodeEventType.DATA_PACKET, pdu.toString());
@@ -989,6 +977,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
*/
private void processPacketData(P25P1Message message)
{
getIdentifierCollection().remove(IdentifierClass.USER);
broadcast(new DecoderStateEvent(this, Event.DECODE, State.DATA));
if(message instanceof SNDCPPacketMessage sndcp)
@@ -1121,6 +1111,7 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
*/
private void processSNDCP(P25P1Message message)
{
getIdentifierCollection().remove(IdentifierClass.USER);
broadcast(new DecoderStateEvent(this, Event.DECODE, State.DATA));
if(message.isValid() && message instanceof SNDCPPacketMessage sndcpPacket)
@@ -1853,9 +1844,7 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
case UNIT_TO_UNIT_VOICE_CHANNEL_USER_EXTENDED:
if(isTerminator)
{
mTrafficChannelManager.processP1TrafficCallEnd(getCurrentFrequency(), timestamp, lcw.toString());
List<Identifier> updated = mPatchGroupManager.update(lcw.getIdentifiers(), timestamp);
getIdentifierCollection().update(updated);
getIdentifierCollection().update(mPatchGroupManager.update(lcw.getIdentifiers(), timestamp));
}
else
{
@@ -1865,16 +1854,14 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
case MOTOROLA_TALK_COMPLETE:
if(lcw instanceof LCMotorolaTalkComplete tc)
{
getIdentifierCollection().update(tc.getAddress());
//Note: don't update the traffic channel manager because the current event is already in closeout
closeCurrentCallEvent(timestamp, lcw.toString());
//TCM has already closed the call event. Remove the FROM user identifiers to prepare for the
//next call.
getIdentifierCollection().remove(IdentifierClass.USER, Role.FROM);
}
break;
//Call termination
case CALL_TERMINATION_OR_CANCELLATION:
closeCurrentCallEvent(timestamp, lcw.toString());
//Note: we only broadcast an END state if this is a network-commanded channel teardown
if(lcw instanceof LCCallTermination lcct && lcct.isNetworkCommandedTeardown())
{
@@ -1884,16 +1871,7 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
//Voice Channel Update messages - indicates calls in-progress on another channel - ignored
case GROUP_VOICE_CHANNEL_UPDATE:
if(isTerminator)
{
closeCurrentCallEvent(timestamp, lcw.toString());
}
break;
case GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT:
if(isTerminator)
{
closeCurrentCallEvent(timestamp, lcw.toString());
}
break;
//Network configuration messages
@@ -1912,11 +1890,6 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
getIdentifierCollection().update(frequencyID);
}
if(isTerminator)
{
closeCurrentCallEvent(timestamp, lcw.toString());
}
mNetworkConfigurationMonitor.process(lcw);
break;
case RFSS_STATUS_BROADCAST_EXPLICIT:
@@ -1934,11 +1907,6 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
getIdentifierCollection().update(frequencyID);
}
if(isTerminator)
{
closeCurrentCallEvent(timestamp, lcw.toString());
}
mNetworkConfigurationMonitor.process(lcw);
break;
@@ -1974,11 +1942,6 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
getIdentifierCollection().update(frequencyID);
}
if(isTerminator)
{
closeCurrentCallEvent(timestamp, lcw.toString());
}
mNetworkConfigurationMonitor.process(lcw);
break;
@@ -1990,10 +1953,6 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
case SECONDARY_CONTROL_CHANNEL_BROADCAST:
case SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT:
case SYSTEM_SERVICE_BROADCAST:
if(isTerminator)
{
closeCurrentCallEvent(timestamp, lcw.toString());
}
mNetworkConfigurationMonitor.process(lcw);
break;
@@ -2006,32 +1965,17 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
break;
case MOTOROLA_GROUP_REGROUP_VOICE_CHANNEL_UPDATE:
//Voice Channel Update message - indicates calls in-progress on another channel - ignored
if(isTerminator)
{
closeCurrentCallEvent(timestamp, lcw.toString());
}
break;
case MOTOROLA_TALKER_ALIAS_HEADER:
case MOTOROLA_TALKER_ALIAS_DATA_BLOCK:
if(isTerminator)
{
closeCurrentCallEvent(timestamp, lcw.toString());
}
//Inore - we'll pickup the talker alias from the assembler in the MessageProcessor.
break;
//Other events
case CALL_ALERT:
if(isTerminator)
{
closeCurrentCallEvent(timestamp, lcw.toString());
}
broadcastEvent(lcw.getIdentifiers(), timestamp, DecodeEventType.PAGE, "Call Alert");
break;
case EXTENDED_FUNCTION_COMMAND:
if(isTerminator)
{
closeCurrentCallEvent(timestamp, lcw.toString());
}
if(lcw instanceof LCExtendedFunctionCommand efc)
{
broadcastEvent(lcw.getIdentifiers(), timestamp, DecodeEventType.COMMAND, "Function: " +
@@ -2040,27 +1984,15 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
}
break;
case EXTENDED_FUNCTION_COMMAND_EXTENDED:
if(isTerminator)
{
closeCurrentCallEvent(timestamp, lcw.toString());
}
if(lcw instanceof LCExtendedFunctionCommandExtended efce)
{
broadcastEvent(lcw.getIdentifiers(), timestamp, DecodeEventType.COMMAND, "Function: " +
efce.getExtendedFunction() + " Arguments:" + efce.getExtendedFunctionArguments());
}
case GROUP_AFFILIATION_QUERY:
if(isTerminator)
{
closeCurrentCallEvent(timestamp, lcw.toString());
}
broadcastEvent(lcw.getIdentifiers(), timestamp, DecodeEventType.QUERY, "Group Affiliation");
break;
case MESSAGE_UPDATE:
if(isTerminator)
{
closeCurrentCallEvent(timestamp, lcw.toString());
}
if(lcw instanceof LCMessageUpdate mu)
{
broadcastEvent(lcw.getIdentifiers(), timestamp, DecodeEventType.SDM,
@@ -2068,10 +2000,6 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
}
break;
case MESSAGE_UPDATE_EXTENDED:
if(isTerminator)
{
closeCurrentCallEvent(timestamp, lcw.toString());
}
if(lcw instanceof LCMessageUpdateExtended mue)
{
broadcastEvent(lcw.getIdentifiers(), timestamp, DecodeEventType.SDM, "MSG:" +
@@ -2079,17 +2007,9 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
}
break;
case STATUS_QUERY:
if(isTerminator)
{
closeCurrentCallEvent(timestamp, lcw.toString());
}
broadcastEvent(lcw.getIdentifiers(), timestamp, DecodeEventType.QUERY, "Status");
break;
case STATUS_UPDATE:
if(isTerminator)
{
closeCurrentCallEvent(timestamp, lcw.toString());
}
if(lcw instanceof LCStatusUpdate su)
{
broadcastEvent(lcw.getIdentifiers(), timestamp, DecodeEventType.STATUS,
@@ -2097,10 +2017,6 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
}
break;
case STATUS_UPDATE_EXTENDED:
if(isTerminator)
{
closeCurrentCallEvent(timestamp, lcw.toString());
}
if(lcw instanceof LCStatusUpdateExtended sue)
{
broadcastEvent(lcw.getIdentifiers(), timestamp, DecodeEventType.STATUS, "UNIT:" +
@@ -2108,10 +2024,6 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
}
break;
case TELEPHONE_INTERCONNECT_ANSWER_REQUEST:
if(isTerminator)
{
closeCurrentCallEvent(timestamp, lcw.toString());
}
if(lcw instanceof LCTelephoneInterconnectAnswerRequest tiar)
{
broadcastEvent(lcw.getIdentifiers(), timestamp, DecodeEventType.PAGE, "Telephone Call:" +
@@ -2119,24 +2031,12 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
}
break;
case UNIT_AUTHENTICATION_COMMAND:
if(isTerminator)
{
closeCurrentCallEvent(timestamp, lcw.toString());
}
broadcastEvent(lcw.getIdentifiers(), timestamp, DecodeEventType.COMMAND, "Authenticate Unit");
break;
case UNIT_REGISTRATION_COMMAND:
if(isTerminator)
{
closeCurrentCallEvent(timestamp, lcw.toString());
}
broadcastEvent(lcw.getIdentifiers(), timestamp, DecodeEventType.COMMAND, "Unit Registration");
break;
case UNIT_TO_UNIT_ANSWER_REQUEST:
if(isTerminator)
{
closeCurrentCallEvent(timestamp, lcw.toString());
}
broadcastEvent(lcw.getIdentifiers(), timestamp, DecodeEventType.PAGE, "Unit-to-Unit Answer Request");
break;
case L3HARRIS_RETURN_TO_CONTROL_CHANNEL:
@@ -2167,18 +2067,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
.build();
broadcast(event);
}
if(isTerminator)
{
closeCurrentCallEvent(timestamp, lcw.toString());
}
break;
default:
if(isTerminator)
{
closeCurrentCallEvent(timestamp, lcw.toString());
}
// if(lcw.getVendor().isLoggable())
// {
// LOGGING_SUPPRESSOR.info(lcw.getVendor().toString() + lcw.getOpcodeNumber() + lcw.getMessage().toHexString(),
@@ -2190,16 +2080,6 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
}
}
/**
* Closes the call event on the current channel.
* @param timestamp for the closure
*/
private void closeCurrentCallEvent(long timestamp, String context)
{
mTrafficChannelManager.processP1TrafficCallEnd(getCurrentFrequency(), timestamp, context);
getIdentifierCollection().remove(IdentifierClass.USER);
}
@Override
public String getActivitySummary()
{