Network Event Sender Code in Java for Android

Do you have questions about writing plugins or scripts in Python? Meet the coders here.
Post Reply
lspatricio
Posts: 5
Joined: Sat Jul 04, 2015 3:42 am

Network Event Sender Code in Java for Android

Post by lspatricio » Sat Jul 04, 2015 4:15 am

Hi all. I'm new to eventghost and at this forum.

I'm sorry if this has been posted before. But I tried searching and nothing came up.

I'd like do an Anrdoid program of sending events to evenghost on my PC. Network Event Sender works perfectly for me on my laptop communicating to my desktop PC via eventghost. Has anybody got a code for the Network Event Sender in Java for Android?

Basically I want to write an Android App for my old Android 2.2 cellphone which is just gathering dust inside my drawer. I tried searching for applications on the store (including Evenghost for android) but they don't function the way I want it. My old Froyo phone has a broken touchscreen and has a built-in hardware QWERTY keyboard, but still working. and most, if not all, apps on the playstore requires newer Android builds and a working touchscreen. Few developers, if not none at all, develops for Android 2.2 anymore. So, I decided to learn to program an app for my Android cp. I'm still new with android programming, but I already know the basics of Activity and catching hardware keypresses, etc. The only thing missing for me right now is how to send an event to evenghost on my PC for a specific hardware key on my phone.

So, is there someome who has an implementation of the Network Event Sender plugin in Java for Android? It would be nice if it's a Public Method in a Public Class so I can just run a method of something like Network_Event_Sender_Class.Send_Event(string EventName, string Payload, string IPAddress, integer PortNumber, string Password) or something like that.

Thanks in advance.

liquid8
Posts: 35
Joined: Thu Feb 19, 2009 4:20 am

Re: Network Event Sender Code in Java for Android

Post by liquid8 » Mon Jul 06, 2015 12:22 am

I figured I would post this for all to see since I haven't been able to work on the EventGhost for Android app in some time. I would love to get the app updated to current standards, but I may just put the source somewhere and hopefully someone else can get something more functional.

Here's the basics for sending an event to EventGhost:

Code: Select all

private static int Send(String server, String port, String password, String event, String payload) {
    if (event == null) {
        return STATUS_NO_EVENT;
    }
    // if settings, hosts, or specified current host is not available,
    // return nothing
    Socket telnetSocket = null;
    DataOutputStream os = null;
    DataInputStream is = null;
    try {
        telnetSocket = new Socket(server, Integer.valueOf(port));
        telnetSocket.setSoTimeout(5000);
        os = new DataOutputStream(telnetSocket.getOutputStream());
        is = new DataInputStream(telnetSocket.getInputStream());
    } catch (UnknownHostException unknown) {
        unknown.printStackTrace();
        return STATUS_ERROR_UNKNOWNHOST;
    } catch (ConnectException ce) {
        return STATUS_ERROR_CONNECTION_REFUSED;
    } catch (SocketException se) {
        return STATUS_ERROR_TIMED_OUT;
    } catch (Exception e) {
        e.printStackTrace();
        return STATUS_ERROR_COMMUNICATION;
    }
    if (telnetSocket != null && os != null && is != null) {
        try {
            // send request to EG
            os.writeBytes("quintessence\n\r");
            String responseLine = null;
            // get cookie response from EG
            responseLine = new String(is.readLine());
            String cookie = responseLine + ":" + password;
            String md5 = MD5Hex.MD5Hex(cookie);
            // send cookie authentication back to EG
            os.writeBytes(md5 + "\n");
            // get response from EG
            String accept = new String(is.readLine());
            if (accept.equals("accept")) {
                // Send all payloads (split with |)
                if (payload != null && payload.length() > 0) {
                    String[] list = payload.split("\\|");
                    for (int i = 0; i < list.length; i++) {
                        String p = list[i];
                        os.writeBytes("payload " + p + "\n");
                    }
                }
                os.writeBytes(event + "\n");
                Log.i(TAG, "Sent Event: " + event + " with payload: " + payload);
            }
            // clean up
            os.writeBytes("close\n");
            os.close();
            is.close();
            telnetSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
            return STATUS_ERROR_COMMUNICATION;
        }
        return STATUS_OK;
    } else {
        return STATUS_ERROR_COMMUNICATION;
    }
}
Basically:

1. Open a socket with the correct port
2. Open an input and output stream
3. write bytes 'quintessence' to EG host
4. listen for response, creating an md5 of response:serverpass
5. send the md5 bytes back
6. if you can an 'accept' back, you can send your payload

As for receiving events, I created a mini-EG service which responds just like the server does when you send an event.

If you are interested for more on that or have any other questions, let me know.

lspatricio
Posts: 5
Joined: Sat Jul 04, 2015 3:42 am

Re: Network Event Sender Code in Java for Android

Post by lspatricio » Mon Jul 06, 2015 2:42 am

liquid8 wrote:I figured I would post this for all to see since I haven't been able to work on the EventGhost for Android app in some time. I would love to get the app updated to current standards, but I may just put the source somewhere and hopefully someone else can get something more functional.


Basically:

1. Open a socket with the correct port
2. Open an input and output stream
3. write bytes 'quintessence' to EG host
4. listen for response, creating an md5 of response:serverpass
5. send the md5 bytes back
6. if you can an 'accept' back, you can send your payload

As for receiving events, I created a mini-EG service which responds just like the server does when you send an event.

If you are interested for more on that or have any other questions, let me know.
Thank you very much for sharing your code! I really appreciate it.

yes, I tried your Android Eventghost but I can't create a new layout because my touchscreen is broken. I would like to use the hardware keyboard and dpad to send events to PC Eventghost. I needed this code. Can't wait to go home from work and try this out!

Many thanks. :-)

lspatricio
Posts: 5
Joined: Sat Jul 04, 2015 3:42 am

Re: Network Event Sender Code in Java for Android

Post by lspatricio » Tue Jul 07, 2015 3:57 am

liquid8 wrote:I figured I would post this for all to see since I haven't been able to work on the EventGhost for Android app in some time. I would love to get the app updated to current standards, but I may just put the source somewhere and hopefully someone else can get something more functional.

Here's the basics for sending an event to EventGhost:

Basically:

1. Open a socket with the correct port
2. Open an input and output stream
3. write bytes 'quintessence' to EG host
4. listen for response, creating an md5 of response:serverpass
5. send the md5 bytes back
6. if you can an 'accept' back, you can send your payload

As for receiving events, I created a mini-EG service which responds just like the server does when you send an event.

If you are interested for more on that or have any other questions, let me know.
Got it working! After an hour of googling around for MD5, I was able to get it to work. Thank you very much for your help! It has given my old cp a new life as a PC remote control and wifi keyboard! :-)

Yes, please, if you could share your mini-EG service which responds just like the server, I would appreciate it very much. Thanks!

liquid8
Posts: 35
Joined: Thu Feb 19, 2009 4:20 am

Re: Network Event Sender Code in Java for Android

Post by liquid8 » Tue Jul 07, 2015 4:30 am

Some of this might not be the best code or as well commented as it should be :) Also keep in mind this was written for older Android versions (when I was still learning things) so it could probably be cleaned up quite a bit. I would rip some of this out to simplify it, but it might be helpful if someone wants to create a full fledged service.

The server is a service which listens on (I believe the first) available network connection it can. It listens with a MultiServer Thread which spawns of ClientConnection threads when a new connection is initiated. The run() part of the ClientConnection thread is where it responds (i.e. a Network Event Receiver) to connections.

1. On connection initiated, listen for 'quintessence\r\n'
2. Create a 'cookie' and send that to the client
3. Wait for response from client - compare it to the token md5('cookie:serverpass') and see if what they send matches
4. If it's valid, send 'accept' and wait for the event and payload

Code: Select all

package com.timhoeck.android.eventghost;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Enumeration;
import java.util.Random;

import com.timhoeck.android.providers.eventghost.DBProvider;
import com.timhoeck.android.eventghost.EventGhostIntent.OutgoingEvent;
import com.timhoeck.android.framework.SecurityHelper;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

public class EventGhostService extends Service {
	private static final String TAG = "EventGhost";
	private static final int NOTIFICATION_SERVER_STATUS = 1;
	
	// Broadcasts that we act upon
	private static final String BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED";
	public static final String WIFI_STATE_CHANGED = "android.net.wifi.WIFI_STATE_CHANGED";

	// Wifi States
	public static final int WIFI_STATE_DISABLED = 1;
	public static final int WIFI_STATE_ENABLED = 3;
	public static final int WIFI_STATE_UNKNOWN = 4;

	private static final boolean USE_EMULATOR_DEV_ADDRESS = false;
	
	private final IBinder mBinder = new LocalBinder();
	private InetAddress serverInetAddress;
	MultiServerThread server;
	ServerSocket serverSocket;
	
	String serverPassword = "EG4Android!";
	int serverPort = 1024;
	
	Context mContext;
	
	public static boolean startServer(Context context) {
		Log.i(TAG, "Starting Server");
		Intent start = new Intent();
		start.setClass(context, EventGhostService.class);
		if (context.startService(start) == null) {
			//service was started
			return false;
		} else {
			//service was already running
			return true;
		}
	}
	public static void stopServer(Context context) {
		Log.i(TAG, "Stopping Server");
		Intent stop = new Intent();
		stop.setClass(context, EventGhostService.class);
		context.stopService(stop);
		context.getSharedPreferences(PreferencesActivity.PREFS_SETTINGS, 0).edit().putString(PreferencesActivity.KEY_SERVER_DETAILS, PreferencesActivity.SERVER_DETAILS_DISABLED).commit();
		dismissNotification(context);
	}
	
	@Override
	public void onStart(Intent intent, int startId) {
		mContext = this.getBaseContext();
		super.onStart(intent, startId);
		
		SharedPreferences settings = mContext.getSharedPreferences(PreferencesActivity.PREFS_SETTINGS, 0);
		if (settings.getBoolean(PreferencesActivity.KEY_ENABLE_SERVER, PreferencesActivity.KEY_ENABLE_SERVER_DEFAULT)) {
			try {
				serverPort = Integer.valueOf(settings.getString(PreferencesActivity.KEY_SERVER_PORT, PreferencesActivity.KEY_SERVER_PORT_DEFAULT));
			} catch (Exception e){
				serverPort = 1024;
			}
			
			String pw = settings.getString(PreferencesActivity.KEY_SERVER_PASSWORD, null);
			if (pw != null) serverPassword = SecurityHelper.decrypt(pw); else serverPassword = PreferencesActivity.KEY_SERVER_PASSWORD_DEFAULT;
			//start our server
			serverInetAddress = getServerAddress();
			try { 
				if (serverInetAddress != null) {
					//If we have an availabled address, create a server socket
					serverSocket = new ServerSocket(serverPort);
					//serverSocket = new ServerSocket(serverPort, 10, serverInetAddress);
					server = new MultiServerThread();
					server.start();
					
					//Find all addresses (IPv4/IPv6) associated with this interface
					NetworkInterface n = NetworkInterface.getByInetAddress(serverInetAddress);
					Enumeration<InetAddress> ips = n.getInetAddresses();
					String ipString = "";
					while (ips.hasMoreElements()) {
						InetAddress i = ips.nextElement();
						ipString += i.getHostAddress();
						if (ips.hasMoreElements()) ipString += " / ";
					}
					Log.i(TAG, "Attempting to Bind on " + serverPort + " at address: " + ipString);
					
					String details = "Listening on " + ipString  + ", port " + serverPort;
					mContext.getSharedPreferences(PreferencesActivity.PREFS_SETTINGS, 0).edit().putString(PreferencesActivity.KEY_SERVER_DETAILS, details).commit();
					serverNotification(mContext, details);
				} else {
					Log.e(TAG, "Could not start server, no available InetAddress");
					mContext.getSharedPreferences(PreferencesActivity.PREFS_SETTINGS, 0).edit().putString(PreferencesActivity.KEY_SERVER_DETAILS, PreferencesActivity.SERVER_DETAILS_DISABLED).commit();
				}
			} catch (Exception e) {
				e.printStackTrace();
				Log.w(TAG, "Error starting server (already running?)");
				mContext.getSharedPreferences(PreferencesActivity.PREFS_SETTINGS, 0).edit().putString(PreferencesActivity.KEY_SERVER_DETAILS, PreferencesActivity.SERVER_DETAILS_DISABLED).commit();
			}
		}
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
		Log.i(TAG, "EventGhost Service destroyed");
		if (serverSocket != null) {
			try {
				mContext.getSharedPreferences(PreferencesActivity.PREFS_SETTINGS, 0).edit().putString(PreferencesActivity.KEY_SERVER_DETAILS, PreferencesActivity.SERVER_DETAILS_DISABLED).commit();
				serverSocket.close();
			} catch (IOException e) {
				Log.w(TAG, "Unable to close server socket");
			}
		}
	}

	@Override
	public void onCreate() {
		super.onCreate();
	}

	
	@Override
	public IBinder onBind(Intent intent) {
		return mBinder;
	}

	public class LocalBinder extends Binder {
		EventGhostService getService() {
			return EventGhostService.this;
		}
	}

	
	/** 
	 * Searches network interfaces for available inet address
	 * @return If using the Emulator, it returns the internal ip, otherwise it looks for
	 * the first interface that is not the loopback
	 */
	public static InetAddress getServerAddress() {
		try {
			Enumeration<NetworkInterface> ni = NetworkInterface
			.getNetworkInterfaces();
			if (USE_EMULATOR_DEV_ADDRESS) {
				return InetAddress.getByName("10.0.2.15");
			} else {
				// Find the appropriate network interface to bind to
				while (ni.hasMoreElements()) {
					NetworkInterface n = ni.nextElement();
					Log.i(TAG, "Found network: " + n.getDisplayName());
					Log.d(TAG, "	Details: " + n.toString());
					// we aren't interested in binding to localhost
					if (!n.getDisplayName().equals("lo")) {
						Enumeration<InetAddress> in = n.getInetAddresses();
						while (in.hasMoreElements()) {
							InetAddress i = in.nextElement();
							Log.i(TAG, "	IP: " + i.getHostAddress());
							
							return i;
						}
					} else {
						//Log.d(TAG, "Ignoring network: " + n.getDisplayName());
					}
				}
			}
		} catch (Exception e) {}
		return null;
	}
 
	public static class ServiceStarter extends BroadcastReceiver {

		@Override
		public void onReceive(Context context, Intent intent) {
			SharedPreferences settings = context.getSharedPreferences(PreferencesActivity.PREFS_SETTINGS, 0);
			if (settings.getBoolean(PreferencesActivity.KEY_ENABLE_SERVER, PreferencesActivity.KEY_ENABLE_SERVER_DEFAULT)) {
				if (intent.getAction().equals(BOOT_COMPLETED)) {
					startServer(context);
				} else if (intent.getAction().equals(WIFI_STATE_CHANGED)) {
					stopServer(context);
					Log.i(TAG, "Received Wifi State Change, waiting for new network");
					serverNotification(context, "Disabled, waiting for network connection");
					int state = intent.getIntExtra("wifi_state",
							WIFI_STATE_UNKNOWN);
					if (state == WIFI_STATE_ENABLED) {
						new ConnectivityWait().execute(context, true);
					} else if (state == WIFI_STATE_DISABLED) {
						//Log.i(TAG, "Received Wifi State Disabled, waiting for new network");
						new ConnectivityWait().execute(context, false);
					}
				}
			}

		}

	}

	private static void dismissNotification(Context context) {
		NotificationManager mgr = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
		mgr.cancel(NOTIFICATION_SERVER_STATUS);
	}
	private static void serverNotification(Context context, String text) {
		NotificationManager mgr = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
		Notification notification = new Notification();
		notification.flags = Notification.FLAG_ONGOING_EVENT;
		notification.icon = R.drawable.icon_24;
		Intent eg = new Intent();
		eg.setClass(context, PreferencesActivity.class);
		PendingIntent pending = PendingIntent.getActivity(context, 0, eg, PendingIntent.FLAG_UPDATE_CURRENT);
		notification.setLatestEventInfo(context, "EventGhost Server", text, pending);
		mgr.notify(NOTIFICATION_SERVER_STATUS, notification);
	}

	// Since WIFI_STATE_CHANGED doesn't confirm that the new network is active,
	// we wait for it.
	private static class ConnectivityWait extends
			AsyncTask<Object, Integer, Object> {
		@Override
		protected Object doInBackground(Object... info) {
			Context context = (Context) info[0];
			boolean state = (Boolean) info[1];
			return waitForConnectivity(context, state);
		}

		protected void onPostExecute(Object... result) {
			// WHY ARE YOU NOT WORKING?!
			Log.i(TAG, "ConnectivityWait finished");
		}
	}

	private static boolean waitForConnectivity(Context context, boolean wifiState) {
		try {
			ConnectivityManager connMgr = (ConnectivityManager) context
			.getSystemService(Context.CONNECTIVITY_SERVICE);
			NetworkInfo info = null;
			String currentNetwork = null;
			int sleepTime = 0;
			do {
					Thread.sleep(3000);
					sleepTime += 3000;
					info = connMgr.getActiveNetworkInfo();
					currentNetwork = info.getTypeName();
					if (wifiState && currentNetwork.equals("WIFI")) break;
					if (!wifiState && currentNetwork.equals("MOBILE")) break;
					//Log.i(TAG, "Current Network: " + currentNetwork + ", Time: " + sleepTime / 1000 + "secs");
			} while (sleepTime < 120000);
			if (sleepTime < 120000) {
				//we found a network, attempt to restart
				Log.i(TAG, "Restarting Service due to Wifi change");
				startServer(context);
			} else {
				serverNotification(context, "Disabled, timed out waiting for network");
				context.getSharedPreferences(PreferencesActivity.PREFS_SETTINGS, 0).edit().putBoolean(PreferencesActivity.KEY_ENABLE_SERVER, false).commit();
			}
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	public static void restartServer(Context context) {
		// restart server
		stopServer(context);
		startServer(context);
	}

	/***
	 * The MultiServerThread waits for incoming connections.
	 * When one arrives, it spawns a ClientConnection thread to handle that connection.
	 * @author thoeck
	 *
	 */
	public class MultiServerThread extends Thread {

		public void run() {
			boolean listening = true;
			try {
				Log.i(TAG, "Listening on " + serverInetAddress.getHostAddress()
						+ ":" + serverPort);
				
				while (listening) {
					new ClientConnection(serverSocket.accept(), serverPassword)
							.start();
				}
			} catch (Exception e) {
				e.printStackTrace();
				Log.w(TAG, "MultiServerThread closed");
				try {
					serverSocket.close();
				} catch (Exception e2) {
					Log.w(TAG, "Unable to close server socket");
				}
			}
		}

	}

	/***
	 * A new ClientConnection class is created each time a connection is initiated
 	 * by a client to the server:port.
	 * @author thoeck
	 *
	 */
	public class ClientConnection extends Thread {
		private Socket client;
		private String serverPassword;

		public ClientConnection(Socket client, String serverPassword) {
			this.client = client;
			this.serverPassword = serverPassword;
		}

		public void run() {
			try {
				client.setKeepAlive(true);
				client.setSoTimeout(5000);
				DataInputStream in = new DataInputStream(client
						.getInputStream());
				DataOutputStream out = null;
				byte[] ping = new byte[14];
				in.read(ping);
				String str = new String(ping);

				// Get Incoming Request from EventGhost
				//Clients use the string 'quintessence' to initiate authentication with the server
				if (str.equals("quintessence\n\r")) {
					
					//EventGhost Servers send a generated authentication 'cookie' to the client
					Random gen = new Random();
					String cookie = String.valueOf(gen.nextInt(65536));
					String token = cookie + ":" + serverPassword;
					String expected = MD5Hex.MD5Hex(token);
					out = new DataOutputStream(client.getOutputStream());
					out.writeBytes(cookie + "\n");
					
					//To authenticate, the EventGhost client will send a response to the cookie,
					//which contains the cookie, plus the password to validate the password
					String passResponse = new String(in.readLine());
					String serverhost = client.getInetAddress().getHostAddress();
					if (passResponse.equals(expected)) {
						//password is valid
						out.writeBytes("accept\n");
						
						//wait for the event/payload
						String line = new String(in.readLine());
						//Log.d(TAG, "Got line: " + line);
						String event;
						String payload;
						
						//if there is a payload, it's received first
						if (line.startsWith("payload ")) {
							payload = line.replace("payload ", "");
							event = new String(in.readLine());
						} else {
							payload = null;
							event = line;
						}
						//Log.i(TAG, "Received Event: " + event + ", sending GENERATE_EVENT broadcast");
						//TODO FromApp shouldn't be server host
						OutgoingEvent sendEvent = new OutgoingEvent()
							.setEvent(event)
							.setPayload(payload)
							.setFromApp(serverhost);

						EventGhostIntent.generateEvent(mContext, sendEvent); 
					} else {
						//password is invalid
						Log.w(TAG, "Cookie incorrect for connection");
						LogsActivity.addLogEntry(mContext, DBProvider.LogValues.TYPE_EVENT_INCOMING, "N/A", "N/A", serverhost, "Invalid Password from Server", false);
						//Log.w(TAG, "Expected:" + expected);
					}
				} else {
					//If we didn't get 'quintessence', the connection was not initiated properly by the client
					//Log.w(TAG, "Invalid authentication method");
					//Log.w(TAG, "Expected: quintessence");
				}
				
				//notify the client we are closing the connection
				if (out != null) {
					out.writeBytes("close\n");
					out.close();
				}
				//close the server connection
				in.close();
			} catch (Exception e) {
				//something didn't go right
				e.printStackTrace();
				Log.w(TAG, "Error communicating with client");
			} finally {
				//close out the client connection
				try {
					client.close();
				} catch (Exception e2) {
					Log.w(TAG, "Unable to close server socket");
				}
			}
		}
	}

}

lspatricio
Posts: 5
Joined: Sat Jul 04, 2015 3:42 am

Re: Network Event Sender Code in Java for Android

Post by lspatricio » Tue Jul 07, 2015 5:14 am

liquid8 wrote:Some of this might not be the best code or as well commented as it should be :) Also keep in mind this was written for older Android versions (when I was still learning things) so it could probably be cleaned up quite a bit. I would rip some of this out to simplify it, but it might be helpful if someone wants to create a full fledged service.

The server is a service which listens on (I believe the first) available network connection it can. It listens with a MultiServer Thread which spawns of ClientConnection threads when a new connection is initiated. The run() part of the ClientConnection thread is where it responds (i.e. a Network Event Receiver) to connections.

1. On connection initiated, listen for 'quintessence\r\n'
2. Create a 'cookie' and send that to the client
3. Wait for response from client - compare it to the token md5('cookie:serverpass') and see if what they send matches
4. If it's valid, send 'accept' and wait for the event and payload
Thanks for the fast reply! :-)

Post Reply