Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / libjingle / source / talk / examples / android / src / org / appspot / apprtc / AppRTCClient.java
index 5c571fa..5c34fca 100644 (file)
  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-
 package org.appspot.apprtc;
 
-import android.app.Activity;
-import android.os.AsyncTask;
-import android.util.Log;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
+import org.webrtc.IceCandidate;
 import org.webrtc.MediaConstraints;
 import org.webrtc.PeerConnection;
+import org.webrtc.SessionDescription;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.net.URLConnection;
-import java.util.LinkedList;
 import java.util.List;
-import java.util.Scanner;
-
-/**
- * Negotiates signaling for chatting with apprtc.appspot.com "rooms".
- * Uses the client<->server specifics of the apprtc AppEngine webapp.
- *
- * To use: create an instance of this object (registering a message handler) and
- * call connectToRoom().  Once that's done call sendMessage() and wait for the
- * registered handler to be called with received messages.
- */
-public class AppRTCClient {
-  private static final String TAG = "AppRTCClient";
-  private GAEChannelClient channelClient;
-  private final Activity activity;
-  private final GAEChannelClient.MessageHandler gaeHandler;
-  private final IceServersObserver iceServersObserver;
-
-  // These members are only read/written under sendQueue's lock.
-  private LinkedList<String> sendQueue = new LinkedList<String>();
-  private AppRTCSignalingParameters appRTCSignalingParameters;
 
+public interface AppRTCClient {
   /**
-   * Callback fired once the room's signaling parameters specify the set of
-   * ICE servers to use.
+   * Asynchronously connect to an AppRTC room URL, e.g.
+   * https://apprtc.appspot.com/?r=NNN. Once connection is established
+   * onConnectedToRoom() callback with room parameters is invoked.
    */
-  public static interface IceServersObserver {
-    public void onIceServers(List<PeerConnection.IceServer> iceServers);
-  }
-
-  public AppRTCClient(
-      Activity activity, GAEChannelClient.MessageHandler gaeHandler,
-      IceServersObserver iceServersObserver) {
-    this.activity = activity;
-    this.gaeHandler = gaeHandler;
-    this.iceServersObserver = iceServersObserver;
-  }
+  public void connectToRoom(String url);
 
   /**
-   * Asynchronously connect to an AppRTC room URL, e.g.
-   * https://apprtc.appspot.com/?r=NNN and register message-handling callbacks
-   * on its GAE Channel.
+   * Send local SDP (offer or answer, depending on role) to the
+   * other participant.
    */
-  public void connectToRoom(String url) {
-    while (url.indexOf('?') < 0) {
-      // Keep redirecting until we get a room number.
-      (new RedirectResolver()).execute(url);
-      return;  // RedirectResolver above calls us back with the next URL.
-    }
-    (new RoomParameterGetter()).execute(url);
-  }
+  public void sendLocalDescription(final SessionDescription sdp);
 
   /**
-   * Disconnect from the GAE Channel.
+   * Send Ice candidate to the other participant.
    */
-  public void disconnect() {
-    if (channelClient != null) {
-      channelClient.close();
-      channelClient = null;
-    }
-  }
+  public void sendLocalIceCandidate(final IceCandidate candidate);
 
   /**
-   * Queue a message for sending to the room's channel and send it if already
-   * connected (other wise queued messages are drained when the channel is
-     eventually established).
+   * Disconnect from the channel.
    */
-  public synchronized void sendMessage(String msg) {
-    synchronized (sendQueue) {
-      sendQueue.add(msg);
-    }
-    requestQueueDrainInBackground();
-  }
-
-  public boolean isInitiator() {
-    return appRTCSignalingParameters.initiator;
-  }
-
-  public MediaConstraints pcConstraints() {
-    return appRTCSignalingParameters.pcConstraints;
-  }
-
-  public MediaConstraints videoConstraints() {
-    return appRTCSignalingParameters.videoConstraints;
-  }
-
-  public MediaConstraints audioConstraints() {
-    return appRTCSignalingParameters.audioConstraints;
-  }
+  public void disconnect();
 
-  // Struct holding the signaling parameters of an AppRTC room.
-  private class AppRTCSignalingParameters {
+  /**
+   * Struct holding the signaling parameters of an AppRTC room.
+   */
+  public class AppRTCSignalingParameters {
     public final List<PeerConnection.IceServer> iceServers;
-    public final String gaeBaseHref;
-    public final String channelToken;
-    public final String postMessageUrl;
     public final boolean initiator;
     public final MediaConstraints pcConstraints;
     public final MediaConstraints videoConstraints;
@@ -146,13 +69,9 @@ public class AppRTCClient {
 
     public AppRTCSignalingParameters(
         List<PeerConnection.IceServer> iceServers,
-        String gaeBaseHref, String channelToken, String postMessageUrl,
         boolean initiator, MediaConstraints pcConstraints,
         MediaConstraints videoConstraints, MediaConstraints audioConstraints) {
       this.iceServers = iceServers;
-      this.gaeBaseHref = gaeBaseHref;
-      this.channelToken = channelToken;
-      this.postMessageUrl = postMessageUrl;
       this.initiator = initiator;
       this.pcConstraints = pcConstraints;
       this.videoConstraints = videoConstraints;
@@ -160,289 +79,42 @@ public class AppRTCClient {
     }
   }
 
-  // Load the given URL and return the value of the Location header of the
-  // resulting 302 response.  If the result is not a 302, throws.
-  private class RedirectResolver extends AsyncTask<String, Void, String> {
-    @Override
-    protected String doInBackground(String... urls) {
-      if (urls.length != 1) {
-        throw new RuntimeException("Must be called with a single URL");
-      }
-      try {
-        return followRedirect(urls[0]);
-      } catch (IOException e) {
-        throw new RuntimeException(e);
-      }
-    }
-
-    @Override
-    protected void onPostExecute(String url) {
-      connectToRoom(url);
-    }
-
-    private String followRedirect(String url) throws IOException {
-      HttpURLConnection connection = (HttpURLConnection)
-          new URL(url).openConnection();
-      connection.setInstanceFollowRedirects(false);
-      int code = connection.getResponseCode();
-      if (code != HttpURLConnection.HTTP_MOVED_TEMP) {
-        throw new IOException("Unexpected response: " + code + " for " + url +
-            ", with contents: " + drainStream(connection.getInputStream()));
-      }
-      int n = 0;
-      String name, value;
-      while ((name = connection.getHeaderFieldKey(n)) != null) {
-        value = connection.getHeaderField(n);
-        if (name.equals("Location")) {
-          return value;
-        }
-        ++n;
-      }
-      throw new IOException("Didn't find Location header!");
-    }
-  }
-
-  // AsyncTask that converts an AppRTC room URL into the set of signaling
-  // parameters to use with that room.
-  private class RoomParameterGetter
-      extends AsyncTask<String, Void, AppRTCSignalingParameters> {
-    @Override
-    protected AppRTCSignalingParameters doInBackground(String... urls) {
-      if (urls.length != 1) {
-        throw new RuntimeException("Must be called with a single URL");
-      }
-      try {
-        return getParametersForRoomUrl(urls[0]);
-      } catch (JSONException e) {
-        throw new RuntimeException(e);
-      } catch (IOException e) {
-        throw new RuntimeException(e);
-      }
-    }
-
-    @Override
-    protected void onPostExecute(AppRTCSignalingParameters params) {
-      channelClient =
-          new GAEChannelClient(activity, params.channelToken, gaeHandler);
-      synchronized (sendQueue) {
-        appRTCSignalingParameters = params;
-      }
-      requestQueueDrainInBackground();
-      iceServersObserver.onIceServers(appRTCSignalingParameters.iceServers);
-    }
-
-    // Fetches |url| and fishes the signaling parameters out of the JSON.
-    private AppRTCSignalingParameters getParametersForRoomUrl(String url)
-        throws IOException, JSONException {
-      url = url + "&t=json";
-      JSONObject roomJson = new JSONObject(
-          drainStream((new URL(url)).openConnection().getInputStream()));
-
-      if (roomJson.has("error")) {
-        JSONArray errors = roomJson.getJSONArray("error_messages");
-        throw new IOException(errors.toString());
-      }
-
-      String gaeBaseHref = url.substring(0, url.indexOf('?'));
-      String token = roomJson.getString("token");
-      String postMessageUrl = "/message?r=" +
-          roomJson.getString("room_key") + "&u=" +
-          roomJson.getString("me");
-      boolean initiator = roomJson.getInt("initiator") == 1;
-      LinkedList<PeerConnection.IceServer> iceServers =
-          iceServersFromPCConfigJSON(roomJson.getString("pc_config"));
-
-      boolean isTurnPresent = false;
-      for (PeerConnection.IceServer server : iceServers) {
-        if (server.uri.startsWith("turn:")) {
-          isTurnPresent = true;
-          break;
-        }
-      }
-      if (!isTurnPresent) {
-        iceServers.add(requestTurnServer(roomJson.getString("turn_url")));
-      }
-
-      MediaConstraints pcConstraints = constraintsFromJSON(
-          roomJson.getString("pc_constraints"));
-      addDTLSConstraintIfMissing(pcConstraints);
-      Log.d(TAG, "pcConstraints: " + pcConstraints);
-      MediaConstraints videoConstraints = constraintsFromJSON(
-          getAVConstraints("video",
-              roomJson.getString("media_constraints")));
-      Log.d(TAG, "videoConstraints: " + videoConstraints);
-      MediaConstraints audioConstraints = constraintsFromJSON(
-          getAVConstraints("audio",
-              roomJson.getString("media_constraints")));
-      Log.d(TAG, "audioConstraints: " + audioConstraints);
-
-      return new AppRTCSignalingParameters(
-          iceServers, gaeBaseHref, token, postMessageUrl, initiator,
-          pcConstraints, videoConstraints, audioConstraints);
-    }
-
-    // Mimic Chrome and set DtlsSrtpKeyAgreement to true if not set to false by
-    // the web-app.
-    private void addDTLSConstraintIfMissing(
-        MediaConstraints pcConstraints) {
-      for (MediaConstraints.KeyValuePair pair : pcConstraints.mandatory) {
-        if (pair.getKey().equals("DtlsSrtpKeyAgreement")) {
-          return;
-        }
-      }
-      for (MediaConstraints.KeyValuePair pair : pcConstraints.optional) {
-        if (pair.getKey().equals("DtlsSrtpKeyAgreement")) {
-          return;
-        }
-      }
-      // DTLS isn't being suppressed (e.g. for debug=loopback calls), so enable
-      // it by default.
-      pcConstraints.optional.add(
-          new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
-    }
-
-    // Return the constraints specified for |type| of "audio" or "video" in
-    // |mediaConstraintsString|.
-    private String getAVConstraints(
-        String type, String mediaConstraintsString) {
-      try {
-        JSONObject json = new JSONObject(mediaConstraintsString);
-        // Tricksy handling of values that are allowed to be (boolean or
-        // MediaTrackConstraints) by the getUserMedia() spec.  There are three
-        // cases below.
-        if (!json.has(type) || !json.optBoolean(type, true)) {
-          // Case 1: "audio"/"video" is not present, or is an explicit "false"
-          // boolean.
-          return null;
-        }
-        if (json.optBoolean(type, false)) {
-          // Case 2: "audio"/"video" is an explicit "true" boolean.
-          return "{\"mandatory\": {}, \"optional\": []}";
-        }
-        // Case 3: "audio"/"video" is an object.
-        return json.getJSONObject(type).toString();
-      } catch (JSONException e) {
-        throw new RuntimeException(e);
-      }
-    }
-
-    private MediaConstraints constraintsFromJSON(String jsonString) {
-      if (jsonString == null) {
-        return null;
-      }
-      try {
-        MediaConstraints constraints = new MediaConstraints();
-        JSONObject json = new JSONObject(jsonString);
-        JSONObject mandatoryJSON = json.optJSONObject("mandatory");
-        if (mandatoryJSON != null) {
-          JSONArray mandatoryKeys = mandatoryJSON.names();
-          if (mandatoryKeys != null) {
-            for (int i = 0; i < mandatoryKeys.length(); ++i) {
-              String key = mandatoryKeys.getString(i);
-              String value = mandatoryJSON.getString(key);
-              constraints.mandatory.add(
-                  new MediaConstraints.KeyValuePair(key, value));
-            }
-          }
-        }
-        JSONArray optionalJSON = json.optJSONArray("optional");
-        if (optionalJSON != null) {
-          for (int i = 0; i < optionalJSON.length(); ++i) {
-            JSONObject keyValueDict = optionalJSON.getJSONObject(i);
-            String key = keyValueDict.names().getString(0);
-            String value = keyValueDict.getString(key);
-            constraints.optional.add(
-                new MediaConstraints.KeyValuePair(key, value));
-          }
-        }
-        return constraints;
-      } catch (JSONException e) {
-        throw new RuntimeException(e);
-      }
-    }
-
-    // Requests & returns a TURN ICE Server based on a request URL.  Must be run
-    // off the main thread!
-    private PeerConnection.IceServer requestTurnServer(String url) {
-      try {
-        URLConnection connection = (new URL(url)).openConnection();
-        connection.addRequestProperty("user-agent", "Mozilla/5.0");
-        connection.addRequestProperty("origin", "https://apprtc.appspot.com");
-        String response = drainStream(connection.getInputStream());
-        JSONObject responseJSON = new JSONObject(response);
-        String uri = responseJSON.getJSONArray("uris").getString(0);
-        String username = responseJSON.getString("username");
-        String password = responseJSON.getString("password");
-        return new PeerConnection.IceServer(uri, username, password);
-      } catch (JSONException e) {
-        throw new RuntimeException(e);
-      } catch (IOException e) {
-        throw new RuntimeException(e);
-      }
-    }
-  }
-
-  // Return the list of ICE servers described by a WebRTCPeerConnection
-  // configuration string.
-  private LinkedList<PeerConnection.IceServer> iceServersFromPCConfigJSON(
-      String pcConfig) {
-    try {
-      JSONObject json = new JSONObject(pcConfig);
-      JSONArray servers = json.getJSONArray("iceServers");
-      LinkedList<PeerConnection.IceServer> ret =
-          new LinkedList<PeerConnection.IceServer>();
-      for (int i = 0; i < servers.length(); ++i) {
-        JSONObject server = servers.getJSONObject(i);
-        String url = server.getString("urls");
-        String credential =
-            server.has("credential") ? server.getString("credential") : "";
-        ret.add(new PeerConnection.IceServer(url, "", credential));
-      }
-      return ret;
-    } catch (JSONException e) {
-      throw new RuntimeException(e);
-    }
-  }
-
-  // Request an attempt to drain the send queue, on a background thread.
-  private void requestQueueDrainInBackground() {
-    (new AsyncTask<Void, Void, Void>() {
-      public Void doInBackground(Void... unused) {
-        maybeDrainQueue();
-        return null;
-      }
-    }).execute();
-  }
-
-  // Send all queued messages if connected to the room.
-  private void maybeDrainQueue() {
-    synchronized (sendQueue) {
-      if (appRTCSignalingParameters == null) {
-        return;
-      }
-      try {
-        for (String msg : sendQueue) {
-          URLConnection connection = new URL(
-              appRTCSignalingParameters.gaeBaseHref +
-              appRTCSignalingParameters.postMessageUrl).openConnection();
-          connection.setDoOutput(true);
-          connection.getOutputStream().write(msg.getBytes("UTF-8"));
-          if (!connection.getHeaderField(null).startsWith("HTTP/1.1 200 ")) {
-            throw new IOException(
-                "Non-200 response to POST: " + connection.getHeaderField(null) +
-                " for msg: " + msg);
-          }
-        }
-      } catch (IOException e) {
-        throw new RuntimeException(e);
-      }
-      sendQueue.clear();
-    }
-  }
-
-  // Return the contents of an InputStream as a String.
-  private static String drainStream(InputStream in) {
-    Scanner s = new Scanner(in).useDelimiter("\\A");
-    return s.hasNext() ? s.next() : "";
+  /**
+   * Callback interface for messages delivered on signalling channel.
+   *
+   * Methods are guaranteed to be invoked on the UI thread of |activity|.
+   */
+  public static interface AppRTCSignalingEvents {
+    /**
+     * Callback fired once the room's signaling parameters
+     * AppRTCSignalingParameters are extracted.
+     */
+    public void onConnectedToRoom(final AppRTCSignalingParameters params);
+
+    /**
+     * Callback fired once channel for signaling messages is opened and
+     * ready to receive messages.
+     */
+    public void onChannelOpen();
+
+    /**
+     * Callback fired once remote SDP is received.
+     */
+    public void onRemoteDescription(final SessionDescription sdp);
+
+    /**
+     * Callback fired once remote Ice candidate is received.
+     */
+    public void onRemoteIceCandidate(final IceCandidate candidate);
+
+    /**
+     * Callback fired once channel is closed.
+     */
+    public void onChannelClose();
+
+    /**
+     * Callback fired once channel error happened.
+     */
+    public void onChannelError(int code, String description);
   }
 }