Fix bluetooth sco connection still active when call ends issue

This commit is contained in:
Sylvain Berfini 2014-03-26 17:26:37 +01:00
parent c9beba1481
commit e7ffd98857
4 changed files with 53 additions and 136 deletions

View file

@ -120,11 +120,6 @@
</receiver> </receiver>
<receiver android:name="org.linphone.BluetoothManager"> <receiver android:name="org.linphone.BluetoothManager">
<intent-filter>
<action android:name="android.bluetooth.device.action.ACL_CONNECTED"/>
<action android:name="android.bluetooth.device.action.ACL_DISCONNECTED"/>
<action android:name="android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED"/>
</intent-filter>
</receiver> </receiver>
<receiver android:name="org.linphone.BootReceiver"> <receiver android:name="org.linphone.BootReceiver">
@ -134,8 +129,10 @@
<receiver android:name="org.linphone.PhoneStateChangedReceiver"> <receiver android:name="org.linphone.PhoneStateChangedReceiver">
<intent-filter><action android:name="android.intent.action.PHONE_STATE" /></intent-filter> <intent-filter><action android:name="android.intent.action.PHONE_STATE" /></intent-filter>
</receiver> </receiver>
<receiver android:name="KeepAliveHandler" > <receiver android:name="KeepAliveHandler" >
</receiver> </receiver>
<!-- Needed for push notification --> <!-- Needed for push notification -->
<receiver android:name="org.linphone.gcm.GCMReceiver" android:permission="com.google.android.c2dm.permission.SEND" > <receiver android:name="org.linphone.gcm.GCMReceiver" android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter> <intent-filter>

View file

@ -1,54 +0,0 @@
package org.linphone;
/*
BluetoothActionReceiver.java
Copyright (C) 2014 Belledonne Communications, Grenoble, France
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
import org.linphone.mediastream.Log;
import android.annotation.TargetApi;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class BluetoothActionReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT)) {
BluetoothDevice device = (BluetoothDevice) intent.getExtras().get(BluetoothDevice.EXTRA_DEVICE);
String command = intent.getExtras().getString(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD);
int type = intent.getExtras().getInt(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE);
Object[] temp = (Object[]) intent.getExtras().get(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS);
String[] args = new String[temp.length];
String logArgs = "";
for (int i = 0; i < temp.length; i++) {
args[i] = (String) temp[i];
logArgs += args[i] + " ";
}
Log.d("Bluetooth headset event from " + device.getName() + ", command: " + command + " (type: " + type + ") with args: " + logArgs);
//TODO: parse command/args and do something with it
}
}
}

View file

@ -20,12 +20,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
import java.util.List; import java.util.List;
import org.linphone.LinphoneSimpleListener.LinphoneOnAudioChangedListener.AudioState; import org.linphone.LinphoneSimpleListener.LinphoneOnAudioChangedListener.AudioState;
import org.linphone.compatibility.Compatibility;
import org.linphone.mediastream.Log; import org.linphone.mediastream.Log;
import org.linphone.mediastream.Version; import org.linphone.mediastream.Version;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothAssignedNumbers;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile;
@ -49,9 +49,7 @@ public class BluetoothManager extends BroadcastReceiver {
private BluetoothHeadset mBluetoothHeadset; private BluetoothHeadset mBluetoothHeadset;
private BluetoothDevice mBluetoothDevice; private BluetoothDevice mBluetoothDevice;
private BluetoothProfile.ServiceListener mProfileListener; private BluetoothProfile.ServiceListener mProfileListener;
private BroadcastReceiver bluetoothActionReceiver = new BluetoothActionReceiver();
private boolean isBluetoothConnected; private boolean isBluetoothConnected;
private boolean isUsingBluetoothAudioRoute;
public static BluetoothManager getInstance() { public static BluetoothManager getInstance() {
if (instance == null) { if (instance == null) {
@ -60,17 +58,11 @@ public class BluetoothManager extends BroadcastReceiver {
return instance; return instance;
} }
/** private BluetoothManager() {
* Do not call !
*/
public BluetoothManager() {
isBluetoothConnected = false; isBluetoothConnected = false;
isUsingBluetoothAudioRoute = false;
try {
mContext = LinphoneManager.getInstance().getContext(); mContext = LinphoneManager.getInstance().getContext();
mAudioManager = ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE)); mAudioManager = ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE));
instance = this; instance = this;
} catch (Exception e) {}
} }
public void startBluetooth() { public void startBluetooth() {
@ -80,9 +72,9 @@ public class BluetoothManager extends BroadcastReceiver {
} }
IntentFilter filter = new IntentFilter(); IntentFilter filter = new IntentFilter();
filter.addAction("android.bluetooth.device.action.ACL_CONNECTED"); filter.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY + "." + BluetoothAssignedNumbers.PLANTRONICS);
filter.addAction("android.bluetooth.device.action.ACL_DISCONNECTED"); filter.addAction("android.media.ACTION_SCO_AUDIO_STATE_UPDATED");
filter.addAction("android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED"); filter.addAction(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT);
mContext.registerReceiver(this, filter); mContext.registerReceiver(this, filter);
Log.d("Bluetooth receiver started"); Log.d("Bluetooth receiver started");
@ -99,14 +91,12 @@ public class BluetoothManager extends BroadcastReceiver {
public void onServiceConnected(int profile, BluetoothProfile proxy) { public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (profile == BluetoothProfile.HEADSET) { if (profile == BluetoothProfile.HEADSET) {
Log.d("Bluetooth headset connected"); Log.d("Bluetooth headset connected");
mContext.registerReceiver(bluetoothActionReceiver, new IntentFilter(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT));
mBluetoothHeadset = (BluetoothHeadset) proxy; mBluetoothHeadset = (BluetoothHeadset) proxy;
isBluetoothConnected = true; isBluetoothConnected = true;
} }
} }
public void onServiceDisconnected(int profile) { public void onServiceDisconnected(int profile) {
if (profile == BluetoothProfile.HEADSET) { if (profile == BluetoothProfile.HEADSET) {
mContext.unregisterReceiver(bluetoothActionReceiver);
mBluetoothHeadset = null; mBluetoothHeadset = null;
isBluetoothConnected = false; isBluetoothConnected = false;
Log.d("Bluetooth headset disconnected"); Log.d("Bluetooth headset disconnected");
@ -134,27 +124,22 @@ public class BluetoothManager extends BroadcastReceiver {
} }
if (mBluetoothAdapter.isEnabled() && mAudioManager.isBluetoothScoAvailableOffCall()) { if (mBluetoothAdapter.isEnabled() && mAudioManager.isBluetoothScoAvailableOffCall()) {
isUsingBluetoothAudioRoute = isBluetoothHeadsetAvailable(); if (isBluetoothHeadsetAvailable()) {
if (mAudioManager != null && !mAudioManager.isBluetoothScoOn()) {
if (isUsingBluetoothAudioRoute) {
if (mAudioManager != null) {
mAudioManager.setBluetoothScoOn(true); mAudioManager.setBluetoothScoOn(true);
mAudioManager.startBluetoothSco(); mAudioManager.startBluetoothSco();
LinphoneManager.getInstance().audioStateChanged(AudioState.BLUETOOTH);
} }
} else {
LinphoneManager.getInstance().audioStateChanged(AudioState.SPEAKER);
} }
isUsingBluetoothAudioRoute = mBluetoothHeadset.isAudioConnected(mBluetoothDevice); boolean ok = mBluetoothHeadset != null && mBluetoothHeadset.isAudioConnected(mBluetoothDevice);
if (!isUsingBluetoothAudioRoute && !isRetry) { if (!ok && !isRetry) {
Log.w("Routing audio to bluetooth headset failed, retry...."); Log.w("Routing audio to bluetooth headset failed, retry....");
try { try {
Thread.sleep(100); Thread.sleep(100);
} catch (InterruptedException e) {} } catch (InterruptedException e) {}
return routeAudioToBluetooth(true); return routeAudioToBluetooth(true);
} else if (isRetry) { } else if (isRetry) {
if (isUsingBluetoothAudioRoute) { if (ok) {
Log.d("Retry worked, audio is routed to bluetooth headset"); Log.d("Retry worked, audio is routed to bluetooth headset");
} else { } else {
Log.e("Retry not worked, audio isn't routed to bluetooth headset..."); Log.e("Retry not worked, audio isn't routed to bluetooth headset...");
@ -164,14 +149,14 @@ public class BluetoothManager extends BroadcastReceiver {
Log.d("Routing audio to bluetooth headset worked at first try"); Log.d("Routing audio to bluetooth headset worked at first try");
} }
return isUsingBluetoothAudioRoute; return ok;
} }
return false; return false;
} }
public boolean isUsingBluetoothAudioRoute() { public boolean isUsingBluetoothAudioRoute() {
return mBluetoothHeadset.isAudioConnected(mBluetoothDevice); return mBluetoothHeadset != null && mBluetoothHeadset.isAudioConnected(mBluetoothDevice);
} }
public boolean isBluetoothHeadsetAvailable() { public boolean isBluetoothHeadsetAvailable() {
@ -184,6 +169,7 @@ public class BluetoothManager extends BroadcastReceiver {
boolean isHeadsetConnected = false; boolean isHeadsetConnected = false;
if (mBluetoothHeadset != null) { if (mBluetoothHeadset != null) {
List<BluetoothDevice> devices = mBluetoothHeadset.getConnectedDevices(); List<BluetoothDevice> devices = mBluetoothHeadset.getConnectedDevices();
mBluetoothDevice = null;
for (final BluetoothDevice dev : devices) { for (final BluetoothDevice dev : devices) {
if (mBluetoothHeadset.getConnectionState(dev) == BluetoothHeadset.STATE_CONNECTED) { if (mBluetoothHeadset.getConnectionState(dev) == BluetoothHeadset.STATE_CONNECTED) {
mBluetoothDevice = dev; mBluetoothDevice = dev;
@ -201,8 +187,10 @@ public class BluetoothManager extends BroadcastReceiver {
} }
public void disableBluetoothSCO() { public void disableBluetoothSCO() {
isUsingBluetoothAudioRoute = false; if (mAudioManager != null && mAudioManager.isBluetoothScoOn()) {
if (mAudioManager != null) { mAudioManager.stopBluetoothSco();
mAudioManager.setBluetoothScoOn(false);
Log.w("Hack: stopping bluetooth sco again since first time won't have succedded");
mAudioManager.stopBluetoothSco(); mAudioManager.stopBluetoothSco();
mAudioManager.setBluetoothScoOn(false); mAudioManager.setBluetoothScoOn(false);
Log.w("Bluetooth sco disconnected!"); Log.w("Bluetooth sco disconnected!");
@ -212,28 +200,17 @@ public class BluetoothManager extends BroadcastReceiver {
public void stopBluetooth() { public void stopBluetooth() {
Log.w("Stopping bluetooth..."); Log.w("Stopping bluetooth...");
isBluetoothConnected = false; isBluetoothConnected = false;
isUsingBluetoothAudioRoute = false;
if (mAudioManager != null) { disableBluetoothSCO();
mAudioManager.stopBluetoothSco();
mAudioManager.setBluetoothScoOn(false);
}
if (mProfileListener != null && mBluetoothHeadset != null) { if (mProfileListener != null && mBluetoothHeadset != null) {
mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset); mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);
mContext.unregisterReceiver(bluetoothActionReceiver);
mProfileListener = null; mProfileListener = null;
mBluetoothHeadset = null;
} }
mBluetoothDevice = null; mBluetoothDevice = null;
Log.w("Bluetooth stopped!"); Log.w("Bluetooth stopped!");
if (LinphoneManager.getLc().getCallsNb() > 0) {
Log.w("Bluetooth disabled, Going back to incall mode");
Compatibility.setAudioManagerInCallMode(mAudioManager);
}
try { try {
mContext.unregisterReceiver(this); mContext.unregisterReceiver(this);
Log.d("Bluetooth receiver stopped"); Log.d("Bluetooth receiver stopped");
@ -252,38 +229,42 @@ public class BluetoothManager extends BroadcastReceiver {
} }
} }
@SuppressWarnings("deprecation")
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
if (!LinphoneManager.isInstanciated()) if (!LinphoneManager.isInstanciated())
return; return;
String action = intent.getAction(); String action = intent.getAction();
if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
Log.d("Bluetooth Received Event" + " ACTION_ACL_DISCONNECTED");
stopBluetooth();
}
else if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
Log.d("Bluetooth Received Event" + " ACTION_ACL_CONNECTED");
startBluetooth();
}
else if (AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED.equals(action)) {
int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, 0);
Log.d("Bluetooth sco state changed : " + state);
if (state == AudioManager.SCO_AUDIO_STATE_CONNECTED) {
startBluetooth();
} else if (state == AudioManager.SCO_AUDIO_STATE_DISCONNECTED) {
stopBluetooth();
}
}
//Using real value instead of constant because not available before sdk 11 //Using real value instead of constant because not available before sdk 11
if ("android.media.ACTION_SCO_AUDIO_STATE_UPDATED".equals(action)) { // AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED
int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, 0);
if (state == AudioManager.SCO_AUDIO_STATE_CONNECTED) {
Log.d("Bluetooth sco state updated to connected");
LinphoneManager.getInstance().audioStateChanged(AudioState.BLUETOOTH);
//isUsingBluetoothAudioRoute = true;
} else if (state == AudioManager.SCO_AUDIO_STATE_DISCONNECTED) {
Log.d("Bluetooth sco state updated to disconnected");
LinphoneManager.getInstance().audioStateChanged(AudioState.SPEAKER);
//isUsingBluetoothAudioRoute = false;
}
}
else if ("android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED".equals(action)) { //BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED else if ("android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED".equals(action)) { //BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED
int currentConnState = intent.getIntExtra("android.bluetooth.adapter.extra.CONNECTION_STATE", //BluetoothAdapter.EXTRA_CONNECTION_STATE int state = intent.getIntExtra("android.bluetooth.adapter.extra.CONNECTION_STATE", //BluetoothAdapter.EXTRA_CONNECTION_STATE
0); //BluetoothAdapter.STATE_DISCONNECTED 0); //BluetoothAdapter.STATE_DISCONNECTED
Log.d("Bluetooth state changed: " + currentConnState); if (state == 0) {
if (currentConnState == 2) { //BluetoothAdapter.STATE_CONNECTED Log.d("Bluetooth state updated to disconnected");
startBluetooth(); } else if (state == 2) {
} Log.d("Bluetooth state updated to connected");
}
}
else if (intent.getAction().equals(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT)) {
String command = intent.getExtras().getString(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD);
int type = intent.getExtras().getInt(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE);
Object[] args = (Object[]) intent.getExtras().get(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS);
String eventName = (String) args[0];
Log.d("Bluetooth event: " + command + " (type: " + type + ") = " + eventName);
//TODO: parse command/args and do something with it
} }
} }
} }

View file

@ -885,8 +885,10 @@ public class LinphoneManager implements LinphoneCoreListener {
if (activity != null) { if (activity != null) {
TelephonyManager tm = (TelephonyManager) activity.getSystemService(Context.TELEPHONY_SERVICE); TelephonyManager tm = (TelephonyManager) activity.getSystemService(Context.TELEPHONY_SERVICE);
if (tm.getCallState() == TelephonyManager.CALL_STATE_IDLE) { if (tm.getCallState() == TelephonyManager.CALL_STATE_IDLE) {
mAudioManager.setMode(AudioManager.MODE_NORMAL);
Log.d("---AudioManager: back to MODE_NORMAL"); Log.d("---AudioManager: back to MODE_NORMAL");
mAudioManager.setMode(AudioManager.MODE_NORMAL);
Log.d("All call terminated, routing back to earpiece");
routeAudioToReceiver();
} }
} }
} }
@ -1248,15 +1250,6 @@ public class LinphoneManager implements LinphoneCoreListener {
enableCamera(call, sendCamera); enableCamera(call, sendCamera);
} }
Context activity = getContext();
if (activity != null) {
TelephonyManager tm = (TelephonyManager) activity.getSystemService(Context.TELEPHONY_SERVICE);
if (state == State.CallEnd && mLc.getCallsNb() == 0 && tm.getCallState() == TelephonyManager.CALL_STATE_IDLE) {
Log.d("All call terminated, routing back to earpiece");
routeAudioToReceiver();
}
}
if (serviceListener != null) serviceListener.onCallStateChanged(call, state, message); if (serviceListener != null) serviceListener.onCallStateChanged(call, state, message);
for (LinphoneOnCallStateChangedListener l : getSimpleListeners(LinphoneOnCallStateChangedListener.class)) { for (LinphoneOnCallStateChangedListener l : getSimpleListeners(LinphoneOnCallStateChangedListener.class)) {
l.onCallStateChanged(call, state, message); l.onCallStateChanged(call, state, message);