From ad194d1bab3839c5234d260d601733bfb50c55bb Mon Sep 17 00:00:00 2001 From: Denny Sheirer Date: Mon, 25 May 2020 06:29:56 -0400 Subject: [PATCH] #818 Resolves P25 duplicate traffic channel issue where duplicate traffic channels were being allocated for the same frequency. (#834) Co-authored-by: Denny --- .../decode/p25/P25TrafficChannelManager.java | 163 ++++++++---------- 1 file changed, 76 insertions(+), 87 deletions(-) diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25TrafficChannelManager.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25TrafficChannelManager.java index 1c21630a..10c68ae1 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/P25TrafficChannelManager.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25TrafficChannelManager.java @@ -67,7 +67,6 @@ import java.util.Objects; import java.util.Queue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.function.Consumer; /** * Monitors channel grant and channel grant update messages to allocate traffic channels to capture @@ -98,10 +97,9 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv private Queue mAvailablePhase2TrafficChannelQueue = new ConcurrentLinkedQueue<>(); private List mManagedPhase2TrafficChannels; - private Map mAllocatedTrafficChannelMap = new ConcurrentHashMap<>(); - private Map mPhase1ChannelGrantEventMap = new ConcurrentHashMap<>(); - private Map mPhase2TS0ChannelGrantEventMap = new ConcurrentHashMap<>(); - private Map mPhase2TS1ChannelGrantEventMap = new ConcurrentHashMap<>(); + private Map mAllocatedTrafficChannelMap = new ConcurrentHashMap<>(); + private Map mTS0ChannelGrantEventMap = new ConcurrentHashMap<>(); + private Map mTS1ChannelGrantEventMap = new ConcurrentHashMap<>(); private Listener mChannelEventListener; private Listener mDecodeEventListener; @@ -114,10 +112,7 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv private boolean mIgnoreDataCalls; /** - * Monitors call events and allocates traffic decoder channels in response - * to traffic channel allocation call events. Manages a pool of reusable - * traffic channel allocations. - * + * Constructs an instance. * @param parentChannel that owns this traffic channel manager */ public P25TrafficChannelManager(Channel parentChannel) @@ -273,8 +268,9 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv private void processPhase1ChannelGrant(APCO25Channel apco25Channel, ServiceOptions serviceOptions, IdentifierCollection identifierCollection, Opcode opcode, long timestamp) { + long frequency = apco25Channel.getDownlinkFrequency(); - P25ChannelGrantEvent event = mPhase1ChannelGrantEventMap.get(apco25Channel); + P25ChannelGrantEvent event = mTS0ChannelGrantEventMap.get(frequency); if(event != null && isSameCall(identifierCollection, event.getIdentifierCollection())) { @@ -294,7 +290,7 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv .identifiers(identifierCollection) .build(); - mPhase1ChannelGrantEventMap.put(apco25Channel, continuationGrantEvent); + mTS0ChannelGrantEventMap.put(frequency, continuationGrantEvent); broadcast(continuationGrantEvent); } } @@ -305,7 +301,7 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv //Even though we have an event, the initial channel grant may have been rejected. Check to see if there //is a traffic channel allocated. If not, allocate one and update the event description. - if(!mAllocatedTrafficChannelMap.containsKey(apco25Channel) && !(mIgnoreDataCalls && opcode.isDataChannelGrant())) + if(!mAllocatedTrafficChannelMap.containsKey(frequency) && !(mIgnoreDataCalls && opcode.isDataChannelGrant())) { Channel trafficChannel = mAvailablePhase1TrafficChannelQueue.poll(); @@ -316,9 +312,9 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv event.setChannelDescriptor(apco25Channel); broadcast(event); SourceConfigTuner sourceConfig = new SourceConfigTuner(); - sourceConfig.setFrequency(apco25Channel.getDownlinkFrequency()); + sourceConfig.setFrequency(frequency); trafficChannel.setSourceConfiguration(sourceConfig); - mAllocatedTrafficChannelMap.put(apco25Channel, trafficChannel); + mAllocatedTrafficChannelMap.put(frequency, trafficChannel); broadcast(new ChannelGrantEvent(trafficChannel, Event.REQUEST_ENABLE, apco25Channel, identifierCollection)); } } @@ -335,8 +331,7 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv .identifiers(identifierCollection) .build(); - mPhase1ChannelGrantEventMap.put(apco25Channel, channelGrantEvent); - + mTS0ChannelGrantEventMap.put(frequency, channelGrantEvent); broadcast(channelGrantEvent); return; } @@ -348,11 +343,10 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv .identifiers(identifierCollection) .build(); - mPhase1ChannelGrantEventMap.put(apco25Channel, channelGrantEvent); + mTS0ChannelGrantEventMap.put(frequency, channelGrantEvent); //Allocate a traffic channel for the downlink frequency if one isn't already allocated - //NOTE: we could also allocate a traffic channel for the uplink frequency here, in the future - if(!mAllocatedTrafficChannelMap.containsKey(apco25Channel)) + if(!mAllocatedTrafficChannelMap.containsKey(frequency)) { Channel trafficChannel = mAvailablePhase1TrafficChannelQueue.poll(); @@ -364,9 +358,9 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv } SourceConfigTuner sourceConfig = new SourceConfigTuner(); - sourceConfig.setFrequency(apco25Channel.getDownlinkFrequency()); + sourceConfig.setFrequency(frequency); trafficChannel.setSourceConfiguration(sourceConfig); - mAllocatedTrafficChannelMap.put(apco25Channel, trafficChannel); + mAllocatedTrafficChannelMap.put(frequency, trafficChannel); broadcast(new ChannelGrantEvent(trafficChannel, Event.REQUEST_ENABLE, apco25Channel, identifierCollection)); } @@ -392,16 +386,17 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv } int timeslot = apco25Channel.getTimeslot(); + long frequency = apco25Channel.getDownlinkFrequency(); P25ChannelGrantEvent event = null; if(timeslot == 0) { - event = mPhase2TS0ChannelGrantEventMap.get(apco25Channel); + event = mTS0ChannelGrantEventMap.get(frequency); } else if(timeslot == 1) { - event = mPhase2TS1ChannelGrantEventMap.get(apco25Channel); + event = mTS1ChannelGrantEventMap.get(frequency); } else { @@ -431,11 +426,11 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv if(timeslot == 0) { - mPhase2TS0ChannelGrantEventMap.put(apco25Channel, continuationGrantEvent); + mTS0ChannelGrantEventMap.put(frequency, continuationGrantEvent); } else { - mPhase2TS1ChannelGrantEventMap.put(apco25Channel, continuationGrantEvent); + mTS1ChannelGrantEventMap.put(frequency, continuationGrantEvent); } broadcast(continuationGrantEvent); @@ -448,8 +443,7 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv //Even though we have an event, the initial channel grant may have been rejected. Check to see if there //is a traffic channel allocated. If not, allocate one and update the event description. - if(!mAllocatedTrafficChannelMap.containsKey(apco25Channel) && - !(mIgnoreDataCalls && opcode.isDataChannelGrant())) + if(!mAllocatedTrafficChannelMap.containsKey(frequency) && !(mIgnoreDataCalls && opcode.isDataChannelGrant())) { Channel trafficChannel = mAvailablePhase2TrafficChannelQueue.poll(); @@ -460,9 +454,9 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv event.setChannelDescriptor(apco25Channel); broadcast(event); SourceConfigTuner sourceConfig = new SourceConfigTuner(); - sourceConfig.setFrequency(apco25Channel.getDownlinkFrequency()); + sourceConfig.setFrequency(frequency); trafficChannel.setSourceConfiguration(sourceConfig); - mAllocatedTrafficChannelMap.put(apco25Channel, trafficChannel); + mAllocatedTrafficChannelMap.put(frequency, trafficChannel); //If we have valid scramble/randomizer parameters, set them in the decode config if(mPhase2ScrambleParameters != null) @@ -487,7 +481,7 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv .identifiers(identifierCollection) .build(); - mPhase1ChannelGrantEventMap.put(apco25Channel, channelGrantEvent); + mTS0ChannelGrantEventMap.put(frequency, channelGrantEvent); broadcast(channelGrantEvent); return; @@ -502,16 +496,15 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv if(timeslot == 0) { - mPhase2TS0ChannelGrantEventMap.put(apco25Channel, channelGrantEvent); + mTS0ChannelGrantEventMap.put(frequency, channelGrantEvent); } else { - mPhase2TS1ChannelGrantEventMap.put(apco25Channel, channelGrantEvent); + mTS1ChannelGrantEventMap.put(frequency, channelGrantEvent); } //Allocate a traffic channel for the downlink frequency if one isn't already allocated - //NOTE: we could also allocate a traffic channel for the uplink frequency here, in the future - if(!mAllocatedTrafficChannelMap.containsKey(apco25Channel)) + if(!mAllocatedTrafficChannelMap.containsKey(apco25Channel.getDownlinkFrequency())) { Channel trafficChannel = mAvailablePhase2TrafficChannelQueue.poll(); @@ -523,9 +516,9 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv } SourceConfigTuner sourceConfig = new SourceConfigTuner(); - sourceConfig.setFrequency(apco25Channel.getDownlinkFrequency()); + sourceConfig.setFrequency(frequency); trafficChannel.setSourceConfiguration(sourceConfig); - mAllocatedTrafficChannelMap.put(apco25Channel, trafficChannel); + mAllocatedTrafficChannelMap.put(frequency, trafficChannel); broadcast(new ChannelGrantEvent(trafficChannel, Event.REQUEST_ENABLE, apco25Channel, identifierCollection)); } @@ -660,6 +653,11 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv { broadcast(new ChannelEvent(trafficChannel, Event.REQUEST_DISABLE)); } + + for(Channel trafficChannel : mAvailablePhase2TrafficChannelQueue) + { + broadcast(new ChannelEvent(trafficChannel, Event.REQUEST_DISABLE)); + } } /** @@ -701,6 +699,9 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv mLog.info("Stopping traffic channel: " + channel); broadcast(new ChannelEvent(channel, Event.REQUEST_DISABLE)); } + + mTS0ChannelGrantEventMap.clear(); + mTS1ChannelGrantEventMap.clear(); } /** @@ -715,21 +716,16 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv { if(mMessageListener == null) { - mMessageListener = new Listener() - { - @Override - public void receive(IMessage message) + mMessageListener = message -> { + if(mPhase2ScrambleParameters == null && message.isValid()) { - if(mPhase2ScrambleParameters == null && message.isValid()) + if(message instanceof NetworkStatusBroadcast) { - if(message instanceof NetworkStatusBroadcast) - { - mPhase2ScrambleParameters = ((NetworkStatusBroadcast)message).getScrambleParameters(); - } - else if(message instanceof AMBTCNetworkStatusBroadcast) - { - mPhase2ScrambleParameters = ((AMBTCNetworkStatusBroadcast)message).getScrambleParameters(); - } + mPhase2ScrambleParameters = ((NetworkStatusBroadcast)message).getScrambleParameters(); + } + else if(message instanceof AMBTCNetworkStatusBroadcast) + { + mPhase2ScrambleParameters = ((AMBTCNetworkStatusBroadcast)message).getScrambleParameters(); } } }; @@ -770,24 +766,22 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv public class TrafficChannelTeardownMonitor implements Listener { /** - * Removes any call events that are associated with the specified channel. - * @param channel that was removed + * Removes an allocated traffic channel and adds it to the available channel queue + * @param channel to reset + * @param frequency of the channel + * @param isPhase1 true or false if it is a phase 2 channel */ - private void cleanupCallEvents(Channel channel) + private void resetTrafficChannel(Channel channel, long frequency, boolean isPhase1) { - if(channel.getSourceConfiguration() instanceof SourceConfigTuner) - { - long frequency = ((SourceConfigTuner)channel.getSourceConfiguration()).getFrequency(); + mAllocatedTrafficChannelMap.remove(frequency); - if(channel.getDecodeConfiguration().getDecoderType() == DecoderType.P25_PHASE1) - { - mPhase1ChannelGrantEventMap.entrySet().removeIf(entry -> entry.getKey().getDownlinkFrequency() == frequency); - } - else - { - mPhase2TS0ChannelGrantEventMap.entrySet().removeIf(entry -> entry.getKey().getDownlinkFrequency() == frequency); - mPhase2TS1ChannelGrantEventMap.entrySet().removeIf(entry -> entry.getKey().getDownlinkFrequency() == frequency); - } + if(isPhase1) + { + mAvailablePhase1TrafficChannelQueue.add(channel); + } + else + { + mAvailablePhase2TrafficChannelQueue.add(channel); } } @@ -810,48 +804,43 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv .filter(entry -> entry.getValue() == channel) .map(Map.Entry::getKey) .findFirst() - .ifPresent(toRemove -> { - mAllocatedTrafficChannelMap.remove(toRemove); - - if (isPhase1) { - mAvailablePhase1TrafficChannelQueue.add(channel); - } else { - mAvailablePhase2TrafficChannelQueue.add(channel); - } + .ifPresent(frequencyToRemove -> { + resetTrafficChannel(channel, frequencyToRemove, isPhase1); + mTS0ChannelGrantEventMap.remove(frequencyToRemove); + mTS1ChannelGrantEventMap.remove(frequencyToRemove); }); - - cleanupCallEvents(channel); break; case NOTIFICATION_PROCESSING_START_REJECTED: mAllocatedTrafficChannelMap.entrySet().stream() .filter(entry -> entry.getValue() == channel) .map(Map.Entry::getKey) .findFirst() - .ifPresent(rejected -> { - mAllocatedTrafficChannelMap.remove(rejected); + .ifPresent(rejectedFrequency -> { + resetTrafficChannel(channel, rejectedFrequency, isPhase1); - if (isPhase1) { - mAvailablePhase1TrafficChannelQueue.add(channel); - } else { - mAvailablePhase2TrafficChannelQueue.add(channel); + P25ChannelGrantEvent event = mTS0ChannelGrantEventMap.remove(rejectedFrequency); + + if(event == null) + { + event = mTS1ChannelGrantEventMap.remove(rejectedFrequency); } - P25ChannelGrantEvent event = mPhase1ChannelGrantEventMap.get(rejected); - - if (event != null) { + if (event != null) + { event.setEventDescription(event.getEventDescription() + " - Rejected"); - if (channelEvent.getDescription() != null) { + if (channelEvent.getDescription() != null) + { event.setDetails(channelEvent.getDescription() + " - " + event.getDetails()); - } else { + } + else + { event.setDetails(CHANNEL_START_REJECTED + " - " + event.getDetails()); } broadcast(event); } }); - - cleanupCallEvents(channel); break; } }