/*******************************************************************************
* Copyright (c) 2011 Bug Labs, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - Neither the name of Bug Labs, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/
package com.buglabs.bug.networking;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.LinkedList;
import java.util.Vector;
import org.freedesktop.DBus;
import org.freedesktop.dbus.DBusConnection;
import org.freedesktop.dbus.DBusSigHandler;
import org.freedesktop.dbus.Variant;
import org.freedesktop.dbus.exceptions.DBusException;
import net.connman.Manager;
import net.connman.Struct1;
import net.connman.Service;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;
import com.buglabs.bug.networking.pub.IAccessPoint;
import com.buglabs.bug.networking.pub.IAccessPointSecurity;
import com.buglabs.bug.networking.pub.IAccessPointsEvent;
import com.buglabs.bug.networking.pub.IAccessPointsListener;
import com.buglabs.bug.networking.pub.IActivationEvent;
import com.buglabs.bug.networking.pub.IActivationListener;
import com.buglabs.bug.networking.pub.IConnectionState;
import com.buglabs.bug.networking.pub.IIPv4Settings;
import com.buglabs.bug.networking.pub.IIPv4SettingsMethod;
import com.buglabs.bug.networking.pub.INetworking;
import com.buglabs.bug.networking.pub.INetworkingDevice;
import com.buglabs.bug.networking.pub.ISettingsEvent;
import com.buglabs.bug.networking.pub.ISettingsListener;
import com.buglabs.bug.networking.pub.IStateEvent;
import com.buglabs.bug.networking.pub.IStateListener;
public class KitchenSink implements INetworking {
private static KitchenSink instance;
private DBusConnection bus;
private Manager manager;
private BundleContext context;
private List<Service> allServices = new LinkedList<Service>();
List<IAccessPoint> accessPoints = new LinkedList<IAccessPoint>();
private INetworkingDevice ethernetDevice;
private INetworkingDevice wifiDevice;
private ManagerObserver managerObserver;
private ServiceObserver serviceObserver;
private List<IStateListener> stateListeners;
private List<IActivationListener> activationListeners;
private List<ISettingsListener> settingsListeners;
private String SECURITY_STRING_NONE = "none";
private String SECURITY_STRING_WEP= "wep";
private String SECURITY_STRING_WPA = "wpa";
private class ManagerObserver implements DBusSigHandler<Manager.PropertyChanged> {
private ServiceTracker activationListenerTracker;
private boolean ethernetEnabled;
private boolean wifiEnabled;
private ServiceTracker accessPointsListenerTracker;
private String ENABLED_TECHNOLOGIES = "EnabledTechnologies";
private String ENABLED_TECHNOLOGIES_WIFI = "wifi";
private String ENABLED_TECHNOLOGIES_ETHERNET = "ethernet";
private String SERVICES = "Services";
public ManagerObserver() {
ethernetEnabled = getEthernetDevice().isEnabled();
wifiEnabled = getWifiDevice().isEnabled();
activationListenerTracker = new ServiceTracker(context, IActivationListener.class.getName(), null);
activationListenerTracker.open();
accessPointsListenerTracker = new ServiceTracker(context, IAccessPointsListener.class.getName(), null);
accessPointsListenerTracker.open();
}
public void handle(final Manager.PropertyChanged pc) {
if (pc.a.compareTo(ENABLED_TECHNOLOGIES) == 0) {
Activator.logDebug("enabled technologies changed");
boolean isEthernetEnabled = false;
boolean isWifiEnabled = false;
for (String enabledTechnology : (Vector<String>) pc.b.getValue()) {
if (enabledTechnology.compareTo(ENABLED_TECHNOLOGIES_ETHERNET) == 0) {
isEthernetEnabled = true;
} else if (enabledTechnology.compareTo(ENABLED_TECHNOLOGIES_WIFI) == 0) {
isWifiEnabled = true;
}
}
if (ethernetEnabled != isEthernetEnabled) {
IActivationEvent activationEvent = new ActivationEvent(DeviceType.Ethernet(), isEthernetEnabled);
Activator.logDebug("looking for ethernet activation listeners");
Object als [] = activationListenerTracker.getServices();
if (als != null) {
for (Object al : als) {
Activator.logDebug("found an ethernet activation listener");
((IActivationListener) al).activationChanged(activationEvent);
}
}
ethernetEnabled = isEthernetEnabled;
}
if (wifiEnabled != isWifiEnabled) {
IActivationEvent activationEvent = new ActivationEvent(DeviceType.Wifi(), isWifiEnabled);
Object als [] = activationListenerTracker.getServices();
if (als != null) {
for (Object al : als) {
((IActivationListener) al).activationChanged(activationEvent);
}
}
wifiEnabled = isWifiEnabled;
}
}
}
}
private class ServiceObserver implements DBusSigHandler<Service.PropertyChanged> {
private ServiceTracker stateListenerTracker;
private ServiceTracker settingsListenerTracker;
private final String PROPERTY_STATE = "State";
private final String PROPERTY_IPV4= "IPv4";
private final String PROPERTY_NAMESERVERS = "Nameservers";
private final String PROPERTY_SEARCHDOMAINS = "Domains";
private final String STATE_ASSOCIATION = "association";
private final String STATE_CONFIGURATION = "configuration";
private final String STATE_READY = "ready";
private final String STATE_ONLINE = "online";
private final String STATE_DISCONNECT = "disconnect";
private final String STATE_FAILURE = "failure";
private final String STATE_IDLE = "idle";
private final HashMap<String, ConnectionState.State> stringToState = new HashMap<String, ConnectionState.State>() {{
put(STATE_ASSOCIATION, ConnectionState.State.ASSOCIATION);
put(STATE_CONFIGURATION, ConnectionState.State.CONFIGURATION);
put(STATE_READY, ConnectionState.State.READY);
put(STATE_ONLINE, ConnectionState.State.ONLINE);
put(STATE_DISCONNECT, ConnectionState.State.DISCONNECT);
put(STATE_FAILURE, ConnectionState.State.FAILURE);
put(STATE_IDLE, ConnectionState.State.IDLE);
}};
public ServiceObserver() {
settingsListenerTracker = new ServiceTracker(context, ISettingsListener.class.getName(), null);
settingsListenerTracker.open();
stateListenerTracker = new ServiceTracker(context, IStateListener.class.getName(), null);
stateListenerTracker.open();
}
public void handle(final Service.PropertyChanged pc) {
Activator.logDebug("Service property changed:[" + pc.getPath() + "] " + pc.a + ":" + pc.b.getValue());
String serviceNamePathParts[] = pc.getPath().split("/");
String serviceName = serviceNamePathParts[serviceNamePathParts.length - 1];
if (pc.a.compareTo(PROPERTY_STATE) == 0) {
String stateName = (String) pc.b.getValue();
DeviceType stateDeviceType = null;
if (serviceName.startsWith("wifi")) {
stateDeviceType = DeviceType.Wifi();
} else if (serviceName.startsWith("ethernet")) {
stateDeviceType = DeviceType.Ethernet();
}
if (stateDeviceType != null) {
IConnectionState state = new ConnectionState(stringToState.get(stateName));
if (state != null) {
IStateEvent stateEvent = new StateEvent(state, stateDeviceType);
Object sls[] = stateListenerTracker.getServices();
if (sls != null) {
for (Object sl : sls) {
((IStateListener) sl).stateChanged(stateEvent);
}
}
}
}
} else if (pc.a.compareTo(PROPERTY_IPV4) == 0 ||
pc.a.compareTo(PROPERTY_NAMESERVERS) == 0 ||
pc.a.compareTo(PROPERTY_SEARCHDOMAINS) == 0) {
DeviceType deviceType = null;
if (serviceName.startsWith("wifi")) {
deviceType = DeviceType.Wifi();
} else if (serviceName.startsWith("cable")) {
deviceType = DeviceType.Ethernet();
}
if (deviceType != null) {
ISettingsEvent settingsEvent = new SettingsEvent(deviceType);
Object sls[] = settingsListenerTracker.getServices();
if (sls != null) {
for (Object sl : settingsListenerTracker.getServices()) {
((ISettingsListener) sl).settingsChanged(settingsEvent);
}
}
}
}
}
}
private KitchenSink(DBusConnection bus, BundleContext context) {
this.bus = bus;
this.context = context;
stateListeners = new LinkedList<IStateListener>();
activationListeners = new LinkedList<IActivationListener>();
settingsListeners = new LinkedList<ISettingsListener>();
try {
manager = bus.getRemoteObject("net.connman", "/", Manager.class);
} catch (Exception e) {
// TODO: Handle this exception.
Activator.logError("trouble creating manager");
}
managerObserver = new ManagerObserver();
serviceObserver = new ServiceObserver();
}
public static void createInstance(DBusConnection bus, BundleContext context) {
if (instance == null) {
instance = new KitchenSink(bus, context);
}
}
public static KitchenSink getInstance() {
return instance;
}
public void scanAccessPoints() {
manager.RequestScan("wifi");
}
private void resetAccessPoints() {
synchronized(accessPoints) {
accessPoints = new LinkedList<IAccessPoint>();
}
}
public List<IAccessPoint> getAccessPoints() {
synchronized(accessPoints) {
if (accessPoints.isEmpty()) {
for (Service service : getServices()) {
try {
String type = (String) service.GetProperties().get("Type").getValue();
if (type != null && (type.compareTo("wifi") == 0)) {
service.GetProperties();
Activator.logDebug("adding service: " + service);
accessPoints.add(new AccessPoint((Service) service));
}
} catch (Exception e) {
// TODO: Add better exception handling here.
Activator.logDebug("service no longer exists: " + service);
}
}
}
}
return accessPoints;
}
public void connectToAccessPoint(String ssid, IAccessPointSecurity security, String passphrase) {
/*
* From the Connman DBus documentation
* (http://moblin.org/documentation/moblin-sdk/coding-tutorials/developers-introduction-connman)
* here's what we need to establish a connection:
*
* {
* 'Type':'wifi',
* 'Mode':'managed',
* 'SSID':'ssid',
* 'Security':'WEP',
* 'Passphrase':'secret'
*}
*/
Activator.logDebug("Connecting to hidden AP ...");
HashMap<String, Variant> serviceDict = new HashMap<String, Variant>();
serviceDict.put("Type", new Variant<String>("wifi"));
serviceDict.put("Mode", new Variant<String>("managed"));
serviceDict.put("SSID", new Variant<String>(ssid));
serviceDict.put("Security", new Variant<String>(securityStringFromSecurity(security)));
serviceDict.put("Passphrase", new Variant<String>(passphrase));
Activator.logDebug("Connecting to AP '" + ssid +"' with security '" + securityStringFromSecurity(security) + "' and passphrase '" + passphrase + "'");
manager.ConnectService(serviceDict);
}
public INetworkingDevice getEthernetDevice() {
if (ethernetDevice == null) {
ethernetDevice = new NetworkDevice(manager, Technology.ETHERNET_TECHNOLOGY);
}
return ethernetDevice;
}
public INetworkingDevice getWifiDevice() {
if (wifiDevice == null) {
wifiDevice = new NetworkDevice(manager, Technology.WIFI_TECHNOLOGY);
}
return wifiDevice;
}
public IIPv4Settings getEthernetIPv4Settings() {
return getConnectedServiceSettings(Technology.ETHERNET_TECHNOLOGY, "eth0");
}
public IIPv4Settings getWifiIPv4Settings() {
return getConnectedServiceSettings(Technology.WIFI_TECHNOLOGY, "wlan0");
}
public void setEthernetIPv4Settings(IIPv4Settings settings) {
applyIPv4Settings(settings, Technology.ETHERNET_TECHNOLOGY);
}
public void setWifiIPv4Settings(IIPv4Settings settings) {
applyIPv4Settings(settings, Technology.WIFI_TECHNOLOGY);
}
public IAccessPointSecurity accessPointSecurityFactory() {
return new AccessPointSecurity();
}
public IIPv4SettingsMethod IPv4SettingsMethodFactory() {
return new IPv4SettingsMethod();
}
public void setup() {
addObservers();
}
public void teardown() {
removeObservers();
}
public void addStateListener(IStateListener l) {
stateListeners.add(l);
}
public void removeStateListener(IStateListener l) {
stateListeners.remove(l);
}
public void addActivationListener(IActivationListener l) {
activationListeners.add(l);
}
public void removeActivationListener(IActivationListener l) {
activationListeners.remove(l);
}
public void addSettingsListener(ISettingsListener l) {
settingsListeners.add(l);
}
public void removeSettingsListener(ISettingsListener l) {
settingsListeners.remove(l);
}
private void addObservers() {
addManagerObserver();
addServiceObserver();
}
private void removeObservers() {
removeManagerObserver();
removeServiceObserver();
}
private void addManagerObserver() {
try {
bus.addSigHandler(Manager.PropertyChanged.class, managerObserver);
} catch (DBusException e) {
// TODO Throw a better exception.
}
}
private void addServiceObserver() {
try {
bus.addSigHandler(Service.PropertyChanged.class, serviceObserver);
} catch (DBusException e) {
// TODO Throw a better exception.
}
}
private void removeManagerObserver() {
try {
Activator.logDebug("removing manager observer");
bus.removeSigHandler(Manager.PropertyChanged.class, managerObserver);
Activator.logDebug("removed manager observer");
} catch (DBusException e) {
Activator.logError("exception removing manager observer");
// TODO Throw a better exception.
}
}
private void removeServiceObserver() {
try {
Activator.logDebug("removing service observer");
bus.removeSigHandler(Service.PropertyChanged.class, serviceObserver);
Activator.logDebug("removed service observer");
} catch (DBusException e) {
Activator.logError("exception removing service observer");
// TODO Throw a better exception.
}
}
private IIPv4Settings getConnectedServiceSettings(Technology technology, String interfaceName) {
Activator.logDebug("getting connected service settings for " + interfaceName);
Service connectedService = getConnectedService(technology, interfaceName);
IIPv4Settings connectedServiceSettings;
if (connectedService != null) {
connectedServiceSettings = new IPv4Settings(connectedService);
} else {
connectedServiceSettings = new IPv4UnconnectedSettings();
}
return connectedServiceSettings;
}
private Service getConnectedService(Technology technology, String interfaceName) {
Service connectedService = null;
Activator.logDebug("Trying to get connected interface.");
for (Service service : getServices()) {
try {
String serviceTechnology = (String) service.GetProperties().get("Type").getValue();
String serviceState = (String) service.GetProperties().get("State").getValue();
Map m = (Map) service.GetProperties().get("Ethernet").getValue();
Variant v = (Variant) m.get("Interface");
String serviceInterfaceName = (String) v.getValue();
Activator.logDebug("Interface: " + serviceInterfaceName + ", State: " + serviceState);
if (serviceTechnology.compareTo(technology.typeString()) == 0 &&
serviceInterfaceName.compareTo(interfaceName) == 0 &&
(serviceState.compareTo("ready") == 0|| serviceState.compareTo("online") == 0)
) {
connectedService = service;
break;
}
} catch (Exception e) {
// TODO: enventually we need to take care of all of these GetProperties exceptions in one place.
}
}
return connectedService;
}
private void resetServices() {
synchronized (allServices) {
allServices = new LinkedList<Service>();
}
}
private void resetServices(Vector servicePaths) {
synchronized (allServices) {
allServices = new LinkedList<Service>();
for (Object s : servicePaths) {
allServices.add(getServiceByPath(s.toString()));
}
}
}
private List<Service> getServices() {
Variant servicePaths = manager.GetProperties().get("Services");
synchronized(allServices) {
if (allServices.isEmpty()) {
for (Object path : (Vector) servicePaths.getValue()) {
try {
Service service = (Service) bus.getRemoteObject("net.connman", path.toString() , Service.class);
// Do this is case GetProperties doesn't exist on this service.
service.GetProperties();
allServices.add(service);
} catch (Exception e) {
// TODO: Add better exception handling here.
Activator.logError("Problem examining " + path + " for inclusion in wifi services");
}
}
}
}
return new LinkedList<Service>(allServices);
}
private Service getServiceByPath(String path) {
Service service = null;
try {
service = (Service) bus.getRemoteObject("net.connman", path, Service.class);
} catch (Exception e) {
// TODO: Add better exception handling here.
Activator.logError("Problem getting service " + path);
}
return service;
}
private String securityStringFromSecurity(IAccessPointSecurity security) {
String securityString = "";
if (security.isNone()) {
securityString = SECURITY_STRING_NONE;
} else if (security.isWEP()) {
securityString = SECURITY_STRING_WEP;
} else if (security.isWPA()) {
securityString = SECURITY_STRING_WPA;
} else {
// TODO: We should probably throw an exception here.
}
return securityString;
}
private void applyIPv4Settings(IIPv4Settings settings, Technology technology) {
if (technology == Technology.ETHERNET_TECHNOLOGY) {
applyIPv4Settings(settings, technology, "eth0");
} else if (technology == Technology.WIFI_TECHNOLOGY) {
applyIPv4Settings(settings, technology, "wlan0");
}
}
private void applyIPv4Settings(IIPv4Settings settings, Technology technology, String interfaceName) {
Service service = getConnectedService(technology, interfaceName);
IPv4Settings.applySettings(settings, service);
}
}