Back to project page Android-NetPowerctrl-Shared.
The source code is released under:
Copyright (c) 2014, David Gr?ff All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: *...
If you think the Android project Android-NetPowerctrl-Shared listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package oly.netpowerctrl.device_base.device; //from ww w .ja va 2 s. c o m import android.support.annotation.NonNull; import android.util.JsonReader; import android.util.JsonWriter; import android.util.Log; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.InetAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.UUID; import java.util.concurrent.Semaphore; import oly.netpowerctrl.device_base.data.JSONHelper; import oly.netpowerctrl.device_base.data.StorableInterface; import oly.netpowerctrl.device_base.executables.ExecutableReachability; // An object of this class contains all the info about a specific device public class Device implements Comparable<Device>, StorableInterface { public static final int CHANGE_DEVICE = 1; public static final int CHANGE_DEVICE_REACHABILITY = 2; public static final int CHANGE_CONNECTION_REACHABILITY = 4; public static final int UPDATED_NOT_SINCE_LOADING = -1; private long updated = UPDATED_NOT_SINCE_LOADING; private static final boolean debugLock = false; // Connections to the destination device. This is prioritized, the first reachable connection // is preferred before the second reachable etc. private final List<DeviceConnection> mDeviceConnections = new ArrayList<>(); // Device Ports private final Map<Integer, DevicePort> mDevicePorts = new TreeMap<>(); // Identity of the device public String pluginID; List<String> lockMethods = new ArrayList<>(); private Semaphore mLockDevicePorts = new Semaphore(1); private Semaphore mLockDevice = new Semaphore(1); // User visible data of this device private String deviceName = ""; // Name of the device as reported by the device private String version = ""; // version of the device firmware // Access to the device private String userName = ""; private String password = ""; // Additional features private List<DeviceFeatureInterface> Features = new ArrayList<>(); private DeviceConnection cached_deviceConnection; // Temporary state variables private boolean configured = false; // a device unique id e.g. the mac address associated with a device private String UniqueDeviceID = UUID.randomUUID().toString(); private boolean enabled = true; private int changesFlag = 0; private Object pluginInterface = null; // Invalid Device @SuppressWarnings("unused") public Device() { } public Device(boolean lockFreeDevice) { if (lockFreeDevice) overwriteLockSemaphore(); } public Device(String pluginID, boolean lockFreeDevice) { this.pluginID = pluginID; if (lockFreeDevice) overwriteLockSemaphore(); } private void overwriteLockSemaphore() { mLockDevice = new Semaphore(0) { @Override public void release() { } @Override public void acquireUninterruptibly() { } @Override public int availablePermits() { return 0; } }; mLockDevicePorts = new Semaphore(0) { @Override public void release() { } @Override public void acquireUninterruptibly() { } @Override public int availablePermits() { return 0; } }; } public void addFeatures(DeviceFeatureInterface... features) { checkLock(); if (Features.size() != features.length) { changesFlag |= CHANGE_DEVICE; updated = System.currentTimeMillis(); } Features.clear(); Features.addAll(Arrays.asList(features)); } public String getPassword() { return password; } public void setPassword(String password) { checkLock(); if (!this.password.equals(password)) { changesFlag |= CHANGE_DEVICE; updated = System.currentTimeMillis(); } this.password = password; } public String getUserName() { return userName; } public void setUserName(String userName) { checkLock(); if (!this.userName.equals(userName)) { changesFlag |= CHANGE_DEVICE; updated = System.currentTimeMillis(); } this.userName = userName; } public String getVersion() { return version; } public void setVersion(String version) { checkLock(); if (!this.version.equals(version)) { changesFlag |= CHANGE_DEVICE; updated = System.currentTimeMillis(); } this.version = version; } public boolean isConfigured() { return configured; } public void setConfigured(boolean configured) { this.configured = configured; } /** * Getting the state of the changed flag will automatically * reset the flag! * * @return Return the changed flag. */ public int getAndClearChangedFlag() { int changesFlagL = changesFlag; changesFlag = 0; // Reset flag return changesFlagL; } public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; compute_first_reachable(); } public String getUniqueDeviceID() { return UniqueDeviceID; } /** * Set the unique device id. This may be a mac address or internal device id. * You may not set this to null! * * @param uniqueDeviceID The unique id */ public void setUniqueDeviceID(String uniqueDeviceID) { checkLock(); if (uniqueDeviceID == null) throw new RuntimeException("Device::setUniqueDeviceID should not be null. Use makeTemporaryDevice instead."); UniqueDeviceID = uniqueDeviceID; } private void checkLock() { if (mLockDevice.availablePermits() > 0) throw new RuntimeException("Device not locked!"); } private void checkDevicePortLock() { if (mLockDevicePorts.availablePermits() > 0) throw new RuntimeException("Device:DevicePorts not locked!"); } /** * This will erase all DevicePorts, all DeviceConnections and the unique ID. * You have to assign DeviceConnections manually after this call to use this device object! * <p/> * To make this device useful for storage, you have to at least call {@link #setUniqueDeviceID} * and assign device Ports. */ public void makeTemporaryDevice() { checkLock(); UniqueDeviceID = null; mDeviceConnections.clear(); mDevicePorts.clear(); } public void compute_first_reachable() { if (!enabled) { if (cached_deviceConnection != null) changesFlag |= CHANGE_DEVICE_REACHABILITY; cached_deviceConnection = null; return; } for (DeviceConnection deviceConnection : mDeviceConnections) if (deviceConnection.reachableState() != ExecutableReachability.NotReachable) { if (cached_deviceConnection == null) changesFlag |= CHANGE_DEVICE_REACHABILITY; cached_deviceConnection = deviceConnection; return; } } public DeviceConnection getFirstReachableConnection() { return cached_deviceConnection; } public DeviceConnection getFirstReachableConnection(String protocol) { mLockDevice.acquireUninterruptibly(); for (DeviceConnection deviceConnection : mDeviceConnections) if (deviceConnection.reachableState() != ExecutableReachability.NotReachable && deviceConnection.getProtocol().equals(protocol)) { mLockDevice.release(); return deviceConnection; } mLockDevice.release(); return null; } /** * Set all device connections to not reachable, which match the given protocol. * * @param protocol A protocol like UDP or HTTP * @param not_reachable_reason The not reachable reason. Set to null to make the connections * @param clearReachability Clear the used counter and make reachableState() return false therefore. */ public void setStatusMessage(String protocol, String not_reachable_reason, boolean clearReachability) { for (DeviceConnection di : mDeviceConnections) if (di.getProtocol().equals(protocol)) { di.setStatusMessage(not_reachable_reason, clearReachability); break; } if (clearReachability) { compute_first_reachable(); } } public void connectionUsed(DeviceConnection deviceConnection) { deviceConnection.incReceivedPackets(); deviceConnection.clearStatusMessage(); compute_first_reachable(); updated = System.currentTimeMillis(); } public void setStatusMessage(DeviceConnection deviceConnection, String not_reachable_reason, boolean clearReachability) { deviceConnection.setStatusMessage(not_reachable_reason, clearReachability); if (clearReachability && deviceConnection == cached_deviceConnection) { compute_first_reachable(); } } public void setStatusMessageAllConnections(@NonNull String not_reachable_reason) { checkLock(); for (DeviceConnection di : mDeviceConnections) di.setStatusMessage(not_reachable_reason, true); if (cached_deviceConnection != null) changesFlag |= CHANGE_DEVICE_REACHABILITY; cached_deviceConnection = null; } public void clearStatusMessageAllConnections() { for (DeviceConnection di : mDeviceConnections) di.clearStatusMessage(); compute_first_reachable(); } @Override public int compareTo(@NonNull Device device) { if (device.UniqueDeviceID.equals(UniqueDeviceID)) return 0; return 1; } /** * Every device belongs to a plugin. This method returns the corresponding plugin. * * @return The plugin this device belongs to. This is of type PluginInterface, cast it * to your desired plugin before use. */ public Object getPluginInterface() { return pluginInterface; } public void setPluginInterface(Object pluginInterface) { this.pluginInterface = pluginInterface; } /** * Copy values from another device. * * @param other Another DeviceInfo object from where to copy data from. */ public void copyValuesFromUpdated(@NonNull Device other) { if (other.pluginInterface == null && pluginInterface == null) throw new RuntimeException("Device::copyValuesFromUpdated: pluginInterface not set!"); if (pluginInterface == null) { // Update plugin object reference pluginInterface = other.pluginInterface; } updated = other.updated; if (copyFeatures(other) || !version.equals(other.version)) { changesFlag |= CHANGE_DEVICE; } lockDevicePorts(); makeAllDevicePortsInvalid(); for (Map.Entry<Integer, DevicePort> integerDevicePortEntry : other.mDevicePorts.entrySet()) { updatePort(integerDevicePortEntry.getValue()); } removeInvalidDevicePorts(); releaseDevicePorts(); version = other.version; } /** * This will update reachable information of all device connections, which * are also used in the updated device. * * @param other_deviceConnections Updated device connections */ public void replaceAutomaticAssignedConnections(@NonNull List<DeviceConnection> other_deviceConnections) { if (other_deviceConnections == mDeviceConnections) throw new RuntimeException("replaceAutomaticAssignedConnections with same object!"); // Remove non used assigned-by-device connections int hashOfRemoved = 0; Iterator<DeviceConnection> it = mDeviceConnections.iterator(); while (it.hasNext()) { DeviceConnection deviceConnection = it.next(); if (deviceConnection.isAssignedByDevice()) { hashOfRemoved += deviceConnection.computeHash(); it.remove(); } } // add connections int hashOfAdded = 0; for (DeviceConnection deviceConnection : other_deviceConnections) { mDeviceConnections.add(deviceConnection); hashOfAdded += deviceConnection.computeHash(); } compute_first_reachable(); if (hashOfAdded != hashOfRemoved) { changesFlag |= CHANGE_CONNECTION_REACHABILITY; } } private boolean copyFeatures(@NonNull Device other) { checkLock(); if (other.Features.size() > Features.size()) this.Features = other.Features; return false; } /** * Add or update a DevicePort of this device. This will set the changes flag if the given device port * changes this device object. * * @param devicePort_values If the port with that id did not exist, that object will be added * to this device otherwise this is only used for the id and the values. */ public void updatePort(@NonNull DevicePort devicePort_values) { checkDevicePortLock(); DevicePort current_devicePort = mDevicePorts.get(devicePort_values.id); if (current_devicePort == null) { // new port mDevicePorts.put(devicePort_values.id, devicePort_values); changesFlag |= CHANGE_DEVICE; } else { if (current_devicePort.copyValues(devicePort_values)) changesFlag |= CHANGE_DEVICE; } updated = System.currentTimeMillis(); } public void makeAllDevicePortsInvalid() { checkDevicePortLock(); for (Map.Entry<Integer, DevicePort> integerDevicePortEntry : mDevicePorts.entrySet()) { DevicePort devicePort = integerDevicePortEntry.getValue(); devicePort.setValid(false); } } public void removeInvalidDevicePorts() { checkDevicePortLock(); Iterator<Map.Entry<Integer, DevicePort>> it = mDevicePorts.entrySet().iterator(); while (it.hasNext()) { DevicePort devicePort = it.next().getValue(); if (!devicePort.isValid()) { it.remove(); changesFlag |= CHANGE_DEVICE; } } } /** * Return true if this and the other DeviceInfo are the same configured DeviceInfo. * * @param other Compare to other DeviceInfo * @return Return true if unique id is equal. */ @SuppressWarnings("unused") public boolean equalsByUniqueID(@NonNull Device other) { return UniqueDeviceID != null && UniqueDeviceID.equals(other.UniqueDeviceID); } @Override public boolean equals(Object other) { return other instanceof Device && UniqueDeviceID != null && UniqueDeviceID.equals(((Device) other).UniqueDeviceID); } /** * Return the json representation of this scene * * @return JSON String */ @Override public String toString() { try { JSONHelper h = new JSONHelper(); toJSON(h.createWriter()); return h.getString(); } catch (IOException ignored) { return null; } } private void toJSON(@NonNull JsonWriter writer) throws IOException { lockDevice(); try { writer.beginObject(); writer.name("DeviceName").value(deviceName); writer.name("Type").value(pluginID); writer.name("UniqueDeviceID").value(UniqueDeviceID); writer.name("UserName").value(userName); writer.name("Password").value(password); writer.name("version").value(version); writer.name("Enabled").value(enabled); writer.name("Features").beginArray(); for (DeviceFeatureInterface deviceFeature : Features) { deviceFeature.toJSON(writer); } writer.endArray(); writer.name("Connections").beginArray(); // Be sure not to run into a ConcurrentModificationException because of a simultaneous refresh List<DeviceConnection> copy = new ArrayList<>(mDeviceConnections); for (DeviceConnection deviceConnection : copy) { deviceConnection.toJSON(writer); } writer.endArray(); } finally { releaseDevice(); } writer.name("DevicePorts").beginArray(); lockDevicePorts(); // Be sure not to run into a ConcurrentModificationException because of a simultaneous refresh Collection<DevicePort> devicePorts = new ArrayList<>(mDevicePorts.values()); releaseDevicePorts(); for (DevicePort entry : devicePorts) { entry.toJSON(writer, false); } writer.endArray(); writer.endObject(); writer.close(); } public long getUpdatedTime() { return updated; } public DevicePort getFirst() { Iterator<Map.Entry<Integer, DevicePort>> it = mDevicePorts.entrySet().iterator(); if (!it.hasNext()) return null; return it.next().getValue(); } public void lockDevicePorts() { mLockDevicePorts.acquireUninterruptibly(); } public void releaseDevicePorts() { mLockDevicePorts.release(); } public void lockDevice() { if (debugLock) { StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace(); String methodName = stacktrace[3].getClassName() + ":" + stacktrace[3].getMethodName() + "->" + stacktrace[4].getClassName() + ":" + stacktrace[4].getMethodName(); lockMethods.add(methodName); int s = mLockDevice.availablePermits(); if (s == 0) { Log.w("lockDevice", "Hold locks from " + (deviceName.length() > 0 ? deviceName : String.valueOf(hashCode()))); for (String string : lockMethods) Log.w("lockDevice", "\t\tlock: " + string); } } mLockDevice.acquireUninterruptibly(); } public void releaseDevice() { if (debugLock) { StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace(); String methodName = stacktrace[3].getClassName() + ":" + stacktrace[3].getMethodName() + "->" + stacktrace[4].getClassName() + ":" + stacktrace[4].getMethodName(); lockMethods.remove(methodName); Log.w("releaseDevice", (deviceName.length() > 0 ? deviceName : String.valueOf(hashCode())) + ". Method: " + methodName); } mLockDevice.release(); } public Iterator<DevicePort> getDevicePortIterator() { checkDevicePortLock(); return mDevicePorts.values().iterator(); } /** * Remove DevicePort by ID * * @param id The index/id of the device port. */ public void remove(int id) { checkDevicePortLock(); mDevicePorts.remove(id); } public String getFeatureString() { String f = ""; for (DeviceFeatureInterface feature : Features) f += feature.getString() + " "; return f; } public ExecutableReachability reachableState() { if (!enabled || pluginInterface == null || cached_deviceConnection == null) return ExecutableReachability.NotReachable; return cached_deviceConnection.reachableState(); } public void setReachable(int index) { connectionUsed(mDeviceConnections.get(index)); } public void removeConnection(int index) { checkLock(); mDeviceConnections.remove(index); compute_first_reachable(); } public void addConnection(@NonNull DeviceConnection newConnection) { checkLock(); if (newConnection instanceof DeviceConnectionUDP) { mDeviceConnections.add(0, newConnection); } else { mDeviceConnections.add(newConnection); } } public InetAddress[] getHostnameIPs(boolean lookupDNSName) { List<InetAddress> addresses = new ArrayList<>(); lockDevice(); for (DeviceConnection connection : mDeviceConnections) { Collections.addAll(addresses, connection.getHostnameIPs(lookupDNSName)); } releaseDevice(); InetAddress[] a = new InetAddress[addresses.size()]; addresses.toArray(a); return a; } // This has to be executed in another thread not the gui thread if lookupDNSName is set. public boolean hasAddress(InetAddress[] hostnameIPs, boolean lookupDNSName) { if (hostnameIPs == null || hostnameIPs.length == 0) return false; lockDevice(); for (DeviceConnection connection : mDeviceConnections) { if (connection.hasAddress(hostnameIPs, lookupDNSName)) { releaseDevice(); return true; } } releaseDevice(); return false; } @Override public String getStorableName() { return UniqueDeviceID; } /** * For plugins only: Add/Update your device port here. * * @param devicePort The new/updated device port. */ public void putPort(DevicePort devicePort) { mDevicePorts.put(devicePort.id, devicePort); } public void load(@NonNull JsonReader reader) throws IOException, ClassNotFoundException { lockDevice(); try { // locked device reader.beginObject(); updated = System.currentTimeMillis(); configured = true; while (reader.hasNext()) { String name = reader.nextName(); assert name != null; switch (name) { case "DeviceName": deviceName = reader.nextString(); break; case "UniqueDeviceID": UniqueDeviceID = reader.nextString(); break; case "UserName": userName = reader.nextString(); break; case "Password": password = reader.nextString(); break; case "version": version = reader.nextString(); break; case "Enabled": enabled = reader.nextBoolean(); break; case "Type": pluginID = reader.nextString(); break; case "Features": Features.clear(); reader.beginArray(); while (reader.hasNext()) { DeviceFeatureInterface feature = DeviceFeatureFabric.fromJSON(reader); if (feature != null) Features.add(feature); } reader.endArray(); break; case "Connections": mDeviceConnections.clear(); reader.beginArray(); while (reader.hasNext()) { DeviceConnection connection = DeviceConnectionFabric.fromJSON(reader, this); if (connection != null) { mDeviceConnections.add(connection); } } reader.endArray(); break; case "DevicePorts": mDevicePorts.clear(); reader.beginArray(); while (reader.hasNext()) { try { DevicePort devicePort = DevicePort.fromJSON(reader, this); mDevicePorts.put(devicePort.id, devicePort); } catch (ClassNotFoundException e) { reader.skipValue(); } } reader.endArray(); break; default: reader.skipValue(); break; } } reader.endObject(); compute_first_reachable(); changesFlag = 0; } finally { releaseDevice(); } if (pluginID.isEmpty()) throw new ClassNotFoundException(); } @Override public void load(@NonNull InputStream input) throws IOException, ClassNotFoundException { load(new JsonReader(new InputStreamReader(input))); } @Override public void save(@NonNull OutputStream output) throws IOException { toJSON(JSONHelper.createWriter(output)); } public DeviceConnection getConnectionByID(Integer id) { checkLock(); if (id == null || id < 0 || id >= mDeviceConnections.size()) return null; return mDeviceConnections.get(id); } public DevicePort getDevicePortByUid(String devicePort_unique_id) { checkDevicePortLock(); for (Map.Entry<Integer, DevicePort> integerDevicePortEntry : mDevicePorts.entrySet()) { DevicePort devicePort = integerDevicePortEntry.getValue(); if (devicePort.getUid().equals(devicePort_unique_id)) return devicePort; } return null; } public void setChangesFlag(int flag) { changesFlag = flag; } public void updateConnection(int connection_id, DeviceConnection deviceConnection) { checkLock(); int hashBefore = mDeviceConnections.get(connection_id).computeHash(); mDeviceConnections.set(connection_id, deviceConnection); if (hashBefore != deviceConnection.computeHash()) { changesFlag |= CHANGE_CONNECTION_REACHABILITY; if (deviceConnection.reachableState() != ExecutableReachability.NotReachable) compute_first_reachable(); } } public String getDeviceName() { return deviceName; } public void setDeviceName(String deviceName) { checkLock(); if (!this.deviceName.equals(deviceName)) { changesFlag |= CHANGE_DEVICE; updated = System.currentTimeMillis(); } this.deviceName = deviceName; } public List<DeviceConnection> getDeviceConnections() { checkLock(); return mDeviceConnections; } public boolean hasNoConnections() { return mDeviceConnections.size() == 0; } public int countDevicePorts() { return mDevicePorts.size(); } }