blob: 90c8b34fb6ab17ea0e84d9c4bc1097ee63d36d3b [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.components.devtools_bridge;
import java.util.List;
/**
* DevTools Bridge server session. Handles connection with a ClientSession.
* See SessionBase description for more detais.
*/
public class ServerSession extends SessionBase implements SessionBase.ServerSessionInterface {
private NegotiationCallback mNegotiationCallback;
private IceExchangeCallback mIceExchangeCallback;
private boolean mIceEchangeRequested = false;
protected int mGatheringDelayMs = 200;
public ServerSession(SessionDependencyFactory factory,
Executor executor,
String defaultSocketName) {
super(factory, executor, new SocketTunnelServer(defaultSocketName));
}
@Override
public void stop() {
super.stop();
if (mNegotiationCallback != null) {
mNegotiationCallback.onFailure("Session stopped");
mNegotiationCallback = null;
}
if (mIceExchangeCallback != null) {
mIceExchangeCallback.onFailure("Session stopped");
mIceExchangeCallback = null;
}
}
@Override
public void startSession(RTCConfiguration config,
String offer,
NegotiationCallback callback) {
checkCalledOnSessionThread();
if (isStarted()) {
callback.onFailure("Session already started");
return;
}
ClientMessageHandler handler = new ClientMessageHandler();
start(config, handler);
negotiate(offer, callback);
}
@Override
public void renegotiate(String offer, NegotiationCallback callback) {
checkCalledOnSessionThread();
if (!isStarted()) {
callback.onFailure("Session is not started");
return;
}
callback.onFailure("Not implemented");
}
private void negotiate(String offer, NegotiationCallback callback) {
if (mNegotiationCallback != null) {
callback.onFailure("Negotiation already in progress");
return;
}
mNegotiationCallback = callback;
// If success will call onRemoteDescriptionSet.
connection().setRemoteDescription(
AbstractPeerConnection.SessionDescriptionType.OFFER, offer);
}
protected void onRemoteDescriptionSet() {
// If success will call onLocalDescriptionCreatedAndSet.
connection().createAndSetLocalDescription(
AbstractPeerConnection.SessionDescriptionType.ANSWER);
}
@Override
protected void onLocalDescriptionCreatedAndSet(
AbstractPeerConnection.SessionDescriptionType type, String description) {
assert type == AbstractPeerConnection.SessionDescriptionType.ANSWER;
mNegotiationCallback.onSuccess(description);
mNegotiationCallback = null;
onSessionNegotiated();
}
protected void onSessionNegotiated() {
if (!isControlChannelOpened())
startAutoCloseTimer();
}
@Override
public void iceExchange(List<String> clientCandidates,
IceExchangeCallback callback) {
checkCalledOnSessionThread();
if (!isStarted()) {
callback.onFailure("Session disposed");
return;
}
if (mNegotiationCallback != null || mIceExchangeCallback != null) {
callback.onFailure("Concurrent requests detected");
return;
}
mIceExchangeCallback = callback;
addIceCandidates(clientCandidates);
// Give libjingle some time for gathering ice candidates.
postOnSessionThread(mGatheringDelayMs, new Runnable() {
@Override
public void run() {
if (isStarted())
sendIceCandidatesBack();
}
});
}
private void sendIceCandidatesBack() {
mIceExchangeCallback.onSuccess(takeIceCandidates());
mIceExchangeCallback = null;
mIceEchangeRequested = false;
}
@Override
protected void onControlChannelOpened() {
stopAutoCloseTimer();
}
@Override
protected void onFailure(String message) {
if (mNegotiationCallback != null) {
mNegotiationCallback.onFailure(message);
mNegotiationCallback = null;
}
super.onFailure(message);
}
@Override
protected void onIceCandidate(String candidate) {
super.onIceCandidate(candidate);
if (isControlChannelOpened() && !mIceEchangeRequested) {
// New ICE candidate may improve connection even if control channel operable.
// If control channel closed client will exchange candidates anyway.
sendControlMessage(new SessionControlMessages.IceExchangeMessage());
mIceEchangeRequested = true;
}
}
protected SocketTunnelServer createSocketTunnelServer(String serverSocketName) {
return new SocketTunnelServer(serverSocketName);
}
private final class ClientMessageHandler extends SessionControlMessages.ClientMessageHandler {
@Override
protected void onMessage(SessionControlMessages.ClientMessage message) {
switch (message.type) {
case UNKNOWN_REQUEST:
sendControlMessage(((SessionControlMessages.UnknownRequestMessage) message)
.createResponse());
break;
}
}
}
@Override
protected void sendControlMessage(SessionControlMessages.Message<?> message) {
assert message instanceof SessionControlMessages.ServerMessage;
super.sendControlMessage(message);
}
}