/* * Copyright (C) 2005-2015 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with XBMC Remote; see the file license. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * http://www.gnu.org/copyleft/gpl.html * */ package org.xbmc.android.zeroconf; import android.app.IntentService; import android.content.Context; import android.content.Intent; import android.net.wifi.WifiManager; import android.net.wifi.WifiManager.MulticastLock; import android.text.format.Formatter; import android.util.Log; import de.greenrobot.event.EventBus; import org.xbmc.android.app.event.ZeroConf; import org.xbmc.android.app.injection.Injector; import javax.inject.Inject; import javax.jmdns.JmDNS; import javax.jmdns.ServiceEvent; import javax.jmdns.ServiceListener; import java.io.IOException; import java.net.Inet6Address; import java.net.InetAddress; import java.net.UnknownHostException; import static org.xbmc.android.app.event.ZeroConf.*; /** * This service scans for xbmc-jsonrpc hosts and returns one by one by using the * callback receiver. * * @author freezy <freezy@xbmc.org> */ public class DiscoveryService extends IntentService { private static final String TAG = DiscoveryService.class.getSimpleName(); @Inject protected EventBus bus; private static final int TIMEOUT = 1000; // private static final String SERVICENAME_JSONRPC = "_xbmc-jsonrpc._tcp.local."; private static final String SERVICENAME_JSONRPC = "_xbmc-jsonrpc-h._tcp.local."; private WifiManager wifiManager; private MulticastLock multicastLock; private InetAddress wifiAddress = null; private JmDNS jmDns = null; private ServiceListener mListener = null; public DiscoveryService() { super(TAG); } @Override public void onCreate() { super.onCreate(); Injector.inject(this); wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); } @Override protected void onHandleIntent(Intent intent) { try { final int ipAddress = wifiManager.getConnectionInfo().getIpAddress(); if (ipAddress != 0) { wifiAddress = InetAddress.getByName(Formatter.formatIpAddress(ipAddress)); Log.i(TAG, "Discovering XBMC hosts through " + wifiAddress.getHostAddress() + "..."); } else { Log.i(TAG, "Discovering XBMC hosts on all interfaces..."); } } catch (UnknownHostException e) { Log.e(TAG, "Cannot parse Wifi IP address.", e); // continue, JmDNS can also run without IP address. } acquireMulticastLock(); new Thread() { @Override public void run() { listen(); } }.start(); try { Thread.sleep(TIMEOUT); } catch (InterruptedException e) { Log.e(TAG, "Error sleeping " + TIMEOUT + "ms.", e); } bus.post(new ZeroConf(STATUS_FINISHED)); releaseMulticastLock(); } /** * Launches the zeroconf listener */ private void listen() { try { if (wifiAddress == null) { jmDns = JmDNS.create(); } else { jmDns = JmDNS.create(wifiAddress); } jmDns.addServiceListener(SERVICENAME_JSONRPC, mListener = new ServiceListener() { @Override public void serviceResolved(ServiceEvent event) { Log.d(TAG, "Service resolved: " + event.getName() + " / " + event.getType()); DiscoveryService.this.serviceResolved(event); } @Override public void serviceRemoved(ServiceEvent event) { Log.d(TAG, "Service removed: " + event.getName() + " / " + event.getType()); } @Override public void serviceAdded(ServiceEvent event) { Log.d(TAG, "Service added:" + event.getName() + " / " + event.getType()); // Required to force serviceResolved to be called // again (after the first search) jmDns.requestServiceInfo(event.getType(), event.getName(), 1); } }); } catch (IOException e) { Log.e(TAG, "Error listening to multicast: " + e.getMessage(), e); bus.post(new ZeroConf(STATUS_ERROR)); } } private void serviceResolved(ServiceEvent event) { final InetAddress[] addresses = event.getInfo().getInet4Addresses(); final String hostname = event.getInfo().getServer().replace(".local.", ""); final XBMCHost host; if (addresses.length == 0) { Inet6Address[] v6addresses = event.getInfo().getInet6Addresses(); if (v6addresses.length == 0) { Log.e(TAG, "Could not obtain IP address for " + event.getInfo()); bus.post(new ZeroConf(STATUS_ERROR)); return; } else { host = new XBMCHost(v6addresses[0].getHostAddress(), hostname, event.getInfo().getPort(), event.getInfo().getName()); } } else { Log.d(TAG, "Discovered IP address: " + addresses[0].getHostAddress()); host = new XBMCHost(addresses[0].getHostAddress(), hostname, event.getInfo().getPort(), event.getInfo().getName()); } bus.post(new ZeroConf(STATUS_RESOLVED, host)); } private void acquireMulticastLock() { multicastLock = wifiManager.createMulticastLock("xbmc-zeroconf-discover"); multicastLock.setReferenceCounted(true); multicastLock.acquire(); Log.d(TAG, "Multicast lock acquired."); } private void releaseMulticastLock() { Log.d(TAG, "Multicast lock released."); if (jmDns != null) { if (mListener != null) { jmDns.removeServiceListener(SERVICENAME_JSONRPC, mListener); mListener = null; } try { jmDns.unregisterAllServices(); jmDns.close(); } catch (IOException e) { Log.e(TAG, "Error closing JmDNS: " + e.getMessage(), e); } jmDns = null; } multicastLock.release(); } }