1 package com.example.imsmediatestingapp;
2 
3 import android.content.SharedPreferences;
4 import android.util.Log;
5 
6 import java.io.ByteArrayInputStream;
7 import java.io.IOException;
8 import java.io.ObjectInputStream;
9 import java.net.DatagramPacket;
10 import java.net.DatagramSocket;
11 
12 /**
13  * The HandshakeReceiver handles and stores information about incoming packets
14  * during the
15  * handshake process.
16  */
17 public class HandshakeReceiver implements Runnable {
18 
19     private static final int MAX_UDP_DATAGRAM_LEN = 65527;
20     private final String HANDSHAKE_PORT_PREF = "HANDSHAKE_PORT_OPEN";
21     private final String CONFIRMATION_MESSAGE = "CONNECTED";
22     private static final String LOG_PREFIX = HandshakeReceiver.class.getName();
23     private boolean running = true;
24     private boolean isConfirmationReceived = false;
25     private boolean isHandshakeReceived = false;
26     private DeviceInfo receivedDeviceInfo;
27     DatagramSocket socket = null;
28     SharedPreferences.Editor editor;
29 
HandshakeReceiver(SharedPreferences preferences)30     public HandshakeReceiver(SharedPreferences preferences) {
31         editor = preferences.edit();
32     }
33 
run()34     public void run() {
35         DeviceInfo deviceInfo = null;
36         String confirmation = null;
37         byte[] buffer = new byte[MAX_UDP_DATAGRAM_LEN];
38         DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
39 
40         try {
41             socket = new DatagramSocket();
42             socket.setReuseAddress(true);
43 
44             editor.putBoolean(HANDSHAKE_PORT_PREF, true).apply();
45 
46             while (running) {
47                 socket.receive(packet);
48                 Object dataReceived = deserializePacket(packet.getData());
49 
50                 if (dataReceived instanceof DeviceInfo && !isHandshakeReceived) {
51                     deviceInfo = (DeviceInfo) dataReceived;
52                     if (verifyHandshakePacket(deviceInfo)) {
53                         isHandshakeReceived = true;
54                         receivedDeviceInfo = deviceInfo;
55                         Log.d(LOG_PREFIX, "RECEIVED: Device Info");
56                     }
57 
58                 } else if (dataReceived instanceof String && !isConfirmationReceived) {
59                     confirmation = (String) dataReceived;
60                     if (verifyConfirmationPacket(confirmation)) {
61                         isConfirmationReceived = true;
62                         Log.d(LOG_PREFIX, "RECEIVED: Confirmation");
63                         running = false;
64 
65                     }
66                 }
67 
68             }
69         } catch (Throwable e) {
70             System.out.println(e.getLocalizedMessage());
71         }
72     }
73 
74     /**
75      * Reads the data into a ByteArrayInputStream and ObjectInputStream to determine
76      * the type of the data, then casts it to the correct type and returns it
77      * @param data byte array from the packet received from the DatagramSocket
78      * @param <T>  either a String or DeviceInfo
79      * @return string value of hte conformation string, or the DeviceInfo
80      */
81     @SuppressWarnings("TypeParameterUnusedInFormals")
deserializePacket(byte[] data)82     private <T> T deserializePacket(byte[] data) {
83         DeviceInfo deviceInfo = null;
84         String confirmationMessage = null;
85         ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data);
86         try {
87             ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
88             Object readObject = objectInputStream.readObject();
89             if (readObject instanceof DeviceInfo) {
90                 deviceInfo = (DeviceInfo) readObject;
91                 return (T) deviceInfo;
92 
93             } else if (readObject instanceof String) {
94                 confirmationMessage = (String) readObject;
95                 return (T) confirmationMessage;
96             }
97 
98         } catch (IOException | ClassNotFoundException e) {
99             Log.e(LOG_PREFIX, "Exception: " + e.toString());
100             Log.d(LOG_PREFIX, new String(data));
101             return (T) new String(data);
102         }
103         return null;
104     }
105 
getBoundSocket()106     public int getBoundSocket() {
107         return socket.getLocalPort();
108     }
109 
close()110     public void close() {
111         Log.d("", "Closing the socket on port: " + socket.getLocalPort());
112         running = false;
113         socket.close();
114         editor.putBoolean(HANDSHAKE_PORT_PREF, false).apply();
115     }
116 
117     /**
118      * Verifies that the incoming DeviceInfo has all valid port numbers.
119      * @param deviceInfo device info to verify
120      * @return boolean if the DeviceInfo has all the right info
121      */
verifyHandshakePacket(DeviceInfo deviceInfo)122     private boolean verifyHandshakePacket(DeviceInfo deviceInfo) {
123         if (deviceInfo.getHandshakePort() == -1 || deviceInfo.getAudioRtpPort() == -1) {
124             Log.d(LOG_PREFIX,
125                     "One or more of the ports sent in the handshake have not been opened.");
126             return false;
127         }
128 
129         return true;
130     }
131 
verifyConfirmationPacket(String confirmation)132     private boolean verifyConfirmationPacket(String confirmation) {
133         return confirmation.equals(CONFIRMATION_MESSAGE);
134     }
135 
isConfirmationReceived()136     public boolean isConfirmationReceived() {
137         return isConfirmationReceived;
138     }
139 
isHandshakeReceived()140     public boolean isHandshakeReceived() {
141         return isHandshakeReceived;
142     }
143 
getReceivedDeviceInfo()144     public DeviceInfo getReceivedDeviceInfo() {
145         return receivedDeviceInfo;
146     }
147 }
148