/******************************************************************************* * Code contributed to the webinos project * * 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. * * Copyright 2012 Ziran Sun Samsung Electronics(UK) Ltd ******************************************************************************/ package org.webinos.android.impl.discovery; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; import javax.jmdns.JmDNS; import javax.jmdns.ServiceEvent; import javax.jmdns.ServiceInfo; import javax.jmdns.ServiceListener; import android.util.Log; import org.meshpoint.anode.AndroidContext; import org.meshpoint.anode.module.IModule; import org.meshpoint.anode.module.IModuleContext; import org.webinos.api.PendingOperation; import org.webinos.api.discovery.DiscoveryManager; import org.webinos.api.discovery.Filter; import org.webinos.api.discovery.FindCallback; import org.webinos.api.discovery.Options; import org.webinos.api.discovery.Service; import org.webinos.api.discovery.ServiceType; import android.content.Context; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.WifiManager.MulticastLock; import android.provider.Settings.System; import android.widget.Toast; import android.os.Handler; import android.os.Looper; public class DiscoveryMdnsImpl extends DiscoveryManager implements IModule { private Context androidContext; private static final String TAG = "org.webinos.android.impl.DiscoveryMdnsImpl"; private static final boolean D = true; private String ptl_type = null; private int server_port = 4321; //randomly choose server port private boolean adverton = false; //watch advertServices status private boolean findon = false; //watch findServices status private JmDNS mZeroconf = null; private ServiceListener mListener = null; private ServiceInfo mServiceInfo; private WifiManager mWiFiManager; private MulticastLock mLock; private WifiInfo mInfo; private DNSFindService dnsFindService; DiscoveryServiceImpl srv = new DiscoveryServiceImpl(); /***************************** * DiscoveryManager methods *****************************/ @Override public synchronized PendingOperation findServices( ServiceType serviceType, FindCallback findCallback, Options options, Filter filter){ if(D) Log.v(TAG, "DiscoveryMdnsImpl: findservices"); if(serviceType == null) { Log.e(TAG, "DiscoveryMdnsImpl: Please specify a serviceType"); return null; } else{ if(serviceType.api == null){ Log.e(TAG, "DiscoveryMdnsImpl: Please specify serviceType.api"); return null; } else { ptl_type = serviceType.api; if(D) Log.v(TAG, "Mdns findServices - service type "+ptl_type); } } // new thread dnsFindService = new DNSFindService(serviceType, findCallback, options, filter); dnsFindService.start(); if(D) Log.v(TAG, "Mdns findServices - thread started with id "+(int)dnsFindService.getId()); return new DiscoveryPendingOperation(dnsFindService, dnsFindService); } public void advertServices(String serviceType){ //clean up before start if(!adverton && !findon) cleanup(); //start advertisement if(D) Log.v(TAG, "Mdns advertServices "+serviceType); String hostname = System.getString(androidContext.getContentResolver(),System.ANDROID_ID); InetAddress addr = getselfAddress(); if(D) Log.d(TAG, String.format("own address: addr=%s", addr.toString())); //Replace "." with "_" String tmp = addr.toString(); String mIp = tmp.replace(".", "_"); String mNameIp = hostname + mIp; mServiceInfo = ServiceInfo.create(serviceType, mNameIp, server_port, "Create new service type in android"); try { if(mZeroconf == null) mZeroconf = JmDNS.create(addr); mZeroconf.registerService(mServiceInfo); adverton = true; } catch (IOException e) { e.printStackTrace(); } } public String getServiceId(String serviceType){ // TODO Auto-generated method stub return null; } public Service createService(){ DiscoveryServiceImpl srv = new DiscoveryServiceImpl(); return srv; } /***************************** * IModule methods *****************************/ @Override public Object startModule(IModuleContext ctx){ if(D) Log.v(TAG, "DiscoveryMdnsImpl: startModule"); androidContext = ((AndroidContext)ctx).getAndroidContext(); // Setup WiFi mWiFiManager = (WifiManager) androidContext.getSystemService(Context.WIFI_SERVICE); if(!mWiFiManager.isWifiEnabled()){ //if(D) Log.v(TAG, "DiscoveryMdnsImpl: Enable WiFi"); //mWiFiManager.setWifiEnabled(true); Handler handler = new Handler(Looper.getMainLooper()); handler.post(new Runnable() { @Override public void run() { Toast.makeText( androidContext, "Please turn on WiFi to have ZeroConf PZP peer Discovery Functions", Toast.LENGTH_LONG).show(); } }); } return this; } @Override public void stopModule() { /* * perform any module shutdown here ... */ if(D) Log.v(TAG, "DiscoveryMdnsImpl: stopModule"); } /***************************** * Helpers *****************************/ public InetAddress getselfAddress (){ mInfo = mWiFiManager.getConnectionInfo(); int intaddr = mInfo.getIpAddress(); byte[] byteaddr = new byte[] { (byte) (intaddr & 0xff), (byte) (intaddr >> 8 & 0xff), (byte) (intaddr >> 16 & 0xff), (byte) (intaddr >> 24 & 0xff) }; InetAddress addr = null; try { addr = InetAddress.getByAddress(byteaddr); } catch (UnknownHostException e1) { e1.printStackTrace(); } if(D) Log.d(TAG, String.format("found intaddr=%d, addr=%s", intaddr, addr.toString())); return addr; } public void cleanup(){ if(mZeroconf != null) { if(D) Log.v(TAG, "clean up"); if(mListener != null){ if(ptl_type != null) { mZeroconf.removeServiceListener(ptl_type, mListener); mListener = null; } } mZeroconf.unregisterAllServices(); try { mZeroconf.close(); } catch (IOException e) { if(D) Log.d(TAG, String.format("ZeroConf Error: %s", e.getMessage())); e.printStackTrace(); } mZeroconf = null; } //release lock if (mLock != null) mLock.release(); } class DNSFindService extends Thread implements Runnable { private ServiceType serviceType; private FindCallback findCallback; private Options options; private Filter filter; private DNSFindService(ServiceType srvType, FindCallback findCB, Options opts, Filter fltr) { this.serviceType = srvType; this.findCallback = findCB; this.options = opts; this.filter = fltr; if(D) Log.v(TAG,"constructed WiFiFindService"); } public void run() { InetAddress addr = getselfAddress(); if(D) Log.d(TAG, String.format("own address: addr=%s", addr.toString())); String hostname = System.getString(androidContext.getContentResolver(),System.ANDROID_ID); if(!adverton && !findon) cleanup(); // Setup DNS mLock = mWiFiManager.createMulticastLock("MdnsLock"); mLock.setReferenceCounted(true); mLock.acquire(); //TODO: replacing the arrays with HashTable String[] Addresses = {null, null , null, null, null, null}; srv.deviceAddresses = Addresses; String[] Devicenames = {null, null , null, null, null, null}; srv.deviceNames = Devicenames; try { if(D) Log.d(TAG, "Listener handling\n"); if(mZeroconf == null) mZeroconf = JmDNS.create(addr); findon = true; if(D) Log.d(TAG, "ptl_typ = " + ptl_type); mZeroconf.addServiceListener(ptl_type, mListener = new ServiceListener() { @Override public void serviceResolved(ServiceEvent ev) { if(D) Log.d(TAG, "Service resolved: " + ev.getInfo().getQualifiedName()); if(D) Log.d(TAG, " port:\n" + ev.getInfo().getPort() + "\nIP Address:\n" + ev.getInfo().getHostAddresses()[0] + "\nserver name:\n" + ev.getInfo().getServer() + "\nprotocol:\n" + ev.getInfo().getProtocol() + "\ntype:\n" + ev.getType()); //start passing data String[] hostAdresses = ev.getInfo().getHostAddresses(); int i = 0; for (String ha : hostAdresses) { int intaddr = mInfo.getIpAddress(); byte[] byteaddr = new byte[] { (byte) (intaddr & 0xff), (byte) (intaddr >> 8 & 0xff), (byte) (intaddr >> 16 & 0xff), (byte) (intaddr >> 24 & 0xff) }; InetAddress mHostaddr = null; try { mHostaddr = InetAddress.getByAddress(byteaddr); } catch (UnknownHostException e1) { e1.printStackTrace(); } String hostaddr = mHostaddr.toString(); hostaddr = hostaddr.substring(1); //remove "/" String devicename = System.getString(androidContext.getContentResolver(),System.ANDROID_ID); //filter out itself if ((!(hostAdresses[i].equals(hostaddr))) && (!(devicename.equals(ev.getName())))) { srv.deviceAddresses[i] = hostAdresses[i]; if(D) Log.d(TAG, "Service IP addresses: " + srv.deviceAddresses[i]); srv.deviceNames[i] = ev.getName(); if(D) Log.d(TAG, "Hostnames:" + srv.deviceNames[i]); i++; } } findCallback.onFound(srv); } @Override public void serviceRemoved(ServiceEvent ev) { if(D) Log.d(TAG, "Service removed: " + ev.getName()); } @Override public void serviceAdded(ServiceEvent ev) { // Required to force serviceResolved to be called again (after the first search) if(D) Log.d(TAG, "serviceAdded" ); mZeroconf.requestServiceInfo(ev.getType(), ev.getName(), 1); } }); } catch (IOException e) { e.printStackTrace(); } } } class DiscoveryPendingOperation extends PendingOperation { private Thread t=null; private Runnable r=null; public DiscoveryPendingOperation(Thread t, Runnable r) { this.t = t; this.r = r; } public void cancel() { if(D) Log.d(TAG, "DiscoveryPendingOperation cancel"); if(t!=null) { if(D) Log.v(TAG, "DiscoveryPendingOperation cancel - send interrupt..."); t.interrupt(); } } } }