Home | History | Annotate | Download | only in apprtc
      1 /*
      2  *  Copyright 2014 The WebRTC Project Authors. All rights reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 package org.appspot.apprtc;
     12 
     13 import org.appspot.apprtc.AppRTCClient.SignalingParameters;
     14 import org.appspot.apprtc.util.AsyncHttpURLConnection;
     15 import org.appspot.apprtc.util.AsyncHttpURLConnection.AsyncHttpEvents;
     16 
     17 import android.util.Log;
     18 
     19 import org.json.JSONArray;
     20 import org.json.JSONException;
     21 import org.json.JSONObject;
     22 import org.webrtc.IceCandidate;
     23 import org.webrtc.PeerConnection;
     24 import org.webrtc.SessionDescription;
     25 
     26 import java.io.IOException;
     27 import java.io.InputStream;
     28 import java.net.HttpURLConnection;
     29 import java.net.URL;
     30 import java.util.LinkedList;
     31 import java.util.Scanner;
     32 
     33 /**
     34  * AsyncTask that converts an AppRTC room URL into the set of signaling
     35  * parameters to use with that room.
     36  */
     37 public class RoomParametersFetcher {
     38   private static final String TAG = "RoomRTCClient";
     39   private static final int TURN_HTTP_TIMEOUT_MS = 5000;
     40   private final RoomParametersFetcherEvents events;
     41   private final String roomUrl;
     42   private final String roomMessage;
     43   private AsyncHttpURLConnection httpConnection;
     44 
     45   /**
     46    * Room parameters fetcher callbacks.
     47    */
     48   public static interface RoomParametersFetcherEvents {
     49     /**
     50      * Callback fired once the room's signaling parameters
     51      * SignalingParameters are extracted.
     52      */
     53     public void onSignalingParametersReady(final SignalingParameters params);
     54 
     55     /**
     56      * Callback for room parameters extraction error.
     57      */
     58     public void onSignalingParametersError(final String description);
     59   }
     60 
     61   public RoomParametersFetcher(String roomUrl, String roomMessage,
     62       final RoomParametersFetcherEvents events) {
     63     this.roomUrl = roomUrl;
     64     this.roomMessage = roomMessage;
     65     this.events = events;
     66   }
     67 
     68   public void makeRequest() {
     69     Log.d(TAG, "Connecting to room: " + roomUrl);
     70     httpConnection = new AsyncHttpURLConnection(
     71         "POST", roomUrl, roomMessage,
     72         new AsyncHttpEvents() {
     73           @Override
     74           public void onHttpError(String errorMessage) {
     75             Log.e(TAG, "Room connection error: " + errorMessage);
     76             events.onSignalingParametersError(errorMessage);
     77           }
     78 
     79           @Override
     80           public void onHttpComplete(String response) {
     81             roomHttpResponseParse(response);
     82           }
     83         });
     84     httpConnection.send();
     85   }
     86 
     87   private void roomHttpResponseParse(String response) {
     88     Log.d(TAG, "Room response: " + response);
     89     try {
     90       LinkedList<IceCandidate> iceCandidates = null;
     91       SessionDescription offerSdp = null;
     92       JSONObject roomJson = new JSONObject(response);
     93 
     94       String result = roomJson.getString("result");
     95       if (!result.equals("SUCCESS")) {
     96         events.onSignalingParametersError("Room response error: " + result);
     97         return;
     98       }
     99       response = roomJson.getString("params");
    100       roomJson = new JSONObject(response);
    101       String roomId = roomJson.getString("room_id");
    102       String clientId = roomJson.getString("client_id");
    103       String wssUrl = roomJson.getString("wss_url");
    104       String wssPostUrl = roomJson.getString("wss_post_url");
    105       boolean initiator = (roomJson.getBoolean("is_initiator"));
    106       if (!initiator) {
    107         iceCandidates = new LinkedList<IceCandidate>();
    108         String messagesString = roomJson.getString("messages");
    109         JSONArray messages = new JSONArray(messagesString);
    110         for (int i = 0; i < messages.length(); ++i) {
    111           String messageString = messages.getString(i);
    112           JSONObject message = new JSONObject(messageString);
    113           String messageType = message.getString("type");
    114           Log.d(TAG, "GAE->C #" + i + " : " + messageString);
    115           if (messageType.equals("offer")) {
    116             offerSdp = new SessionDescription(
    117                 SessionDescription.Type.fromCanonicalForm(messageType),
    118                 message.getString("sdp"));
    119           } else if (messageType.equals("candidate")) {
    120             IceCandidate candidate = new IceCandidate(
    121                 message.getString("id"),
    122                 message.getInt("label"),
    123                 message.getString("candidate"));
    124             iceCandidates.add(candidate);
    125           } else {
    126             Log.e(TAG, "Unknown message: " + messageString);
    127           }
    128         }
    129       }
    130       Log.d(TAG, "RoomId: " + roomId + ". ClientId: " + clientId);
    131       Log.d(TAG, "Initiator: " + initiator);
    132       Log.d(TAG, "WSS url: " + wssUrl);
    133       Log.d(TAG, "WSS POST url: " + wssPostUrl);
    134 
    135       LinkedList<PeerConnection.IceServer> iceServers =
    136           iceServersFromPCConfigJSON(roomJson.getString("pc_config"));
    137       boolean isTurnPresent = false;
    138       for (PeerConnection.IceServer server : iceServers) {
    139         Log.d(TAG, "IceServer: " + server);
    140         if (server.uri.startsWith("turn:")) {
    141           isTurnPresent = true;
    142           break;
    143         }
    144       }
    145       // Request TURN servers.
    146       if (!isTurnPresent) {
    147         LinkedList<PeerConnection.IceServer> turnServers =
    148             requestTurnServers(roomJson.getString("turn_url"));
    149         for (PeerConnection.IceServer turnServer : turnServers) {
    150           Log.d(TAG, "TurnServer: " + turnServer);
    151           iceServers.add(turnServer);
    152         }
    153       }
    154 
    155       SignalingParameters params = new SignalingParameters(
    156           iceServers, initiator,
    157           clientId, wssUrl, wssPostUrl,
    158           offerSdp, iceCandidates);
    159       events.onSignalingParametersReady(params);
    160     } catch (JSONException e) {
    161       events.onSignalingParametersError(
    162           "Room JSON parsing error: " + e.toString());
    163     } catch (IOException e) {
    164       events.onSignalingParametersError("Room IO error: " + e.toString());
    165     }
    166   }
    167 
    168   // Requests & returns a TURN ICE Server based on a request URL.  Must be run
    169   // off the main thread!
    170   private LinkedList<PeerConnection.IceServer> requestTurnServers(String url)
    171       throws IOException, JSONException {
    172     LinkedList<PeerConnection.IceServer> turnServers =
    173         new LinkedList<PeerConnection.IceServer>();
    174     Log.d(TAG, "Request TURN from: " + url);
    175     HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
    176     connection.setConnectTimeout(TURN_HTTP_TIMEOUT_MS);
    177     connection.setReadTimeout(TURN_HTTP_TIMEOUT_MS);
    178     int responseCode = connection.getResponseCode();
    179     if (responseCode != 200) {
    180       throw new IOException("Non-200 response when requesting TURN server from "
    181           + url + " : " + connection.getHeaderField(null));
    182     }
    183     InputStream responseStream = connection.getInputStream();
    184     String response = drainStream(responseStream);
    185     connection.disconnect();
    186     Log.d(TAG, "TURN response: " + response);
    187     JSONObject responseJSON = new JSONObject(response);
    188     String username = responseJSON.getString("username");
    189     String password = responseJSON.getString("password");
    190     JSONArray turnUris = responseJSON.getJSONArray("uris");
    191     for (int i = 0; i < turnUris.length(); i++) {
    192       String uri = turnUris.getString(i);
    193       turnServers.add(new PeerConnection.IceServer(uri, username, password));
    194     }
    195     return turnServers;
    196   }
    197 
    198   // Return the list of ICE servers described by a WebRTCPeerConnection
    199   // configuration string.
    200   private LinkedList<PeerConnection.IceServer> iceServersFromPCConfigJSON(
    201       String pcConfig) throws JSONException {
    202     JSONObject json = new JSONObject(pcConfig);
    203     JSONArray servers = json.getJSONArray("iceServers");
    204     LinkedList<PeerConnection.IceServer> ret =
    205         new LinkedList<PeerConnection.IceServer>();
    206     for (int i = 0; i < servers.length(); ++i) {
    207       JSONObject server = servers.getJSONObject(i);
    208       String url = server.getString("urls");
    209       String credential =
    210           server.has("credential") ? server.getString("credential") : "";
    211       ret.add(new PeerConnection.IceServer(url, "", credential));
    212     }
    213     return ret;
    214   }
    215 
    216   // Return the contents of an InputStream as a String.
    217   private static String drainStream(InputStream in) {
    218     Scanner s = new Scanner(in).useDelimiter("\\A");
    219     return s.hasNext() ? s.next() : "";
    220   }
    221 
    222 }
    223