I tried example code google refers as below to detect connected bluetooth devices
BluetoothHeadset mBluetoothHeadset;
// Get the default adapter
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
// Establish connection to the proxy.
mBluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.HEADSET);
private BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (profile == BluetoothProfile.HEADSET) {
mBluetoothHeadset = (BluetoothHeadset) proxy;
}
}
public void onServiceDisconnected(int profile) {
if (profile == BluetoothProfile.HEADSET) {
mBluetoothHeadset = null;
}
}
};
// ... call functions on mBluetoothHeadset
But I got the following problems:
mBluetoothHeadset is only available inside onServiceConnected. I use getConnectedDevices to detect live bluetooth headset. but if I place the code below
List ConnectedDevices = mBluetoothHeadset.getConnectedDevices();
out of onServiceConnected, running program lead always crash. What's wrong here?
is there any possibility to use mBluetoothHeadset value outside onServiceConnected ? like the example show? Or May I trans some parameter/value from onServiceConnected to outside?
Actually the example codes don't work. i have to place additional code after mProfileListener:
if (mBluetoothAdapter.getProfileProxy(this, mProfileListener,BluetoothProfile.HEADSET)==false) { ....
What's the reason? or what's wrong with my code?
From system log the code seems work, but when I run it, program stay in onServiceConnected, never go to onServiceDisconnected, or outside if no other action the user perform(e.g press a confirm button). What's wrong?
Related
I have Android application which exposes BLE Server. I connect with BluetoothGattServer#connect. It works - my app gets call to BluetoothGattServerCallback#onConnectionStateChange with STATE_CONNECTED. When I'm done with the client I try to disconnect from my app with BluetoothGattServer#cancelConnection.
But I do not get call to BluetoothGattServerCallback#onConnectionStateChange and it seems that the connection is still active as my BLE client does not start to advertise (which it does when nothing is connected to it).
In logcat I see only:
BluetoothGattServer: cancelConnection() - device: XX:XX:XX:XX:XX:XX
The funny part is, my app gets call to BluetoothGattServerCallback#onConnectionStateChange with STATE_DISCONNECTED as soon as I turn off BT completely.
Similar issues in Google's tracker: 63461 and 63464.
When newState==BluetoothProfile.STATE_CONNECTED, you have to call BluetoothGattServer.connect();.
#Override
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
super.onConnectionStateChange(device, status, newState);
if (newState == BluetoothProfile.STATE_CONNECTED){
mDevice = device;
mBluetoothGattServer.connect(device, false);
}else {
mDevice = null;
}
}
private void cancelConnection(){
if (mDevice != null) {
mBluetoothGattServer.cancelConnection(mDevice);
}
}
Encountering same issue when calling disconnect() method.. no disconnect is given in onConnectionStateChange in my BluetoothGattCallback.
Cycling Bluetooth seems the be the only thing that works.
edit:
also, after disconnect() and close() method are called, I am still connected according to this code:
public int getConnectedBLEDevices() {
int i = 0;
List<BluetoothDevice> devices = mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT);
for(BluetoothDevice device : devices) {
if(device.getType() == BluetoothDevice.DEVICE_TYPE_LE) {
Logs.writeEvent(TAG+".getConnectedBLEDevices()", device.getAddress() + "\n"+ getStateAsString(mBluetoothManager.getConnectionState(device, BluetoothProfile.GATT)));
i++;
}
}
return i;
}
pls see https://issuetracker.google.com/issues/37127644
Status: Won't Fix (Intended Behavior)
You must call BluetoothGattServer.connect() to mark connection as used, then BluetoothGattServer.disconnect() to mark it as no longer used. Then after a timeout stack can decide to disconnect from the remote if no one else is using the connection.
If BluetoothGattServer.connect() is not called after the connection is established, then the stack is keeping the connection until some gatt client/server app start using this connection.
I've been writing a chat app to work with bluetooth headsets/earphones.
So far I've been able to record audio files via the mic in a bluetooth headset and
I've been able to get Speech-to-text working with the Android device's built in microphone, using RecogniserIntent etc.
But I can't find a way of getting SpeechRecogniser to listen through the Bluetooth mic.Is it even possible to do so, and if so, how?
Current Device: Samsung Galax
Android Version: 4.4.2
Edit: I found some options hidden in my tablets settings for the Speech Recognizer, one of these is a tick box labeled "use bluetooth microphone" but it seems to have no effect.
Found the answer to my own question so I'm posting it for others to use:
In order to get speak recognition to work with a Bluetooth Mic you first need to get the device as a BluetoothHeadset Object and then call .startVoiceRecognition() on it, this will set the mode to Voice recognition.
Once finished you need to call .stopVoiceRecognition().
You get the BluetoothHeadset as such:
private void SetupBluetooth()
{
btAdapter = BluetoothAdapter.getDefaultAdapter();
pairedDevices = btAdapter.getBondedDevices();
BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy)
{
if (profile == BluetoothProfile.HEADSET)
{
btHeadset = (BluetoothHeadset) proxy;
}
}
public void onServiceDisconnected(int profile)
{
if (profile == BluetoothProfile.HEADSET) {
btHeadset = null;
}
}
};
btAdapter.getProfileProxy(SpeechActivity.this, mProfileListener, BluetoothProfile.HEADSET);
}
Then you get call startVoiceRecognition() and send off your voice recognition intent like so:
private void startVoice()
{
if(btAdapter.isEnabled())
{
for (BluetoothDevice tryDevice : pairedDevices)
{
//This loop tries to start VoiceRecognition mode on every paired device until it finds one that works(which will be the currently in use bluetooth headset)
if (btHeadset.startVoiceRecognition(tryDevice))
{
break;
}
}
}
recogIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
recogIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
recog = SpeechRecognizer.createSpeechRecognizer(SpeechActivity.this);
recog.setRecognitionListener(new RecognitionListener()
{
.........
});
recog.startListening(recogIntent);
}
I am trying to send all the audio of an application via SCO.
I am able to successfully send the audio,
But when an incoming call comes I need to disconnect form SCO so that the application audio will not interfere with the call,
The problem is that, when I try to reroute the audio to SCO after the call, it does not work.
Here is the code I use to send the audio to SCO:
public class BluetoothManager {
// For Bluetooth connectvity
private static String TAG = "BluetoothManager";
private static BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
private static AudioManager aM;
/**
* Set the audio manager of the device.
* #param c: The context this method is called from
*/
public static void setAudioManager(Context c) {
aM = (android.media.AudioManager)c.getSystemService(Context.AUDIO_SERVICE);
}
/**
* Check if a Bluetooth headset is connected. If so, route audio to Bluetooth SCO.
*/
private static void initializeAudioMode(Context context) {
BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (profile == BluetoothProfile.HEADSET) {
BluetoothHeadset bh = (BluetoothHeadset) proxy;
List<BluetoothDevice> devices = bh.getConnectedDevices();
if (devices.size() > 0) {
enableBluetoothSCO();
}
}
mBluetoothAdapter.closeProfileProxy(profile, proxy);
}
public void onServiceDisconnected(int profile) {}
};
mBluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.HEADSET);
}
/**
* Bluetooth Connectvity
* The following methods are associated with enabling/disabling Bluetooth.
* In the future we may want to disable other sources of audio.
*/
private static void enableBluetoothSCO() {
aM.setMode(AudioManager.MODE_IN_CALL);
aM.startBluetoothSco();
aM.setBluetoothScoOn(true);
}
/** Right now, this simply enables Bluetooth */
#SuppressLint("NewApi")
public static boolean enableBluetooth(Context c) {
// If there is an adapter, enable it if not already enabled
if (mBluetoothAdapter != null) {
if (!mBluetoothAdapter.isEnabled()) {
mBluetoothAdapter.enable();
}
setAudioManager(c);
initializeAudioMode(c);
Log.e(TAG, "SCO: " + aM.isBluetoothScoOn());
Log.e(TAG, "A2DP: " + aM.isSpeakerphoneOn());
return true;
} else {
Log.v(TAG, "There is no bluetooth adapter");
return false;
}
}
/** Right now, this simply disables Bluetooth */
public static void disableBluetooth() {
// If there is an adapter, disabled it if not already disabled
if (mBluetoothAdapter != null) {
if (mBluetoothAdapter.isEnabled()) {
mBluetoothAdapter.disable();
}
} else {
Log.v(TAG, "There is no bluetooth adapter");
}
}
public static void restartBluetooth(){
aM.setMode(AudioManager.MODE_IN_CALL);
}
public static void stopBluetooth(){
aM.setMode(AudioManager.MODE_NORMAL);
}
}
When I call stopBluetooth() correctly the audio of the application is not sent to the headset anymore,
But when I call restartBluetooth() the audio plays NOT form the headset as intended, but from the phone speakers.
Is it possible that the SCO link was brought down after the call ended? If this is the case then the SCO link would also have to be brought up along with routing the audio.
Have you tried calling enableBluetoothSCO() within restartBluetooth()
You probably need to call:
aM.startBluetoothSco();
aM.setBluetoothScoOn(true);
after you set the mode.
inside your restart function initialize everything again, and see if it works. like so:
public static void restartBluetooth(){
enableBluetooth(getApplicationContext());
}
if this works then it means that when the call is ended the last initialization is lost for some reason.
Google Doc say's that
"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."
So when call is disconnected you must have to re-establish the connection by calling startBluetoothSco()
For anyone that is still having issues with this, there are a few things that need to be done. The first thing you need to do is to keep track of the phone state. You can see how to do that here:
How to know Phone call has ended?
When the state is idle that means the incoming call has ended. Now if you try to reconnect the bluetooth at this point you'll find it still does not work since it takes a while (roughly 2 seconds) for the call to "release" the bluetooth device.
So you have two option, wait a bit then try to reconnect, or you can add another listener to BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED.
You can then add a global boolean value isIdle that is true when TelephonyManager.CALL_STATE_IDLE or false when TelephonyManager.CALL_STATE_OFFHOOK (Otherwise you'll reconnect to BlueTooth during the incoming call). At this point when BluetoothHeadset.STATE_DISCONNECTED and isIdle is true, then reconnect to Bluetooth.
#Override public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals((BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED))){
int state = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
switch(state) {
case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
if (isIdle){
//reconnect bluetooth
}
break;
}
}
if(("OFFHOOK").equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE))) {
isIdle = false;
// turn bluetooth off
}
if(("IDLE").equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE))) {
isIdle = true;
}
}
I am trying to connect a bluetooth headset to my android device using the android developer page as a reference. http://developer.android.com/guide/topics/connectivity/bluetooth.html
My problem is when i trying calling the getProfileProxy(context, mProfileListener, BluetoothProfile.HEADSET) method, I am unsure of what to pass for context? I located this error from the question here:
can not connect to bluetooth headset in android
I am extremely new to this so I will apologize in advance if this is a silly question. I have spent a lot of time trying to research this but every example and documentation I find just has a context variable passed in so I am not sure where I am going wrong. My code, which is more or less a copy from the android documentation is:
// Establish connection to the proxy.
boolean mProfileProxy = mBluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.HEADSET);
Log.d(TAGP,"Get Adapter Success: "+mProfileProxy);
Log.d(TAGP,"Context: "+context);
BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (profile == BluetoothProfile.HEADSET) {
mBluetoothHeadset = (BluetoothHeadset) proxy;
Log.d(TAGP,"BLuetooth Headset: "+mBluetoothHeadset);
Log.d(TAGP,"Proxy: "+proxy);
}
}
public void onServiceDisconnected(int profile) {
if (profile == BluetoothProfile.HEADSET) {
mBluetoothHeadset = null;
}
}
};
The context can be an activity or service context. So if the code above is in a class that extends Activity or Service you can pass this.
You can use my answer at Using the Android RecognizerIntent with a bluetooth headset
After steeping in the wrong path for almost two months i found out what my mistake was.
Now i am pacing a new problem which I cannot find the answer to:
Using this function while trying to connect to the headset:
mBluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.HEADSET);
final BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (profile == BluetoothProfile.HEADSET) {
mBluetoothHeadset = (BluetoothHeadset) proxy;
}
}
public void onServiceDisconnected(int profile) {
if (profile == BluetoothProfile.HEADSET) {
mBluetoothHeadset = null;
}
}
};
I cannot initialize the mBluetoothHeadset object ,for some reason the debugger wont step into the onServiceConnected function..
Any help will be appreciated...realy needs one
shai
More Info:
Inded what haped was that after a android restart Bluetooth nneded to be enabled'solved in code:
This is the function code:
Log("PM.CheckForHeadSet","In");
if (mBluetoothAdapter == null) {
Log("PM.CheckForHeadSet","BlueTooth adapter not found");
return "Error Bluetooth adapter";
}
switch (mBluetoothAdapter.getState()){
case BluetoothAdapter.STATE_OFF:
Log("PM.CheckForHeadSet.getState"," STATE_OFF");
mBluetoothAdapter.enable();
break;
case BluetoothAdapter.STATE_TURNING_ON:
Log("PM.CheckForHeadSet.getState","STATE_TURNING_ON");
break;
case BluetoothAdapter.STATE_ON:
Log("PM.CheckForHeadSet.getState","STATE_ON");
break;
case BluetoothAdapter.STATE_TURNING_OFF:
Log("PM.CheckForHeadSet.getState","STATE_TURNING_OFF");
break;
}
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
// If there are paired devices, add each one to the ArrayAdapter
if (pairedDevices.size() == 1) {
for (BluetoothDevice device : pairedDevices)
if(device.getBondState() == BluetoothDevice.BOND_BONDED){
Log("PM.CheckForHeadSet Connected to:",device.getName());
}
}
Log("PM.CheckForHeadSet ServiceListener:","In");
final BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (profile == BluetoothProfile.HEADSET) {
mBluetoothHeadset = (BluetoothHeadset) proxy;
}
}
public void onServiceDisconnected(int profile) {
if (profile == BluetoothProfile.HEADSET) {
mBluetoothHeadset = null; }
}};
if(mBluetoothHeadset == null)
Log("PM.CheckForHeadSet","mBluetoothHeadset = null");
else
Log("PM.CheckForHeadSet","mBluetoothHeadset = " + mBluetoothHeadset.toString());
if(context == null)
Log("PM.CheckForHeadSet","context = null");
else
Log("PM.CheckForHeadSet","context = " + context.toString());
if(mProfileListener == null)
Log("PM.CheckForHeadSet","mProfileListener = null");
else
Log("PM.CheckForHeadSet","mProfileListener = " + mProfileListener.toString());
if(mBluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.HEADSET) == true)
Log("PM.CheckForHeadSet.getProfileProxy","true");
else
Log("PM.CheckForHeadSet.getProfileProxy","false");
Log("PM.CheckForHeadSet","Out");
return "Set Headset";
If i place the GetProfileProxy above the new ProfileListener (as in the docomantaion example|) the mProfileListener var is still null and getProfileProxy return false
And this is the logcat:
03-12 10:09:49.906: D/SpySitter(4205): PM.CheckForHeadSet-In
03-12 10:09:50.968: D/dalvikvm(4205): threadid=1: still suspended after undo (sc=1 dc=1)
03-12 10:09:59.453: D/SpySitter(4205): PM.CheckForHeadSet.getState-STATE_ON
03-12 10:10:02.640: D/SpySitter(4205): PM.CheckForHeadSet Connected to:-Motorola H790
03-12 10:10:04.226: D/SpySitter(4205): PM.CheckForHeadSet ServiceListener:-In
03-12 10:10:13.945: D/SpySitter(4205): PM.CheckForHeadSet-mBluetoothHeadset = null
03-12 10:10:17.984: D/SpySitter(4205): PM.CheckForHeadSet-context = android.app.Application#408472a0
03-12 10:10:21.820: D/SpySitter(4205): PM.CheckForHeadSet-mProfileListener = com.example.HelloForm.Tools$1#40894d00
03-12 10:10:28.796: D/SpySitter(4205): PM.CheckForHeadSet.getProfileProxy-true
03-12 10:10:31.226: D/SpySitter(4205): PM.CheckForHeadSet-Out
getProxyProfile returns false in three cases (this is according to the android-15 source, so it might be slightly different in android-11):
the Context passed in is null
the BluetoothProfile.ServiceListener passed in is null
the int passed in is not one of BluetoothProfile.HEADSET, BluetoothProfile.A2DP, BluetoothProfile.INPUT_DEVICE, BluetoothProfile.PAN, or BluetoothProfile.HEALTH
I'd suggest setting a breakpoint on your call to getProxyProfile to figure out which of these cases is causing the false return value.
edit: Further info
Bluetooth has to be supported and enabled for the interface methods to be called. If Bluetooth is not supported, getDefaultAdapter() will return null. If you have an adapter, you can check whether Bluetooth is enabled by calling isEnabled() on the adapter. That's pretty straightforward, so you may have already done that.
onServiceConnected is called by the android.content.ServiceConnection member (mServiceConnection) in the BluetoothHeadset class, which is in turn called by the Android OS by virtue of a bindService call when the BluetoothHeadset object is created. If the binding of this service fails, there should be an error in the logcat message log something like:
"Could not bind to Bluetooth Headset Service"
with a tag of "BluetoothHeadset". If you don't see this error, things are probably working as they should under the hood.
A final note is that Android only supports one connected headset at a time. Make sure that no headset is connected when you are running this code. On a similar note, many devices only support one bluetooth connection at a time. If that applies to your device, you'll want to make sure it is not already connected when you run this code.
That pretty much exhausts what I know and can easily determine about Bluetooth connections. Hope it helps you solve your problem.