I am working on a VoIP-Android-App and The app needs to be able to accept/decline call thought Bluetooth headset.
But the problem is that after adding connection to SCO
audioManager.startBluetoothSco()
audioManager.isBluetoothScoOn = true
Once I click to the headset button I can hear a sound that usually comes when I accept call using telephony, so I assume that some android system component catch this signal and doesn't throw it further
What I've tried already:
1) Telephony State listener (It is always IDLE)
val tm = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
phoneStateListener = MyPhoneStateListener()
tm.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE)
2) MediaSession + silent noise + media button listener
Doesn't work for the first click, second+ clicks handled correctly
3) MEDIA_BUTTON receiver doesn't work
I found a similar question on SO but without the answer how to make it work
Accepting a Call via Bluetooth Headset
So is there anyway how I can intercept Bluetooth button click from Bluetooth Headset Service?
Accepting a Call via Bluetooth Headset
Adding my answer from there to here too.
These events are handled internally in HeadsetStateMachine (under packages/apps/Bluetooth).
These events are forwarded to IBluetoothHeadsetPhone interface. The single application to which all the events are forwarded is defined at run-time by following binding code in HeadsetStateMachine.java. This is to allow phone manufacturers to forward them to custom phone application instead of default one in cases where default one is not used.
Intent intent = new Intent(IBluetoothHeadsetPhone.class.getName());
intent.setComponent(intent.resolveSystemService(context.getPackageManager(), 0));
if (intent.getComponent() == null || !context.bindService(intent, mConnection, 0)) {
Log.e(TAG, "Could not bind to Bluetooth Headset Phone Service");
}
To make the events get forwarded to your application instead of default phone application you would have to modify aosp code.
You would need to intercept the events at one of HeadsetStateMachine , BluetoothHeadsetPhone proxy or the phone application.
Unfortunately what you are looking for is currently not possible without modifying aosp code. Some headsets like Plantronics have custom BT events which are forwarded to all applications - some of the existing VoIP applications support these custom intents to support at-least answering calls for some of the headsets.
Related
I am wondering if there is a way I can detect a skype call/telegram/whatsapp/fb messenger call etc in progress or incoming call etc just like the regular phone call with telephony manager/listener? I would like to have some kind of mechanism that detects an ongoing /incoming etc call from skype/telegram etc for my app. I came across this solution :Call Detection for skype in android but not sure if it'll work for all generic messenger apps. Is there a hack or any kind of listener I can implement on my side that allows me to detect these? Any ideas would be awesome.
Thanks!
You can use the AudioManager system service and check for audio mode using AudioManager#getMode(). According to the docs, AudioManager#getMode() returns the current audio mode which can be one of following:
MODE_NORMAL (0x00000000): Normal audio mode: not ringing and no call established.
MODE_RINGTONE (0x00000001): Ringing audio mode. An incoming is being signaled.
MODE_IN_CALL (0x00000002): In call audio mode. A telephony call is established.
MODE_IN_COMMUNICATION (0x00000003): In communication audio mode. An audio/video chat or VoIP call is established (added in API 11).
Using AudioManager#getMode() you can check if any other app is currently holding an audio focus of type MODE_IN_COMMUNICATION and handle your call accordingly.
AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
final int mode = am.getMode();
if(AudioManager.MODE_IN_CALL == mode){
// device is in a telephony call
} else if(AudioManager.MODE_IN_COMMUNICATION == mode){
// device is in communiation mode, i.e. in a VoIP or video call
} else if(AudioManager.MODE_RINGTONE == mode){
// device is in ringing mode, some incoming is being signalled
} else {
// device is in normal mode, no incoming and no audio being played
}
P.S. I've tested this with whatsapp messenger and it works! Cannot say the same for other messenger apps.
I have two phones which are paired and connected via bluetooth. How to programmatically check at one phone's end whether the other phone is getting an incoming call? Should I use a particular profile for this, that is, PBAP or HFP? If yes, how I am to do this?
Once I detect this, if I want to receive the incoming call via the connected phone, how should I implement that?
Download Hands Free Profile pdf. It is available easily. It provides you all details about how HFP works and AT commands supported by HFP for communication. No APIs available for this.
As eliasj said, you need to implement HFP and when two phones get connected, you can communicate between them via AT commands.
Suppose you have 1st phone which is Android device and 2nd phone Android or any device and they both are connected over HFP.
I don't have complete code but I can suggest you some AT commands -
1. Using AT+CIND? command you can read indicator status of other phone.
2. To enable reporting for Indicator status change, you need to use AT+CMER=3,0,0,1 command.
3. Once you get valid response from 'AT+CMER' command, you can use AlarmManager that will start a service which continuously reads the input stream of Bluetooth Socket.
4. Because of step 2., if the 2nd phone is having any incoming call, the input stream of Bluetooth Socket will contains RING as an alert.
I have used service implementing a Runnable. Here is a sample code for step 4.-
public void run()
{
try
{
// Get input and output streams from Bluetooth socket.
m_oInputStream = m_oBluetoothSocket.getInputStream();
m_oOutputStream = m_oBluetoothSocket.getOutputStream();
// Read input stream for +CIEV response is given or not.
byte[] buffer = new byte[200];
int nNumberOfBytesRead = m_oInputStream.read(buffer);
String strResponse = new String(buffer).trim();
if(true == strResponse.contains("RING"))
{
// Contains RING Alert. Answer the call.
// Start Activity for handling Incoming Call.
Intent oIncomingCallActivityIntent = new Intent(getApplicationContext(), IncomingCallActivity.class);
oIncomingCallActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
getApplicationContext().startActivity(oIncomingCallActivityIntent);
// Stop service.
stopSelf();
}
}
catch(Exception e)
{
// Log the error.
}
}
You need to implement acivity that handles incoming call. It will accept or reject call. To accept incoming call AT+ATA command is used. You will receive "OK" as a response from 2nd phone.
I hope this will help you.
You should implement HFP (the hands-free side). When I looked at this problem over a year ago it was not possible to send the audio between to phones (Android) but it could have change now.
Look at the Q/A in How to send AT commands based on BT Hands-Free profile in android? (hit on how to connect) and in the HFP spec https://www.bluetooth.org/docman/handlers/downloaddoc.ashx?doc_id=238193 (for how the profile works (incoming call on page 36))
I'm trying to answer calls programmatically. (I'm not even going to discuss about using reflection as this only works in froyo and earlier versions). My approach was to send a headset button event to answer the call. Unfortunately this only works if a headset is connected to the phone so I send a broadcast to make the phone think that a headset is connected like this:
Intent headSetUnPluggedintent = new Intent(Intent.ACTION_HEADSET_PLUG);
headSetUnPluggedintent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
headSetUnPluggedintent.putExtra("state", 1); // 0 = unplugged 1 = Headset with microphone 2 = Headset without microphone
headSetUnPluggedintent.putExtra("name", "Headset");
sendOrderedBroadcast(headSetUnPluggedintent, null);
The call is successfuly answered. BUT after that I must "disconnect" the headset otherwise there is no audio (as it is redirected to the fake headset). So I send another broadcast like the previous one setting the "state" key to 0. But it doesn't work. The virtual headset is still connected. Can anyone help with this?
Seems like this headache went away with a simple project-Clean. It was just Eclipse making fun of me AGAIN. The code works fine now.
I am trying to identify how to route a very short audio stream (a notification) to a bluetooth headphone that is already paired with the device, while the device is ringing.
When I play any audio at any time, it is routed to the bluetooth device, no problem.
But if I try to start playing the audio when receiving an android.intent.action.PHONE_STATE, in RINGING state, the audio is not routed as expected.
I can see that the AudioManager's setBluetoothA2dpOn method has became deprecated, but I actually tried it but is seems has no effect.
I have tried the MediaRouter object, but I can see that MediaRouter.getSelectedRoute(MediaRouter.ROUTE_TYPE_LIVE_AUDIO) points to the RouteInfo of the Bluetooth device while the device is ringing, and the AudioManager.isBluetoothA2dpOn is true.
So, can any one tell me why the audio route is like this in the ringing moment? is there any way to force the audio to be routed to the Bluetooth device in such case?
[UPDATED]
I have tried again today and I have discovered something that may be the cause of the problem.
I have created a BroadcaseReceiver to detect the change in the android.intent.action.PHONE_STATE. if an intent is received and the state is currently ringing, check for AudioManager's mode and you will find it is MODE_NORMAL. but few seconds later the phone will start actually ringing and the mode is going to be changed into MODE_RINGTONE. trying to manually set the mode using the method setMode(AudioManager.MODE_NORMAL) is useless then, the state remains MODE_RINGTONE even after setting it to MODE_NORMAL.
Now, I think the cause of the problem is that in the MODE_RINGTONE mode, all the streams are directed to the phone speaker and here there is no way offered by the android system to change the mode.
I think the Media player realease the bluetooth connection when the phone is ringing. You can try to obtain the bluetooth audio connection and see if Media player now play through your obtained connection. You can use my class at my answer Using the Android RecognizerIntent with a bluetooth headset and see if it works. The audio in the class is Sco only.
As stated in the JavaDoc for the StartBluetoothSco method:
Note that the phone application always has the priority on the usage of the SCO connection for telephony. If this method is called while the phone is in call it will be ignored. Similarly, if a call is received or sent while an application is using the SCO connection, the connection will be lost for the application and NOT returned automatically when the call ends.`
I tried to start sco then play a music clip in normal mode, it played to the Bluetooth headset without problems, although I couldnot stop the microphone that caused the input stream plays to the headset. I then tried to call my target phone from another phone while the music is still playing, I found that the stream has got redirected automatically to the phone speaker. After the ringing mode is finished, the stream did not get redirected again to the Bluetooth headset and I think that behavior is normal according to what is stated in the JavaDoc above.
My guessing is that Android tries to protect the ringing and in-call modes as possible in order not to allow any unwanted interference from applications. In other words, when in ringing mode then no sound is going to be played to the headset until the call is accepted, and you cannot even change the AudioManager mode from ringing to another mode, your call for mode setter will be ignored.
I have tried the AudioTrack instead of MediaPlayer, but that makes no difference.
I have then tried the TextToSpeech engine like this:
in the main activity, initialize on create:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textToSpeech=new TextToSpeech(this, this);
textToSpeech.setLanguage(Locale.US);
textToSpeech.addEarcon("[wwww]", "XXXXXXXXXXXXXXXXXXXXXXX", R.raw.a);
}
in the broadcast receiver class when rining starting the Bluetooth utility class and adding the below to the onScoAudioConnected method
textToSpeech.playEarcon("[wwww]", TextToSpeech.QUEUE_FLUSH, null);
This did not work too.
There is a problem when using Google Navigation on Android with a Bluetooth device. Navigation sends the audio for the turn-by-turn instructions over the A2DP stream. I have a Motorola T605 Bluetooth car kit and it supports HFP and A2DP. I often just listen to the old fashioned car radio though. In this scenario I never hear turn-by-turn directions because my live A2DP stream is being sent to the radio AUX input but I am listening to the radio instead. My app has a feature where it reads out SMS messages to me. I have it set up so you can pick the stream you want to use for this. I prefer using AudioManager.STREAM_VOICE_CALL since it uses a dedicated separate speaker on the T605.
I have looked into a few ways to handle this:
1) Reroute notifications to Bluetooth SCO instead of A2DP. I have not fund a way to do this. I am able to send TTS messages I create over this path and it works great (I have an SMS reader built into my app). I tried activating SCO but the turn-by-turn instructions stream moves to the phone internal speaker instead of the voice call stream like I would prefer.
2) Capture the navigation messages and echo them back over the SCO path. I can't find a way to intercept or capture the navigation stream though. Does Google Navigation use the TTS engine or its own methods to create the voice output? It would be great to just get the directions in a text string since I could easily send that to the TTS engine and route it where I want like I do with SMS strings.
You can see my app source here: http://code.google.com/p/a2dpvolume/
Any thoughts?
Unfortunately I think you're SOOL when it comes to rerouting the navigation messages to BT SCO. I would expect the navigation app to use either the TTS or NOTIFICATION stream type to play the messages, and both of those stream types follow the MEDIA routing strategy (for NOTIFICATIONs that is at least typically true when there's no active voice call).
Although the behavior is up to each vendor to decide, my guess is that you'll find that in most implementations streams that follow the MEDIA routing strategy won't ever be routed to BT SCO, except during an ongoing voice call that is routed to BT SCO.
The best you could do in terms of routing is to force the routing to the loudspeaker, or to "anything but A2DP" (which means wired headset if one is attached, or the loudspeaker otherwise).
Here's how you could do that (I haven't verified that this works on every phone out there):
Class audioSystemClass = Class.forName("android.media.AudioSystem");
Method setForceUse = audioSystemClass.getMethod("setForceUse",
int.class,
int.class);
// 1 == FOR_MEDIA, 10 == FORCE_NO_BT_A2DP (FORCE_SPEAKER would be 1).
setForceUse.invoke(null, 1, 10);