package net.gotev.hostmonitor; import android.app.IntentService; import android.content.Context; import android.content.Intent; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.PowerManager; import java.net.Socket; /** * Service which performs reachability checks of the configured hosts and ports. * @author gotev (Aleksandar Gotev) */ public class HostMonitor extends IntentService { private static final String LOG_TAG = HostMonitor.class.getSimpleName(); private static final String ACTION_CHECK = "net.gotev.hostmonitor.check"; private static final String PARAM_CONNECTION_TYPE = "net.gotev.hostmonitor.connection_type"; /** * Name of the parameter passed in the broadcast intent. */ public static final String PARAM_STATUS = "HostStatus"; public HostMonitor() { super(LOG_TAG); } /** * Returns the {@link Intent} to start the service reachability check. * @param context application context * @return intent used to launch the service */ static Intent getCheckIntent(Context context) { Intent intent = new Intent(context, HostMonitor.class); intent.setAction(ACTION_CHECK); return intent; } /** * Starts the host monitor check * @param context application context */ static void start(Context context) { context.startService(getCheckIntent(context)); } /** * Starts the host monitor check. * @param context application context * @param connectionType current connection type */ static void start(Context context, ConnectionType connectionType) { Intent intent = new Intent(context, HostMonitor.class); intent.setAction(ACTION_CHECK); intent.putExtra(PARAM_CONNECTION_TYPE, connectionType.ordinal()); context.startService(intent); } /** * Stops the host monitor check. * @param context application context */ public static void stop(Context context) { context.stopService(new Intent(context, HostMonitor.class)); } @Override protected void onHandleIntent(Intent intent) { if (intent == null || !ACTION_CHECK.equals(intent.getAction())) return; PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE); PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getClass().getSimpleName()); wakeLock.acquire(); HostMonitorConfig config = new HostMonitorConfig(this); if (config.getHostsMap().isEmpty()) { Logger.debug(LOG_TAG, "No hosts to check at this moment"); } else { ConnectionType connectionType = getConnectionType(intent); if (connectionType == ConnectionType.NONE) { notifyThatAllTheHostsAreUnreachable(connectionType, config); } else { checkReachability(connectionType, config); } } wakeLock.release(); } private void notifyThatAllTheHostsAreUnreachable(ConnectionType connectionType, HostMonitorConfig config) { Logger.debug(LOG_TAG, "No active connection. Notifying that all the hosts are unreachable"); for (Host host : config.getHostsMap().keySet()) { Status previousStatus = config.getHostsMap().get(host); Status newStatus = new Status(false, connectionType); if (!newStatus.equals(previousStatus)) { Logger.debug(LOG_TAG, "Host " + host.getHost() + " is currently unreachable on port " + host.getPort()); config.getHostsMap().put(host, newStatus); notifyStatus(config.getBroadcastAction(), host, previousStatus, newStatus); } } config.saveHostsMap(); } private void checkReachability(ConnectionType connectionType, HostMonitorConfig config) { Logger.debug(LOG_TAG, "Starting reachability check"); for (Host host : config.getHostsMap().keySet()) { Status previousStatus = config.getHostsMap().get(host); boolean currentReachable = isReachable(host, config.getSocketTimeout(), config.getMaxAttempts()); Status newStatus = new Status(currentReachable, connectionType); if (!newStatus.equals(previousStatus)) { Logger.debug(LOG_TAG, "Host " + host.getHost() + " is currently " + (currentReachable ? "reachable" : "unreachable") + " on port " + host.getPort() + " via " + connectionType); config.getHostsMap().put(host, newStatus); notifyStatus(config.getBroadcastAction(), host, previousStatus, newStatus); } } config.saveHostsMap(); Logger.debug(LOG_TAG, "Reachability check finished!"); } private ConnectionType getConnectionType(Intent intent) { int connTypeInt = intent.getIntExtra(PARAM_CONNECTION_TYPE, -1); ConnectionType type; if (connTypeInt < 0) { type = getCurrentConnectionType(this); } else { type = ConnectionType.values()[connTypeInt]; } return type; } static ConnectionType getCurrentConnectionType(Context context) { ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); if (networkInfo == null || !networkInfo.isConnected()) { return ConnectionType.NONE; } int type = networkInfo.getType(); if (type == ConnectivityManager.TYPE_MOBILE) return ConnectionType.MOBILE; if (type == ConnectivityManager.TYPE_WIFI) return ConnectionType.WIFI; Logger.error(LOG_TAG, "Unsupported connection type: " + type + ". Returning NONE"); return ConnectionType.NONE; } private boolean isReachable(Host host, int connectTimeout, int maxAttempts) { int attempts = 0; boolean reachable = false; while (attempts < maxAttempts) { reachable = isReachable(host, connectTimeout); if (reachable) break; attempts++; } return reachable; } private boolean isReachable(Host host, int connectTimeout) { boolean reachable; Socket socket = null; try { socket = new Socket(); socket.connect(host.resolve(), connectTimeout); reachable = true; } catch (Exception exc) { reachable = false; } finally { if (socket != null) { try { socket.close(); } catch (Exception exc) { Logger.debug(LOG_TAG, "Error while closing socket."); } } } return reachable; } private void notifyStatus(String broadcastAction, Host host, Status previousStatus, Status currentStatus) { HostStatus status = new HostStatus() .setHost(host.getHost()) .setPort(host.getPort()) .setPreviousReachable(previousStatus.isReachable()) .setPreviousConnectionType(previousStatus.getConnectionType()) .setReachable(currentStatus.isReachable()) .setConnectionType(currentStatus.getConnectionType()); Logger.debug(LOG_TAG, "Broadcast with action: " + broadcastAction + " and status: " + status); Intent broadcastStatus = new Intent(broadcastAction); broadcastStatus.putExtra(PARAM_STATUS, status); sendBroadcast(broadcastStatus); } }