/* * Copyright 2014 Sony Corporation */ package com.almalence.sony.cameraremote; import android.util.Log; 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; /** * A SSDP client class for this sample application. This implementation keeps * simple so that many developers understand quickly. */ public class SimpleSsdpClient { private static final String TAG = SimpleSsdpClient.class.getSimpleName(); private static final int SSDP_RECEIVE_TIMEOUT = 10000; // msec private static final int PACKET_BUFFER_SIZE = 1024; private static final int SSDP_PORT = 1900; private static final int SSDP_MX = 1; private static final String SSDP_ADDR = "239.255.255.250"; private static final String SSDP_ST = "urn:schemas-sony-com:service:ScalarWebAPI:1"; /** 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(); } 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) { Log.w(TAG, "search() already searching."); return false; } if (handler == null) { throw new NullPointerException("handler is null."); } Log.i(TAG, "search() Start."); 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(); 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 Log.i(TAG, "search() Send Datagram packet 3 times."); socket.send(packet); Thread.sleep(100); socket.send(packet); Thread.sleep(100); socket.send(packet); } catch (SocketException e) { Log.e(TAG, "search() DatagramSocket error:", e); if (socket != null && !socket.isClosed()) { socket.close(); } handler.onErrorFinished(); return; } catch (IOException e) { Log.e(TAG, "search() IOException :", e); if (socket != null && !socket.isClosed()) { socket.close(); } handler.onErrorFinished(); return; } catch (InterruptedException e) { // do nothing. Log.d(TAG, "search() InterruptedException :", e); } // Receive reply packets mSearching = true; long startTime = System.currentTimeMillis(); List<String> foundDevices = new ArrayList<String>(); byte[] array = new byte[PACKET_BUFFER_SIZE]; ServerDevice device = null; try { while (mSearching) { receivePacket = new DatagramPacket(array, array.length); socket.setSoTimeout(SSDP_RECEIVE_TIMEOUT); socket.receive(receivePacket); String ssdpReplyMessage = new String(receivePacket.getData(), 0, // receivePacket.getLength(), "UTF-8"); 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. device = ServerDevice.fetch(ddLocation); // Note that it's a irresponsible rule // for the sample application. if (device != null && device.hasApiService("camera")) { handler.onDeviceFound(device); } } if (SSDP_RECEIVE_TIMEOUT < System.currentTimeMillis() - startTime) { break; } } } catch (InterruptedIOException e) { Log.d(TAG, "search() Timeout."); if (device == null) { mSearching = false; handler.onErrorFinished(); return; } } catch (IOException e) { Log.d(TAG, "search() IOException2. : " + e); mSearching = false; handler.onErrorFinished(); return; } finally { Log.d(TAG, "search() Finish "); mSearching = false; if (socket != null && !socket.isClosed()) { socket.close(); } } handler.onFinished(); }; }.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" */ private static String findParameterValue(String ssdpMessage, 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; } }