📄 vojxtacallcontrol.java
字号:
} if (receiveTimeoutThread != null) { receiveTimeoutThread.stopThread(); } if (micControl != null) { micControl.endMic(); micControl.releaseHardware(); } if (speakerControl != null) { speakerControl.endSpeaker(); speakerControl.releaseHardware(); } } /** * Called from UI signaling user accepts call invitation */ public void viewActionAcceptCall() { if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) { LOG.info("viewActionAcceptCall"); } sendCommand(this.COMMAND_VOJXTA_INVITE_ACCEPT, null); setProtocolState(this.SESSION_VOJXTA_INVITE_ACCEPT_SENT); } /** * Called from IU signaling user wishes to place call on hold. */ public void viewActionHoldCall() { if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) { LOG.info("viewActionHoldCall"); } sendCommand(this.COMMAND_VOJXTA_HOLD_REQUEST, this.COMMAND_VOJXTA_HOLD_ACCEPT); setProtocolState(this.SESSION_VOJXTA_HOLD_REQUEST_SENT); } /** * Called from IU signaling user wishes to hangup call. */ public void viewActionHangupCall() { if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) { LOG.info("viewActionHangupCall"); } sendCommand(this.COMMAND_VOJXTA_HANGUP_REQUEST, this.COMMAND_VOJXTA_HANGUP_ACCEPT); setProtocolState(this.SESSION_VOJXTA_HANGUP_REQUEST_SENT); setCallEndTime(); micControl.endMic(); micControl.releaseHardware(); speakerControl.endSpeaker(); speakerControl.releaseHardware(); } /** * Called from IU signaling user wishes to resume a call previsously placed * on hold. */ public void viewActionResumeCall() { if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) { LOG.info("viewActionResumeCall"); } sendCommand(this.COMMAND_VOJXTA_RESUME_REQUEST, this.COMMAND_VOJXTA_RESUME_ACCEPT); setProtocolState(this.SESSION_VOJXTA_RESUME_REQUEST_SENT); } private void sendCommand(String command, String ackCommand) { sendCommand(command, ackCommand, null); } /** * Sends a command on the vojxta dialog pipe. This does not change the * Protocol state. We have to remember to do this ourselves. */ private void sendCommand(String command, String ackCommand, HashMap elementMap) { if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) { LOG.info("sendCommand : " + command); } DialogMessage msg = (DialogMessage) templateMessage.clone(); StringMessageElement commandElement = new StringMessageElement( TAG_SESSION_COMMAND, command, null); msg.addMessageElement(TAG_SESSION_COMMAND, commandElement); if (elementMap != null) { Iterator keys = elementMap.keySet().iterator(); while (keys.hasNext()) { String elementName = (String) keys.next(); MessageElement element = (MessageElement) elementMap.get(elementName); msg.addMessageElement(elementName, element); LOG.info("added element " + elementName); } } if (ackCommand != null) { this.messageAckThread = new MessageAckThread(ackCommand); this.messageAckThread.start(); } this.vojxtaDialog.dispatch(msg); } /** * Session state was meant for the UI as a messaging signaler to users */ private void setSessionState(int sessionState) { this.sessionState = sessionState; this.vojxtaView.sessionStateChanged(this.sessionState); } /** * Returns the current session state */ public int getSessionState() { return this.sessionState; } /** * This method updates the current protocol state. Also signals the UI * of a protocol state change. */ private void setProtocolState(int protocolState) { this.protocolState = protocolState; if (this.protocolState == this.SESSION_VOJXTA_INVITE_REQUEST_SENT || this.protocolState == this.SESSION_VOJXTA_INVITE_REQUEST_RECEIVED) { setSessionState(this.SESSION_VOJXTA_CONNECTING); } else if (this.protocolState == this.SESSION_VOJXTA_INVITE_ACCEPT_SENT || this.protocolState == this.SESSION_VOJXTA_INVITE_ACCEPT_RECEIVED) { setSessionState(this.SESSION_VOJXTA_CONNECTED); } else if (this.protocolState == this.SESSION_VOJXTA_START_REQUEST_SENT || this.protocolState == this.SESSION_VOJXTA_START_REQUEST_RECEIVED) { setSessionState(this.SESSION_VOJXTA_STARTING); } else if (this.protocolState == this.SESSION_VOJXTA_START_ACCEPT_SENT || this.protocolState == this.SESSION_VOJXTA_START_ACCEPT_RECEIVED) { setSessionState(this.SESSION_VOJXTA_STARTED); } else if (this.protocolState == this.SESSION_VOJXTA_HOLD_ACCEPT_SENT || this.protocolState == this.SESSION_VOJXTA_HOLD_ACCEPT_RECEIVED) { setSessionState(this.SESSION_VOJXTA_HOLDING); } else if (this.protocolState == this.SESSION_VOJXTA_INCALL) { setSessionState(this.SESSION_VOJXTA_INCALL); } else if (this.protocolState == this.SESSION_VOJXTA_RESUME_ACCEPT_SENT || this.protocolState == this.SESSION_VOJXTA_RESUME_ACCEPT_RECEIVED) { setSessionState(this.SESSION_VOJXTA_INCALL); } else if (this.protocolState == this.SESSION_VOJXTA_HANGUP_REQUEST_SENT || this.protocolState == this.SESSION_VOJXTA_HANGUP_REQUEST_RECEIVED) { setSessionState(this.SESSION_VOJXTA_ENDING); } else if (this.protocolState == this.SESSION_VOJXTA_HANGUP_ACCEPT_SENT || this.protocolState == this.SESSION_VOJXTA_HANGUP_ACCEPT_RECEIVED) { setSessionState(this.SESSION_VOJXTA_ENDED); } else if (this.protocolState == this.SESSION_VOJXTA_DISCONNECT_REQUEST_SENT || this.protocolState == this.SESSION_VOJXTA_DISCONNECT_REQUEST_RECEIVED) { setSessionState(this.SESSION_VOJXTA_DISCONNECTING); } else if (this.protocolState == this.SESSION_VOJXTA_DISCONNECT_ACCEPT_SENT || this.protocolState == this.SESSION_VOJXTA_DISCONNECT_ACCEPT_RECEIVED) { setSessionState(this.SESSION_VOJXTA_DISCONNECTED); } if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) { LOG.info("ProtocolStateChanged to " + protocolState); } this.vojxtaView.protocolStateChanged(protocolState); } /** * Returns the protocol state */ public int getProtocolState() { return this.protocolState; } /** * Returns the current time in milliseconds to wait for a message respose * before session shutdown. */ public long getMessageAckTimeout() { return (messageAckTimeout > this.MINIMUM_MESSAGE_ACK_TIMEOUT) ? messageAckTimeout : DEFAULT_MESSAGE_ACK_TIMEOUT; } public void setMessageAckTimeout(long timeInMilliseconds) { if (timeInMilliseconds > MINIMUM_MESSAGE_ACK_TIMEOUT) { this.messageAckTimeout = timeInMilliseconds; } } /** * Message ack thread has timed out and no message ack has been received. * End session. */ protected void localActionMessageGoneUnAcknowledged() { if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) { LOG.info(" localActionMessageGoneUnAcknowledged "); } localActionEndSession(); /** Normally we'd just call sendcommand(END_SESSION) and wait for a * end END_SEESION_ACCEPT then go down gracefully. Since we aren't * receiving command messages we choose to just exit hard. */ /* DialogMessage msg = (DialogMessage)templateMessage.clone (); StringMessageElement commandElement = new StringMessageElement ( TAG_SESSION_COMMAND, this.COMMAND_VOJXTA_DISCONNECT_REQUEST, null); msg.addMessageElement (TAG_SESSION_COMMAND, commandElement); this.vojxtaDialog.dispatch (msg); **/ sendCommand(this.COMMAND_VOJXTA_DISCONNECT_REQUEST, null); this.setProtocolState(this.SESSION_VOJXTA_DISCONNECTED); } /** * After a message is sent this thread is instantiated, started and will * wait till it's lock is notified of an incoming message or till a timeout * is reached. The idea being each commmand message dispatched will return * an accept response. If that response is received then +1 if not this thread * will notify us and shutdown the session as gracefully as possible. A new * Thread is started for each command message sent. * TODO * Better option then creating a new thread for each command. Reuse of a single * thread maybe. */ class MessageAckThread extends Thread { private AckWaitLock ackWaitLock = null; private String commandName = null; private boolean threadEnded = false; public MessageAckThread(String commandName) { if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) { LOG.info("MessageAckThread waiting for command " + commandName); } ackWaitLock = new AckWaitLock(); this.commandName = commandName; this.setPriority(Thread.MIN_PRIORITY); //this.setDaemon (true); } public void run() { if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) { LOG.info("MessageAckThread : Run "); } try { synchronized (ackWaitLock) { ackWaitLock.wait(getMessageAckTimeout()); } } catch (InterruptedException ix) { ix.printStackTrace(); } synchronized (ackWaitLock) { if (!ackWaitLock.isAcknowledged()) { if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) { LOG.info("MessageAckThread : " + this.commandName + " command NOT acknowledged"); } VoJxtaCallControl.this.localActionMessageGoneUnAcknowledged(); } } this.threadEnded = true; } /** * Avoids a race condition... needs to be reworked. */ public boolean isThreadEnded() { while (!this.threadEnded) { try { Thread.sleep(100); } catch (InterruptedException ix) { ix.printStackTrace(); } } return this.threadEnded; } /** * A message was received and this thread is alerted. */ public void setMessageAcknowledged(String commandName) { synchronized (ackWaitLock) { if (this.commandName.equals(commandName)) { if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) { LOG.info("MessageAckThread : Received RIGHT Ack " + commandName + " for " + this.commandName + " : Command Ack"); } ackWaitLock.setAcknowledged(true); } else { if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) { LOG.info("MessageAckThread : Received WRONG Ack " + commandName + " for " + this.commandName + " : Command Ack"); } } ackWaitLock.notify(); } } class AckWaitLock extends Object { private boolean acknowledged = false; public boolean isAcknowledged() { return acknowledged; } public void setAcknowledged(boolean acknowledged) { this.acknowledged = acknowledged; if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) { LOG.info("AckWaitLock : SetAcknowldge " + acknowledged); } } } } /** * Because we push the voice payload through without periodic ack's we need * a thread to count a timeout in case the other side goes down. Time out * of 40 seconds while IN CALL should be enough for any relay to catch up. * A more elegant solution is needed though i'd still like to keep it simple. * With reliable pipes SIP type acks seem too much */ class ReceivedMessageTimeoutThread extends Thread { public static final int SLEEP_TIME = 1000; public static final long DEFAULT_PAYLOAD_MESSAGE_TIMEOUT = 1000 * 40; public static final long MINIMUM_PAYLOAD_MESSAGE_TIMEOUT = 1000 * 40; private long payloadMessageTimeout = DEFAULT_PAYLOAD_MESSAGE_TIMEOUT; private boolean run = true; public ReceivedMessageTimeoutThread() { setPriority(Thread.MIN_PRIORITY); //this.setDaemon (true); } public long getPayloadMessageTimeout() { return payloadMessageTimeout; } public void setPayloadMessageTimeout(long payloadMessageTimeout) { if (payloadMessageTimeout >= MINIMUM_PAYLOAD_MESSAGE_TIMEOUT) { this.payloadMessageTimeout = payloadMessageTimeout; } } public void stopThread() { run = false; } public void run() { while (run) { try { Thread.sleep(VoJxtaCallControl.SECOND); } catch (InterruptedException ix) { ix.printStackTrace(); } if (getProtocolState() == VoJxtaCallControl.SESSION_VOJXTA_HOLDING) { continue; } long lastMessageReceived = 0; if (speakerControl != null) { lastMessageReceived = speakerControl.getTimeOfLastVoiceMessage(); } long currentTime = System.currentTimeMillis(); if (getProtocolState() == VoJxtaCallControl.SESSION_VOJXTA_INCALL && (currentTime - lastMessageReceived) > getPayloadMessageTimeout()) { if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) { LOG.info(" diff " + (currentTime - lastMessageReceived) + " > " + getPayloadMessageTimeout()); } VoJxtaCallControl.this.localActionMessageGoneUnAcknowledged(); } } } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -