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 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 android:name="org.linphone.BootReceiver">
@ -134,8 +129,10 @@
<receiver android:name="org.linphone.PhoneStateChangedReceiver">
<intent-filter><action android:name="android.intent.action.PHONE_STATE" /></intent-filter>
</receiver>
<receiver android:name="KeepAliveHandler" >
</receiver>
<!-- Needed for push notification -->
<receiver android:name="org.linphone.gcm.GCMReceiver" android:permission="com.google.android.c2dm.permission.SEND" >
<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 org.linphone.LinphoneSimpleListener.LinphoneOnAudioChangedListener.AudioState;
import org.linphone.compatibility.Compatibility;
import org.linphone.mediastream.Log;
import org.linphone.mediastream.Version;
import android.annotation.TargetApi;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothAssignedNumbers;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothProfile;
@ -49,9 +49,7 @@ public class BluetoothManager extends BroadcastReceiver {
private BluetoothHeadset mBluetoothHeadset;
private BluetoothDevice mBluetoothDevice;
private BluetoothProfile.ServiceListener mProfileListener;
private BroadcastReceiver bluetoothActionReceiver = new BluetoothActionReceiver();
private boolean isBluetoothConnected;
private boolean isUsingBluetoothAudioRoute;
public static BluetoothManager getInstance() {
if (instance == null) {
@ -60,17 +58,11 @@ public class BluetoothManager extends BroadcastReceiver {
return instance;
}
/**
* Do not call !
*/
public BluetoothManager() {
private BluetoothManager() {
isBluetoothConnected = false;
isUsingBluetoothAudioRoute = false;
try {
mContext = LinphoneManager.getInstance().getContext();
mAudioManager = ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE));
instance = this;
} catch (Exception e) {}
}
public void startBluetooth() {
@ -80,9 +72,9 @@ public class BluetoothManager extends BroadcastReceiver {
}
IntentFilter filter = new IntentFilter();
filter.addAction("android.bluetooth.device.action.ACL_CONNECTED");
filter.addAction("android.bluetooth.device.action.ACL_DISCONNECTED");
filter.addAction("android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED");
filter.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY + "." + BluetoothAssignedNumbers.PLANTRONICS);
filter.addAction("android.media.ACTION_SCO_AUDIO_STATE_UPDATED");
filter.addAction(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT);
mContext.registerReceiver(this, filter);
Log.d("Bluetooth receiver started");
@ -99,14 +91,12 @@ public class BluetoothManager extends BroadcastReceiver {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (profile == BluetoothProfile.HEADSET) {
Log.d("Bluetooth headset connected");
mContext.registerReceiver(bluetoothActionReceiver, new IntentFilter(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT));
mBluetoothHeadset = (BluetoothHeadset) proxy;
isBluetoothConnected = true;
}
}
public void onServiceDisconnected(int profile) {
if (profile == BluetoothProfile.HEADSET) {
mContext.unregisterReceiver(bluetoothActionReceiver);
mBluetoothHeadset = null;
isBluetoothConnected = false;
Log.d("Bluetooth headset disconnected");
@ -134,27 +124,22 @@ public class BluetoothManager extends BroadcastReceiver {
}
if (mBluetoothAdapter.isEnabled() && mAudioManager.isBluetoothScoAvailableOffCall()) {
isUsingBluetoothAudioRoute = isBluetoothHeadsetAvailable();
if (isUsingBluetoothAudioRoute) {
if (mAudioManager != null) {
if (isBluetoothHeadsetAvailable()) {
if (mAudioManager != null && !mAudioManager.isBluetoothScoOn()) {
mAudioManager.setBluetoothScoOn(true);
mAudioManager.startBluetoothSco();
LinphoneManager.getInstance().audioStateChanged(AudioState.BLUETOOTH);
}
} else {
LinphoneManager.getInstance().audioStateChanged(AudioState.SPEAKER);
}
isUsingBluetoothAudioRoute = mBluetoothHeadset.isAudioConnected(mBluetoothDevice);
if (!isUsingBluetoothAudioRoute && !isRetry) {
boolean ok = mBluetoothHeadset != null && mBluetoothHeadset.isAudioConnected(mBluetoothDevice);
if (!ok && !isRetry) {
Log.w("Routing audio to bluetooth headset failed, retry....");
try {
Thread.sleep(100);
} catch (InterruptedException e) {}
return routeAudioToBluetooth(true);
} else if (isRetry) {
if (isUsingBluetoothAudioRoute) {
if (ok) {
Log.d("Retry worked, audio is routed to bluetooth headset");
} else {
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");
}
return isUsingBluetoothAudioRoute;
return ok;
}
return false;
}
public boolean isUsingBluetoothAudioRoute() {
return mBluetoothHeadset.isAudioConnected(mBluetoothDevice);
return mBluetoothHeadset != null && mBluetoothHeadset.isAudioConnected(mBluetoothDevice);
}
public boolean isBluetoothHeadsetAvailable() {
@ -184,6 +169,7 @@ public class BluetoothManager extends BroadcastReceiver {
boolean isHeadsetConnected = false;
if (mBluetoothHeadset != null) {
List<BluetoothDevice> devices = mBluetoothHeadset.getConnectedDevices();
mBluetoothDevice = null;
for (final BluetoothDevice dev : devices) {
if (mBluetoothHeadset.getConnectionState(dev) == BluetoothHeadset.STATE_CONNECTED) {
mBluetoothDevice = dev;
@ -201,8 +187,10 @@ public class BluetoothManager extends BroadcastReceiver {
}
public void disableBluetoothSCO() {
isUsingBluetoothAudioRoute = false;
if (mAudioManager != null) {
if (mAudioManager != null && mAudioManager.isBluetoothScoOn()) {
mAudioManager.stopBluetoothSco();
mAudioManager.setBluetoothScoOn(false);
Log.w("Hack: stopping bluetooth sco again since first time won't have succedded");
mAudioManager.stopBluetoothSco();
mAudioManager.setBluetoothScoOn(false);
Log.w("Bluetooth sco disconnected!");
@ -212,28 +200,17 @@ public class BluetoothManager extends BroadcastReceiver {
public void stopBluetooth() {
Log.w("Stopping bluetooth...");
isBluetoothConnected = false;
isUsingBluetoothAudioRoute = false;
if (mAudioManager != null) {
mAudioManager.stopBluetoothSco();
mAudioManager.setBluetoothScoOn(false);
}
disableBluetoothSCO();
if (mProfileListener != null && mBluetoothHeadset != null) {
mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);
mContext.unregisterReceiver(bluetoothActionReceiver);
mProfileListener = null;
mBluetoothHeadset = null;
}
mBluetoothDevice = null;
Log.w("Bluetooth stopped!");
if (LinphoneManager.getLc().getCallsNb() > 0) {
Log.w("Bluetooth disabled, Going back to incall mode");
Compatibility.setAudioManagerInCallMode(mAudioManager);
}
try {
mContext.unregisterReceiver(this);
Log.d("Bluetooth receiver stopped");
@ -252,38 +229,42 @@ public class BluetoothManager extends BroadcastReceiver {
}
}
@SuppressWarnings("deprecation")
public void onReceive(Context context, Intent intent) {
if (!LinphoneManager.isInstanciated())
return;
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
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
0); //BluetoothAdapter.STATE_DISCONNECTED
Log.d("Bluetooth state changed: " + currentConnState);
if (currentConnState == 2) { //BluetoothAdapter.STATE_CONNECTED
startBluetooth();
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
int state = intent.getIntExtra("android.bluetooth.adapter.extra.CONNECTION_STATE", //BluetoothAdapter.EXTRA_CONNECTION_STATE
0); //BluetoothAdapter.STATE_DISCONNECTED
if (state == 0) {
Log.d("Bluetooth state updated to disconnected");
} 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) {
TelephonyManager tm = (TelephonyManager) activity.getSystemService(Context.TELEPHONY_SERVICE);
if (tm.getCallState() == TelephonyManager.CALL_STATE_IDLE) {
mAudioManager.setMode(AudioManager.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);
}
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);
for (LinphoneOnCallStateChangedListener l : getSimpleListeners(LinphoneOnCallStateChangedListener.class)) {
l.onCallStateChanged(call, state, message);