diff --git a/talk/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java b/talk/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java index a83e6696ed..b4e11d6a6c 100644 --- a/talk/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java +++ b/talk/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java @@ -90,6 +90,7 @@ public class AppRTCDemoActivity extends Activity private ImageButton videoScalingButton; private String roomName; private boolean commandLineRun; + private boolean activityRunning; private int runTimeMs; private int startBitrate; private boolean hwCodec; @@ -231,8 +232,8 @@ public class AppRTCDemoActivity extends Activity } else { roomNameView.setText(roomName); } + // For command line execution run connection for and exit. if (commandLineRun && runTimeMs > 0) { - // For command line execution run connection for and exit. videoView.postDelayed(new Runnable() { public void run() { disconnect(); @@ -266,6 +267,7 @@ public class AppRTCDemoActivity extends Activity public void onPause() { super.onPause(); videoView.onPause(); + activityRunning = false; if (pc != null) { pc.stopVideoSource(); } @@ -275,6 +277,7 @@ public class AppRTCDemoActivity extends Activity public void onResume() { super.onResume(); videoView.onResume(); + activityRunning = true; if (pc != null) { pc.startVideoSource(); } @@ -284,6 +287,7 @@ public class AppRTCDemoActivity extends Activity protected void onDestroy() { disconnect(); super.onDestroy(); + activityRunning = false; } private void updateVideoView() { @@ -319,7 +323,7 @@ public class AppRTCDemoActivity extends Activity } private void disconnectWithErrorMessage(final String errorMessage) { - if (commandLineRun) { + if (commandLineRun || !activityRunning) { Log.e(TAG, "Critical error: " + errorMessage); disconnect(); } else { @@ -453,7 +457,7 @@ public class AppRTCDemoActivity extends Activity if (audioManager != null) { // Store existing audio settings and change audio mode to // MODE_IN_COMMUNICATION for best possible VoIP performance. - logAndToast("Initializing the audio manager..."); + Log.d(TAG, "Initializing the audio manager..."); audioManager.init(); } signalingParameters = params; @@ -461,7 +465,7 @@ public class AppRTCDemoActivity extends Activity this, true, true, hwCodec, VideoRendererGui.getEGLContext()), "Failed to initializeAndroidGlobals"); logAndToast("Creating peer connection..."); - pc = new PeerConnectionClient( this, localRender, remoteRender, + pc = new PeerConnectionClient(localRender, remoteRender, signalingParameters, this, startBitrate); if (pc.isHDVideo()) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); diff --git a/talk/examples/android/src/org/appspot/apprtc/PeerConnectionClient.java b/talk/examples/android/src/org/appspot/apprtc/PeerConnectionClient.java index da94cdbb18..64343f49ad 100644 --- a/talk/examples/android/src/org/appspot/apprtc/PeerConnectionClient.java +++ b/talk/examples/android/src/org/appspot/apprtc/PeerConnectionClient.java @@ -27,7 +27,8 @@ package org.appspot.apprtc; -import android.app.Activity; +import android.os.Handler; +import android.os.Looper; import android.util.Log; import org.appspot.apprtc.AppRTCClient.SignalingParameters; @@ -57,7 +58,7 @@ public class PeerConnectionClient { public static final String VIDEO_TRACK_ID = "ARDAMSv0"; public static final String AUDIO_TRACK_ID = "ARDAMSa0"; - private final Activity activity; + private final Handler uiHandler; private PeerConnectionFactory factory; private PeerConnection pc; private VideoSource videoSource; @@ -80,17 +81,16 @@ public class PeerConnectionClient { private MediaStream mediaStream = null; public PeerConnectionClient( - Activity activity, VideoRenderer.Callbacks localRender, VideoRenderer.Callbacks remoteRender, SignalingParameters signalingParameters, PeerConnectionEvents events, int startBitrate) { - this.activity = activity; this.localRender = localRender; this.remoteRender = remoteRender; this.events = events; this.startBitrate = startBitrate; + uiHandler = new Handler(Looper.getMainLooper()); isInitiator = signalingParameters.initiator; queuedRemoteCandidates = new LinkedList(); @@ -162,7 +162,7 @@ public class PeerConnectionClient { } public void createOffer() { - activity.runOnUiThread(new Runnable() { + uiHandler.post(new Runnable() { public void run() { if (pc != null) { isInitiator = true; @@ -173,7 +173,7 @@ public class PeerConnectionClient { } public void createAnswer() { - activity.runOnUiThread(new Runnable() { + uiHandler.post(new Runnable() { public void run() { if (pc != null) { isInitiator = false; @@ -184,7 +184,7 @@ public class PeerConnectionClient { } public void addRemoteIceCandidate(final IceCandidate candidate) { - activity.runOnUiThread(new Runnable() { + uiHandler.post(new Runnable() { public void run() { if (pc != null) { if (queuedRemoteCandidates != null) { @@ -198,7 +198,7 @@ public class PeerConnectionClient { } public void setRemoteDescription(final SessionDescription sdp) { - activity.runOnUiThread(new Runnable() { + uiHandler.post(new Runnable() { public void run() { if (pc != null) { String sdpDescription = preferISAC(sdp.description); @@ -231,7 +231,7 @@ public class PeerConnectionClient { } public void close() { - activity.runOnUiThread(new Runnable() { + uiHandler.post(new Runnable() { public void run() { Log.d(TAG, "Closing peer connection."); if (pc != null) { @@ -284,7 +284,7 @@ public class PeerConnectionClient { private void reportError(final String errorMessage) { Log.e(TAG, "Peerconnection error: " + errorMessage); - activity.runOnUiThread(new Runnable() { + uiHandler.post(new Runnable() { public void run() { events.onPeerConnectionError(errorMessage); } @@ -325,8 +325,7 @@ public class PeerConnectionClient { videoSource.dispose(); } - videoSource = factory.createVideoSource( - capturer, videoConstraints); + videoSource = factory.createVideoSource(capturer, videoConstraints); String trackExtension = frontFacing ? "frontFacing" : "backFacing"; VideoTrack videoTrack = factory.createVideoTrack(VIDEO_TRACK_ID + trackExtension, videoSource); @@ -480,7 +479,7 @@ public class PeerConnectionClient { private class PCObserver implements PeerConnection.Observer { @Override public void onIceCandidate(final IceCandidate candidate){ - activity.runOnUiThread(new Runnable() { + uiHandler.post(new Runnable() { public void run() { events.onIceCandidate(candidate); } @@ -498,13 +497,13 @@ public class PeerConnectionClient { PeerConnection.IceConnectionState newState) { Log.d(TAG, "IceConnectionState: " + newState); if (newState == IceConnectionState.CONNECTED) { - activity.runOnUiThread(new Runnable() { + uiHandler.post(new Runnable() { public void run() { events.onIceConnected(); } }); } else if (newState == IceConnectionState.DISCONNECTED) { - activity.runOnUiThread(new Runnable() { + uiHandler.post(new Runnable() { public void run() { events.onIceDisconnected(); } @@ -522,7 +521,7 @@ public class PeerConnectionClient { @Override public void onAddStream(final MediaStream stream){ - activity.runOnUiThread(new Runnable() { + uiHandler.post(new Runnable() { public void run() { abortUnless(stream.audioTracks.size() <= 1 && stream.videoTracks.size() <= 1, @@ -537,7 +536,7 @@ public class PeerConnectionClient { @Override public void onRemoveStream(final MediaStream stream){ - activity.runOnUiThread(new Runnable() { + uiHandler.post(new Runnable() { public void run() { stream.videoTracks.get(0).dispose(); } @@ -566,7 +565,7 @@ public class PeerConnectionClient { final SessionDescription sdp = new SessionDescription( origSdp.type, preferISAC(origSdp.description)); localSdp = sdp; - activity.runOnUiThread(new Runnable() { + uiHandler.post(new Runnable() { public void run() { if (pc != null) { Log.d(TAG, "Set local SDP from " + sdp.type); @@ -578,7 +577,7 @@ public class PeerConnectionClient { @Override public void onSetSuccess() { - activity.runOnUiThread(new Runnable() { + uiHandler.post(new Runnable() { public void run() { if (pc == null) { return; diff --git a/talk/examples/android/src/org/appspot/apprtc/WebSocketChannelClient.java b/talk/examples/android/src/org/appspot/apprtc/WebSocketChannelClient.java index 33aa63b80d..e30f0db821 100644 --- a/talk/examples/android/src/org/appspot/apprtc/WebSocketChannelClient.java +++ b/talk/examples/android/src/org/appspot/apprtc/WebSocketChannelClient.java @@ -36,12 +36,10 @@ import de.tavendo.autobahn.WebSocketException; import de.tavendo.autobahn.WebSocket.WebSocketConnectionObserver; import java.io.IOException; -import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; -import java.net.URLEncoder; import java.util.LinkedList; import org.json.JSONException; @@ -64,10 +62,6 @@ public class WebSocketChannelClient { private String roomID; private String clientID; private WebSocketConnectionState state; - // Http post/delete message queue. Messages are added from UI thread in - // post() and disconnect() calls. Messages are consumed by AsyncTask's - // background thread. - private LinkedList wsHttpQueue; // WebSocket send queue. Messages are added to the queue when WebSocket // client is not registered and are consumed in register() call. private LinkedList wsSendQueue; @@ -92,7 +86,6 @@ public class WebSocketChannelClient { uiHandler = new Handler(Looper.getMainLooper()); roomID = null; clientID = null; - wsHttpQueue = new LinkedList(); wsSendQueue = new LinkedList(); state = WebSocketConnectionState.NEW; } @@ -192,10 +185,7 @@ public class WebSocketChannelClient { // send through websocket before SDP answer sent through http post will be // resolved. public void post(String message) { - synchronized (wsHttpQueue) { - wsHttpQueue.add(new WsHttpMessage("POST", message)); - } - requestHttpQueueDrainInBackground(); + sendWSSMessage("POST", message); } public void disconnect() { @@ -210,11 +200,7 @@ public class WebSocketChannelClient { ws.disconnect(); // Send DELETE to http WebSocket server. - synchronized (wsHttpQueue) { - wsHttpQueue.clear(); - wsHttpQueue.add(new WsHttpMessage("DELETE", "")); - } - requestHttpQueueDrainInBackground(); + sendWSSMessage("DELETE", ""); state = WebSocketConnectionState.CLOSED; } @@ -241,67 +227,43 @@ public class WebSocketChannelClient { public final String message; } - // TODO(glaznev): This is not good implementation due to discrepancy - // between JS encodeURIComponent() and Java URLEncoder.encode(). - // Remove this once WebSocket server will switch to a different encoding. - private String encodeURIComponent(String s) { - String result = null; - try { - result = URLEncoder.encode(s, "UTF-8") - .replaceAll("\\+", "%20") - .replaceAll("\\%21", "!") - .replaceAll("\\%27", "'") - .replaceAll("\\%28", "(") - .replaceAll("\\%29", ")") - .replaceAll("\\%7E", "~"); - } catch (UnsupportedEncodingException e) { - result = s; + // Asynchronously send POST/DELETE to WebSocket server. + private void sendWSSMessage(String method, String message) { + final WsHttpMessage wsHttpMessage = new WsHttpMessage(method, message); + Runnable runAsync = new Runnable() { + public void run() { + sendWSSMessageAsync(wsHttpMessage); + } + }; + new Thread(runAsync).start(); + } + + private void sendWSSMessageAsync(WsHttpMessage wsHttpMessage) { + if (roomID == null || clientID == null) { + return; } - return result; - } - - // Request an attempt to drain the send queue, on a background thread. - private void requestHttpQueueDrainInBackground() { - (new AsyncTask() { - public Void doInBackground(Void... unused) { - maybeDrainWsHttpQueue(); - return null; + try { + // Send POST or DELETE request. + String postUrl = postServerUrl + "/" + roomID + "/" + clientID; + Log.d(TAG, "WS " + wsHttpMessage.method + " : " + postUrl + " : " + + wsHttpMessage.message); + HttpURLConnection connection = + (HttpURLConnection) new URL(postUrl).openConnection(); + connection.setRequestProperty( + "content-type", "text/plain; charset=utf-8"); + connection.setRequestMethod(wsHttpMessage.method); + if (wsHttpMessage.method.equals("POST")) { + connection.setDoOutput(true); + String message = wsHttpMessage.message; + connection.getOutputStream().write(message.getBytes("UTF-8")); } - }).execute(); - } - - // Send all queued websocket messages. - private void maybeDrainWsHttpQueue() { - synchronized (wsHttpQueue) { - if (roomID == null || clientID == null) { - return; + int responseCode = connection.getResponseCode(); + if (responseCode != 200) { + reportError("Non-200 response to " + wsHttpMessage.method + " : " + + connection.getHeaderField(null)); } - try { - for (WsHttpMessage wsHttpMessage : wsHttpQueue) { - // Send POST request. - String postUrl = postServerUrl + "/" + roomID + "/" + clientID; - Log.d(TAG, "WS " + wsHttpMessage.method + " : " + postUrl + " : " + - wsHttpMessage.message); - HttpURLConnection connection = - (HttpURLConnection) new URL(postUrl).openConnection(); - connection.setDoOutput(true); - connection.setRequestProperty( - "Content-type", "application/x-www-form-urlencoded"); - connection.setRequestMethod(wsHttpMessage.method); - if (wsHttpMessage.message.length() > 0) { - String message = "msg=" + encodeURIComponent(wsHttpMessage.message); - connection.getOutputStream().write(message.getBytes("UTF-8")); - } - String replyHeader = connection.getHeaderField(null); - if (!replyHeader.startsWith("HTTP/1.1 200 ")) { - reportError("Non-200 response to " + wsHttpMessage.method + " : " + - connection.getHeaderField(null)); - } - } - } catch (IOException e) { - reportError("WS POST error: " + e.getMessage()); - } - wsHttpQueue.clear(); + } catch (IOException e) { + reportError("WS POST error: " + e.getMessage()); } }