Phone app (based on Ali's Control for iotcloud benchmark) to control alarm in the...
[iot2.git] / benchmarks / other / PhoneInterface / Control / app / src / main / java / iotcloud / CloudComm.java
diff --git a/benchmarks/other/PhoneInterface/Control/app/src/main/java/iotcloud/CloudComm.java b/benchmarks/other/PhoneInterface/Control/app/src/main/java/iotcloud/CloudComm.java
new file mode 100644 (file)
index 0000000..4f51e5c
--- /dev/null
@@ -0,0 +1,838 @@
+package iotcloud;
+
+import java.io.*;
+import java.net.*;
+import java.util.Arrays;
+import javax.crypto.*;
+import javax.crypto.spec.*;
+import java.security.SecureRandom;
+import android.util.*;
+import java.nio.charset.StandardCharsets;
+import org.spongycastle.crypto.generators.PKCS5S2ParametersGenerator;
+import org.spongycastle.crypto.digests.SHA256Digest;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.PBEParametersGenerator;
+import android.content.*;
+
+/**
+ * This class provides a communication API to the webserver.  It also
+ * validates the HMACs on the slots and handles encryption.
+ * @author Brian Demsky <bdemsky@uci.edu>
+ * @version 1.0
+ */
+
+
+class CloudComm {
+       private static final int SALT_SIZE = 8;
+       private static final int TIMEOUT_MILLIS = 2000; // 100
+
+       /** Sets the size for the HMAC. */
+       static final int HMAC_SIZE = 32;
+
+       private String baseurl;
+       private Cipher encryptCipher;
+       private Cipher decryptCipher;
+       private Mac mac;
+       private String password;
+       private SecureRandom random;
+       private byte salt[];
+       private Table table;
+       private int listeningPort = -1;
+       private Thread localServerThread = null;
+       private boolean doEnd = false;
+
+       private TimingSingleton timer = null;
+
+       private Context context;
+
+
+
+
+       /**
+        * Empty Constructor needed for child class.
+        */
+       CloudComm() {
+               timer = TimingSingleton.getInstance();
+       }
+
+       private void deleteFile(Context context) {
+               File fd = context.getFileStreamPath("config.txt");
+               fd.delete();
+       }
+
+
+       private void writeToFile(byte[] data,Context context) {
+               try {
+//                     OutputStreamWriter outputStreamWriter = new OutputStreamWriter(context.openFileOutput("config.txt", Context.MODE_PRIVATE));
+//                     outputStreamWriter.write(data);
+//                     outputStreamWriter.close();
+
+                       FileOutputStream outputStreamWriter = context.openFileOutput("config.txt", Context.MODE_PRIVATE);
+                       outputStreamWriter.write(data);
+                       outputStreamWriter.close();
+
+               }
+               catch (IOException e) {
+                       Log.e("Exception", "File write failed: " + e.toString());
+               }
+       }
+
+       private byte[] readFromFile(Context context) throws FileNotFoundException {
+
+               byte[] ret1 = null;
+
+               try {
+                       InputStream inputStream = context.openFileInput("config.txt");
+
+                       if ( inputStream != null ) {
+
+
+                               ret1 = new byte[inputStream.available()];
+                               for(int i = 0; i < ret1.length;i++)
+                               {
+                                       ret1[i] = (byte)inputStream.read();
+                               }
+
+
+
+
+//                             InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
+//                             BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
+//                             String receiveString = "";
+//                             StringBuilder stringBuilder = new StringBuilder();
+
+//                             while ( (receiveString = bufferedReader.readLine()) != null ) {
+//                                     stringBuilder.append(receiveString);
+//                             }
+
+                               inputStream.close();
+//                             ret = stringBuilder.toString();
+                       }
+               }
+               catch (FileNotFoundException e) {
+                       Log.e("login activity", "File not found: " + e.toString());
+
+                       throw e;
+               } catch (IOException e) {
+                       Log.e("login activity", "Can not read file: " + e.toString());
+               }
+
+               return ret1;
+       }
+
+
+
+       /**
+        * Constructor for actual use. Takes in the url and password.
+        */
+       CloudComm(Table _table,  String _baseurl, String _password, int _listeningPort, Context _context) {
+               timer = TimingSingleton.getInstance();
+               this.table = _table;
+               this.baseurl = _baseurl;
+               this.password = _password;
+               this.random = new SecureRandom();
+               this.listeningPort = _listeningPort;
+               this.context = _context;
+
+
+               if (this.listeningPort > 0) {
+                       localServerThread = new Thread(new Runnable() {
+                               public void run() {
+                                       localServerWorkerFunction();
+                               }
+                       });
+                       localServerThread.start();
+               }
+       }
+
+       /**
+        * Generates Key from password.
+        */
+       private SecretKeySpec initKey() {
+               try {
+
+                       Log.e("Ali:::::", "KEY KEY KEY......");
+
+
+
+                       boolean doCrypt = false;
+
+                       byte[] keySaved = null;
+
+                       try {
+//                             String file = readFromFile(context);
+                               byte[] dat = readFromFile(context);//file.getBytes();
+
+                               boolean saltMatch = true;
+                               for(int i = 0; i < salt.length; i++)
+                               {
+
+                                       Log.e("ALIasdasdaS:", " " + ((int) salt[i] & 255) + " " + ((int) dat[i] & 255));
+
+                                       if(dat[i] != salt[i])
+                                       {
+                                               saltMatch = false;
+//                                             break;
+                                       }
+                               }
+
+                               if(saltMatch )
+                               {
+                                       keySaved = new byte[dat.length - salt.length];
+                                       for(int i = salt.length; i < dat.length;i++)
+                                       {
+                                               keySaved[i-salt.length] = dat[i];
+                                       }
+                               }
+                               else
+                               {
+                                       doCrypt = true;
+                                       Log.e("Ali:::::", "Salt No Match......");
+
+                               }
+
+
+
+
+
+                       }
+                       catch (Exception e)
+                       {
+                               doCrypt = true;
+                       }
+
+
+
+                       if(doCrypt) {
+                               Log.e("Ali:::::", "Doing Crypt......");
+                               PKCS5S2ParametersGenerator generator = new PKCS5S2ParametersGenerator(new SHA256Digest());
+                               generator.init(PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(password.toCharArray()), salt, 65536);
+                               KeyParameter key = (KeyParameter) generator.generateDerivedMacParameters(128);
+
+
+//                     PBEKeySpec keyspec = new PBEKeySpec(password.toCharArray(),
+//                                                         salt,
+//                                                         65536,
+//                                                         128);
+//                     SecretKey tmpkey = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1").generateSecret(keyspec);
+//                     SecretKey tmpkey = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256").generateSecret(keyspec);
+
+
+//                     return new SecretKeySpec(tmpkey.getEncoded(), "AES");
+
+
+                               byte[] keyDat = key.getKey();
+                               byte[] saveDat = new byte[salt.length + keyDat.length];
+
+                               for (int i = 0 ; i < salt.length;i++)
+                               {
+                                       saveDat[i] = salt[i];
+                               }
+
+                               for (int i = 0 ; i < keyDat.length;i++)
+                               {
+                                       saveDat[i + salt.length] = keyDat[i];
+                               }
+
+
+                               deleteFile(context);
+                               writeToFile(saveDat, context);
+
+                               return new SecretKeySpec(key.getKey(), "AES");
+                       }
+                       else{
+
+                               Log.e("Ali:::::", "Using Saved......");
+
+                               return new SecretKeySpec(keySaved, "AES");
+                       }
+
+
+               } catch (Exception e) {
+                       StringWriter sw = new StringWriter();
+                       PrintWriter pw = new PrintWriter(sw);
+                       e.printStackTrace(pw);
+                        // stack trace as a string
+
+
+                       throw new Error("Failed generating key.   "  + sw.toString());
+               }
+       }
+
+       /**
+        * Inits all the security stuff
+        */
+       public void initSecurity() throws ServerException {
+               // try to get the salt and if one does not exist set one
+               if (!getSalt()) {
+                       //Set the salt
+                       setSalt();
+               }
+
+               initCrypt();
+       }
+
+       /**
+        * Inits the HMAC generator.
+        */
+       private void initCrypt() {
+
+               if (password == null) {
+                       return;
+               }
+
+               try {
+                       SecretKeySpec key = initKey();
+                       password = null; // drop password
+                       mac = Mac.getInstance("HmacSHA256");
+                       mac.init(key);
+                       encryptCipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
+                       encryptCipher.init(Cipher.ENCRYPT_MODE, key);
+                       decryptCipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
+                       decryptCipher.init(Cipher.DECRYPT_MODE, key);
+               } catch (Exception e) {
+                       e.printStackTrace();
+                       throw new Error("Failed To Initialize Ciphers");
+               }
+       }
+
+       /*
+        * Builds the URL for the given request.
+        */
+       private URL buildRequest(boolean isput, long sequencenumber, long maxentries) throws IOException {
+               String reqstring = isput ? "req=putslot" : "req=getslot";
+               String urlstr = baseurl + "?" + reqstring + "&seq=" + sequencenumber;
+               if (maxentries != 0)
+                       urlstr += "&max=" + maxentries;
+               return new URL(urlstr);
+       }
+
+       private void setSalt() throws ServerException {
+
+               if (salt != null) {
+                       // Salt already sent to server so dont set it again
+                       return;
+               }
+
+               try {
+                       byte[] saltTmp = new byte[SALT_SIZE];
+                       random.nextBytes(saltTmp);
+
+                       URL url = new URL(baseurl + "?req=setsalt");
+                       
+                       timer.startTime();
+                       URLConnection con = url.openConnection();
+                       HttpURLConnection http = (HttpURLConnection) con;
+
+                       http.setRequestMethod("POST");
+                       http.setFixedLengthStreamingMode(saltTmp.length);
+                       http.setDoOutput(true);
+                       http.setConnectTimeout(TIMEOUT_MILLIS);
+
+                       
+                       http.connect();
+
+                       OutputStream os = http.getOutputStream();
+                       os.write(saltTmp);
+                       os.flush();
+                       
+                       int responsecode = http.getResponseCode();
+                       if (responsecode != HttpURLConnection.HTTP_OK) {
+                               // TODO: Remove this print
+                               System.out.println(responsecode);
+                               throw new Error("Invalid response");
+                       }
+
+                       timer.endTime();
+
+                       salt = saltTmp;
+               } catch (Exception e) {
+                       // e.printStackTrace();
+                       timer.endTime();
+                       throw new ServerException("Failed setting salt", ServerException.TypeConnectTimeout);
+               }
+       }
+
+       private boolean getSalt() throws ServerException {
+               URL url = null;
+               URLConnection con = null;
+               HttpURLConnection http = null;
+
+               try {
+                       url = new URL(baseurl + "?req=getsalt");
+               } catch (Exception e) {
+                       // e.printStackTrace();
+                       throw new Error("getSlot failed");
+               }
+               try {
+
+                       timer.startTime();
+                       con = url.openConnection();
+                       http = (HttpURLConnection) con;
+                       http.setRequestMethod("POST");
+                       http.setConnectTimeout(TIMEOUT_MILLIS);
+                       http.setReadTimeout(TIMEOUT_MILLIS);
+
+                       
+                       http.connect();
+                       timer.endTime();
+
+
+               } catch (SocketTimeoutException e) {
+                       timer.endTime();
+                       throw new ServerException("getSalt failed", ServerException.TypeConnectTimeout);
+               } catch (Exception e) {
+                       // e.printStackTrace();
+                       throw new Error("getSlot failed  " + e.toString());
+               }
+
+               try {
+
+                       timer.startTime();
+
+                       int responsecode = http.getResponseCode();
+                       if (responsecode != HttpURLConnection.HTTP_OK) {
+                               // TODO: Remove this print
+                               // System.out.println(responsecode);
+                               throw new Error("Invalid response");
+                       }
+
+//                     Log.e("Aaaaa", "Code " + responsecode);
+
+
+                       InputStream is = http.getInputStream();
+//
+//
+//                     BufferedReader rd= new BufferedReader(new InputStreamReader(is));
+//                     int line;
+//                     StringBuilder sb= new StringBuilder();
+//                     while ((line = rd.read())!= -1)
+//                     {
+//                             sb.append((char)line);
+//                             Log.e("Aaaaa", "line " + line);
+//
+//                     }
+//
+//
+//                     int sdfsdfds = (int)sb.toString().charAt(0);
+//                     Log.e("Aaaaa", "length " + (int)sb.toString().charAt(0));
+//                     Log.e("Aaaaa", "Res " + sb.toString().length());
+
+
+//                     is = new ByteArrayInputStream(sb.toString().getBytes(StandardCharsets.UTF_8));
+
+
+//                     if (is.available() > 0) {
+//                     if (sb.toString().length() > 0) {
+                       if(true)
+                       {
+                               try {
+                                       DataInputStream dis = new DataInputStream(is);
+                                       int salt_length = dis.readInt();
+                                       byte[] tmp = new byte[salt_length];
+//                             byte [] tmp = new byte[8];
+                                       dis.readFully(tmp);
+                                       salt = tmp;
+
+                                       for (int i = 0; i < 8; i++) {
+                                               Log.e("ALIasdasdaS:", "asd " + ((int) salt[i] & 255));
+                                       }
+
+
+                                       timer.endTime();
+
+                                       return true;
+                               }
+                               catch (Exception e)
+                               {
+                                       timer.endTime();
+
+                                       Log.e("Aaaaa", "Salt No Data");
+
+                                       return false;
+                               }
+                       }
+                       else {
+
+
+                               return false;
+                       }
+               } catch (SocketTimeoutException e) {
+                       timer.endTime();
+
+                       throw new ServerException("getSalt failed", ServerException.TypeInputTimeout);
+               } catch (Exception e) {
+
+                       throw new Error("getSlot failed + " + e);
+               }
+       }
+
+       /*
+        * API for putting a slot into the queue.  Returns null on success.
+        * On failure, the server will send slots with newer sequence
+        * numbers.
+        */
+       public Slot[] putSlot(Slot slot, int max) throws ServerException {
+               URL url = null;
+               URLConnection con = null;
+               HttpURLConnection http = null;
+
+               try {
+                       if (salt == null) {
+                               if (!getSalt()) {
+                                       throw new ServerException("putSlot failed", ServerException.TypeSalt);
+                               }
+                               initCrypt();
+                       }
+
+                       long sequencenumber = slot.getSequenceNumber();
+                       byte[] bytes = slot.encode(mac);
+                       bytes = encryptCipher.doFinal(bytes);
+
+
+
+
+                       url = buildRequest(true, sequencenumber, max);
+
+                       timer.startTime();
+                       con = url.openConnection();
+                       http = (HttpURLConnection) con;
+
+                       http.setRequestMethod("POST");
+                       http.setFixedLengthStreamingMode(bytes.length);
+                       http.setDoOutput(true);
+                       http.setConnectTimeout(TIMEOUT_MILLIS);
+                       http.setReadTimeout(TIMEOUT_MILLIS);
+                       http.connect();
+
+                       OutputStream os = http.getOutputStream();
+                       os.write(bytes);
+                       os.flush();
+
+                       timer.endTime();
+
+
+                       // System.out.println("Bytes Sent: " + bytes.length);
+               } catch (ServerException e) {
+                       timer.endTime();
+
+                       throw e;
+               } catch (SocketTimeoutException e) {
+                       timer.endTime();
+
+                       throw new ServerException("putSlot failed", ServerException.TypeConnectTimeout);
+               } catch (Exception e) {
+                       // e.printStackTrace();
+                       throw new Error("putSlot failed");
+               }
+
+
+
+               try {
+                       timer.startTime();
+                       InputStream is = http.getInputStream();
+                       DataInputStream dis = new DataInputStream(is);
+                       byte[] resptype = new byte[7];
+                       dis.readFully(resptype);
+                       timer.endTime();
+
+                       if (Arrays.equals(resptype, "getslot".getBytes()))
+                       {       
+                               return processSlots(dis);
+                       }
+                       else if (Arrays.equals(resptype, "putslot".getBytes()))
+                       {
+                               return null;
+                       }
+                       else
+                               throw new Error("Bad response to putslot");
+
+               } catch (SocketTimeoutException e) {
+                               timer.endTime();
+                       throw new ServerException("putSlot failed", ServerException.TypeInputTimeout);
+               } catch (Exception e) {
+                       // e.printStackTrace();
+                       throw new Error("putSlot failed");
+               }
+       }
+
+       /**
+        * Request the server to send all slots with the given
+        * sequencenumber or newer.
+        */
+       public Slot[] getSlots(long sequencenumber) throws ServerException {
+               URL url = null;
+               URLConnection con = null;
+               HttpURLConnection http = null;
+
+               try {
+                       if (salt == null) {
+                               if (!getSalt()) {
+                                       throw new ServerException("getSlots failed", ServerException.TypeSalt);
+                               }
+                               initCrypt();
+                       }
+
+                       url = buildRequest(false, sequencenumber, 0);
+                       timer.startTime();
+                       con = url.openConnection();
+                       http = (HttpURLConnection) con;
+                       http.setRequestMethod("POST");
+                       http.setConnectTimeout(TIMEOUT_MILLIS);
+                       http.setReadTimeout(TIMEOUT_MILLIS);
+
+                       
+
+                       http.connect();
+                       timer.endTime();
+
+               } catch (SocketTimeoutException e) {
+                       timer.endTime();
+
+                       throw new ServerException("getSlots failed", ServerException.TypeConnectTimeout);
+               } catch (ServerException e) {
+                       timer.endTime();
+
+                       throw e;
+               } catch (Exception e) {
+                       // e.printStackTrace();
+                       throw new Error("getSlots failed   " + e.toString());
+               }
+
+               try {
+                       
+                       timer.startTime();
+                       InputStream is = http.getInputStream(); 
+                       DataInputStream dis = new DataInputStream(is);
+                       byte[] resptype = new byte[7];
+                       
+                       dis.readFully(resptype);
+                       timer.endTime();
+
+                       if (!Arrays.equals(resptype, "getslot".getBytes()))
+                               throw new Error("Bad Response: " + new String(resptype));
+
+                       return processSlots(dis);
+               } catch (SocketTimeoutException e) {
+                       timer.endTime();
+
+                       throw new ServerException("getSlots failed", ServerException.TypeInputTimeout);
+               } catch (Exception e) {
+                       // e.printStackTrace();
+                       StringWriter sw = new StringWriter();
+                       PrintWriter pw = new PrintWriter(sw);
+                       e.printStackTrace(pw);
+                       throw new Error("getSlots failed   " + sw.toString());
+               }
+       }
+
+       /**
+        * Method that actually handles building Slot objects from the
+        * server response.  Shared by both putSlot and getSlots.
+        */
+       private Slot[] processSlots(DataInputStream dis) throws Exception {
+               int numberofslots = dis.readInt();
+               int[] sizesofslots = new int[numberofslots];
+
+               Slot[] slots = new Slot[numberofslots];
+               for (int i = 0; i < numberofslots; i++)
+                       sizesofslots[i] = dis.readInt();
+
+               for (int i = 0; i < numberofslots; i++) {
+
+                       byte[] data = new byte[sizesofslots[i]];
+                       dis.readFully(data);
+
+                       data = decryptCipher.doFinal(data);
+
+                       slots[i] = Slot.decode(table, data, mac);
+
+                       Log.e("Ali::::", "Slot Process");
+               }
+               dis.close();
+               return slots;
+       }
+
+       public byte[] sendLocalData(byte[] sendData, String host, int port) {
+
+               if (salt == null) {
+                       return null;
+               }
+               try {
+
+                       System.out.println("Passing Locally");
+
+                       mac.update(sendData);
+                       byte[] genmac = mac.doFinal();
+                       byte[] totalData = new byte[sendData.length + genmac.length];
+                       System.arraycopy(sendData, 0, totalData, 0, sendData.length);
+                       System.arraycopy(genmac, 0, totalData, sendData.length, genmac.length);
+
+                       // Encrypt the data for sending
+                       // byte[] encryptedData = encryptCipher.doFinal(totalData);
+                       byte[] encryptedData = encryptCipher.doFinal(totalData);
+
+                       // Open a TCP socket connection to a local device
+                       Socket socket = new Socket(host, port);
+                       socket.setReuseAddress(true);
+                       DataOutputStream output = new DataOutputStream(socket.getOutputStream());
+                       DataInputStream input = new DataInputStream(socket.getInputStream());
+
+
+                       timer.startTime();
+                       // Send data to output (length of data, the data)
+                       output.writeInt(encryptedData.length);
+                       output.write(encryptedData, 0, encryptedData.length);
+                       output.flush();
+
+                       int lengthOfReturnData = input.readInt();
+                       byte[] returnData = new byte[lengthOfReturnData];
+                       input.readFully(returnData);
+
+                       timer.endTime();
+
+                       returnData = decryptCipher.doFinal(returnData);
+
+                       // We are done with this socket
+                       socket.close();
+
+                       mac.update(returnData, 0, returnData.length - HMAC_SIZE);
+                       byte[] realmac = mac.doFinal();
+                       byte[] recmac = new byte[HMAC_SIZE];
+                       System.arraycopy(returnData, returnData.length - realmac.length, recmac, 0, realmac.length);
+
+                       if (!Arrays.equals(recmac, realmac))
+                               throw new Error("Local Error: Invalid HMAC!  Potential Attack!");
+
+                       byte[] returnData2 = new byte[lengthOfReturnData - recmac.length];
+                       System.arraycopy(returnData, 0, returnData2, 0, returnData2.length);
+
+                       return returnData2;
+               } catch (SocketTimeoutException e) {
+
+               } catch (BadPaddingException e) {
+
+               } catch (IllegalBlockSizeException e) {
+
+               } catch (UnknownHostException e) {
+
+               } catch (IOException e) {
+
+               }
+
+               return null;
+       }
+
+       private void localServerWorkerFunction() {
+
+               ServerSocket inputSocket = null;
+
+               try {
+                       // Local server socket
+                       inputSocket = new ServerSocket(listeningPort);
+                       inputSocket.setReuseAddress(true);
+                       inputSocket.setSoTimeout(TIMEOUT_MILLIS);
+               } catch (Exception e) {
+                       e.printStackTrace();
+                       throw new Error("Local server setup failure...");
+               }
+
+               while (!doEnd) {
+
+                       try {
+                               // Accept incoming socket
+                               Socket socket = inputSocket.accept();
+
+                               DataInputStream input = new DataInputStream(socket.getInputStream());
+                               DataOutputStream output = new DataOutputStream(socket.getOutputStream());
+
+                               // Get the encrypted data from the server
+                               int dataSize = input.readInt();
+                               byte[] readData = new byte[dataSize];
+                               input.readFully(readData);
+
+                               timer.endTime();
+
+                               // Decrypt the data
+                               readData = decryptCipher.doFinal(readData);
+
+                               mac.update(readData, 0, readData.length - HMAC_SIZE);
+                               byte[] genmac = mac.doFinal();
+                               byte[] recmac = new byte[HMAC_SIZE];
+                               System.arraycopy(readData, readData.length - recmac.length, recmac, 0, recmac.length);
+
+                               if (!Arrays.equals(recmac, genmac))
+                                       throw new Error("Local Error: Invalid HMAC!  Potential Attack!");
+
+                               byte[] returnData = new byte[readData.length - recmac.length];
+                               System.arraycopy(readData, 0, returnData, 0, returnData.length);
+
+                               // Process the data
+                               // byte[] sendData = table.acceptDataFromLocal(readData);
+                               byte[] sendData = table.acceptDataFromLocal(returnData);
+
+                               mac.update(sendData);
+                               byte[] realmac = mac.doFinal();
+                               byte[] totalData = new byte[sendData.length + realmac.length];
+                               System.arraycopy(sendData, 0, totalData, 0, sendData.length);
+                               System.arraycopy(realmac, 0, totalData, sendData.length, realmac.length);
+
+                               // Encrypt the data for sending
+                               byte[] encryptedData = encryptCipher.doFinal(totalData);
+
+
+                               timer.startTime();
+                               // Send data to output (length of data, the data)
+                               output.writeInt(encryptedData.length);
+                               output.write(encryptedData, 0, encryptedData.length);
+                               output.flush();
+
+                               // close the socket
+                               socket.close();
+                       } catch (SocketTimeoutException e) {
+
+                       } catch (BadPaddingException e) {
+
+                       } catch (IllegalBlockSizeException e) {
+
+                       } catch (UnknownHostException e) {
+
+                       } catch (IOException e) {
+
+                       }
+               }
+
+               if (inputSocket != null) {
+                       try {
+                               inputSocket.close();
+                       } catch (Exception e) {
+                               e.printStackTrace();
+                               throw new Error("Local server close failure...");
+                       }
+               }
+       }
+
+       public void close() {
+               doEnd = true;
+
+               if (localServerThread != null) {
+                       try {
+                               localServerThread.join();
+                       } catch (Exception e) {
+                               e.printStackTrace();
+                               throw new Error("Local Server thread join issue...");
+                       }
+               }
+
+               // System.out.println("Done Closing Cloud Comm");
+       }
+
+       protected void finalize() throws Throwable {
+               try {
+                       close();        // close open files
+               } finally {
+                       super.finalize();
+               }
+       }
+
+}