aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/ConfigProvider.java
diff options
context:
space:
mode:
authorAshlee Young <ashlee@onosfw.com>2015-09-09 22:15:21 -0700
committerAshlee Young <ashlee@onosfw.com>2015-09-09 22:15:21 -0700
commit13d05bc8458758ee39cb829098241e89616717ee (patch)
tree22a4d1ce65f15952f07a3df5af4b462b4697cb3a /framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/ConfigProvider.java
parent6139282e1e93c2322076de4b91b1c85d0bc4a8b3 (diff)
ONOS checkin based on commit tag e796610b1f721d02f9b0e213cf6f7790c10ecd60
Change-Id: Ife8810491034fe7becdba75dda20de4267bd15cd
Diffstat (limited to 'framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/ConfigProvider.java')
-rw-r--r--framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/ConfigProvider.java610
1 files changed, 610 insertions, 0 deletions
diff --git a/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/ConfigProvider.java b/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/ConfigProvider.java
new file mode 100644
index 00000000..dbd80cca
--- /dev/null
+++ b/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/ConfigProvider.java
@@ -0,0 +1,610 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.rest.resources;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+import org.onlab.packet.ChassisId;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onlab.util.Frequency;
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.ChannelSpacing;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.GridType;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
+import org.onosproject.net.Link;
+import org.onosproject.net.MastershipRole;
+import org.onosproject.net.OchPort;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.OduCltPort;
+import org.onosproject.net.OduSignalType;
+import org.onosproject.net.OmsPort;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.SparseAnnotations;
+import org.onosproject.net.device.DefaultDeviceDescription;
+import org.onosproject.net.device.DefaultPortDescription;
+import org.onosproject.net.device.DeviceDescription;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceProvider;
+import org.onosproject.net.device.DeviceProviderRegistry;
+import org.onosproject.net.device.DeviceProviderService;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.device.OchPortDescription;
+import org.onosproject.net.device.OduCltPortDescription;
+import org.onosproject.net.device.OmsPortDescription;
+import org.onosproject.net.device.PortDescription;
+import org.onosproject.net.host.DefaultHostDescription;
+import org.onosproject.net.host.HostProvider;
+import org.onosproject.net.host.HostProviderRegistry;
+import org.onosproject.net.host.HostProviderService;
+import org.onosproject.net.link.DefaultLinkDescription;
+import org.onosproject.net.link.LinkProvider;
+import org.onosproject.net.link.LinkProviderRegistry;
+import org.onosproject.net.link.LinkProviderService;
+import org.onosproject.net.provider.ProviderId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.DeviceId.deviceId;
+import static org.onosproject.net.PortNumber.portNumber;
+import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
+import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED;
+
+/**
+ * Provider of devices and links parsed from a JSON configuration structure.
+ */
+class ConfigProvider implements DeviceProvider, LinkProvider, HostProvider {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private static final ProviderId PID =
+ new ProviderId("cfg", "org.onosproject.rest", true);
+
+ private static final String UNKNOWN = "unknown";
+
+ private static final Frequency CENTER = Frequency.ofTHz(193.1);
+ // C-band has 4.4 THz (4,400 GHz) total bandwidth
+ private static final Frequency TOTAL = Frequency.ofTHz(4.4);
+
+ private CountDownLatch deviceLatch;
+
+ private final JsonNode cfg;
+ private final DeviceService deviceService;
+
+ private final DeviceProviderRegistry deviceProviderRegistry;
+ private final LinkProviderRegistry linkProviderRegistry;
+ private final HostProviderRegistry hostProviderRegistry;
+
+ private DeviceProviderService deviceProviderService;
+ private LinkProviderService linkProviderService;
+ private HostProviderService hostProviderService;
+
+ private DeviceListener deviceEventCounter = new DeviceEventCounter();
+ private List<ConnectPoint> connectPoints = Lists.newArrayList();
+ private Map<ConnectPoint, PortDescription> descriptions = Maps.newHashMap();
+
+ /**
+ * Creates a new configuration provider.
+ *
+ * @param cfg JSON configuration
+ * @param deviceService device service
+ * @param deviceProviderRegistry device provider registry
+ * @param linkProviderRegistry link provider registry
+ * @param hostProviderRegistry host provider registry
+ */
+ ConfigProvider(JsonNode cfg,
+ DeviceService deviceService,
+ DeviceProviderRegistry deviceProviderRegistry,
+ LinkProviderRegistry linkProviderRegistry,
+ HostProviderRegistry hostProviderRegistry) {
+ this.cfg = checkNotNull(cfg, "Configuration cannot be null");
+ this.deviceService = checkNotNull(deviceService, "Device service cannot be null");
+ this.deviceProviderRegistry = checkNotNull(deviceProviderRegistry, "Device provider registry cannot be null");
+ this.linkProviderRegistry = checkNotNull(linkProviderRegistry, "Link provider registry cannot be null");
+ this.hostProviderRegistry = checkNotNull(hostProviderRegistry, "Host provider registry cannot be null");
+ }
+
+ /**
+ * Parses the given JSON and provides links as configured.
+ */
+ void parse() {
+ try {
+ register();
+ parseDevices();
+ parseLinks();
+ parseHosts();
+ addMissingPorts();
+ } finally {
+ unregister();
+ }
+ }
+
+ private void register() {
+ deviceProviderService = deviceProviderRegistry.register(this);
+ linkProviderService = linkProviderRegistry.register(this);
+ hostProviderService = hostProviderRegistry.register(this);
+ }
+
+ private void unregister() {
+ deviceProviderRegistry.unregister(this);
+ linkProviderRegistry.unregister(this);
+ hostProviderRegistry.unregister(this);
+ }
+
+ // Parses the given JSON and provides devices.
+ private void parseDevices() {
+ try {
+ JsonNode nodes = cfg.get("devices");
+ if (nodes != null) {
+ prepareForDeviceEvents(nodes.size());
+ for (JsonNode node : nodes) {
+ parseDevice(node);
+
+ // FIXME: hack to make sure device attributes take
+ // This will be fixed when GossipDeviceStore uses ECM
+ parseDevice(node);
+ }
+ }
+ } finally {
+ waitForDeviceEvents();
+ }
+ }
+
+ // Parses the given node with device data and supplies the device.
+ private void parseDevice(JsonNode node) {
+ URI uri = URI.create(get(node, "uri"));
+ Device.Type type = Device.Type.valueOf(get(node, "type", "SWITCH"));
+ String mfr = get(node, "mfr", UNKNOWN);
+ String hw = get(node, "hw", UNKNOWN);
+ String sw = get(node, "sw", UNKNOWN);
+ String serial = get(node, "serial", UNKNOWN);
+ ChassisId cid = new ChassisId(get(node, "mac", "000000000000"));
+ SparseAnnotations annotations = annotations(node.get("annotations"));
+
+ DeviceDescription desc =
+ new DefaultDeviceDescription(uri, type, mfr, hw, sw, serial,
+ cid, annotations);
+ DeviceId deviceId = deviceId(uri);
+ deviceProviderService.deviceConnected(deviceId, desc);
+
+ JsonNode ports = node.get("ports");
+ if (ports != null) {
+ parsePorts(deviceId, ports);
+ }
+ }
+
+ // Parses the given node with list of device ports.
+ private void parsePorts(DeviceId deviceId, JsonNode nodes) {
+ List<PortDescription> ports = new ArrayList<>();
+ for (JsonNode node : nodes) {
+ ports.add(parsePort(deviceId, node));
+ }
+ deviceProviderService.updatePorts(deviceId, ports);
+ }
+
+ // Parses the given node with port information.
+ private PortDescription parsePort(DeviceId deviceId, JsonNode node) {
+ Port.Type type = Port.Type.valueOf(node.path("type").asText("COPPER"));
+ // TL1-based ports have a name
+ PortNumber port = null;
+ if (node.has("name")) {
+ for (Port p : deviceService.getPorts(deviceId)) {
+ if (p.number().name().equals(node.get("name").asText())) {
+ port = p.number();
+ break;
+ }
+ }
+ } else {
+ port = portNumber(node.path("port").asLong(0));
+ }
+
+ if (port == null) {
+ log.error("Cannot find port given in node {}", node);
+ return null;
+ }
+
+ String portName = Strings.emptyToNull(port.name());
+ SparseAnnotations annotations = null;
+ if (portName != null) {
+ annotations = DefaultAnnotations.builder()
+ .set(AnnotationKeys.PORT_NAME, portName).build();
+ }
+ switch (type) {
+ case COPPER:
+ return new DefaultPortDescription(port, node.path("enabled").asBoolean(true),
+ type, node.path("speed").asLong(1_000),
+ annotations);
+ case FIBER:
+ // Currently, assume OMS when FIBER. Provide sane defaults.
+ annotations = annotations(node.get("annotations"));
+ return new OmsPortDescription(port, node.path("enabled").asBoolean(true),
+ CENTER, CENTER.add(TOTAL),
+ Frequency.ofGHz(100), annotations);
+ case ODUCLT:
+ annotations = annotations(node.get("annotations"));
+ OduCltPort oduCltPort = (OduCltPort) deviceService.getPort(deviceId, port);
+ return new OduCltPortDescription(port, node.path("enabled").asBoolean(true),
+ oduCltPort.signalType(), annotations);
+ case OCH:
+ annotations = annotations(node.get("annotations"));
+ OchPort ochPort = (OchPort) deviceService.getPort(deviceId, port);
+ return new OchPortDescription(port, node.path("enabled").asBoolean(true),
+ ochPort.signalType(), ochPort.isTunable(),
+ ochPort.lambda(), annotations);
+ case OMS:
+ annotations = annotations(node.get("annotations"));
+ OmsPort omsPort = (OmsPort) deviceService.getPort(deviceId, port);
+ return new OmsPortDescription(port, node.path("enabled").asBoolean(true),
+ omsPort.minFrequency(), omsPort.maxFrequency(), omsPort.grid(), annotations);
+ default:
+ log.warn("{}: Unsupported Port Type");
+ }
+ return new DefaultPortDescription(port, node.path("enabled").asBoolean(true),
+ type, node.path("speed").asLong(1_000),
+ annotations);
+ }
+
+ // Parses the given JSON and provides links as configured.
+ private void parseLinks() {
+ JsonNode nodes = cfg.get("links");
+ if (nodes != null) {
+ for (JsonNode node : nodes) {
+ parseLink(node, false);
+ if (!node.has("halfplex")) {
+ parseLink(node, true);
+ }
+ }
+ }
+ }
+
+ // Parses the given node with link data and supplies the link.
+ private void parseLink(JsonNode node, boolean reverse) {
+ ConnectPoint src = connectPoint(get(node, "src"));
+ ConnectPoint dst = connectPoint(get(node, "dst"));
+ Link.Type type = Link.Type.valueOf(get(node, "type", "DIRECT"));
+ SparseAnnotations annotations = annotations(node.get("annotations"));
+ // take annotations to update optical ports with correct attributes.
+ updatePorts(src, dst, annotations);
+ DefaultLinkDescription desc = reverse ?
+ new DefaultLinkDescription(dst, src, type, annotations) :
+ new DefaultLinkDescription(src, dst, type, annotations);
+ linkProviderService.linkDetected(desc);
+
+ connectPoints.add(src);
+ connectPoints.add(dst);
+ }
+
+ private void updatePorts(ConnectPoint src, ConnectPoint dst, SparseAnnotations annotations) {
+ final String linkType = annotations.value("optical.type");
+ if ("cross-connect".equals(linkType)) {
+ String value = annotations.value("bandwidth").trim();
+ try {
+ double bw = Double.parseDouble(value);
+ updateOchPort(bw, src, dst);
+ } catch (NumberFormatException e) {
+ log.warn("Invalid bandwidth ({}), can't configure port(s)", value);
+ return;
+ }
+ } else if ("WDM".equals(linkType)) {
+ String value = annotations.value("optical.waves").trim();
+ try {
+ int numChls = Integer.parseInt(value);
+ updateOMSPorts(numChls, src, dst);
+ } catch (NumberFormatException e) {
+ log.warn("Invalid channel ({}), can't configure port(s)", value);
+ return;
+ }
+ }
+ }
+
+ // uses 'bandwidth' annotation to determine the channel spacing.
+ private void updateOchPort(double bw, ConnectPoint srcCp, ConnectPoint dstCp) {
+ Device src = deviceService.getDevice(srcCp.deviceId());
+ Device dst = deviceService.getDevice(dstCp.deviceId());
+ // bandwidth in MHz (assuming Hz - linc is not clear if that or Mb).
+ Frequency spacing = Frequency.ofMHz(bw);
+ // channel bandwidth is smaller than smallest standard channel spacing.
+ ChannelSpacing chsp = null;
+ if (spacing.compareTo(ChannelSpacing.CHL_6P25GHZ.frequency()) <= 0) {
+ chsp = ChannelSpacing.CHL_6P25GHZ;
+ }
+ for (int i = 1; i < ChannelSpacing.values().length; i++) {
+ Frequency val = ChannelSpacing.values()[i].frequency();
+ // pick the next highest or equal channel interval.
+ if (val.isLessThan(spacing)) {
+ chsp = ChannelSpacing.values()[i - 1];
+ break;
+ }
+ }
+ if (chsp == null) {
+ log.warn("Invalid channel spacing ({}), can't configure port(s)", spacing);
+ return;
+ }
+ OchSignal signal = new OchSignal(GridType.DWDM, chsp, 1, 1);
+ if (src.type() == Device.Type.ROADM) {
+ PortDescription portDesc = new OchPortDescription(srcCp.port(), true,
+ OduSignalType.ODU4, true, signal);
+ descriptions.put(srcCp, portDesc);
+ deviceProviderService.portStatusChanged(srcCp.deviceId(), portDesc);
+ }
+ if (dst.type() == Device.Type.ROADM) {
+ PortDescription portDesc = new OchPortDescription(dstCp.port(), true,
+ OduSignalType.ODU4, true, signal);
+ descriptions.put(dstCp, portDesc);
+ deviceProviderService.portStatusChanged(dstCp.deviceId(), portDesc);
+ }
+ }
+
+ private void updateOMSPorts(int numChls, ConnectPoint srcCp, ConnectPoint dstCp) {
+ // round down to largest slot that allows numChl channels to fit into C band range
+ ChannelSpacing chl = null;
+ Frequency perChl = TOTAL.floorDivision(numChls);
+ for (int i = 0; i < ChannelSpacing.values().length; i++) {
+ Frequency val = ChannelSpacing.values()[i].frequency();
+ if (val.isLessThan(perChl)) {
+ chl = ChannelSpacing.values()[i];
+ break;
+ }
+ }
+ if (chl == null) {
+ chl = ChannelSpacing.CHL_6P25GHZ;
+ }
+
+ // if true, there was less channels than can be tightly packed.
+ Frequency grid = chl.frequency();
+ // say Linc's 1st slot starts at CENTER and goes up from there.
+ Frequency min = CENTER.add(grid);
+ Frequency max = CENTER.add(grid.multiply(numChls));
+
+ PortDescription srcPortDesc = new OmsPortDescription(srcCp.port(), true, min, max, grid);
+ PortDescription dstPortDesc = new OmsPortDescription(dstCp.port(), true, min, max, grid);
+ descriptions.put(srcCp, srcPortDesc);
+ descriptions.put(dstCp, dstPortDesc);
+ deviceProviderService.portStatusChanged(srcCp.deviceId(), srcPortDesc);
+ deviceProviderService.portStatusChanged(dstCp.deviceId(), dstPortDesc);
+ }
+
+ // Parses the given JSON and provides hosts as configured.
+ private void parseHosts() {
+ try {
+ JsonNode nodes = cfg.get("hosts");
+ if (nodes != null) {
+ for (JsonNode node : nodes) {
+ parseHost(node);
+
+ // FIXME: hack to make sure host attributes take
+ // This will be fixed when GossipHostStore uses ECM
+ parseHost(node);
+ }
+ }
+ } finally {
+ hostProviderRegistry.unregister(this);
+ }
+ }
+
+ // Parses the given node with host data and supplies the host.
+ private void parseHost(JsonNode node) {
+ MacAddress mac = MacAddress.valueOf(get(node, "mac"));
+ VlanId vlanId = VlanId.vlanId((short) node.get("vlan").asInt(VlanId.UNTAGGED));
+ HostId hostId = HostId.hostId(mac, vlanId);
+ SparseAnnotations annotations = annotations(node.get("annotations"));
+ HostLocation location = new HostLocation(connectPoint(get(node, "location")), 0);
+
+ String[] ipStrings = get(node, "ip", "").split(",");
+ Set<IpAddress> ips = new HashSet<>();
+ for (String ip : ipStrings) {
+ ips.add(IpAddress.valueOf(ip.trim()));
+ }
+
+ DefaultHostDescription desc =
+ new DefaultHostDescription(mac, vlanId, location, ips, annotations);
+ hostProviderService.hostDetected(hostId, desc);
+
+ connectPoints.add(location);
+ }
+
+ // Adds any missing device ports for configured links and host locations.
+ private void addMissingPorts() {
+ deviceService.getDevices().forEach(this::addMissingPorts);
+ }
+
+ // Adds any missing device ports.
+ private void addMissingPorts(Device device) {
+ try {
+ List<Port> ports = deviceService.getPorts(device.id());
+ Set<ConnectPoint> existing = ports.stream()
+ .map(p -> new ConnectPoint(device.id(), p.number()))
+ .collect(Collectors.toSet());
+ Set<ConnectPoint> missing = connectPoints.stream()
+ .filter(cp -> cp.deviceId().equals(device.id()))
+ .filter(cp -> !existing.contains(cp))
+ .collect(Collectors.toSet());
+
+ if (!missing.isEmpty()) {
+ List<PortDescription> newPorts = Stream.concat(
+ ports.stream().map(this::description),
+ missing.stream().map(this::description)
+ ).collect(Collectors.toList());
+ deviceProviderService.updatePorts(device.id(), newPorts);
+ }
+ } catch (IllegalArgumentException e) {
+ log.warn("Error pushing ports: {}", e.getMessage());
+ }
+ }
+
+ // Creates a port description from the specified port.
+ private PortDescription description(Port p) {
+ switch (p.type()) {
+ case OMS:
+ OmsPort op = (OmsPort) p;
+ return new OmsPortDescription(
+ op.number(), op.isEnabled(), op.minFrequency(), op.maxFrequency(), op.grid());
+ case OCH:
+ OchPort ochp = (OchPort) p;
+ return new OchPortDescription(
+ ochp.number(), ochp.isEnabled(), ochp.signalType(), ochp.isTunable(), ochp.lambda());
+ case ODUCLT:
+ OduCltPort odup = (OduCltPort) p;
+ return new OduCltPortDescription(
+ odup.number(), odup.isEnabled(), odup.signalType());
+ default:
+ return new DefaultPortDescription(p.number(), p.isEnabled(), p.type(), p.portSpeed());
+ }
+ }
+
+ // Creates a port description from the specified connection point if none created earlier.
+ private PortDescription description(ConnectPoint cp) {
+ PortDescription saved = descriptions.get(cp);
+ if (saved != null) {
+ return saved;
+ }
+ Port p = deviceService.getPort(cp.deviceId(), cp.port());
+ if (p == null) {
+ return new DefaultPortDescription(cp.port(), true);
+ }
+ return description(p);
+ }
+
+ // Produces set of annotations from the given JSON node.
+ private SparseAnnotations annotations(JsonNode node) {
+ if (node == null) {
+ return DefaultAnnotations.EMPTY;
+ }
+
+ DefaultAnnotations.Builder builder = DefaultAnnotations.builder();
+ Iterator<String> it = node.fieldNames();
+ while (it.hasNext()) {
+ String k = it.next();
+ builder.set(k, node.get(k).asText());
+ }
+ return builder.build();
+ }
+
+ // Produces a connection point from the specified uri/port text.
+ private ConnectPoint connectPoint(String text) {
+ int i = text.lastIndexOf("/");
+ String portName = text.substring(i + 1);
+ DeviceId deviceId = deviceId(text.substring(0, i));
+
+ for (Port port : deviceService.getPorts(deviceId)) {
+ PortNumber pn = port.number();
+ if (pn.name().equals(portName)) {
+ return new ConnectPoint(deviceId, pn);
+ }
+ }
+
+ long portNum;
+ try {
+ portNum = Long.parseLong(portName);
+ } catch (NumberFormatException e) {
+ portNum = 0;
+ }
+
+ return new ConnectPoint(deviceId, portNumber(portNum, portName));
+ }
+
+ // Returns string form of the named property in the given JSON object.
+ private String get(JsonNode node, String name) {
+ return node.path(name).asText();
+ }
+
+ // Returns string form of the named property in the given JSON object.
+ private String get(JsonNode node, String name, String defaultValue) {
+ return node.path(name).asText(defaultValue);
+ }
+
+ @Override
+ public void roleChanged(DeviceId device, MastershipRole newRole) {
+ deviceProviderService.receivedRoleReply(device, newRole, newRole);
+ }
+
+ @Override
+ public void triggerProbe(DeviceId deviceId) {
+ }
+
+ @Override
+ public void triggerProbe(Host host) {
+ }
+
+ @Override
+ public ProviderId id() {
+ return PID;
+ }
+
+ @Override
+ public boolean isReachable(DeviceId device) {
+ return true;
+ }
+
+ /**
+ * Prepares to count device added/available/removed events.
+ *
+ * @param count number of events to count
+ */
+ protected void prepareForDeviceEvents(int count) {
+ deviceLatch = new CountDownLatch(count);
+ deviceService.addListener(deviceEventCounter);
+ }
+
+ /**
+ * Waits for all expected device added/available/removed events.
+ */
+ protected void waitForDeviceEvents() {
+ try {
+ deviceLatch.await(2, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ log.warn("Device events did not arrive in time");
+ }
+ deviceService.removeListener(deviceEventCounter);
+ }
+
+ // Counts down number of device added/available/removed events.
+ private class DeviceEventCounter implements DeviceListener {
+ @Override
+ public void event(DeviceEvent event) {
+ DeviceEvent.Type type = event.type();
+ if (type == DEVICE_ADDED || type == DEVICE_AVAILABILITY_CHANGED) {
+ deviceLatch.countDown();
+ }
+ }
+ }
+
+}