I've made my own SIP client for Android. I'm facing an issue. I've made the test on SipDroid : if I call someone and hang up before he unhooked, the call end of the both side.
With my client, when I hang up, the call is not ended for the receiver, leading to a communication on one side.
Here is my code for ending a call :
if (call != null)
{
Log.i(TAG, "Trying to end call");
try
{
call.endCall();
}
catch (SipException e)
{
Log.i(TAG, "Service failed to end the call", e);
}
call.close();
updateStatus("Call Ended");
}
Where call is a SipAudioCall.
I get my call like this :
call = manager.makeAudioCall(profile.getUriString(), sipAddressToCall, listener, _Params.callWait);
I don't know about your API, but the SIP protocol itself has the "CANCEL" request to "hang up" before the call is answered.
On a normal, ongoing call, you send "BYE".
Related
i am building my app on android repository by Fernando Cejas and i have a problem with subscribing to observable after calling dispose.
When i come to dashboard, i call method subscribeOnUserMessages.execute(new Subscriber(), new Params(token)), which is method in UseCase class
public void execute(DisposableObserver<T> observer, Params params) {
Preconditions.checkNotNull(observer);
final Observable<T> observable = this.buildUseCaseObservable(params)
.subscribeOn(Schedulers.from(threadExecutor))
.observeOn(postExecutionThread.getScheduler());
addDisposable(observable.subscribeWith(observer));
}
In child class SubscribeOnUserMessages i simply call repository like this
return messageRepository.subscribeOnUserMessages(params);
In my socket implementation i create like this
return Observable.create(emitter -> {
if (!isThereInternetConnection()) {
Timber.w("Network connection exception");
emitter.onError(new NetworkConnectionException());
return;
}
/*
* Open socket if not opened
*/
openSocket(params.getToken());
String channelName = CHANNEL_PRIVATE_USER + params.getAuthenticated().getUuid();
if (subscribedChannels.contains(channelName)) {
Timber.d("Channel %s is already subscribed", channelName);
return;
}
JSONObject auth;
try {
auth = createAuthJson(CHANNEL, channelName, params.getToken());
} catch (JSONException e) {
Timber.e("Couldn't create auth json");
emitter.onError(e);
return;
}
mSocket.emit(SUBSCRIBE, auth);
Timber.d("Emitted subscribe with channel: %s ", CHANNEL_PRIVATE_USER + params.getAuthenticated().getUuid());
subscribedChannels.add(CHANNEL_PRIVATE_USER + params.getAuthenticated().getUuid());
Timber.d("Subscribing on event: %s\n with user: %s", EVENT_USER_NEW_MESSAGE, params.getAuthenticated().getUuid());
if (mSocket.hasListeners(EVENT_USER_NEW_MESSAGE)) {
Timber.v("Socket already has listener on event: %s", EVENT_USER_NEW_MESSAGE);
return;
}
mSocket.on(EVENT_USER_NEW_MESSAGE, args -> {
if (args[1] == null) {
emitter.onError(new EmptyResponseException());
}
Timber.d("Event - %s %s", EVENT_USER_NEW_MESSAGE, args[1].toString());
try {
MessageEntity messageEntity = messageEntityJsonMapper.transform(args[1]);
emitter.onNext(messageEntity);
} catch (JSONException e) {
Timber.e(e, "Could not parse message json");
emitter.onError(e);
}
});
});
Symptoms are that first time i subscribe everything is going through to presentation layer. When i dispose after going to second screen and come back i only see logs coming to socket implementation, but not going through.
My question is: Is there a method for subscribing to same observable again? I've already tried to save that observable in my use case in singleton and subscribe to that observable, didn't help.
Without additional info and details regrading socket implementation it is hard to spot the problem exactly, but, from the code you've posted, you don't have dispose logic, so while you might properly call dispose() to the Observable at the correct lifecycle event, your socket will actually stay open, and it might not got disconnected/closed properly ever.
That might lead to a problems opening and connecting to the socket at the 2nd time, as you might try to reopen already open socket and depends on your internal socket impl that might be a problem.
(I can see in the comment that openSocket if not already opened, but still there might be problem elsewhere calling some method on the socket multiple times or setting listeners, again depends on the socket impl)
As a general guidelines, you should add dispose logic using emitter.setCancellable()/emitter.setDisposable() in order to dispose properly the socket resources when you no longer need them, thus - when applying subscribe again (whether the same object or not) will invoke your subscription logic again that will reopen the socket and listen to it.
It is not clear to me if you like to keep the socket open when you moving to a different screen (I don't think it is a good practice, as you will keep this resource open and might never get back to the screen again to use it), but if that's the case as #Phoenix Wang mentioned, you can use publish kind operators to multicast the Observable, so every new Subscriber will not try to reopen the socket (i.e. invoking the subscription logic) but will just get notify about messages running in the already opened socket.
I'm working on a project that improves Automation Test for Android's App. What I want to do is very "easy": I have this very simple SIP Client with a basic UI and developed just reading the API guides on the android developer website (https://developer.android.com/guide/topics/connectivity/sip.html) that receives and makes SIP calls.
I need to control remotely this app from my PC, connected at the same local network or the same wifi, by sending commands or similar (without interact with the phone) to the app itslef running normally on my phone.For a specific example I posted the method initiateCall() that calls sipAddress(in the app, sipAddress is taken from a Text Box), what I want to do is:
Starting the app on my phone
calling the method initiateCall() from my pc giving a sipAddress as a parameter (I must not use the UI from the app running, that's why I need to give the sipAddress)
check if an outgoing call starts from the app running on my phone
I thought that the solution must be something about web-services,but I don't have any better ideas and i don't know how to start and where to start solving this problem,that's why i need you help.
public void initiateCall() {
try {
SipAudioCall.Listener listener = new SipAudioCall.Listener() {
// set up the listener for outgoing calls
#Override
public void onCallEstablished(SipAudioCall call) {
call.startAudio();
call.setSpeakerMode(true);
updateStatus(call, 2);
}
#Override
public void onCallEnded(SipAudioCall call) {
updateStatus("Call End");
}
};
call = manager.makeAudioCall(me.getUriString(), sipAddress,
listener, 30);
} catch (Exception e) {
Log.i("WalkieTalkieActivity/InitiateCall",
"Error when trying to close manager.", e);
if (me != null) {
try {
manager.close(me.getUriString());
} catch (Exception ee) {
Log.i("WalkieTalkieActivity/InitiateCall",
"Error when trying to close manager.", ee);
ee.printStackTrace();
}
}
if (call != null) {
call.close();
}
}
}
You could do it REST API style. You would need to set up a minimalistic webserver.
If you access for example the url phoneip/ctrl/makecall?number=yournumber a serverside method us called if set up correctly. Then you can call you method and use the GET or POST variables as arguments.
You would have to look into Java Webserver Libraries/Frameworks. You can pick a lightweight one for that purpose. For example this one.
You could then also add security features (authentification to protect it) quite easily.
Example with sparkjava
import static spark.Spark.*;
....
get("/ctrl/makecall", (request, response) -> {
String phonenum = request.queryParams("number"); //may not be accurate; you have to determine the GET variable called "number" in that case; you can rename it; see docs!!!
//call your method with proper arguments
});
I used the Android built-in sip library to write an app that makes calls via my server. The calls are being made correctly, but most of the time, the calls aren't ended correctly.
This is my code to end the call:
public void stopCalling(){
try {
call.endCall();
call.close();
} catch (SipException e) {
e.printStackTrace();
}
}
But it does not Ended properly.
Is there any other way to ended the sip call.
I have to Users (User A and B) and one Chromecast device (C1).
User B starts a stream on C1.
User A connects to C1
Now User A should be able to control the stream running on C1. But every time I want to start a session the running stream on C1 is shut down and the receiver app is restarting.
Is there a way to join an active session? Or is that a job which has to be done by the web app running on the Chromecast device?
EDIT:
my sender app is a native Android app
Thanks!
You should have a look to the TicTacToe application. I think it does exactly that where 2 players can join the same game :
https://github.com/googlecast/cast-android-tictactoe
Hope this helps.
JN
What sort of sender are you using? Is it a native app (i.e. using Android or iOs SDK on a mobile device) or the sender is a chrome app?
On the receiver, you create a Receiver object and a ChannelHandler. You use the receiver to generate a ChannelFactory which you then pass to the ChannelHandler. The ChannelHandler now handles the creation of channels on the receiver. You will want to add an EventListener to the handler to listen to messages. Based on those messages you can do various things.
receiver = new cast.receiver.Receiver(YOUR_APP_ID, [YOUR_PROTOCOL], "", 5);
var dashHandler = new cast.receiver.ChannelHandler(YOUR_PROTOCOL);
dashHandler.addChannelFactory(receiver.createChannelFactory(YOUR_PROTOCOL));
dashHandler.addEventListener(cast.receiver.Channel.EventType.MESSAGE, onMessage.bind(this));
receiver.start();
...
onMessage = function (e) {
var message = e.message;
switch (message.type) {
...
}
}
On the sender, after a session is created you will want to send a check status message to the receiver to see if there are already channels attached. You can do this via your MessageStream and your receiver needs to respond in such a way that the MessageStream gets its status updated. You check that status to see if there are channels. If there are you can start listening to updates for your receiver. If not you can send a load event to the receiver to start your activity.
MediaProtocolCommand cmd = mMessageStream.requestStatus();
cmd.setListener(new MediaProtocolCommand.Listener() {
#Override
public void onCompleted(MediaProtocolCommand mPCommand) {
if (mMessageStream.getState() == 'channelsExist') {
//Start New Activity
} else {
//Join Existing Activity
}
#Override
public void onCancelled(MediaProtocolCommand mPCommand) {
}
});
This is kind of a vague response, but it could be more specific if I knew what you were trying to do. My app is using Google's RAMP protocol to play videos so my MessageStream and all it's messages are already defined. If you're doing something different, you need to create your own MessageStream.
Sorry for the late answer, but I figured it out by myself: It wasn't such complicated at all
I started the an Application like this
try {
mSession.startSession(applicationName,applicationArgs);
} catch (IllegalStateException e) {
Log.e(getClass().getSimpleName(), e.getMessage(), e);
} catch (IOException e) {
Log.e(getClass().getSimpleName(), e.getMessage(), e);
}
But it seems, that the MimeData applicationArgs is not needed at all. By removing the arguments and starting the session like below it works really fine!
try {
mSession.startSession(applicationName);
} catch (IllegalStateException e) {
Log.e(getClass().getSimpleName(), e.getMessage(), e);
} catch (IOException e) {
Log.e(getClass().getSimpleName(), e.getMessage(), e);
}
I hope this works for you too!
Ok, I understood, that when you bind to a remote Service, it won't bind until you return from the callback. I need to bind to a service and execute some method from it immediately. Is there any way? My code looks like:
try {
Parser parser = FrameworkBridge.getFrameworkBridge(this).getParser();
if (parser != null) {
for (Article a : parser.getArticlesList("http://www.bt.dk/mecommobile/latest/news_article/%s/%s?output_type=xml", 1379, 20)) {
listAdapter.add(a);
}
}
} catch (RemoteException e) {
Log.d(TAG, "Service communication failure");
} catch (FrameworkNotInstalledException e) {
Log.d(TAG, "No framework installed. Install it!");
}
Here FrameworkBridge.getFrameworkBridge(this).getParser() performs all service connection routine and returns remote interface. The problem is -- when I'm in the code above, the connection is not performed yet, therefore parser is null. How can I make it connect before exiting the code?
onServiceConnected(..) will tell you when the service is connected and the remote interface is established. Don't try to call any methods in the service until this is triggered.
mContext.bindService( new Intent("name of the class"), this, Context.BIND_AUTO_CREATE);
the above method call on the copy of the context you store should automatically bind the service when you need it. I am not 100% sure, but this should help you out somewhat.