From 4ab37735e8bad1d517ab03c9ca35a1c5f5c7644c Mon Sep 17 00:00:00 2001 From: Denny Sheirer Date: Sat, 12 Jan 2019 04:42:52 -0500 Subject: [PATCH] Adds support for Motorola Opcode 07 Deny Response message. (#482) --- .../module/decode/p25/P25DecoderState.java | 22 ++- .../decode/p25/message/tsbk/Opcode.java | 6 +- .../p25/message/tsbk/TSBKMessageFactory.java | 23 +++ .../motorola/osp/MotorolaDenyResponse.java | 132 ++++++++++++++++++ .../decode/p25/reference/DenyReason.java | 130 ++++++++++------- 5 files changed, 256 insertions(+), 57 deletions(-) create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/MotorolaDenyResponse.java diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderState.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderState.java index 74b21473..714d2019 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderState.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderState.java @@ -1,7 +1,7 @@ /* * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2018 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 @@ -84,6 +84,7 @@ import io.github.dsheirer.module.decode.p25.message.pdu.packet.sndcp.SNDCPPacket import io.github.dsheirer.module.decode.p25.message.pdu.umbtc.isp.UMBTCTelephoneInterconnectRequestExplicitDialing; import io.github.dsheirer.module.decode.p25.message.tdu.TDULinkControlMessage; import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; +import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp.MotorolaDenyResponse; import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp.PatchGroupVoiceChannelGrant; import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp.PatchGroupVoiceChannelGrantUpdate; import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.CancelServiceRequest; @@ -2134,9 +2135,22 @@ public class P25DecoderState extends DecoderState implements IChannelEventListen .identifiers(icRoaming) .build()); break; - case MOTOROLA_OSP_OPCODE_7: - mLog.info("MOTOROLA OPCODE 7: " + tsbk.getMessage().toHexString()); - break; + case MOTOROLA_OSP_DENY_RESPONSE: + if(tsbk instanceof MotorolaDenyResponse) + { + MotorolaDenyResponse dr = (MotorolaDenyResponse)tsbk; + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.RESPONSE.toString()) + .details("DENY: " + dr.getDeniedServiceType().getDescription() + + " REASON: " + dr.getDenyReason() + " - INFO: " + dr.getAdditionalInfo()) + .identifiers(ic) + .build()); + } default: // mLog.debug("Unrecognized TSBK Opcode: " + tsbk.getOpcode().name() + " VENDOR:" + tsbk.getVendor() + // " OPCODE:" + tsbk.getOpcodeNumber()); diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/Opcode.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/Opcode.java index cd446fd0..8135cb2d 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/Opcode.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/Opcode.java @@ -1,7 +1,7 @@ /* * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2018 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 @@ -170,7 +170,7 @@ public enum Opcode MOTOROLA_OSP_PATCH_GROUP_CHANNEL_GRANT(2, "PTCH GRP VCHN GR", "MOTOROLA PATCH GROUP CHANNEL GRANT"), MOTOROLA_OSP_PATCH_GROUP_CHANNEL_GRANT_UPDATE(3, "PTCH GRP VCH UPD", "MOTOROLA PATCH GROUP CHANNEL GRANT UPDATE"), MOTOROLA_OSP_TRAFFIC_CHANNEL_ID(5, "CHANNEL CWID", "CHANNEL CWID"), - MOTOROLA_OSP_OPCODE_7(7, "MOTOROLA OPCODE 7", "MOTOROLA OPCODE 07"), + MOTOROLA_OSP_DENY_RESPONSE(7, "DENY RESPONSE", "MOTOROLA DENY RESPONSE"), MOTOROLA_OSP_SYSTEM_LOADING(9, "SYSTEM LOADING", "SYSTEM LOADING"), MOTOROLA_OSP_CONTROL_CHANNEL_ID(11, "CCH BASE STAT ID", "CONTROL CHANNEL BASE STATION ID"), MOTOROLA_OSP_CONTROL_CHANNEL_PLANNED_SHUTDOWN(14, "CCH PLND SHUTDWN", "CONTROL CHANNEL PLANNED SHUTDOWN"), @@ -294,7 +294,7 @@ public enum Opcode case 0x05: return MOTOROLA_OSP_TRAFFIC_CHANNEL_ID; case 0x07: - return MOTOROLA_OSP_OPCODE_7; + return MOTOROLA_OSP_DENY_RESPONSE; case 0x09: return MOTOROLA_OSP_SYSTEM_LOADING; case 0x0B: diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/TSBKMessageFactory.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/TSBKMessageFactory.java index b2e0503a..c23d8f9c 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/TSBKMessageFactory.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/TSBKMessageFactory.java @@ -1,3 +1,23 @@ +/* + * ****************************************************************************** + * sdrtrunk + * 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 + * ***************************************************************************** + */ + package io.github.dsheirer.module.decode.p25.message.tsbk; import io.github.dsheirer.bits.CorrectedBinaryMessage; @@ -6,6 +26,7 @@ import io.github.dsheirer.module.decode.p25.P25Interleave; import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.isp.UnknownMotorolaISPMessage; import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp.ChannelCWID; import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp.ChannelLoading; +import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp.MotorolaDenyResponse; import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp.PatchGroupAdd; import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp.PatchGroupDelete; import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp.PatchGroupVoiceChannelGrant; @@ -270,6 +291,8 @@ public class TSBKMessageFactory return new ChannelCWID(dataUnitID, message, nac, timestamp); case MOTOROLA_OSP_CONTROL_CHANNEL_PLANNED_SHUTDOWN: return new PlannedChannelShutdown(dataUnitID, message, nac, timestamp); + case MOTOROLA_OSP_DENY_RESPONSE: + return new MotorolaDenyResponse(dataUnitID, message, nac, timestamp); case MOTOROLA_OSP_TRAFFIC_CHANNEL_ID: return new ChannelCWID(dataUnitID, message, nac, timestamp); case MOTOROLA_OSP_PATCH_GROUP_ADD: diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/MotorolaDenyResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/MotorolaDenyResponse.java new file mode 100644 index 00000000..1ec4b41e --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/MotorolaDenyResponse.java @@ -0,0 +1,132 @@ +/* + * ****************************************************************************** + * sdrtrunk + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.message.tsbk.Opcode; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.DenyReason; +import io.github.dsheirer.module.decode.p25.reference.Direction; +import io.github.dsheirer.module.decode.p25.reference.Vendor; + +import java.util.ArrayList; +import java.util.List; + +/** + * Deny response + */ +public class MotorolaDenyResponse extends OSPMessage +{ + private static final int ADDITIONAL_INFORMATION_FLAG = 16; + private static final int[] SERVICE_TYPE = {18, 19, 20, 21, 22, 23}; + private static final int[] REASON = {24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] ADDITIONAL_INFO = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55}; + private static final int[] TARGET_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private DenyReason mDenyReason; + private String mAdditionalInfo; + private Identifier mTargetAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public MotorolaDenyResponse(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" SERVICE:").append(getDeniedServiceType()); + sb.append(" REASON:").append(getDenyReason()); + + if(hasAdditionalInformation()) + { + sb.append(" INFO:").append(getAdditionalInfo()); + } + + return sb.toString(); + } + + private boolean hasAdditionalInformation() + { + return getMessage().get(ADDITIONAL_INFORMATION_FLAG); + } + + public String getAdditionalInfo() + { + if(mAdditionalInfo == null) + { + mAdditionalInfo = getMessage().getHex(ADDITIONAL_INFO, 6); + } + + return mAdditionalInfo; + } + + /** + * Opcode representing the service type that is being acknowledged by the radio unit. + */ + public Opcode getDeniedServiceType() + { + return Opcode.fromValue(getMessage().getInt(SERVICE_TYPE), Direction.INBOUND, Vendor.STANDARD); + } + + public DenyReason getDenyReason() + { + if(mDenyReason == null) + { + mDenyReason = DenyReason.fromCode(getMessage().getInt(REASON)); + } + + return mDenyReason; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/DenyReason.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/DenyReason.java index 666141b3..57c8c1d0 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/reference/DenyReason.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/DenyReason.java @@ -1,125 +1,155 @@ +/* + * ****************************************************************************** + * sdrtrunk + * 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 + * ***************************************************************************** + */ + package io.github.dsheirer.module.decode.p25.reference; public enum DenyReason { - RESERVED( 0x00 ), - REQUESTING_UNIT_NOT_VALID( 0x10 ), - REQUESTING_UNIT_NOT_AUTHORIZED_FOR_SERVICE( 0x11 ), - TARGET_UNIT_NOT_VALID( 0x20 ), - TARGET_UNIT_NOT_AUTHORIZED_FOR_SERVICE( 0x21 ), - TARGET_UNIT_REFUSED_CALL( 0x2F ), - TARGET_GROUP_NOT_VALID( 0x30 ), - TARGET_GROUP_NOT_AUTHORIZED_FOR_SERVICE( 0x31 ), - INVALID_DIALING( 0x40 ), - TELEPHONE_NUMBER_NOT_AUTHORIZED( 0x41 ), - PSTN_NOT_VALID( 0x42 ), - CALL_TIMEOUT( 0x50 ), - LANDLINE_TERMINATED_CALL( 0x51 ), - SUBSCRIBER_UNIT_TERMINATED_CALL( 0x52 ), - CALL_PREEMPTED( 0x5F ), - SITE_ACCESS_DENIAL( 0x60 ), - USER_OR_SYSTEM_DEFINED( 0x61 ), - CALL_OPTIONS_NOT_VALID_FOR_SERVICE( 0xF0 ), - PROTECTION_SERVICE_OPTION_NOT_VALID( 0xF1 ), - DUPLEX_SERVICE_OPTION_NOT_VALID( 0xF2 ), - CIRCUIT_OR_PACKET_MODE_OPTION_NOT_VALID( 0xF3 ), - SYSTEM_DOES_NOT_SUPPORT_SERVICE( 0xFF ), - UNKNOWN( -1 ); - + RESERVED(0x00), + REQUESTING_UNIT_NOT_VALID(0x10), + REQUESTING_UNIT_NOT_AUTHORIZED_FOR_SERVICE(0x11), + TARGET_UNIT_NOT_VALID(0x20), + TARGET_UNIT_NOT_AUTHORIZED_FOR_SERVICE(0x21), + TARGET_UNIT_REFUSED_CALL(0x2F), + TARGET_GROUP_NOT_VALID(0x30), + TARGET_GROUP_NOT_AUTHORIZED_FOR_SERVICE(0x31), + INVALID_DIALING(0x40), + TELEPHONE_NUMBER_NOT_AUTHORIZED(0x41), + PSTN_NOT_VALID(0x42), + CALL_TIMEOUT(0x50), + LANDLINE_TERMINATED_CALL(0x51), + SUBSCRIBER_UNIT_TERMINATED_CALL(0x52), + CALL_PREEMPTED(0x5F), + SITE_ACCESS_DENIAL(0x60), + USER_OR_SYSTEM_DEFINED(0x61), //0x61 - 0xEF + PTT_COLLIDE(0x67), + PTT_BONK(0x77), + CALL_OPTIONS_NOT_VALID_FOR_SERVICE(0xF0), + PROTECTION_SERVICE_OPTION_NOT_VALID(0xF1), + DUPLEX_SERVICE_OPTION_NOT_VALID(0xF2), + CIRCUIT_OR_PACKET_MODE_OPTION_NOT_VALID(0xF3), + SYSTEM_DOES_NOT_SUPPORT_SERVICE(0xFF), + UNKNOWN(-1); + private int mCode; - - private DenyReason( int code ) + + private DenyReason(int code) { mCode = code; } - - public static DenyReason fromCode( int code ) + + public static DenyReason fromCode(int code) { - if( code == 0x10 ) + if(code == 0x10) { return DenyReason.REQUESTING_UNIT_NOT_VALID; } - else if( code == 0x11 ) + else if(code == 0x11) { return REQUESTING_UNIT_NOT_AUTHORIZED_FOR_SERVICE; } - else if( code == 0x20 ) + else if(code == 0x20) { return DenyReason.TARGET_UNIT_NOT_VALID; } - else if( code == 0x21 ) + else if(code == 0x21) { return TARGET_UNIT_NOT_AUTHORIZED_FOR_SERVICE; } - else if( code == 0x2F ) + else if(code == 0x2F) { return DenyReason.TARGET_UNIT_REFUSED_CALL; } - else if( code == 0x30 ) + else if(code == 0x30) { return TARGET_GROUP_NOT_VALID; } - else if( code == 0x31 ) + else if(code == 0x31) { return DenyReason.TARGET_GROUP_NOT_AUTHORIZED_FOR_SERVICE; } - else if( code == 0x40 ) + else if(code == 0x40) { return DenyReason.INVALID_DIALING; } - else if( code == 0x41 ) + else if(code == 0x41) { return DenyReason.TELEPHONE_NUMBER_NOT_AUTHORIZED; } - else if( code == 0x42 ) + else if(code == 0x42) { return DenyReason.PSTN_NOT_VALID; } - else if( code == 0x50 ) + else if(code == 0x50) { return CALL_TIMEOUT; } - else if( code == 0x51 ) + else if(code == 0x51) { return LANDLINE_TERMINATED_CALL; } - else if( code == 0x52 ) + else if(code == 0x52) { return SUBSCRIBER_UNIT_TERMINATED_CALL; } - else if( code == 0x5F ) + else if(code == 0x5F) { return DenyReason.CALL_PREEMPTED; } - else if( code == 0x60 ) + else if(code == 0x60) { return SITE_ACCESS_DENIAL; } - else if( code == 0xF0 ) + else if(code == 0xF0) { return CALL_OPTIONS_NOT_VALID_FOR_SERVICE; } - else if( code == 0xF1 ) + else if(code == 0xF1) { return PROTECTION_SERVICE_OPTION_NOT_VALID; } - else if( code == 0xF2 ) + else if(code == 0xF2) { return DenyReason.DUPLEX_SERVICE_OPTION_NOT_VALID; } - else if( code == 0xF3 ) + else if(code == 0xF3) { return CIRCUIT_OR_PACKET_MODE_OPTION_NOT_VALID; } - else if( code <= 0x5E ) + else if(code == 0x67) + { + return PTT_COLLIDE; + } + else if(code == 0x77) + { + return PTT_BONK; + } + else if(code <= 0x5E) { return DenyReason.RESERVED; } - else if( code >= 0x61 ) + else if(code >= 0x61) { return DenyReason.USER_OR_SYSTEM_DEFINED; } - + return UNKNOWN; } }