package im.rosetta.calls; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import im.rosetta.client.ClientManager; import im.rosetta.packet.Packet26SignalPeer; import im.rosetta.packet.runtime.NetworkSignalType; import io.g365sfu.Room; import io.orprotocol.ProtocolException; import io.orprotocol.client.Client; import io.orprotocol.packet.Packet; public class CallManager { private List callSessions = new ArrayList<>(); private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); private static final long RINGING_TIMEOUT = 30 * 1000; private ClientManager clientManager; public CallManager(ClientManager clientManager) { this.clientManager = clientManager; scheduler.scheduleAtFixedRate(this::cleanupCallSessions, 0, 1, TimeUnit.SECONDS); } public void cleanupCallSessions() { /** * Такая конструкция нужна для избежания ConcurrentModificationException, * так как мы не можем удалять элементы из списка, по которому проходим в цикле, * поэтому мы сначала собираем сессии звонков, которые нужно удалить, а потом * удаляем их из основного списка */ List sessionsToRemove = new ArrayList<>(); for (CallSession session : this.callSessions) { if (session.shouldRemove()) { /** * Отправляем всем в сессии что звонок завершился, так как он устарел, и удаляем сессию из списка активных сессий */ Packet26SignalPeer rtout = new Packet26SignalPeer(); rtout.setSignalType(NetworkSignalType.RINGING_TIMEOUT); Packet26SignalPeer endCallPacket = new Packet26SignalPeer(); endCallPacket.setSignalType(NetworkSignalType.END_CALL); endCallPacket.setJoinToken(session.getJoinToken()); endCallPacket.setCallId(session.getCallId()); try { session.sendPacket(rtout, null); this.sendPacketToRinging(session, endCallPacket); } catch (ProtocolException e) { e.printStackTrace(); } sessionsToRemove.add(session); } } for (CallSession session : sessionsToRemove) { this.callSessions.remove(session); } } public CallSession createCall(String callId, String joinToken) { CallSession session = new CallSession(callId, joinToken, RINGING_TIMEOUT); this.callSessions.add(session); return session; } public CallSession getCallSession(String callId, String joinToken) { for (CallSession session : this.callSessions) { if (session.getCallId().equals(callId) && session.getJoinToken().equals(joinToken)) { return session; } } return null; } public CallSession getCallSession(Room room) { for (CallSession session : this.callSessions) { if (session.getRoom() != null && session.getRoom().equals(room)) { return session; } } return null; } public boolean isBusy(String publicKey) { for (CallSession session : this.callSessions) { if (session.clients.containsKey(publicKey)) { return true; } if(session.ringing.containsKey(publicKey)) { return true; } } return false; } public void sendPacketToRinging(CallSession session, Packet packet) throws ProtocolException { for (String publicKey : session.ringing.keySet()) { this.clientManager.sendPacketToAuthorizedPK(publicKey, packet); } } public CallSession getCallSession(Client client) { for (CallSession session : this.callSessions) { if (session.clients.containsValue(client)) { return session; } } return null; } public void removeSession(CallSession session) { this.callSessions.remove(session); } }