void dataChannelSend(int dataChannelId, String data, String type) { DataChannel dataChannel = dataChannels.get(dataChannelId); if (dataChannel != null) { byte[] byteArray; if (type.equals("text")) { try { byteArray = data.getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { Log.d(TAG, "Could not encode text string as UTF-8."); return; } } else if (type.equals("binary")) { byteArray = Base64.decode(data, Base64.NO_WRAP); } else { Log.e(TAG, "Unsupported data type: " + type); return; } ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray); DataChannel.Buffer buffer = new DataChannel.Buffer(byteBuffer, type.equals("binary")); dataChannel.send(buffer); } else { Log.d(TAG, "dataChannelSend() dataChannel is null"); } }
@Override public void onMessage(DataChannel.Buffer buffer) { WritableMap params = Arguments.createMap(); params.putInt("id", mId); params.putInt("peerConnectionId", peerConnectionId); byte[] bytes; if (buffer.data.hasArray()) { bytes = buffer.data.array(); } else { bytes = new byte[buffer.data.remaining()]; buffer.data.get(bytes); } if (buffer.binary) { params.putString("type", "binary"); params.putString("data", Base64.encodeToString(bytes, Base64.NO_WRAP)); } else { params.putString("type", "text"); params.putString("data", new String(bytes, Charset.forName("UTF-8"))); } webRTCModule.sendEvent("dataChannelReceiveMessage", params); }
private void handleAnswerCall(Intent intent) { if (callState != CallState.STATE_LOCAL_RINGING) { Log.w(TAG, "Can only answer from ringing!"); return; } if (peerConnection == null || dataChannel == null || recipient == null || callId == null) { throw new AssertionError("assert"); } DatabaseFactory.getSmsDatabase(this).insertReceivedCall(recipient.getNumber()); this.peerConnection.setAudioEnabled(true); this.peerConnection.setVideoEnabled(true); this.dataChannel.send(new DataChannel.Buffer(ByteBuffer.wrap(Data.newBuilder().setConnected(Connected.newBuilder().setId(this.callId)).build().toByteArray()), false)); intent.putExtra(EXTRA_CALL_ID, callId); intent.putExtra(EXTRA_REMOTE_NUMBER, recipient.getNumber()); handleCallConnected(intent); }
private void handleDenyCall(Intent intent) { if (callState != CallState.STATE_LOCAL_RINGING) { Log.w(TAG, "Can only deny from ringing!"); return; } if (recipient == null || callId == null || dataChannel == null) { throw new AssertionError("assert"); } this.dataChannel.send(new DataChannel.Buffer(ByteBuffer.wrap(Data.newBuilder().setHangup(Hangup.newBuilder().setId(this.callId)).build().toByteArray()), false)); sendMessage(this.recipient, SignalServiceCallMessage.forHangup(new HangupMessage(this.callId))); DatabaseFactory.getSmsDatabase(this).insertMissedCall(recipient.getNumber()); this.terminate(); }
/** * Send a message to the remote client through the direct connection. * * @param message The message to send * @param completionListener A listener to receive a notification on the success of the asynchronous operation */ public void sendMessage(String message, final Respoke.TaskCompletionListener completionListener) { if (isActive()) { JSONObject jsonMessage = new JSONObject(); try { jsonMessage.put("message", message); byte[] rawMessage = jsonMessage.toString().getBytes(Charset.forName("UTF-8")); ByteBuffer directData = ByteBuffer.allocateDirect(rawMessage.length); directData.put(rawMessage); directData.flip(); DataChannel.Buffer data = new DataChannel.Buffer(directData, false); if (dataChannel.send(data)) { Respoke.postTaskSuccess(completionListener); } else { Respoke.postTaskError(completionListener, "Error sending message"); } } catch (JSONException e) { Respoke.postTaskError(completionListener, "Unable to encode message to JSON"); } } else { Respoke.postTaskError(completionListener, "DataChannel not in an open state"); } }
/** * Notify the direct connection instance that the peer connection has opened the specified data channel * * @param newDataChannel The DataChannel that has opened */ public void peerConnectionDidOpenDataChannel(DataChannel newDataChannel) { if (null != dataChannel) { // Replacing the previous connection, so disable observer messages from the old instance dataChannel.unregisterObserver(); } else { new Handler(Looper.getMainLooper()).post(new Runnable() { public void run() { if (null != listenerReference) { Listener listener = listenerReference.get(); if (null != listener) { listener.onStart(RespokeDirectConnection.this); } } } }); } dataChannel = newDataChannel; newDataChannel.registerObserver(this); }
void createDataChannel(String label, ReadableMap config) { DataChannel.Init init = new DataChannel.Init(); if (config != null) { if (config.hasKey("id")) { init.id = config.getInt("id"); } if (config.hasKey("ordered")) { init.ordered = config.getBoolean("ordered"); } if (config.hasKey("maxRetransmitTime")) { init.maxRetransmitTimeMs = config.getInt("maxRetransmitTime"); } if (config.hasKey("maxRetransmits")) { init.maxRetransmits = config.getInt("maxRetransmits"); } if (config.hasKey("protocol")) { init.protocol = config.getString("protocol"); } if (config.hasKey("negotiated")) { init.negotiated = config.getBoolean("negotiated"); } } DataChannel dataChannel = peerConnection.createDataChannel(label, init); // XXX RTP data channels are not defined by the WebRTC standard, have // been deprecated in Chromium, and Google have decided (in 2015) to no // longer support them (in the face of multiple reported issues of // breakages). int dataChannelId = init.id; if (-1 != dataChannelId) { dataChannels.put(dataChannelId, dataChannel); registerDataChannelObserver(dataChannelId, dataChannel); } }
void dataChannelClose(int dataChannelId) { DataChannel dataChannel = dataChannels.get(dataChannelId); if (dataChannel != null) { dataChannel.close(); dataChannels.remove(dataChannelId); } else { Log.d(TAG, "dataChannelClose() dataChannel is null"); } }
@Override public void onDataChannel(DataChannel dataChannel) { // XXX Unfortunately, the Java WebRTC API doesn't expose the id // of the underlying C++/native DataChannel (even though the // WebRTC standard defines the DataChannel.id property). As a // workaround, generated an id which will surely not clash with // the ids of the remotely-opened (and standard-compliant // locally-opened) DataChannels. int dataChannelId = -1; // The RTCDataChannel.id space is limited to unsigned short by // the standard: // https://www.w3.org/TR/webrtc/#dom-datachannel-id. // Additionally, 65535 is reserved due to SCTP INIT and // INIT-ACK chunks only allowing a maximum of 65535 streams to // be negotiated (as defined by the WebRTC Data Channel // Establishment Protocol). for (int i = 65536; i <= Integer.MAX_VALUE; ++i) { if (null == dataChannels.get(i, null)) { dataChannelId = i; break; } } if (-1 == dataChannelId) { return; } WritableMap dataChannelParams = Arguments.createMap(); dataChannelParams.putInt("id", dataChannelId); dataChannelParams.putString("label", dataChannel.label()); WritableMap params = Arguments.createMap(); params.putInt("id", id); params.putMap("dataChannel", dataChannelParams); dataChannels.put(dataChannelId, dataChannel); registerDataChannelObserver(dataChannelId, dataChannel); webRTCModule.sendEvent("peerConnectionDidOpenDataChannel", params); }
private void registerDataChannelObserver(int dcId, DataChannel dataChannel) { // DataChannel.registerObserver implementation does not allow to // unregister, so the observer is registered here and is never // unregistered dataChannel.registerObserver( new DataChannelObserver(webRTCModule, id, dcId, dataChannel)); }
@Nullable private String dataChannelStateString(DataChannel.State dataChannelState) { switch (dataChannelState) { case CONNECTING: return "connecting"; case OPEN: return "open"; case CLOSING: return "closing"; case CLOSED: return "closed"; } return null; }
private void handleCallConnected(Intent intent) { if (callState != CallState.STATE_REMOTE_RINGING && callState != CallState.STATE_LOCAL_RINGING) { Log.w(TAG, "Ignoring call connected for unknown state: " + callState); return; } if (!Util.isEquals(this.callId, getCallId(intent))) { Log.w(TAG, "Ignoring connected for unknown call id: " + getCallId(intent)); return; } if (recipient == null || peerConnection == null || dataChannel == null) { throw new AssertionError("assert"); } audioManager.startCommunication(callState == CallState.STATE_REMOTE_RINGING); bluetoothStateManager.setWantsConnection(true); callState = CallState.STATE_CONNECTED; if (localVideoEnabled) lockManager.updatePhoneState(LockManager.PhoneState.IN_VIDEO); else lockManager.updatePhoneState(LockManager.PhoneState.IN_CALL); sendMessage(WebRtcViewModel.State.CALL_CONNECTED, recipient, localVideoEnabled, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); unregisterPowerButtonReceiver(); setCallInProgressNotification(TYPE_ESTABLISHED, recipient); this.peerConnection.setAudioEnabled(microphoneEnabled); this.peerConnection.setVideoEnabled(localVideoEnabled); this.dataChannel.send(new DataChannel.Buffer(ByteBuffer.wrap(Data.newBuilder() .setVideoStreamingStatus(WebRtcDataProtos.VideoStreamingStatus.newBuilder() .setId(this.callId) .setEnabled(localVideoEnabled)) .build().toByteArray()), false)); }
private void handleLocalHangup(Intent intent) { if (this.dataChannel != null && this.recipient != null && this.callId != null) { this.accountManager.cancelInFlightRequests(); this.messageSender.cancelInFlightRequests(); this.dataChannel.send(new DataChannel.Buffer(ByteBuffer.wrap(Data.newBuilder().setHangup(Hangup.newBuilder().setId(this.callId)).build().toByteArray()), false)); sendMessage(this.recipient, SignalServiceCallMessage.forHangup(new HangupMessage(this.callId))); sendMessage(WebRtcViewModel.State.CALL_DISCONNECTED, this.recipient, localVideoEnabled, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); } terminate(); }
private void handleSetMuteVideo(Intent intent) { AudioManager audioManager = ServiceUtil.getAudioManager(this); boolean muted = intent.getBooleanExtra(EXTRA_MUTE, false); this.localVideoEnabled = !muted; if (this.peerConnection != null) { this.peerConnection.setVideoEnabled(this.localVideoEnabled); } if (this.callId != null && this.dataChannel != null) { this.dataChannel.send(new DataChannel.Buffer(ByteBuffer.wrap(Data.newBuilder() .setVideoStreamingStatus(WebRtcDataProtos.VideoStreamingStatus.newBuilder() .setId(this.callId) .setEnabled(localVideoEnabled)) .build().toByteArray()), false)); } if (callState == CallState.STATE_CONNECTED) { if (localVideoEnabled) this.lockManager.updatePhoneState(LockManager.PhoneState.IN_VIDEO); else this.lockManager.updatePhoneState(LockManager.PhoneState.IN_CALL); } if (localVideoEnabled && !audioManager.isSpeakerphoneOn() && !audioManager.isBluetoothScoOn()) { audioManager.setSpeakerphoneOn(true); } sendMessage(viewModelStateFor(callState), this.recipient, localVideoEnabled, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); }
@Override public void onDataChannel(DataChannel dataChannel) { Log.w(TAG, "onDataChannel:" + dataChannel.label()); if (dataChannel.label().equals(DATA_CHANNEL_NAME)) { this.dataChannel = dataChannel; this.dataChannel.registerObserver(this); } }
@Override public void onDataChannel(final DataChannel dc) { Log.d(TAG, "New Data channel " + dc.label()); if (!dataChannelEnabled) return; dc.registerObserver(new DataChannel.Observer() { public void onBufferedAmountChange(long previousAmount) { Log.d(TAG, "Data channel buffered amount changed: " + dc.label() + ": " + dc.state()); } @Override public void onStateChange() { Log.d(TAG, "Data channel state changed: " + dc.label() + ": " + dc.state()); } @Override public void onMessage(final DataChannel.Buffer buffer) { if (buffer.binary) { Log.d(TAG, "Received binary msg over " + dc); return; } ByteBuffer data = buffer.data; final byte[] bytes = new byte[data.capacity()]; data.get(bytes); String strData = new String(bytes); Log.d(TAG, "Got msg: " + strData + " over " + dc); } }); }
private void handleSetMuteVideo(Intent intent) { AudioManager audioManager = ServiceUtil.getAudioManager(this); boolean muted = intent.getBooleanExtra(EXTRA_MUTE, false); this.localVideoEnabled = !muted; if (this.peerConnection != null) { this.peerConnection.setVideoEnabled(this.localVideoEnabled); } if (this.callId != null && this.dataChannel != null) { this.dataChannel.send(new DataChannel.Buffer(ByteBuffer.wrap(Data.newBuilder() .setVideoStreamingStatus(WebRtcDataProtos.VideoStreamingStatus.newBuilder() .setId(this.callId) .setEnabled(localVideoEnabled)) .build().toByteArray()), false)); } if (callState == CallState.STATE_CONNECTED) { if (localVideoEnabled) this.lockManager.updatePhoneState(LockManager.PhoneState.IN_VIDEO); else this.lockManager.updatePhoneState(LockManager.PhoneState.IN_CALL); } if (localVideoEnabled && !audioManager.isSpeakerphoneOn() && !audioManager.isBluetoothScoOn() && !audioManager.isWiredHeadsetOn()) { audioManager.setSpeakerphoneOn(true); } sendMessage(viewModelStateFor(callState), this.recipient, localVideoEnabled, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); }
@Override public void onDataChannel(DataChannel dc) { Log.d(LOG_TAG, "New data channel: " + dc.label()); if (!DC_LABEL.equals(dc.label())) { return; } // If the newly created data channel is the one we want, wrap it. final SecureDataChannel secureDataChannel = WebRTC.this.task.wrapDataChannel(dc); // Notify main class about this new data channel. WebRTC.this.activity.onNewSdc(secureDataChannel); }
/** * A new secure data channel was created. */ void onNewSdc(final SecureDataChannel sdc) { sdc.registerObserver(new DataChannel.Observer() { @Override public void onBufferedAmountChange(long l) { Log.d(LOG_TAG, "Buffered amount changed: " + l); } @Override public void onStateChange() { Log.d(LOG_TAG, "State changed: " + sdc.state()); } /** * Handle incoming messages. * * SaltyRTC only supports binary data, so we encode data as UTF8 on * the browser side and decode the string from UTF8 here. */ @Override public void onMessage(DataChannel.Buffer buffer) { final byte[] bytes = buffer.data.array(); Log.d(LOG_TAG, "New incoming datachannel message: " + bytes.length + " bytes"); String message; try { message = new String(bytes, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); return; } Log.d(LOG_TAG, "Message is: " + message); MainActivity.this.onMessage(message); } }); this.sdc = sdc; }
/** * Send message via DC. * * Must be run on UI thread. */ public void sendDc(View view) { Log.d(LOG_TAG, "Sending message..."); final String text = this.textInput.getText().toString(); final ByteBuffer bytes = StandardCharsets.UTF_8.encode(text); this.sdc.send(new DataChannel.Buffer(bytes, true)); final View msgView = this.getMessageTextView(R.color.colorMessageOut, text); runOnUiThread(new Runnable() { @Override public void run() { MainActivity.this.showMessage(msgView); } }); this.textInput.setText(""); }
public void sendHelloMessage(DataChannel channel) { byte[] rawMessage = "Hello Peer!".getBytes(Charset.forName("UTF-8")); ByteBuffer directData = ByteBuffer.allocateDirect(rawMessage.length); directData.put(rawMessage); directData.flip(); DataChannel.Buffer data = new DataChannel.Buffer(directData, false); channel.send(data); }
@Override public void onStateChange(NBMPeerConnection connection, DataChannel channel) { Log.i(TAG, "[datachannel] DataChannel onStateChange: " + channel.state()); if (channel.state() == DataChannel.State.OPEN) { sendHelloMessage(channel); Log.i(TAG, "[datachannel] Datachannel open, sending first hello"); } }
@Override public void onDataChannel(final DataChannel dataChannel) { if (BuildConfig.DEBUG) { Log.d(TAG, "@@@ onDataChannel"); Log.d(TAG, "@@@ DataChannel: " + dataChannel.toString()); } }
public void run() { if (mediaResourceManager.getLocalMediaStream() == null) { mediaResourceManager.createMediaConstraints(); startLocalMediaSync(); } NBMPeerConnection connection = peerConnectionResourceManager.getConnection(connectionId); if (connection == null) { if (signalingParameters != null) { connection = peerConnectionResourceManager.createPeerConnection( signalingParameters, mediaResourceManager.getPcConstraints(), connectionId); connection.addObserver(observer); connection.addObserver(mediaResourceManager); if (includeLocalMedia) { connection.getPc().addStream(mediaResourceManager.getLocalMediaStream()); } DataChannel.Init init = new DataChannel.Init(); createDataChannel(this.connectionId, "default", init); // Create offer. Offer SDP will be sent to answering client in // PeerConnectionEvents.onLocalDescription event. connection.createOffer(mediaResourceManager.getSdpMediaConstraints()); } } }
public DataChannel createDataChannel(String connectionId, String dataChannelId, DataChannel.Init init) { NBMPeerConnection connection = peerConnectionResourceManager.getConnection(connectionId); if (connection!=null) { return connection.createDataChannel(dataChannelId, init); } else { Log.e(TAG, "Cannot find connection by id: " + connectionId); } return null; }
public ObservedDataChannel(String label, DataChannel.Init init) { channel = pc.createDataChannel(label, init); if (channel != null) { channel.registerObserver(this); Log.i(TAG, "Created data channel with Id: " + label); } else { Log.e(TAG, "Failed to create data channel with Id: " + label); } }
@Override public void onMessage(DataChannel.Buffer buffer) { Log.i(TAG, "[ObservedDataChannel] NBMPeerConnection onMessage"); for (NBMWebRTCPeer.Observer observer : observers) { observer.onMessage(buffer, NBMPeerConnection.this, channel); } }
@SuppressWarnings("unused") public HashMap<String, DataChannel> getDataChannels(){ HashMap<String, DataChannel> channels = new HashMap<>(); for (HashMap.Entry<String, ObservedDataChannel> entry : observedDataChannels.entrySet()) { String key = entry.getKey(); ObservedDataChannel value = entry.getValue(); channels.put(key, value.getChannel()); } return channels; }
public DataChannel getDataChannel(String dataChannelId){ ObservedDataChannel channel = this.observedDataChannels.get(dataChannelId); if (channel == null) { return null; } else { return channel.getChannel(); } }
@Override public void onDataChannel(DataChannel dataChannel) { Log.i(TAG, "[datachannel] Peer opened data channel"); for (NBMWebRTCPeer.Observer observer : observers) { observer.onDataChannel(dataChannel, NBMPeerConnection.this); } }
@Override public void onDataChannel(final DataChannel dc) { if (isActive()) { if (null != directConnection) { directConnection.peerConnectionDidOpenDataChannel(dc); } else { Log.d(TAG, "Direct connection opened, but no object to handle it!"); } } }
/** * Establish a new direct connection instance with the peer connection for the call. This is used internally to the SDK and should not be called directly by your client application. */ public void createDataChannel() { if (null != callReference) { RespokeCall call = callReference.get(); if (null != call) { PeerConnection peerConnection = call.getPeerConnection(); dataChannel = peerConnection.createDataChannel("respokeDataChannel", new DataChannel.Init()); dataChannel.registerObserver(this); } } }
private static void createDataChannelToRegressionTestBug2302( PeerConnection pc) { DataChannel dc = pc.createDataChannel("dcLabel", new DataChannel.Init()); abortUnless("dcLabel".equals(dc.label()), "Unexpected label corruption?"); dc.close(); dc.dispose(); }