#1977 P25 FQSUID handling improvements. P25 fully qualified radio/talkgroup identifiers we're being flagged as invalid when ISSI patch assigned a local address/ID of zero. Changes format of FQSUID and FQTGID to use a value of zero for the local alias when the call type is an ISSI patch and suppress display of the 0 alias value. If the identifier represents a roaming user, then the local address value is displayed with the FQSUID appended in parenthesis. (#1978)

Co-authored-by: Dennis Sheirer <dsheirer@github.com>
This commit is contained in:
Denny Sheirer
2024-09-15 05:37:01 -04:00
committed by GitHub
parent b9068da99c
commit 8bb59d581f
10 changed files with 141 additions and 79 deletions

View File

@@ -21,12 +21,15 @@ package io.github.dsheirer.gui.viewer;
import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.IdentifierCollection; import io.github.dsheirer.identifier.IdentifierCollection;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.TableColumn; import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView; import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.GridPane; import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority; import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import javafx.util.Callback;
/** /**
* JavaFX identifier collection viewer * JavaFX identifier collection viewer
@@ -82,7 +85,19 @@ public class IdentifierCollectionViewer extends VBox
valueColumn.setText("Value"); valueColumn.setText("Value");
valueColumn.setCellValueFactory(new PropertyValueFactory<>("value")); valueColumn.setCellValueFactory(new PropertyValueFactory<>("value"));
mIdentifierTableView.getColumns().addAll(classColumn, formColumn, roleColumn, valueColumn); TableColumn textColumn = new TableColumn();
textColumn.setPrefWidth(160);
textColumn.setText("Text");
textColumn.setCellValueFactory((Callback<TableColumn.CellDataFeatures<Identifier<?>, String>, ObservableValue>) param -> {
if(param.getValue() != null)
{
return new SimpleStringProperty(param.getValue().toString());
}
return null;
});
mIdentifierTableView.getColumns().addAll(classColumn, formColumn, roleColumn, valueColumn, textColumn);
} }
return mIdentifierTableView; return mIdentifierTableView;

View File

@@ -72,12 +72,22 @@ public abstract class FullyQualifiedRadioIdentifier extends RadioIdentifier
} }
/** /**
* Indicates if the radio identify is aliased with a persona value that is different from the radio ID. * Indicates if the radio identity is aliased with a persona value that is different from the radio ID.
* @return true if aliased. * @return true if aliased.
*/ */
public boolean isAliased() public boolean isAliased()
{ {
return getValue() != mRadio; return getValue() != 0 && getValue() != mRadio;
}
/**
* Override the default behavior of RadioIdentifier.isValid() to allow for fully qualified SUID's with a local
* ID of zero which indicates an ISSI patch.
*/
@Override
public boolean isValid()
{
return true;
} }
@Override @Override

View File

@@ -71,12 +71,22 @@ public abstract class FullyQualifiedTalkgroupIdentifier extends TalkgroupIdentif
} }
/** /**
* Indicates if the talkgroup identify is aliased with a persona value that is different from the talkgroup ID. * Indicates if the talkgroup identity is aliased with a persona value that is different from the talkgroup ID.
* @return true if aliased. * @return true if aliased.
*/ */
public boolean isAliased() public boolean isAliased()
{ {
return getValue() != mTalkgroup; return getValue() != 0 && getValue() != mTalkgroup;
}
/**
* Override the default behavior of TalkgroupIdentifier.isValid() to allow for fully qualified TGID's with a local
* ID of zero which indicates an ISSI patch.
*/
@Override
public boolean isValid()
{
return true;
} }
@Override @Override

View File

@@ -32,7 +32,6 @@ import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.identifier.MutableIdentifierCollection; import io.github.dsheirer.identifier.MutableIdentifierCollection;
import io.github.dsheirer.identifier.encryption.EncryptionKeyIdentifier; import io.github.dsheirer.identifier.encryption.EncryptionKeyIdentifier;
import io.github.dsheirer.identifier.patch.PatchGroupPreLoadDataContent; import io.github.dsheirer.identifier.patch.PatchGroupPreLoadDataContent;
import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.identifier.scramble.ScrambleParameterIdentifier; import io.github.dsheirer.identifier.scramble.ScrambleParameterIdentifier;
import io.github.dsheirer.log.LoggingSuppressor; import io.github.dsheirer.log.LoggingSuppressor;
import io.github.dsheirer.message.IMessage; import io.github.dsheirer.message.IMessage;
@@ -307,7 +306,7 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
* @param timeslot for the channel. * @param timeslot for the channel.
* @param isIdleNull to indicate if this close event is trigged by an IDLE/NULL message * @param isIdleNull to indicate if this close event is trigged by an IDLE/NULL message
*/ */
public void closeP2CallEvent(long frequency, int timeslot, long timestamp, boolean isIdleNull) public void closeP2CallEvent(long frequency, int timeslot, boolean isIdleNull)
{ {
/** /**
* Hack: L3Harris systems can issue a channel grant on control/TS1 which creates an event for TS2 and then the * Hack: L3Harris systems can issue a channel grant on control/TS1 which creates an event for TS2 and then the
@@ -325,21 +324,13 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
try try
{ {
DecodeEvent event;
if(timeslot == P25P1Message.TIMESLOT_0 || timeslot == P25P1Message.TIMESLOT_1) if(timeslot == P25P1Message.TIMESLOT_0 || timeslot == P25P1Message.TIMESLOT_1)
{ {
event = mTS1ChannelGrantEventMap.remove(frequency); mTS1ChannelGrantEventMap.remove(frequency);
} }
else else
{ {
event = mTS2ChannelGrantEventMap.remove(frequency); mTS2ChannelGrantEventMap.remove(frequency);
}
if(event != null)
{
event.end(timestamp);
broadcast(event);
} }
} }
finally finally
@@ -1410,55 +1401,50 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
* Compares the TO role identifier(s) from each collection for equality. This is normally used to compare a call * Compares the TO role identifier(s) from each collection for equality. This is normally used to compare a call
* update where we only compare the TO role. * update where we only compare the TO role.
* *
* @param collection1 containing a TO identifier * @param existingIC containing a TO identifier
* @param collection2 containing a TO identifier * @param nextIC containing a TO identifier
* @return true if both collections contain a TO identifier and the TO identifiers are the same value * @return true if both collections contain a TO identifier and the TO identifiers are the same value
*/ */
private boolean isSameCallUpdate(IdentifierCollection collection1, IdentifierCollection collection2) private boolean isSameCallUpdate(IdentifierCollection existingIC, IdentifierCollection nextIC)
{ {
Identifier toIdentifier1 = collection1.getToIdentifier(); Identifier existingTO = existingIC.getToIdentifier();
Identifier toIdentifier2 = collection2.getToIdentifier(); Identifier nextTO = nextIC.getToIdentifier();
return toIdentifier1 != null && toIdentifier1.equals(toIdentifier2); return existingTO != null && existingTO.equals(nextTO);
} }
/** /**
* Compares the TO role identifier(s) from each collection for equality. This is normally used to compare a call * Compares the TO role identifier(s) from each collection for equality. This is normally used to compare a call
* update where we only compare the TO role. * update where we only compare the TO role.
* *
* @param collection1 containing a TO identifier * @param existingIC for the existing/previous event containing a TO identifier
* @param collection2 containing a TO identifier * @param nextIC for the next event containing a TO identifier
* @return true if both collections contain a TO identifier and the TO identifiers are the same value * @return true if both collections contain a TO identifier and the TO identifiers are the same value
*/ */
private boolean isSameCallFull(IdentifierCollection collection1, IdentifierCollection collection2) private boolean isSameCallFull(IdentifierCollection existingIC, IdentifierCollection nextIC)
{ {
Identifier to1 = collection1.getToIdentifier(); Identifier existingTO = existingIC.getToIdentifier();
Identifier to2 = collection2.getToIdentifier(); Identifier nextTO = nextIC.getToIdentifier();
if(to1 != null && to1.equals(to2)) if(existingTO != null && existingTO.equals(nextTO))
{ {
Identifier from1 = collection1.getFromIdentifier(); Identifier existingFROM = existingIC.getFromIdentifier();
//If the FROM identifier hasn't yet been established, then this is the same call. We also ignore the //If the FROM identifier hasn't yet been established, then this is the same call. We also ignore the
//talker alias as a call identifier since on L3Harris systems they can transmit the talker alias before //talker alias as a call identifier since on L3Harris systems they can transmit the talker alias before
//they transmit the radio ID. //they transmit the radio ID.
if(from1 == null || from1.getForm() == Form.TALKER_ALIAS) if(existingFROM == null || existingFROM.getForm() == Form.TALKER_ALIAS)
{ {
return true; return true;
} }
Identifier from2 = collection2.getFromIdentifier(); Identifier nextFROM = nextIC.getFromIdentifier();
if(from2 != null && from2.getForm() == Form.TALKER_ALIAS) if(nextFROM != null && nextFROM.getForm() == Form.TALKER_ALIAS)
{ {
return true; return true;
} }
if(from2 instanceof RadioIdentifier radio && radio.getValue() == 0) return existingFROM.equals(nextFROM);
{
return true;
}
return from1.equals(from2);
} }
return false; return false;

View File

@@ -188,7 +188,7 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
protected void resetState() protected void resetState()
{ {
super.resetState(); super.resetState();
closeCurrentCallEvent(System.currentTimeMillis(), true, false); closeCurrentCallEvent(true, false);
mEndPttOnFacchCounter = 0; mEndPttOnFacchCounter = 0;
} }
@@ -1274,7 +1274,7 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
{ {
if(mac instanceof EndPushToTalk) if(mac instanceof EndPushToTalk)
{ {
closeCurrentCallEvent(message.getTimestamp(), true, false); closeCurrentCallEvent(true, false);
if(message.getDataUnitID().isFACCH()) if(message.getDataUnitID().isFACCH())
{ {
@@ -1561,7 +1561,7 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
{ {
if(mac instanceof MacRelease mr) if(mac instanceof MacRelease mr)
{ {
closeCurrentCallEvent(message.getTimestamp(), true, false); closeCurrentCallEvent(true, false);
broadcast(message, mac, DecodeEventType.COMMAND, broadcast(message, mac, DecodeEventType.COMMAND,
(mr.isForcedPreemption() ? "FORCED " : "") + "CALL PREEMPTION" + (mr.isForcedPreemption() ? "FORCED " : "") + "CALL PREEMPTION" +
(mr.isTalkerPreemption() ? " BY USER" : " BY CONTROLLER")); (mr.isTalkerPreemption() ? " BY USER" : " BY CONTROLLER"));
@@ -1882,13 +1882,12 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
/** /**
* Ends/closes the current call event. * Ends/closes the current call event.
* *
* @param timestamp of the message that indicates the event has ended.
* @param resetIdentifiers to reset the FROM/TO identifiers (true) or reset just the FROM identifiers (false) * @param resetIdentifiers to reset the FROM/TO identifiers (true) or reset just the FROM identifiers (false)
* @param isIdleNull to indicate if the calling trigger is an IDLE/NULL message * @param isIdleNull to indicate if the calling trigger is an IDLE/NULL message
*/ */
private void closeCurrentCallEvent(long timestamp, boolean resetIdentifiers, boolean isIdleNull) private void closeCurrentCallEvent(boolean resetIdentifiers, boolean isIdleNull)
{ {
mTrafficChannelManager.closeP2CallEvent(getCurrentFrequency(), getTimeslot(), timestamp, isIdleNull); mTrafficChannelManager.closeP2CallEvent(getCurrentFrequency(), getTimeslot(), isIdleNull);
if(resetIdentifiers) if(resetIdentifiers)
{ {

View File

@@ -136,11 +136,11 @@ import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorol
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaGroupRegroupChannelGrantUpdate; import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaGroupRegroupChannelGrantUpdate;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaGroupRegroupDeleteCommand; import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaGroupRegroupDeleteCommand;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaGroupRegroupExtendedFunctionCommand; import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaGroupRegroupExtendedFunctionCommand;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaGroupRegroupOpcode145; import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaGroupRegroupTalkerAlias;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaGroupRegroupTalkerAliasContinuation;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaGroupRegroupVoiceChannelUpdate; import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaGroupRegroupVoiceChannelUpdate;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaGroupRegroupVoiceChannelUserAbbreviated; import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaGroupRegroupVoiceChannelUserAbbreviated;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaGroupRegroupVoiceChannelUserExtended; import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaGroupRegroupVoiceChannelUserExtended;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaOpcode149;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaQueuedResponse; import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaQueuedResponse;
import io.github.dsheirer.module.decode.p25.reference.Vendor; import io.github.dsheirer.module.decode.p25.reference.Vendor;
import java.util.ArrayList; import java.util.ArrayList;
@@ -585,9 +585,9 @@ public class MacMessageFactory
case MOTOROLA_89_GROUP_REGROUP_DELETE: case MOTOROLA_89_GROUP_REGROUP_DELETE:
return new MotorolaGroupRegroupDeleteCommand(message, offset); return new MotorolaGroupRegroupDeleteCommand(message, offset);
case MOTOROLA_91_GROUP_REGROUP_UNKNOWN: case MOTOROLA_91_GROUP_REGROUP_UNKNOWN:
return new MotorolaGroupRegroupOpcode145(message, offset); return new MotorolaGroupRegroupTalkerAlias(message, offset);
case MOTOROLA_95_UNKNOWN_149: case MOTOROLA_95_UNKNOWN_149:
return new MotorolaOpcode149(message, offset); return new MotorolaGroupRegroupTalkerAliasContinuation(message, offset);
case MOTOROLA_A0_GROUP_REGROUP_VOICE_CHANNEL_USER_EXTENDED: case MOTOROLA_A0_GROUP_REGROUP_VOICE_CHANNEL_USER_EXTENDED:
return new MotorolaGroupRegroupVoiceChannelUserExtended(message, offset); return new MotorolaGroupRegroupVoiceChannelUserExtended(message, offset);
case MOTOROLA_A3_GROUP_REGROUP_CHANNEL_GRANT_IMPLICIT: case MOTOROLA_A3_GROUP_REGROUP_CHANNEL_GRANT_IMPLICIT:

View File

@@ -30,9 +30,9 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* Motorola Group Regroup Unknown Opcode 145 - 17-bytes long * Motorola Group Regroup Talker Alias - 17-bytes long
*/ */
public class MotorolaGroupRegroupOpcode145 extends MacStructureVendor public class MotorolaGroupRegroupTalkerAlias extends MacStructureVendor
{ {
private static final IntField TALKGROUP = IntField.length16(24); private static final IntField TALKGROUP = IntField.length16(24);
private static final IntField UNKNOWN = IntField.length32(40); private static final IntField UNKNOWN = IntField.length32(40);
@@ -49,7 +49,7 @@ public class MotorolaGroupRegroupOpcode145 extends MacStructureVendor
* @param message containing the message bits * @param message containing the message bits
* @param offset into the message for this structure * @param offset into the message for this structure
*/ */
public MotorolaGroupRegroupOpcode145(CorrectedBinaryMessage message, int offset) public MotorolaGroupRegroupTalkerAlias(CorrectedBinaryMessage message, int offset)
{ {
super(message, offset); super(message, offset);
} }
@@ -60,7 +60,7 @@ public class MotorolaGroupRegroupOpcode145 extends MacStructureVendor
public String toString() public String toString()
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("MOTOROLA GROUP REGROUP UNKNOWN OPCODE 145 TALKGROUP:").append(getTalkgroup()); sb.append("MOTOROLA GROUP REGROUP TALKER ALIAS TALKGROUP:").append(getTalkgroup());
sb.append(" SOURCE SUID RADIO:").append(getRadio()); sb.append(" SOURCE SUID RADIO:").append(getRadio());
sb.append(" UNK:").append(Integer.toHexString(getInt(UNKNOWN)).toUpperCase()); sb.append(" UNK:").append(Integer.toHexString(getInt(UNKNOWN)).toUpperCase());
sb.append(" MSG:").append(getMessage().get(getOffset(), getMessage().length()).toHexString()); sb.append(" MSG:").append(getMessage().get(getOffset(), getMessage().length()).toHexString());

View File

@@ -26,19 +26,30 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* Motorola Unknown Opcode 149 (0x95) * Motorola Unknown Opcode 149 (0x95) Talker Alias continuation message.
* *
* This was observed at the end of a Group Regroup call sequence during HANGTIME, so it seems more of an announcement * This was observed at the end of a Group Regroup call sequence during HANGTIME. Each radio doesn't know the alias
* type message and less as something relevant to the call. * until it receives it once and then queues the alias for subsequent use.
*
* This opcode is observed following an Opcode 145 message and seems to be a continuation message.
* *
* Examples: * Examples:
* 959011 018E58B82BAB4D3B70E9A8457F9D C67 * 959011 018E58B82BAB4D3B70E9A8457F9D C67
* 959011 0287000000000000000000000000 E26 * 959011 0287000000000000000000000000 E26
* *
* The opcode, vendor and length octets are consistent. The first octet seems to be an identifier, 01, 02, etc. * Example 2:
* 9190110BCD02010026BEE004A403151724FD9 Opcode 145
* 959011012436C4C022EC902A9943EDAA69E76 Opcode 149
* 95901102298FAA77683FAE0000000000000AB Opcode 149
* (immediately followed in the same call sequence by a slight variation)
* 9190110BCD02010036BEE004A403151724513 Opcode 145
* 959011013436C4C022EC902A9943EDAA699F6 Opcode 149
* 95901102398FAA77683FAE00000000000072B Opcode 149
*
* The opcode, vendor and length octets are consistent. The first octet seems to be a sequence identifier, 01, 02, etc.
* Nothing else seems to match any of the other identifiers that were active at call time. * Nothing else seems to match any of the other identifiers that were active at call time.
*/ */
public class MotorolaOpcode149 extends MacStructureVendor public class MotorolaGroupRegroupTalkerAliasContinuation extends MacStructureVendor
{ {
private List<Identifier> mIdentifiers; private List<Identifier> mIdentifiers;
@@ -48,7 +59,7 @@ public class MotorolaOpcode149 extends MacStructureVendor
* @param message containing the message bits * @param message containing the message bits
* @param offset into the message for this structure * @param offset into the message for this structure
*/ */
public MotorolaOpcode149(CorrectedBinaryMessage message, int offset) public MotorolaGroupRegroupTalkerAliasContinuation(CorrectedBinaryMessage message, int offset)
{ {
super(message, offset); super(message, offset);
} }
@@ -59,7 +70,7 @@ public class MotorolaOpcode149 extends MacStructureVendor
public String toString() public String toString()
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("MOTOROLA UNKNOWN OPCODE 149"); sb.append("MOTOROLA TALKER ALIAS CONTINUATION");
sb.append(" MSG:").append(getMessage().get(getOffset(), getMessage().length()).toHexString()); sb.append(" MSG:").append(getMessage().get(getOffset(), getMessage().length()).toHexString());
return sb.toString(); return sb.toString();
} }

View File

@@ -227,16 +227,30 @@ public class APCO25TalkgroupFormatter extends AbstractIntegerFormatter
{ {
case DECIMAL: case DECIMAL:
case FORMATTED: case FORMATTED:
sb.append(toDecimal(id, RADIO_DECIMAL_WIDTH)); if(id > 0)
sb.append("(").append(toHex(wacn, WACN_HEXADECIMAL_WIDTH)); {
sb.append(toDecimal(id, RADIO_DECIMAL_WIDTH)).append(" (");
}
sb.append(toHex(wacn, WACN_HEXADECIMAL_WIDTH));
sb.append(".").append(toHex(system, SYSTEM_HEXADECIMAL_WIDTH)); sb.append(".").append(toHex(system, SYSTEM_HEXADECIMAL_WIDTH));
sb.append(".").append(toDecimal(radio, RADIO_DECIMAL_WIDTH)).append(")"); sb.append(".").append(toDecimal(radio, RADIO_DECIMAL_WIDTH));
if(id > 0)
{
sb.append(")");
}
return sb.toString(); return sb.toString();
case HEXADECIMAL: case HEXADECIMAL:
sb.append(toHex(id, RADIO_HEXADECIMAL_WIDTH)); if(id > 0)
sb.append("(").append(toHex(wacn, WACN_HEXADECIMAL_WIDTH)); {
sb.append(toHex(id, RADIO_HEXADECIMAL_WIDTH)).append(" (");
}
sb.append(toHex(wacn, WACN_HEXADECIMAL_WIDTH));
sb.append(".").append(toHex(system, SYSTEM_HEXADECIMAL_WIDTH)); sb.append(".").append(toHex(system, SYSTEM_HEXADECIMAL_WIDTH));
sb.append(".").append(toHex(radio, RADIO_HEXADECIMAL_WIDTH)).append(")"); sb.append(".").append(toHex(radio, RADIO_HEXADECIMAL_WIDTH));
if(id > 0)
{
sb.append(")");
}
return sb.toString(); return sb.toString();
default: default:
throw new IllegalArgumentException("Unrecognized integer format: " + format); throw new IllegalArgumentException("Unrecognized integer format: " + format);
@@ -248,16 +262,30 @@ public class APCO25TalkgroupFormatter extends AbstractIntegerFormatter
{ {
case DECIMAL: case DECIMAL:
case FORMATTED: case FORMATTED:
sb.append(id); if(id > 0)
{
sb.append(id).append(" (");
}
sb.append("(").append(toHex(wacn, WACN_HEXADECIMAL_WIDTH)); sb.append("(").append(toHex(wacn, WACN_HEXADECIMAL_WIDTH));
sb.append(".").append(toHex(system, SYSTEM_HEXADECIMAL_WIDTH)); sb.append(".").append(toHex(system, SYSTEM_HEXADECIMAL_WIDTH));
sb.append(".").append(radio).append(")"); sb.append(".").append(radio);
if(id > 0)
{
sb.append(")");
}
return sb.toString(); return sb.toString();
case HEXADECIMAL: case HEXADECIMAL:
sb.append(toHex(id)); if(id > 0)
sb.append("(").append(toHex(wacn, WACN_HEXADECIMAL_WIDTH)); {
sb.append(toHex(id)).append(" (");
}
sb.append(toHex(wacn, WACN_HEXADECIMAL_WIDTH));
sb.append(".").append(toHex(system, SYSTEM_HEXADECIMAL_WIDTH)); sb.append(".").append(toHex(system, SYSTEM_HEXADECIMAL_WIDTH));
sb.append(".").append(toHex(radio)).append(")"); sb.append(".").append(toHex(radio));
if(id > 0)
{
sb.append(")");
}
return sb.toString(); return sb.toString();
default: default:
throw new IllegalArgumentException("Unrecognized integer format: " + format); throw new IllegalArgumentException("Unrecognized integer format: " + format);

View File

@@ -300,15 +300,18 @@ public class FrequencyController
*/ */
public void removeSourceEventProcessor(ISourceEventProcessor processor) public void removeSourceEventProcessor(ISourceEventProcessor processor)
{ {
mTunable.getLock().lock(); if(mTunable != null)
{
mTunable.getLock().lock();
try try
{ {
mProcessors.remove(processor); mProcessors.remove(processor);
} }
finally finally
{ {
mTunable.getLock().unlock(); mTunable.getLock().unlock();
}
} }
} }