package com.connectsdk.service.upnp; import com.connectsdk.core.MediaInfo; import com.connectsdk.core.Util; import com.connectsdk.service.capability.MediaControl.PlayStateStatus; import com.connectsdk.service.capability.listeners.ResponseListener; import com.connectsdk.service.command.URLServiceSubscription; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.xmlpull.v1.XmlPullParserException; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.net.ServerSocket; import java.net.Socket; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; public class DLNAHttpServer { final int port = 49291; volatile ServerSocket welcomeSocket; volatile boolean running = false; CopyOnWriteArrayList<URLServiceSubscription<?>> subscriptions; public DLNAHttpServer() { subscriptions = new CopyOnWriteArrayList<URLServiceSubscription<?>>(); } public synchronized void start() { if (running) { return; } running = true; try { welcomeSocket = new ServerSocket(this.port); } catch (IOException ex) { ex.printStackTrace(); return; } Util.runInBackground(new Runnable() { @Override public void run() { processRequests(); } }, true); } public synchronized void stop() { if (!running) { return; } for (URLServiceSubscription<?> sub : subscriptions) { sub.unsubscribe(); } subscriptions.clear(); if (welcomeSocket != null && !welcomeSocket.isClosed()) { try { welcomeSocket.close(); } catch (IOException ex) { ex.printStackTrace(); } } welcomeSocket = null; running = false; } private void processRequests() { while (running) { if (welcomeSocket == null || welcomeSocket.isClosed()) { break; } Socket connectionSocket = null; BufferedReader inFromClient = null; DataOutputStream outToClient = null; try { connectionSocket = welcomeSocket.accept(); } catch (IOException ex) { ex.printStackTrace(); // this socket may have been closed, so we'll stop break; } int c = 0; String body = null; try { inFromClient = new BufferedReader(new InputStreamReader(connectionSocket.getInputStream())); StringBuilder sb = new StringBuilder(); while ((c = inFromClient.read()) != -1) { sb.append((char)c); if (sb.toString().endsWith("\r\n\r\n")) break; } sb = new StringBuilder(); while ((c = inFromClient.read()) != -1) { sb.append((char)c); body = sb.toString(); if (body.endsWith("</e:propertyset>")) break; } } catch (IOException ex) { ex.printStackTrace(); } PrintWriter out = null; try { outToClient = new DataOutputStream(connectionSocket.getOutputStream()); out = new PrintWriter(outToClient); out.println("HTTP/1.1 200 OK"); out.println("Connection: Close"); out.println("Content-Length: 0"); out.println(); out.flush(); } catch (IOException ex) { ex.printStackTrace(); } finally { try { inFromClient.close(); out.close(); outToClient.close(); connectionSocket.close(); } catch (IOException ex) { ex.printStackTrace(); } catch (NullPointerException ex) { ex.printStackTrace(); } } if (body == null) continue; InputStream stream = null; try { stream = new ByteArrayInputStream(body.getBytes("UTF-8")); } catch (UnsupportedEncodingException ex) { ex.printStackTrace(); } JSONArray propertySet; DLNANotifyParser parser = new DLNANotifyParser(); try { propertySet = parser.parse(stream); for (int i = 0; i < propertySet.length(); i++) { JSONObject property = propertySet.getJSONObject(i); if (property.has("LastChange")) { JSONObject lastChange = property.getJSONObject("LastChange"); handleLastChange(lastChange); } } } catch (XmlPullParserException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (JSONException e) { e.printStackTrace(); } } } private void handleLastChange(JSONObject lastChange) throws JSONException { if (lastChange.has("InstanceID")) { JSONArray instanceIDs = lastChange.getJSONArray("InstanceID"); for (int i = 0; i < instanceIDs.length(); i++) { JSONArray events = instanceIDs.getJSONArray(i); for (int j = 0; j < events.length(); j++) { JSONObject entry = events.getJSONObject(j); handleEntry(entry); } } } } private void handleEntry(JSONObject entry) throws JSONException { if (entry.has("TransportState")) { String transportState = entry.getString("TransportState"); PlayStateStatus status = PlayStateStatus.convertTransportStateToPlayStateStatus(transportState); for (URLServiceSubscription<?> sub: subscriptions) { if (sub.getTarget().equalsIgnoreCase("playState")) { for (int j = 0; j < sub.getListeners().size(); j++) { @SuppressWarnings("unchecked") ResponseListener<Object> listener = (ResponseListener<Object>) sub.getListeners().get(j); Util.postSuccess(listener, status); } } } } if ((entry.has("Volume")&&!entry.has("channel"))||(entry.has("Volume")&&entry.getString("channel").equals("Master"))) { int intVolume = entry.getInt("Volume"); float volume = (float) intVolume / 100; for (URLServiceSubscription<?> sub : subscriptions) { if (sub.getTarget().equalsIgnoreCase("volume")) { for (int j = 0; j < sub.getListeners().size(); j++) { @SuppressWarnings("unchecked") ResponseListener<Object> listener = (ResponseListener<Object>) sub.getListeners().get(j); Util.postSuccess(listener, volume); } } } } if ((entry.has("Mute")&&!entry.has("channel"))||(entry.has("Mute")&&entry.getString("channel").equals("Master"))) { String muteStatus = entry.getString("Mute"); boolean mute; try { mute = (Integer.parseInt(muteStatus) == 1); } catch(NumberFormatException e) { mute = Boolean.parseBoolean(muteStatus); } for (URLServiceSubscription<?> sub : subscriptions) { if (sub.getTarget().equalsIgnoreCase("mute")) { for (int j = 0; j < sub.getListeners().size(); j++) { @SuppressWarnings("unchecked") ResponseListener<Object> listener = (ResponseListener<Object>) sub.getListeners().get(j); Util.postSuccess(listener, mute); } } } } if (entry.has("CurrentTrackMetaData")) { String trackMetaData = entry.getString("CurrentTrackMetaData"); MediaInfo info = DLNAMediaInfoParser.getMediaInfo(trackMetaData); for (URLServiceSubscription<?> sub : subscriptions) { if (sub.getTarget().equalsIgnoreCase("info")) { for (int j = 0; j < sub.getListeners().size(); j++) { @SuppressWarnings("unchecked") ResponseListener<Object> listener = (ResponseListener<Object>) sub.getListeners().get(j); Util.postSuccess(listener, info); } } } } } public int getPort() { return port; } public List<URLServiceSubscription<?>> getSubscriptions() { return subscriptions; } public void setSubscriptions(List<URLServiceSubscription<?>> subscriptions) { this.subscriptions = new CopyOnWriteArrayList<URLServiceSubscription<?>>(subscriptions); } public boolean isRunning() { return running; } }