I am using nativescript to build an app that will programmatically send a pre-built text to multiple preset parties in case of emergency.
I have an array of phone numbers and want to iterate over each one, using SMSmanager to send the text and the sentIntent argument seen in android docs to verify that the text was sent before moving on to the next array item.
I have created the pendingIntent variable to pass into "sms.sendTextMessage" as follows:
var sms = android.telephony.SmsManager.getDefault();
var utils = require("utils/utils");
//Gets application's current state
var context = utils.ad.getApplicationContext();
//Create a replica of Android's intent object
var intent = new android.content.Intent(context, com.tns.NativeScriptActivity.class);
//Create a replica of Android's pendingIntent object using context and intent
var pendingIntent = android.app.PendingIntent.getActivity(context, 1, intent, android.app.PendingIntent.FLAG_UPDATE_CURRENT);
I then send the text, passing in the pending intent var:
sms.sendTextMessage("5555555555", null, "hello", pendingIntent, null);
I then attempt to make a basic broadcast receiver using the information I found in the nativescript docs which should just log something to the console when it recieves the expected data.
app.android.registerBroadcastReceiver(pendingIntent, function() {
console.log("##### text sent #####");
});
The problem is: nothing happens. I'd expect to get ""##### text sent #####" logged to the console. I've googled a lot and am thinking maybe I need to add something about this broadcast reciever in the manifest, or perhaps my implimentation is wrong somewhere, but this is my first crack at an android app and I'm at a bit of a loss. Any help would be appreciated.
I'm going to answer my own question here in case anyone else runs into this.
The code that worked is:
var app = require("application");
var utils = require("utils/utils");
var context = utils.ad.getApplicationContext();
var sms = android.telephony.SmsManager.getDefault();
var SendMessages = {
init: function() {
var id = "messageSent";
this.sendText(id, this.pendingIntent(id));
},
sendText: function(id, pendingIntent) {
sms.sendTextMessage("5555555555", null, "Hello :)", pendingIntent, null);
this.broadcastReceiver(id, function() {
console.log("$$$$$ text sent $$$$$");
});
},
pendingIntent: function(id) {
var intent = new android.content.Intent(id);
return android.app.PendingIntent.getBroadcast(context, 0, intent, 0);
},
broadcastReceiver: function(id, callback) {
app.android.registerBroadcastReceiver(id, function() {
callback();
});
}
};
module.exports = SendMessages;
To explain: it seems as #Mike M mentioned each intent object needs some string as an id.
Then to make the "pendingIntent" object, again as #Mike M. mentioned I needed to hook to "getBroadcast" method, then I needed to pass pending intent the app context as the first argument, then 0, then the intent object with the id.
The pending intent then is receivable in a simple broadcast receiver function by simply passing the intent id as the first argument and the callback as the second. I've tested and it's working perfectly.
Following is a simplified version, no bs code approach to what you need to make it run. You need permissions and to make sure the user accepts those permissions. This is also one of the two ways (this one is the context-registered receiver way) to create a broadcast receiver, read more about both types here: https://developer.android.com/guide/components/broadcasts#receiving-broadcasts
Info on registering broadcast receiver: https://docs.nativescript.org/api-reference/classes/application.androidapplication.html#registerbroadcastreceiver
AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="__PACKAGE__"
android:versionCode="10000"
android:versionName="1.0">
<!-- ...more code -->
<uses-permission android:name="android.permission.SEND_SMS" />
<!-- ...more code -->
</manifest>
JavaScript:
import * as application from 'tns-core-modules/application';
import * as platform from 'tns-core-modules/platform';
import * as utils from 'tns-core-modules/utils/utils';
import * as permissions from 'nativescript-permissions';
// ...more code
try {
await permissions.requestPermission(
android.Manifest.permission.SEND_SMS,
'Need to send.'
);
console.log('SEND_SMS permission accepted.');
const text = 'Herro.';
const mobileNumber = '55555555';
const intentFilter = 'something_here';
const context = utils.ad.getApplicationContext();
const intent = new android.content.Intent(intentFilter);
const pendingIntent = android.app.PendingIntent.getBroadcast(context, 0, intent, 0);
const sms = android.telephony.SmsManager.getDefault();
application.android.registerBroadcastReceiver(intentFilter, function() {
console.log(`Text has been sent: ${text}`);
});
setTimeout(() => {
console.log('Sending text.');
sms.sendTextMessage(mobileNumber, null, text, pendingIntent, null);
}, 5000);
} catch (error) {
console.log('Permission error:', error);
}
The code above is created inside the activity and uses the main ui thread. This means that if the user exits the activity, the broadcast receiver will linger in limbo and android can destroy it.
Related
With ajax requests it can be done with this code:
let oldXHROpen = window.XMLHttpRequest.prototype.open;
window.lastXhr = '';
window.XMLHttpRequest.prototype.open = function(method, url, async, user, password) {
this.addEventListener('load', function() {
window.lastXhr = this.responseText;
});
return oldXHROpen.apply(this, arguments);
};
lastXhr variable will hold the last response.
But how can this be achieved for websockets too?
you would need to make this wrapper as soon as possible
#brunoff you're correct in that you can always use your functions before a server's by puppet window logic, or you could just hijack the data from the MessageEvent itself:
function listen(fn){
fn = fn || console.log;
let property = Object.getOwnPropertyDescriptor(MessageEvent.prototype, "data");
const data = property.get;
// wrapper that replaces getter
function lookAtMessage() {
let socket = this.currentTarget instanceof WebSocket;
if (!socket) {
return data.call(this);
}
let msg = data.call(this);
Object.defineProperty(this, "data", { value: msg } ); //anti-loop
fn({ data: msg, socket:this.currentTarget, event:this });
return msg;
}
property.get = lookAtMessage;
Object.defineProperty(MessageEvent.prototype, "data", property);
}
listen( ({data}) => console.log(data))
You can try putting in the code and running it in the console on this page and then running their WebSocket example.
To intercept the messages, you will have to spy on the onmessage = fn and addEventListener("message", fn) calls.
To be able to modify the onmessage we have to override the global WebSocket in the first place. The below is intercepting the incoming messages, but in a similar way you can spy on the send method to intercept the outgoing messages (the ones sent by the client to the server).
I tested this on a page using Firebase and it works nicely, but you have to initialize it before the other scripts making sure that the websocket library (it can be socket.io, ws, etc) is using the overridden WebSocket constructor.
Spy the Incoming Messages and modify the data
Eventually you can override the data before calling the real message listener – this becomes handy if you do not have control over the page functionality and want to inject your own data in the message listener.
const OriginalWebsocket = window.WebSocket
const ProxiedWebSocket = function() {
console.log("Intercepting web socket creation")
const ws = new OriginalWebsocket(...arguments)
const originalAddEventListener = ws.addEventListener
const proxiedAddEventListener = function() {
if (arguments[0] === "message") {
const cb = arguments[1]
arguments[1] = function() {
// Here you can get the actual data from the incoming messages
// Here you can even change the data before calling the real message listener
Object.defineProperty(e, "data", { value: 'your injected data' })
console.log("intercepted", arguments[0].data)
return cb.apply(this, arguments)
}
}
return originalAddEventListener.apply(this, arguments)
}
ws.addEventListener = proxiedAddEventListener
Object.defineProperty(ws, "onmessage", {
set(func) {
return proxiedAddEventListener.apply(this, [
"message",
func,
false
]);
}
});
return ws;
};
window.WebSocket = ProxiedWebSocket;
If you do not need to modify the data, you can follow the second part of the answer.
Spy the Incoming messages without modifying the data
If you want to listen for messages only, without overriding the data, things are simpler:
const OriginalWebsocket = window.WebSocket
const ProxiedWebSocket = function() {
const ws = new OriginalWebsocket(...arguments)
ws.addEventListener("message", function (e) {
// Only intercept
console.log(e.data)
})
return ws;
};
window.WebSocket = ProxiedWebSocket;
Spy the Outgoing Messages
In a very similar way, you can proxy the send method which is used to send data to the server.
const OriginalWebsocket = window.WebSocket
const ProxiedWebSocket = function() {
const ws = new OriginalWebsocket(...arguments)
const originalSend = ws.send
const proxiedSend = function() {
console.log("Intercepted outgoing ws message", arguments)
// Eventually change the sent data
// arguments[0] = ...
// arguments[1] = ...
return originalSend.apply(this, arguments)
}
ws.send = proxiedSend
return ws;
};
window.WebSocket = ProxiedWebSocket;
Feel free to ask any questions if anything is unclear.
In a solution similar to yours, where the window.XMLHttpRequest was replaced with a wrapped version that feeds window.lastXhr, we replace window.WebSockets with a wrapped version that feeds window.WebSocketMessages with all messages and timestamps received from all websockets created after this script.
window.watchedWebSockets = [];
window.WebSocketMessages = [];
function WebSocketAttachWatcher(websocket) {
websocket.addEventListener("message", (event)=>window.WebSocketMessages.push([event.data,Date.now()]));
window.watchedWebSockets.push(websocket);
}
// here we replace WebSocket with a wrapped one, that attach listeners on
window.WebSocketUnchanged = window.WebSocket;
window.WebSocket = function(...args) {
const websocket = new window.WebSocketUnchanged(...args);
WebSocketAttachWatcher(websocket);
return websocket;
}
Differently from your XMLRequest case, the websocket may already exist. If you need garanties that all websockets would be catched then you would need to make this wrapper as soon as possible. If you just can't, there's an not so good trick to capture already existing websockets once they send a message:
// here we detect existing websockets on send event... not so trustable
window.WebSocketSendUnchanged = window.WebSocketUnchanged.prototype.send;
window.WebSocket.prototype.send = function(...args) {
console.log("firstsend");
if (!(this in window.watchedWebSockets))
WebSocketAttachWatcher(this);
this.send = window.WebSocketSendUnchanged; // avoid passing here again on next send
window.WebSocketSendUnchanged.call(this, ...args);
}
It is not so trustable since if they don't send but receive they will stay unnoticed.
Intro
The question/bounty/op is specifically asking for a reputable source.
Instead of rolling a custom solution, my proposal is that a known proven library should be used - that has been used, audited, forked, and in general used by the community and that is hosted on github.
The second option is to roll your own (though not recommended) and there are many exccelent answers on how to do it involving the addEventListener
wshook
Wshook is a library (hosted on github) that allows to easily intercept and modify WebSocket requests and message events. It has been starred and forked multiple times.
Disclaimer: I don't have any relationship with the specific project.strong text
Example:
wsHook.before = function(data, url, wsObject) {
console.log("Sending message to " + url + " : " + data);
}
// Make sure your program calls `wsClient.onmessage` event handler somewhere.
wsHook.after = function(messageEvent, url, wsObject) {
console.log("Received message from " + url + " : " + messageEvent.data);
return messageEvent;
}
From the documentation, you will find:
wsHook.before - function(data, url, wsObject):
Invoked just before
calling the actual WebSocket's send() method.
This method must return data which can be modified as well.
wsHook.after - function(event, url, wsObject):
Invoked just after
receiving the MessageEvent from the WebSocket server and before
calling the WebSocket's onmessage Event Handler.
Websocket addEventListener
The WebSocket object supports .addEventListener().
Please see: Multiple Handlers for Websocket Javascript
if you are using nodejs then you can use socket.io
yarn add socket.io
after installation, you can use the middleware of socket.io
io.use(async (socket, next) => {
try {
const user = await fetchUser(socket);
socket.user = user;
} catch (e) {
next(new Error("unknown user"));
}
});
my company choose "Mercure" (https://mercure.rocks/docs/getting-started) to manage Server-Sent Events.
We install "Mercure HUB" on a server and now, in C# .NET 5.0, I must implement the server-side (publisher, that I already implemented) and the client-side (subscriber).
The subscriber must be done with a WPF
From the "getting-started" page I can see a Javascript example that I need to transform into C#
I don't know how to manage a "EventSource" in C#
Any ideas ?
// The subscriber subscribes to updates for the https://example.com/users/dunglas topic
// and to any topic matching https://example.com/books/{id}
const url = new URL('https://localhost/.well-known/mercure');
url.searchParams.append('topic', 'https://example.com/books/{id}');
url.searchParams.append('topic', 'https://example.com/users/dunglas');
// The URL class is a convenient way to generate URLs such as https://localhost/.well-known/mercure?topic=https://example.com/books/{id}&topic=https://example.com/users/dunglas
const eventSource = new EventSource(url);
// The callback will be called every time an update is published
eventSource.onmessage = e => console.log(e); // do something with the payload
The code of this page works (https://makolyte.com/event-driven-dotnet-how-to-consume-an-sse-endpoint-with-httpclient/)
static async Task Main(string[] args)
{
HttpClient client = new HttpClient();
client.Timeout = TimeSpan.FromSeconds(5);
string stockSymbol = "VTSAX";
string url = $"http://localhost:9000/stockpriceupdates/{stockSymbol}";
while (true)
{
try
{
Console.WriteLine("Establishing connection");
using (var streamReader = new StreamReader(await client.GetStreamAsync(url)))
{
while (!streamReader.EndOfStream)
{
var message = await streamReader.ReadLineAsync();
Console.WriteLine($"Received price update: {message}");
}
}
}
catch(Exception ex)
{
//Here you can check for
//specific types of errors before continuing
//Since this is a simple example, i'm always going to retry
Console.WriteLine($"Error: {ex.Message}");
Console.WriteLine("Retrying in 5 seconds");
await Task.Delay(TimeSpan.FromSeconds(5));
}
}
}
I am fairly new to JS/Winappdriver.
The application I am trying to test is a windows based "Click Once" application from .Net, so I have to go to a website from IE and click "Install". This will open the application.
Once the application is running, I have no way to connect the application to perform my UI interactions while using JavaScript.
Using C#, I was looping through the processes looking for a process name, get the window handle, convert it to hex, add that as a capability and create the driver - it worked. Sample code below,
public Setup_TearDown()
{
string TopLevelWindowHandleHex = null;
IntPtr TopLevelWindowHandle = new IntPtr();
foreach (Process clsProcess in Process.GetProcesses())
{
if (clsProcess.ProcessName.StartsWith($"SomeName-{exec_pob}-{exec_env}"))
{
TopLevelWindowHandle = clsProcess.Handle;
TopLevelWindowHandleHex = clsProcess.MainWindowHandle.ToString("x");
}
}
var appOptions = new AppiumOptions();
appOptions.AddAdditionalCapability("appTopLevelWindow", TopLevelWindowHandleHex);
appOptions.AddAdditionalCapability("ms:experimental-webdriver", true);
appOptions.AddAdditionalCapability("ms:waitForAppLaunch", "25");
AppDriver = new WindowsDriver<WindowsElement>(new Uri(WinAppDriverUrl), appOptions);
AppDriver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(60);
}
How do I do this in Javascript ? I can't seem to find any code examples.
Based on an example from this repo, I tried the following in JS to find the process to latch on to but without luck.
import {By2} from "selenium-appium";
// this.appWindow = this.driver.element(By2.nativeAccessibilityId('xxx'));
// this.appWindow = this.driver.element(By2.nativeXpath("//Window[starts-with(#Name,\"xxxx\")]"));
// this.appWindow = this.driver.elementByName('WindowsForms10.Window.8.app.0.13965fa_r11_ad1');
// thisappWindow = this.driver.elementByName('xxxxxxx');
async connectAppDriver(){
await this.waitForAppWindow();
var appWindow = await this.appWindow.getAttribute("NativeWindowHandle");
let hex = (Number(ewarpWindow)).toString(16);
var currentAppCapabilities =
{
"appTopLevelWindow": hex,
"platformName": "Windows",
"deviceName": "WindowsPC",
"newCommandTimeout": "120000"
}
let driverBuilder = new DriverBuilder();
await driverBuilder.stopDriver();
this.driver = await driverBuilder.createDriver(currentEwarpCapabilities);
return this.driver;
}
I keep getting this error in Winappdriver
{"status":13,"value":{"error":"unknown error","message":"An unknown error occurred in the remote end while processing the command."}}
I've also opened this ticket here.
It seems like such an easy thing to do, but I couldn't figure this one out.
Any of nodes packages I could use to get the top level window handle easily?
I am open to suggestions on how to tackle this issue while using JavaScript for Winappdriver.
Hope this helps some one out there,
Got around this by creating an exe using C# that generated hex of the app to connect based on the process name, it looks like something like this.
public string GetTopLevelWindowHandleHex()
{
string TopLevelWindowHandleHex = null;
IntPtr TopLevelWindowHandle = new IntPtr();
foreach (Process clsProcess in Process.GetProcesses())
{
if (clsProcess.ProcessName.StartsWith(_processName))
{
TopLevelWindowHandle = clsProcess.Handle;
TopLevelWindowHandleHex = clsProcess.MainWindowHandle.ToString("x");
}
}
if (!String.IsNullOrEmpty(TopLevelWindowHandleHex))
return TopLevelWindowHandleHex;
else
throw new Exception($"Process: {_processName} cannot be found");
}
Called it from JS to get the hex of the top level window handle, like this,
async getHex () {
var pathToExe =await path.join(process.cwd(), "features\\support\\ProcessUtility\\GetWindowHandleHexByProcessName.exe");
var pathToDir =await path.join(process.cwd(), "features\\support\\ProcessUtility");
const result = await execFileSync(pathToExe, [this.processName]
, {cwd: pathToDir, encoding: 'utf-8'}
, async function (err, data) {
console.log("Error: "+ err);
console.log("Data(hex): "+ data);
return JSON.stringify(data.toString());
});
return result.toString().trim();
}
Used the hex to connect to the app like this,
async connectAppDriver(hex) {
console.log(`Hex received to connect to app using hex: ${hex}`);
const currentAppCapabilities=
{
"browserName": '',
"appTopLevelWindow": hex.trim(),
"platformName": "Windows",
"deviceName": "WindowsPC",
"newCommandTimeout": "120000"
};
const appDriver = await new Builder()
.usingServer("http://localhost:4723/wd/hub")
.withCapabilities(currentAppCapabilities)
.build();
await driver.startWithWebDriver(appDriver);
return driver;
}
Solution:
In WebDriverJS (used by selenium / appium), use getDomAttribute instead of getAttribute. Took several hours to find :(
element.getAttribute("NativeWindowHandle")
POST: /session/270698D2-D93B-4E05-9FC5-3E5FBDA60ECA/execute/sync
Command not implemented: POST: /session/270698D2-D93B-4E05-9FC5-3E5FBDA60ECA/execute/sync
HTTP/1.1 501 Not Implemented
let topLevelWindowHandle = await element.getDomAttribute('NativeWindowHandle')
topLevelWindowHandle = parseInt(topLevelWindowHandle).toString(16)
GET /session/DE4C46E1-CC84-4F5D-88D2-35F56317E34D/element/42.3476754/attribute/NativeWindowHandle HTTP/1.1
HTTP/1.1 200 OK
{"sessionId":"DE4C46E1-CC84-4F5D-88D2-35F56317E34D","status":0,"value":"3476754"}
and topLevelWindowHandle have hex value :)
I am using service workers to intercept requests for me and provide the responses to the fetch requests by communicating with a Web worker (also created from the same parent page).
I have used message channels for direct communication between the worker and service worker. Here is a simple POC I have written:
var otherPort, parentPort;
var dummyObj;
var DummyHandler = function()
{
this.onmessage = null;
var selfRef = this;
this.callHandler = function(arg)
{
if (typeof selfRef.onmessage === "function")
{
selfRef.onmessage(arg);
}
else
{
console.error("Message Handler not set");
}
};
};
function msgFromW(evt)
{
console.log(evt.data);
dummyObj.callHandler(evt);
}
self.addEventListener("message", function(evt) {
var data = evt.data;
if(data.msg === "connect")
{
otherPort = evt.ports[1];
otherPort.onmessage = msgFromW;
parentPort = evt.ports[0];
parentPort.postMessage({"msg": "connect"});
}
});
self.addEventListener("fetch", function(event)
{
var url = event.request.url;
var urlObj = new URL(url);
if(!isToBeIntercepted(url))
{
return fetch(event.request);
}
url = decodeURI(url);
var key = processURL(url).toLowerCase();
console.log("Fetch For: " + key);
event.respondWith(new Promise(function(resolve, reject){
dummyObj = new DummyHandler();
dummyObj.onmessage = function(e)
{
if(e.data.error)
{
reject(e.data.error);
}
else
{
var content = e.data.data;
var blob = new Blob([content]);
resolve(new Response(blob));
}
};
otherPort.postMessage({"msg": "content", param: key});
}));
});
Roles of the ports:
otherPort: Communication with worker
parentPort: Communication with parent page
In the worker, I have a database say this:
var dataBase = {
"file1.txt": "This is File1",
"file2.txt": "This is File2"
};
The worker just serves the correct data according to the key sent by the service worker. In reality these will be very large files.
The problem I am facing with this is the following:
Since I am using a global dummyObj, the older dummyObj and hence the older onmessage is lost and only the latest resource is responded with the received data.
In fact, file2 gets This is File1, because the latest dummyObj is for file2.txt but the worker first sends data for file1.txt.
I tried by creating an iframe directly and all the requests inside it are intercepted:
<html>
<head></head>
<body><iframe src="tointercept/file1.txt" ></iframe><iframe src="tointercept/file2.txt"></iframe>
</body>
</html>
Here is what I get as output:
One approach could be to write all the files that could be fetched into IndexedDB in the worker before creating the iframe. Then in the Service Worker fetch those from indexed DB. But I don't want to save all the resources in IDB. So this approach is not what I want.
Does anybody know a way to accomplish what I am trying to do in some other way? Or is there a fix to what I am doing.
Please Help!
UPDATE
I have got this to work by queuing the dummyObjs in a global queue instead of having a global object. And on receiving the response from the worker in msgFromW I pop an element from the queue and call its callHandler function.
But I am not sure if this is a reliable solution. As it assumes that everything will occur in order. Is this assumption correct?
I'd recommend wrapping your message passing between the service worker and the web worker in promises, and then pass a promise that resolves with the data from the web worker to fetchEvent.respondWith().
The promise-worker library can automate this promise-wrapping for you, or you could do it by hand, using this example as a guide.
If you were using promise-worker, your code would look something like:
var promiseWorker = new PromiseWorker(/* your web worker */);
self.addEventListener('fetch', function(fetchEvent) {
if (/* some optional check to see if you want to handle this event */) {
fetchEvent.respondWith(promiseWorker.postMessage(/* file name */));
}
});
I have two clients :
1) Chrome (version 50.0.2661.102 m) on Windows 7 PC
2) Chrome (version 50.0.2661.89) on Android tablet
Both are in the same network (so no need for STUN/TURN server).
I use my own signal server built with node.js (webSocket) on a VirtualBox VM with Centos 6.
The communication with video/sound between the clients works fine. Now I want to transfer a file from one client to another. As base of my code i use the code of this example
here
As this code suggess, I create the dataChannnel exactly after the creation of PeerConnection.
function createPeerConnection() {
....
myPeerConnection = new RTCPeerConnection(iceServers, optional);
myDataChannel = myPeerConnection.createDataChannel('myDataChannel');
// Set up event handlers for the ICE negotiation process.
myPeerConnection.onicecandidate = handleICECandidateEvent;
myPeerConnection.onaddstream = handleAddStreamEvent;
myPeerConnection.onnremovestream = handleRemoveStreamEvent;
myPeerConnection.oniceconnectionstatechange = handleICEConnectionStateChangeEvent;
myPeerConnection.onicegatheringstatechange = handleICEGatheringStateChangeEvent;
myPeerConnection.onsignalingstatechange = handleSignalingStateChangeEvent;
myPeerConnection.onnegotiationneeded = handleNegotiationNeededEvent;
myPeerConnection.ondatachannel = handleDataChannel;
myDataChannel.onmessage = handleDataChannelMessage;
myDataChannel.onopen = handleDataChannelOpen;
}
...
...
function invite(peerId) {
...
createPeerConnection();
...
}
...
...
function handleVideoOfferMsg(msg) {
thereIsNegotiation = true;
targetUsername = msg.name;
// Call createPeerConnection() to create the RTCPeerConnection.
log("Starting to accept invitation from " + targetUsername);
createPeerConnection();
// We need to set the remote description to the received SDP offer
// so that our local WebRTC layer knows how to talk to the caller.
var desc = new RTCSessionDescription(msg.sdp);
myPeerConnection.setRemoteDescription(desc)
.then(function(stream) {
log("-- Calling myPeerConnection.addStream()");
return myPeerConnection.addStream(localStream);
})
.then(function() {
log("------> Creating answer");
// Now that we've successfully set the remote description, we need to
// start our stream up locally then create an SDP answer. This SDP
// data describes the local end of our call, including the codec
// information, options agreed upon, and so forth.
return myPeerConnection.createAnswer();
})
.then(function(answer) {
log("------> Setting local description after creating answer");
// We now have our answer, so establish that as the local description.
// This actually configures our end of the call to match the settings
// specified in the SDP.
return myPeerConnection.setLocalDescription(answer);
})
.then(function() {
var msg = {
name: clientId,
room: roomId,
target: targetUsername,
type: "video-answer",
sdp: myPeerConnection.localDescription
};
// We've configured our end of the call now. Time to send our
// answer back to the caller so they know that we want to talk
// and how to talk to us.
log("Sending answer packet back to other peer");
sendToServer(msg);
})
.catch(handleGetUserMediaError);
}
When the second client makes the offer, the first client when tries to make the answer, I get the error
Error opening your camera and / or microphone : failed to set local answer
spd: Failed to push down transport description: Local fingerprint provided
but no identity available.
or
Error opening your camera and / or microphone : failed to set local answer
spd: Called in wrong state : STATE_INPROGRESS
Only one time the creation was successful.
Do I have to create DataChannel in other place? Like here :
function handleICEConnectionStateChangeEvent {
switch(myPeerConnection.iceConnectionState) {
...
case "connected":
createDataChannel();
break;
}
}
function createDataChannel(){
myDataChannel = myPeerConnection.createDataChannel('myDataChannel');
myPeerConnection.ondatachannel = handleDataChannel;
myDataChannel.onmessage = handleDataChannelMessage;
myDataChannel.onopen = handleDataChannelOpen;
}
Any suggestions?
The error in this code is that both sender and receiver create new datachannel. The right thing is, one to create the datachannel
myDataChannel = myPeerConnection.createDataChannel('myDataChannel')
and the other to wait for the creation of dataChannel:
myPeerConnection.ondatachannel = handleDataChannel;