| Index: components/devtools_bridge/android/java/src/org/chromium/components/devtools_bridge/SessionDependencyFactory.java
|
| diff --git a/components/devtools_bridge/android/java/src/org/chromium/components/devtools_bridge/SessionDependencyFactory.java b/components/devtools_bridge/android/java/src/org/chromium/components/devtools_bridge/SessionDependencyFactory.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..c39de43774dfb84edf75dc5cf07a7bcbd3a650c3
|
| --- /dev/null
|
| +++ b/components/devtools_bridge/android/java/src/org/chromium/components/devtools_bridge/SessionDependencyFactory.java
|
| @@ -0,0 +1,370 @@
|
| +// 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 org.webrtc.DataChannel;
|
| +import org.webrtc.IceCandidate;
|
| +import org.webrtc.MediaConstraints;
|
| +import org.webrtc.MediaStream;
|
| +import org.webrtc.PeerConnection;
|
| +import org.webrtc.PeerConnectionFactory;
|
| +import org.webrtc.SdpObserver;
|
| +import org.webrtc.SessionDescription;
|
| +
|
| +import java.nio.ByteBuffer;
|
| +import java.util.ArrayList;
|
| +import java.util.List;
|
| +
|
| +/**
|
| + * Implements AbstractDataChannel and AbstractPeerConnection on top of org.webrtc.* API.
|
| + * Isolation is needed because some configuration of DevTools bridge may not be based on
|
| + * Java API. Native implementation of SessionDependencyFactory will be added for this case.
|
| + * In addition abstraction layer isolates SessionBase from complexity of underlying API
|
| + * beside used features.
|
| + */
|
| +public class SessionDependencyFactory {
|
| + private final PeerConnectionFactory mFactory = new PeerConnectionFactory();
|
| +
|
| + public AbstractPeerConnection createPeerConnection(
|
| + RTCConfiguration config, AbstractPeerConnection.Observer observer) {
|
| + MediaConstraints constraints = new MediaConstraints();
|
| + constraints.mandatory.add(
|
| + new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
|
| + return new PeerConnectionAdapter(
|
| + mFactory.createPeerConnection(convert(config), constraints,
|
| + new PeerConnnectionObserverAdapter(observer)), observer);
|
| + }
|
| +
|
| + public void dispose() {
|
| + mFactory.dispose();
|
| + }
|
| +
|
| + private static AbstractPeerConnection.SessionDescriptionType convertType(
|
| + SessionDescription.Type type) {
|
| + switch (type) {
|
| + case OFFER:
|
| + return AbstractPeerConnection.SessionDescriptionType.OFFER;
|
| + case ANSWER:
|
| + return AbstractPeerConnection.SessionDescriptionType.ANSWER;
|
| + default:
|
| + throw new IllegalArgumentException(type.toString());
|
| + }
|
| + }
|
| +
|
| + private static SessionDescription.Type convertType(
|
| + AbstractPeerConnection.SessionDescriptionType type) {
|
| + switch (type) {
|
| + case OFFER:
|
| + return SessionDescription.Type.OFFER;
|
| + case ANSWER:
|
| + return SessionDescription.Type.ANSWER;
|
| + default:
|
| + throw new IllegalArgumentException(type.toString());
|
| + }
|
| + }
|
| +
|
| + private static AbstractPeerConnection.IceCandidate convert(IceCandidate candidate) {
|
| + return new AbstractPeerConnection.IceCandidate(
|
| + candidate.sdpMid, candidate.sdpMLineIndex, candidate.sdp);
|
| + }
|
| +
|
| + private static IceCandidate convert(AbstractPeerConnection.IceCandidate candidate) {
|
| + return new IceCandidate(candidate.sdpMid, candidate.sdpMLineIndex, candidate.sdp);
|
| + }
|
| +
|
| + private static List<PeerConnection.IceServer> convert(RTCConfiguration config) {
|
| + List<PeerConnection.IceServer> result = new ArrayList<PeerConnection.IceServer>();
|
| + for (RTCConfiguration.IceServer server : config.iceServers) {
|
| + result.add(new PeerConnection.IceServer(
|
| + server.uri, server.username, server.credential));
|
| + }
|
| + return result;
|
| + }
|
| +
|
| + public static DataChannelAdapter createDataChannel(PeerConnection connection, int channelId) {
|
| + DataChannel.Init init = new DataChannel.Init();
|
| + init.ordered = true;
|
| + init.negotiated = true;
|
| + init.id = channelId;
|
| + return new DataChannelAdapter(connection.createDataChannel("", init));
|
| + }
|
| +
|
| + private static final class DataChannelAdapter extends AbstractDataChannel {
|
| + private final DataChannel mAdaptee;
|
| +
|
| + public DataChannelAdapter(DataChannel adaptee) {
|
| + mAdaptee = adaptee;
|
| + }
|
| +
|
| + @Override
|
| + public void dispose() {
|
| + mAdaptee.dispose();
|
| + }
|
| +
|
| + @Override
|
| + public void close() {
|
| + mAdaptee.close();
|
| + }
|
| +
|
| + @Override
|
| + public void send(ByteBuffer message, AbstractDataChannel.MessageType type) {
|
| + assert message.remaining() > 0;
|
| + mAdaptee.send(new DataChannel.Buffer(
|
| + message, type == AbstractDataChannel.MessageType.BINARY));
|
| + }
|
| +
|
| + @Override
|
| + public void registerObserver(Observer observer) {
|
| + mAdaptee.registerObserver(new DataChannelObserverAdapter(observer, mAdaptee));
|
| + }
|
| +
|
| + @Override
|
| + public void unregisterObserver() {
|
| + mAdaptee.unregisterObserver();
|
| + }
|
| + }
|
| +
|
| + private static final class DataChannelObserverAdapter implements DataChannel.Observer {
|
| + private final AbstractDataChannel.Observer mAdaptee;
|
| + private final DataChannel mDataChannel;
|
| + private AbstractDataChannel.State mState = AbstractDataChannel.State.CLOSED;
|
| +
|
| + public DataChannelObserverAdapter(
|
| + AbstractDataChannel.Observer adaptee, DataChannel dataChannel) {
|
| + mAdaptee = adaptee;
|
| + mDataChannel = dataChannel;
|
| + }
|
| +
|
| + @Override
|
| + public void onStateChange() {
|
| + AbstractDataChannel.State state = mDataChannel.state() == DataChannel.State.OPEN ?
|
| + AbstractDataChannel.State.OPEN : AbstractDataChannel.State.CLOSED;
|
| + if (mState != state) {
|
| + mState = state;
|
| + mAdaptee.onStateChange(state);
|
| + }
|
| + }
|
| +
|
| + @Override
|
| + public void onMessage(DataChannel.Buffer buffer) {
|
| + assert buffer.data.remaining() > 0;
|
| + mAdaptee.onMessage(buffer.data);
|
| + }
|
| + }
|
| +
|
| + private abstract static class SetHandler implements SdpObserver {
|
| + @Override
|
| + public final void onCreateSuccess(SessionDescription description) {
|
| + assert false;
|
| + }
|
| +
|
| + @Override
|
| + public final void onCreateFailure(String error) {
|
| + assert false;
|
| + }
|
| + }
|
| +
|
| + private abstract static class CreateHandler implements SdpObserver {
|
| + @Override
|
| + public final void onSetSuccess() {
|
| + assert false;
|
| + }
|
| +
|
| + @Override
|
| + public final void onSetFailure(String error) {
|
| + assert false;
|
| + }
|
| + }
|
| +
|
| + private static final class CreateAndSetHandler extends CreateHandler {
|
| + private final PeerConnectionAdapter mConnection;
|
| + private final AbstractPeerConnection.Observer mObserver;
|
| +
|
| + public CreateAndSetHandler(PeerConnectionAdapter connection,
|
| + AbstractPeerConnection.Observer observer) {
|
| + mConnection = connection;
|
| + mObserver = observer;
|
| + }
|
| +
|
| + @Override
|
| + public void onCreateSuccess(final SessionDescription localDescription) {
|
| + mConnection.setLocalDescriptionOnSignalingThread(localDescription);
|
| + }
|
| +
|
| + @Override
|
| + public void onCreateFailure(String description) {
|
| + mObserver.onFailure(description);
|
| + }
|
| + }
|
| +
|
| + private static final class LocalSetHandler extends SetHandler {
|
| + private final SessionDescription mLocalDescription;
|
| + private final AbstractPeerConnection.Observer mObserver;
|
| +
|
| + public LocalSetHandler(SessionDescription localDescription,
|
| + AbstractPeerConnection.Observer observer) {
|
| + mLocalDescription = localDescription;
|
| + mObserver = observer;
|
| + }
|
| +
|
| + @Override
|
| + public void onSetSuccess() {
|
| + mObserver.onLocalDescriptionCreatedAndSet(
|
| + convertType(mLocalDescription.type), mLocalDescription.description);
|
| + }
|
| +
|
| + @Override
|
| + public void onSetFailure(String description) {
|
| + mObserver.onFailure(description);
|
| + }
|
| + }
|
| +
|
| + private static final class SetRemoteDescriptionHandler extends SetHandler {
|
| + private final AbstractPeerConnection.Observer mObserver;
|
| +
|
| + public SetRemoteDescriptionHandler(AbstractPeerConnection.Observer observer) {
|
| + mObserver = observer;
|
| + }
|
| +
|
| + @Override
|
| + public void onSetSuccess() {
|
| + mObserver.onRemoteDescriptionSet();
|
| + }
|
| +
|
| + @Override
|
| + public void onSetFailure(String description) {
|
| + mObserver.onFailure(description);
|
| + }
|
| + }
|
| +
|
| + private static final class PeerConnectionAdapter extends AbstractPeerConnection {
|
| + private PeerConnection mAdaptee;
|
| + private final Observer mObserver;
|
| +
|
| + // Only access from signaling thread and disposing need synchronization.
|
| + private final Object mDisposeLock = new Object();
|
| +
|
| + public PeerConnectionAdapter(PeerConnection adaptee, Observer observer) {
|
| + mAdaptee = adaptee;
|
| + mObserver = observer;
|
| + }
|
| +
|
| + public void setLocalDescriptionOnSignalingThread(SessionDescription description) {
|
| + synchronized (mDisposeLock) {
|
| + if (mAdaptee == null)
|
| + return;
|
| +
|
| + mAdaptee.setLocalDescription(
|
| + new LocalSetHandler(description, mObserver), description);
|
| + }
|
| + }
|
| +
|
| + @Override
|
| + public void createAndSetLocalDescription(SessionDescriptionType type) {
|
| + CreateAndSetHandler handler = new CreateAndSetHandler(this, mObserver);
|
| + switch (type) {
|
| + case OFFER:
|
| + mAdaptee.createOffer(handler, new MediaConstraints());
|
| + break;
|
| +
|
| + case ANSWER:
|
| + mAdaptee.createAnswer(handler, new MediaConstraints());
|
| + break;
|
| +
|
| + default:
|
| + assert false;
|
| + }
|
| + }
|
| +
|
| + @Override
|
| + public void setRemoteDescription(SessionDescriptionType type, String description) {
|
| + mAdaptee.setRemoteDescription(new SetRemoteDescriptionHandler(mObserver),
|
| + new SessionDescription(convertType(type), description));
|
| + }
|
| +
|
| + @Override
|
| + public void addIceCandidate(String candidate) {
|
| + mAdaptee.addIceCandidate(convert(
|
| + AbstractPeerConnection.IceCandidate.fromString(candidate)));
|
| + }
|
| +
|
| + @Override
|
| + public void dispose() {
|
| + synchronized (mDisposeLock) {
|
| + mAdaptee.dispose();
|
| + mAdaptee = null;
|
| + }
|
| + }
|
| +
|
| + @Override
|
| + public AbstractDataChannel createDataChannel(int channelId) {
|
| + DataChannel.Init init = new DataChannel.Init();
|
| + init.ordered = true;
|
| + init.negotiated = true;
|
| + init.id = channelId;
|
| + return new DataChannelAdapter(mAdaptee.createDataChannel("", init));
|
| + }
|
| + }
|
| +
|
| + private static final class PeerConnnectionObserverAdapter implements PeerConnection.Observer {
|
| + private final AbstractPeerConnection.Observer mAdaptee;
|
| + private boolean mConnected = false;
|
| +
|
| + public PeerConnnectionObserverAdapter(AbstractPeerConnection.Observer adaptee) {
|
| + mAdaptee = adaptee;
|
| + }
|
| +
|
| + @Override
|
| + public void onIceCandidate(IceCandidate candidate) {
|
| + mAdaptee.onIceCandidate(convert(candidate).toString());
|
| + }
|
| +
|
| + @Override
|
| + public void onSignalingChange(PeerConnection.SignalingState newState) {}
|
| +
|
| + @Override
|
| + public void onIceConnectionChange(PeerConnection.IceConnectionState newState) {
|
| + boolean connected = isConnected(newState);
|
| + if (mConnected != connected) {
|
| + mConnected = connected;
|
| + mAdaptee.onIceConnectionChange(connected);
|
| + }
|
| + }
|
| +
|
| + private static boolean isConnected(PeerConnection.IceConnectionState newState) {
|
| + switch (newState) {
|
| + case CONNECTED:
|
| + case COMPLETED:
|
| + return true;
|
| + default:
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + @Override
|
| + public void onIceGatheringChange(PeerConnection.IceGatheringState newState) {}
|
| +
|
| + @Override
|
| + public void onDataChannel(DataChannel dataChannel) {
|
| + // Remote peer added non-prenegotiated data channel. It's not supported.
|
| + dataChannel.dispose();
|
| + }
|
| +
|
| + @Override
|
| + public void onAddStream(MediaStream stream) {}
|
| +
|
| + @Override
|
| + public void onRemoveStream(MediaStream stream) {}
|
| +
|
| + @Override
|
| + public void onRenegotiationNeeded() {
|
| + }
|
| +
|
| + @Override
|
| + public void onError() {
|
| + assert false; // TODO(serya): add meaningful handling strategy.
|
| + }
|
| + }
|
| +}
|
|
|