/* * Copyright 2013 Sony Corporation */ package com.example.sony.cameraremote; import java.io.IOException; import java.io.InterruptedIOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetSocketAddress; import java.net.SocketException; import java.util.ArrayList; import java.util.List; /** * SimpleSsdpClient. */ public class SimpleSsdpClient { /** タイムアウトmsec. */ private static final int SSDP_RECEIVE_TIMEOUT = 10000; // msec /** バッファサイズ. */ private static final int PACKET_BUFFER_SIZE = 1024; /** SSDPポート. */ private static final int SSDP_PORT = 1900; /** SSDP MX. */ private static final int SSDP_MX = 1; /** SSDPアドレス. */ private static final String SSDP_ADDR = "239.255.255.250"; /** SSDP URN. */ private static final String SSDP_ST = "urn:schemas-sony-com:service:ScalarWebAPI:1"; /** スレッドスリープ時間. */ private static final int THREAD_SLEEP_TIME_MSEC = 100; /** Handler interface for SSDP search result. */ public interface SearchResultHandler { /** * Called when API server device is found. Note that it's performed by * non-UI thread. * * @param device API server device that is found by searching */ void onDeviceFound(ServerDevice device); /** * Called when searching completes successfully. Note that it's * performed by non-UI thread. */ void onFinished(); /** * Called when searching completes with some errors. Note that it's * performed by non-UI thread. */ void onErrorFinished(); } /** Searching Flag. */ private boolean mSearching = false; /** * Search API server device. * * @param handler result handler * @return true: start successfully, false: already searching now */ public synchronized boolean search(final SearchResultHandler handler) { if (mSearching) { return false; } if (handler == null) { throw new NullPointerException("handler is null."); } final String ssdpRequest = "M-SEARCH * HTTP/1.1\r\n" + String.format("HOST: %s:%d\r\n", SSDP_ADDR, SSDP_PORT) + String.format("MAN: \"ssdp:discover\"\r\n") + String.format("MX: %d\r\n", SSDP_MX) + String.format("ST: %s\r\n", SSDP_ST) + "\r\n"; final byte[] sendData = ssdpRequest.getBytes(); Thread mThread = new Thread() { @Override public void run() { // Send Datagram packets DatagramSocket socket = null; DatagramPacket receivePacket = null; DatagramPacket packet = null; try { socket = new DatagramSocket(); InetSocketAddress iAddress = new InetSocketAddress(SSDP_ADDR, SSDP_PORT); packet = new DatagramPacket(sendData, sendData.length, iAddress); // send 3 times socket.send(packet); Thread.sleep(THREAD_SLEEP_TIME_MSEC); socket.send(packet); Thread.sleep(THREAD_SLEEP_TIME_MSEC); socket.send(packet); } catch (InterruptedException e) { //Exceptionを受けるだけなので処理は行わない } catch (SocketException e) { handler.onErrorFinished(); } catch (IOException e) { handler.onErrorFinished(); } if (socket == null) { return; } // Receive reply packets mSearching = true; long startTime = System.currentTimeMillis(); List<String> foundDevices = new ArrayList<String>(); byte[] array = new byte[PACKET_BUFFER_SIZE]; while (mSearching) { receivePacket = new DatagramPacket(array, array.length); try { socket.setSoTimeout(SSDP_RECEIVE_TIMEOUT); socket.receive(receivePacket); String ssdpReplyMessage = new String(receivePacket.getData(), 0, receivePacket.getLength()); String ddUsn = findParameterValue(ssdpReplyMessage, "USN"); /* * There is possibility to receive multiple packets from * a individual server. */ if (!foundDevices.contains(ddUsn)) { String ddLocation = findParameterValue(ssdpReplyMessage, "LOCATION"); foundDevices.add(ddUsn); // Fetch Device Description XML and parse it. ServerDevice device = ServerDevice.fetch(ddLocation); // Note that it's a irresponsible rule // for the sample application. if (device != null && device.hasApiService("camera")) { handler.onDeviceFound(device); } } } catch (InterruptedIOException e) { break; } catch (IOException e) { handler.onErrorFinished(); return; } if (SSDP_RECEIVE_TIMEOUT < System.currentTimeMillis() - startTime) { break; } } mSearching = false; if (socket != null && !socket.isClosed()) { socket.close(); } handler.onFinished(); }; }; mThread.start(); return true; } /** * Checks whether searching is in progress or not. * * @return true: now searching, false: otherwise */ public boolean isSearching() { return mSearching; } /** * Cancels searching. Note that it cannot stop the operation immediately. */ public void cancelSearching() { mSearching = false; } /** * Find a value string from message line as below. (ex.) * "ST: XXXXX-YYYYY-ZZZZZ" -> "XXXXX-YYYYY-ZZZZZ" * * @param ssdpMessage SSDP message * @param paramName paramName * @return val.trim */ private static String findParameterValue(final String ssdpMessage, final String paramName) { String name = paramName; if (!name.endsWith(":")) { name = name + ":"; } int start = ssdpMessage.indexOf(name); int end = ssdpMessage.indexOf("\r\n", start); if (start != -1 && end != -1) { start += name.length(); String val = ssdpMessage.substring(start, end); if (val != null) { return val.trim(); } } return null; } }