com.connectsdk.discovery.provider.ZeroconfDiscoveryProvider.java Source code

Java tutorial

Introduction

Here is the source code for com.connectsdk.discovery.provider.ZeroconfDiscoveryProvider.java

Source

/*
 * ZeroconfDiscoveryProvider
 * Connect SDK
 * 
 * Copyright (c) 2014 LG Electronics.
 * Created by Hyun Kook Khang on 18 Apr 2014
 * 
 * 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 com.connectsdk.discovery.provider;

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

import javax.jmdns.JmDNS;
import javax.jmdns.ServiceEvent;
import javax.jmdns.ServiceListener;

import org.json.JSONException;
import org.json.JSONObject;

import android.content.Context;
import android.util.Log;

import com.connectsdk.core.Util;
import com.connectsdk.discovery.DiscoveryProvider;
import com.connectsdk.discovery.DiscoveryProviderListener;
import com.connectsdk.service.config.ServiceDescription;

public class ZeroconfDiscoveryProvider implements DiscoveryProvider {
    private static final String HOSTNAME = "connectsdk";

    JmDNS jmdns;
    InetAddress srcAddress;

    private final static int RESCAN_INTERVAL = 10000;
    private final static int RESCAN_ATTEMPTS = 3;
    private final static int TIMEOUT = RESCAN_INTERVAL * RESCAN_ATTEMPTS;

    private Timer dataTimer;

    List<JSONObject> serviceFilters;

    private ConcurrentHashMap<String, ServiceDescription> foundServices;
    private CopyOnWriteArrayList<DiscoveryProviderListener> serviceListeners;

    ServiceListener jmdnsListener = new ServiceListener() {

        @Override
        public void serviceResolved(ServiceEvent ev) {
            @SuppressWarnings("deprecation")
            String ipAddress = ev.getInfo().getHostAddress();
            if (!Util.isIPv4Address(ipAddress)) {
                // Currently, we only support ipv4
                return;
            }

            String uuid = ipAddress;
            String friendlyName = ev.getInfo().getName();
            int port = ev.getInfo().getPort();

            ServiceDescription foundService = foundServices.get(uuid);

            boolean isNew = foundService == null;
            boolean listUpdateFlag = false;

            if (isNew) {
                foundService = new ServiceDescription();
                foundService.setUUID(uuid);
                foundService.setServiceFilter(ev.getInfo().getType());
                foundService.setIpAddress(ipAddress);
                foundService.setServiceID(serviceIdForFilter(ev.getInfo().getType()));
                foundService.setPort(port);
                foundService.setFriendlyName(friendlyName);

                listUpdateFlag = true;
            } else {
                if (!foundService.getFriendlyName().equals(friendlyName)) {
                    foundService.setFriendlyName(friendlyName);
                    listUpdateFlag = true;
                }
            }

            if (foundService != null)
                foundService.setLastDetection(new Date().getTime());

            foundServices.put(uuid, foundService);

            if (listUpdateFlag) {
                for (DiscoveryProviderListener listener : serviceListeners) {
                    listener.onServiceAdded(ZeroconfDiscoveryProvider.this, foundService);
                }
            }
        }

        @Override
        public void serviceRemoved(ServiceEvent ev) {
            @SuppressWarnings("deprecation")
            String uuid = ev.getInfo().getHostAddress();
            final ServiceDescription service = foundServices.get(uuid);

            if (service != null) {
                Util.runOnUI(new Runnable() {

                    @Override
                    public void run() {
                        for (DiscoveryProviderListener listener : serviceListeners) {
                            listener.onServiceRemoved(ZeroconfDiscoveryProvider.this, service);
                        }
                    }
                });
            }
        }

        @Override
        public void serviceAdded(ServiceEvent event) {
            // Required to force serviceResolved to be called again
            // (after the first search)
            jmdns.requestServiceInfo(event.getType(), event.getName(), 1);
        }
    };

    public ZeroconfDiscoveryProvider(Context context) {
        foundServices = new ConcurrentHashMap<String, ServiceDescription>(8, 0.75f, 2);

        serviceListeners = new CopyOnWriteArrayList<DiscoveryProviderListener>();
        serviceFilters = new ArrayList<JSONObject>();

        try {
            srcAddress = Util.getIpAddress(context);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void start() {
        stop();

        dataTimer = new Timer();
        MDNSSearchTask sendSearch = new MDNSSearchTask();
        dataTimer.schedule(sendSearch, 100, RESCAN_INTERVAL);
    }

    private class MDNSSearchTask extends TimerTask {

        @Override
        public void run() {
            List<String> killKeys = new ArrayList<String>();

            long killPoint = new Date().getTime() - TIMEOUT;

            for (String key : foundServices.keySet()) {
                ServiceDescription service = foundServices.get(key);
                if (service == null || service.getLastDetection() < killPoint) {
                    killKeys.add(key);
                }
            }

            for (String key : killKeys) {
                final ServiceDescription service = foundServices.get(key);

                if (service != null) {
                    Util.runOnUI(new Runnable() {

                        @Override
                        public void run() {
                            for (DiscoveryProviderListener listener : serviceListeners) {
                                listener.onServiceRemoved(ZeroconfDiscoveryProvider.this, service);
                            }
                        }
                    });
                }

                if (foundServices.containsKey(key))
                    foundServices.remove(key);
            }

            if (srcAddress != null) {
                try {
                    if (jmdns != null) {
                        jmdns.close();
                    }
                    jmdns = JmDNS.create(srcAddress, HOSTNAME);

                    for (JSONObject searchTarget : serviceFilters) {
                        try {
                            String filter = searchTarget.getString("filter");
                            jmdns.addServiceListener(filter, jmdnsListener);
                        } catch (JSONException e) {
                            e.printStackTrace();
                        }
                    }
                    ;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public void stop() {
        if (dataTimer != null) {
            dataTimer.cancel();
        }

        if (jmdns != null) {
            for (JSONObject searchTarget : serviceFilters) {
                try {
                    String filter = searchTarget.getString("filter");
                    jmdns.removeServiceListener(filter, jmdnsListener);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
            ;
        }
    }

    @Override
    public void reset() {
        foundServices.clear();
    }

    @Override
    public void addListener(DiscoveryProviderListener listener) {
        serviceListeners.add(listener);
    }

    @Override
    public void removeListener(DiscoveryProviderListener listener) {
        serviceListeners.remove(listener);
    }

    @Override
    public void addDeviceFilter(JSONObject parameters) {
        if (!parameters.has("filter")) {
            Log.e("Connect SDK", "This device filter does not have zeroconf filter info");
        } else {
            serviceFilters.add(parameters);
        }
    }

    @Override
    public void removeDeviceFilter(JSONObject parameters) {
        String removalServiceId;
        boolean shouldRemove = false;
        int removalIndex = -1;

        try {
            removalServiceId = parameters.getString("serviceId");

            for (int i = 0; i < serviceFilters.size(); i++) {
                JSONObject serviceFilter = serviceFilters.get(i);
                String serviceId = (String) serviceFilter.get("serviceId");

                if (serviceId.equals(removalServiceId)) {
                    shouldRemove = true;
                    removalIndex = i;
                    break;
                }
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }

        if (shouldRemove) {
            serviceFilters.remove(removalIndex);
        }
    }

    @Override
    public boolean isEmpty() {
        return serviceFilters.size() == 0;
    }

    public String serviceIdForFilter(String filter) {
        String serviceId = "";

        for (JSONObject serviceFilter : serviceFilters) {
            String ssdpFilter;
            try {
                ssdpFilter = serviceFilter.getString("filter");
                if (ssdpFilter.equals(filter)) {
                    return serviceFilter.getString("serviceId");
                }
            } catch (JSONException e) {
                e.printStackTrace();
                continue;
            }
        }

        return serviceId;
    }
}