blob: 0d846912500d10a1f8998aee2b5824b01a0666dc [file] [log] [blame]
/******************************************************************************
*
* Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package com.android.bluetooth.apm;
import static android.Manifest.permission.BLUETOOTH_CONNECT;
import android.bluetooth.BluetoothDevice;
import com.android.bluetooth.hfp.HeadsetService;
import com.android.bluetooth.hfp.HeadsetA2dpSync;
import com.android.bluetooth.apm.ApmConst;
import com.android.bluetooth.apm.MediaAudio;
import com.android.bluetooth.apm.CallControl;
import android.media.AudioManager;
import com.android.bluetooth.apm.ActiveDeviceManagerService;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.btservice.AdapterService;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothProfile;
import com.android.bluetooth.Utils;
import android.content.Context;
import java.lang.Integer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import android.util.Log;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import android.content.Intent;
import android.os.UserHandle;
public class CallAudio {
private static CallAudio mCallAudio;
private static final String TAG = "APM: CallAudio";
Map<String, CallDevice> mCallDevicesMap;
private Context mContext;
private AudioManager mAudioManager;
private ActiveDeviceManagerService mActiveDeviceManager;
private AdapterService mAdapterService;
public static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
public static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
public static final String BLUETOOTH_PRIVILEGED =
android.Manifest.permission.BLUETOOTH_PRIVILEGED;
private static final int MAX_DEVICES = 200;
public boolean mVirtualCallStarted;
private CallControl mCallControl = null;
private CallAudio(Context context) {
Log.d(TAG, "Initialization");
mContext = context;
mCallDevicesMap = new ConcurrentHashMap<String, CallDevice>();
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
mActiveDeviceManager = ActiveDeviceManagerService.get();
mAdapterService = AdapterService.getAdapterService();
mCallControl = CallControl.get();
}
public static CallAudio init(Context context) {
if(mCallAudio == null) {
mCallAudio = new CallAudio(context);
CallAudioIntf.init(mCallAudio);
}
return mCallAudio;
}
public static CallAudio get() {
return mCallAudio;
}
public boolean connect(BluetoothDevice device) {
Log.i(TAG, "connect: " + device);
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
if(device == null)
return false;
boolean status;
if (getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
Log.e(TAG, "Cannot connect to " + device + " : CONNECTION_POLICY_FORBIDDEN");
return false;
}
DeviceProfileMap dMap = DeviceProfileMap.getDeviceProfileMapInstance();
if (dMap == null)
return false;
int profileID = dMap.getSupportedProfile(device, ApmConst.AudioFeatures.CALL_AUDIO);
if (profileID == ApmConst.AudioProfiles.NONE) {
Log.e(TAG, "Can Not connect to " + device + ". Device does not support call service.");
return false;
}
CallDevice mCallDevice = mCallDevicesMap.get(device.getAddress());
if(mCallDevice == null) {
if(mCallDevicesMap.size() >= MAX_DEVICES)
return false;
mCallDevice = new CallDevice(device, profileID);
mCallDevicesMap.put(device.getAddress(), mCallDevice);
} else if(mCallDevice.deviceConnStatus != BluetoothProfile.STATE_DISCONNECTED) {
Log.i(TAG, "Device already connected");
return false;
}
if((ApmConst.AudioProfiles.HFP & profileID) == ApmConst.AudioProfiles.HFP) {
HeadsetService service = HeadsetService.getHeadsetService();
if (service == null) {
return false;
}
service.connectHfp(device);
}
StreamAudioService mStreamService = StreamAudioService.getStreamAudioService();
if(mStreamService != null &&
(ApmConst.AudioProfiles.BAP_CALL & profileID) == ApmConst.AudioProfiles.BAP_CALL) {
mStreamService.connectLeStream(device, profileID);
}
return true;
}
public boolean connect(BluetoothDevice device, Boolean allProfiles) {
if(allProfiles) {
DeviceProfileMap dMap = DeviceProfileMap.getDeviceProfileMapInstance();
if (dMap == null)
return false;
int profileID = dMap.getSupportedProfile(device, ApmConst.AudioFeatures.CALL_AUDIO);
if((ApmConst.AudioProfiles.HFP & profileID) == ApmConst.AudioProfiles.HFP) {
HeadsetService service = HeadsetService.getHeadsetService();
if (service == null) {
return false;
}
return service.connectHfp(device);
} else {
/*Common connect for LE Media and Call handled from StreamAudioService*/
return true;
}
}
return connect(device);
}
public boolean disconnect(BluetoothDevice device) {
Log.i(TAG, " disconnect: " + device);
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
CallDevice mCallDevice;
if(device == null)
return false;
mCallDevice = mCallDevicesMap.get(device.getAddress());
if(mCallDevice == null) {
Log.e(TAG, "Ignore: Device " + device + " not present in list");
return false;
}
if (mCallDevice.profileConnStatus[CallDevice.SCO_STREAM] != BluetoothProfile.STATE_DISCONNECTED) {
HeadsetService service = HeadsetService.getHeadsetService();
if(service != null) {
service.disconnectHfp(device);
}
}
if (mCallDevice.profileConnStatus[CallDevice.LE_STREAM] != BluetoothProfile.STATE_DISCONNECTED) {
StreamAudioService service = StreamAudioService.getStreamAudioService();
if(service != null) {
service.disconnectLeStream(device, true, false);
}
}
return true;
}
public boolean disconnect(BluetoothDevice device, Boolean allProfiles) {
if(allProfiles) {
CallDevice mCallDevice = mCallDevicesMap.get(device.getAddress());
if(mCallDevice == null) {
Log.e(TAG, "Ignore: Device " + device + " not present in list");
return false;
}
if(mCallDevice.profileConnStatus[CallDevice.SCO_STREAM] != BluetoothProfile.STATE_DISCONNECTED) {
return disconnect(device);
} else {
/*Common connect for LE Media and Call handled from StreamAudioService*/
return true;
}
}
return disconnect(device);
}
public boolean startScoUsingVirtualVoiceCall() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
Log.d(TAG, "startScoUsingVirtualVoiceCall");
BluetoothDevice mActivedevice = null;
int profile;
mActiveDeviceManager = ActiveDeviceManagerService.get();
if(mActiveDeviceManager != null) {
mActivedevice = mActiveDeviceManager.getActiveDevice(ApmConst.AudioFeatures.CALL_AUDIO);
if(mActivedevice == null) {
Log.e(TAG, "startScoUsingVirtualVoiceCall failed. Active Device is null");
return false;
}
} else {
return false;
}
checkA2dpState();
profile = mActiveDeviceManager.getActiveProfile(ApmConst.AudioFeatures.CALL_AUDIO);
switch(profile) {
case ApmConst.AudioProfiles.HFP:
HeadsetService headsetService = HeadsetService.getHeadsetService();
if(headsetService != null) {
if(headsetService.startScoUsingVirtualVoiceCall()) {
mVirtualCallStarted = true;
return true;
}
}
break;
case ApmConst.AudioProfiles.BAP_CALL:
case ApmConst.AudioProfiles.TMAP_CALL:
StreamAudioService mStreamAudioService = StreamAudioService.getStreamAudioService();
if(mStreamAudioService != null) {
if(mStreamAudioService.startStream(mActivedevice)) {
mVirtualCallStarted = true;
mCallControl = CallControl.get();
if (mCallControl != null) {
mCallControl.setVirtualCallActive(true);
}
return true;
}
}
break;
default:
Log.e(TAG, "Unhandled profile");
break;
}
Log.e(TAG, "startScoUsingVirtualVoiceCall failed. Device: " + mActivedevice);
if(ApmConst.AudioProfiles.HFP != profile) {
HeadsetService service = HeadsetService.getHeadsetService();
if(service != null) {
service.getHfpA2DPSyncInterface().releaseA2DP(null);
}
}
return false;
}
public boolean stopScoUsingVirtualVoiceCall() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
Log.d(TAG, "stopScoUsingVirtualVoiceCall");
BluetoothDevice mActivedevice = null;
int profile;
mActiveDeviceManager = ActiveDeviceManagerService.get();
if(mActiveDeviceManager != null) {
mActivedevice = mActiveDeviceManager.getActiveDevice(ApmConst.AudioFeatures.CALL_AUDIO);
if(mActivedevice == null) {
Log.e(TAG, "stopScoUsingVirtualVoiceCall failed. Active Device is null");
return false;
}
} else {
return false;
}
profile = mActiveDeviceManager.getActiveProfile(ApmConst.AudioFeatures.CALL_AUDIO);
switch(profile) {
case ApmConst.AudioProfiles.HFP:
HeadsetService headsetService = HeadsetService.getHeadsetService();
if(headsetService != null) {
mVirtualCallStarted = false;
return headsetService.stopScoUsingVirtualVoiceCall();
}
break;
case ApmConst.AudioProfiles.BAP_CALL:
case ApmConst.AudioProfiles.TMAP_CALL:
StreamAudioService mStreamAudioService = StreamAudioService.getStreamAudioService();
if(mStreamAudioService != null) {
mVirtualCallStarted = false;
mCallControl = CallControl.get();
if (mCallControl != null) {
mCallControl.setVirtualCallActive(false);
}
return mStreamAudioService.stopStream(mActivedevice);
}
break;
default:
Log.e(TAG, "Unhandled profile");
break;
}
Log.e(TAG, "stopScoUsingVirtualVoiceCall failed. Device: " + mActivedevice);
return false;
}
void remoteDisconnectVirtualVoiceCall(BluetoothDevice device) {
if(device == null)
return;
ActiveDeviceManagerService mActiveDeviceManager = ActiveDeviceManagerService.get();
if(device.equals(mActiveDeviceManager.getActiveDevice(ApmConst.AudioFeatures.CALL_AUDIO)) &&
mActiveDeviceManager.isStableState(ApmConst.AudioFeatures.CALL_AUDIO)) {
stopScoUsingVirtualVoiceCall();
}
}
int getProfile(BluetoothDevice mDevice) {
DeviceProfileMap dMap = DeviceProfileMap.getDeviceProfileMapInstance();
int profileID = dMap.getProfile(mDevice, ApmConst.AudioFeatures.CALL_AUDIO);
Log.d(TAG," getProfile for device " + mDevice + " profileID " + profileID);
return profileID;
}
void checkA2dpState() {
MediaAudio sMediaAudio = MediaAudio.get();
BluetoothDevice sMediaActivedevice =
mActiveDeviceManager.getActiveDevice(ApmConst.AudioFeatures.MEDIA_AUDIO);
//if(sMediaAudio.isA2dpPlaying(sMediaActivedevice)) {
Log.d(TAG," suspendA2DP isA2dpPlaying true " + " for device " + sMediaActivedevice);
int profileID = mActiveDeviceManager.getActiveProfile(
ApmConst.AudioFeatures.CALL_AUDIO);
if(ApmConst.AudioProfiles.HFP != profileID) {
HeadsetService service = HeadsetService.getHeadsetService();
if(service != null) {
service.getHfpA2DPSyncInterface().suspendA2DP(
HeadsetA2dpSync.A2DP_SUSPENDED_BY_CS_CALL, null);
}
}
//}
}
public boolean connectAudio() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
BluetoothDevice mActivedevice = null;
boolean status = false;
mActiveDeviceManager = ActiveDeviceManagerService.get();
if(mActiveDeviceManager != null) {
mActivedevice = mActiveDeviceManager.getActiveDevice(ApmConst.AudioFeatures.CALL_AUDIO);
}
Log.i(TAG, "connectAudio: device=" + mActivedevice + ", " + Utils.getUidPidString());
int profileID = mActiveDeviceManager.getActiveProfile(ApmConst.AudioFeatures.CALL_AUDIO);
checkA2dpState();
if(ApmConst.AudioProfiles.HFP == profileID) {
HeadsetService service = HeadsetService.getHeadsetService();
if (service == null) {
status = false;
}
status = service.connectAudio(mActivedevice);
} else if(ApmConst.AudioProfiles.BAP_CALL == profileID) {
StreamAudioService service = StreamAudioService.getStreamAudioService();
if(service != null) {
status = service.startStream(mActivedevice);
}
} else {
Log.e(TAG, "Unhandled connect audio request for profile: " + profileID);
status = false;
}
if(status == false) {
Log.e(TAG, "failed connect audio request for device: " + mActivedevice);
if(ApmConst.AudioProfiles.HFP != profileID) {
HeadsetService service = HeadsetService.getHeadsetService();
if(service != null) {
service.getHfpA2DPSyncInterface().releaseA2DP(null);
}
}
}
return status;
}
public boolean disconnectAudio() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
BluetoothDevice mActivedevice = null;
boolean mStatus = false;
if(mActiveDeviceManager != null) {
mActivedevice = mActiveDeviceManager.getActiveDevice(ApmConst.AudioFeatures.CALL_AUDIO);
}
Log.i(TAG, "disconnectAudio: device=" + mActivedevice + ", " + Utils.getUidPidString());
int profileID = mActiveDeviceManager.getActiveProfile(ApmConst.AudioFeatures.CALL_AUDIO);
if(ApmConst.AudioProfiles.HFP == profileID) {
HeadsetService service = HeadsetService.getHeadsetService();
if (service == null) {
mStatus = false;
}
mStatus = service.disconnectAudio();
} else if(ApmConst.AudioProfiles.BAP_CALL == profileID) {
StreamAudioService service = StreamAudioService.getStreamAudioService();
if(service != null) {
mStatus = service.stopStream(mActivedevice);
}
} else {
Log.e(TAG, "Unhandled disconnectAudio request for profile: " + profileID);
mStatus = true;
}
if(ApmConst.AudioProfiles.HFP != profileID) {
HeadsetService service = HeadsetService.getHeadsetService();
if(service != null) {
service.getHfpA2DPSyncInterface().releaseA2DP(null);
}
}
return mStatus;
}
public boolean setConnectionPolicy(BluetoothDevice device, Integer connectionPolicy) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
"Need BLUETOOTH_PRIVILEGED permission");
boolean mStatus;
Log.d(TAG, "setConnectionPolicy: device=" + device
+ ", connectionPolicy=" + connectionPolicy + ", " + Utils.getUidPidString());
mStatus = mAdapterService.getDatabase()
.setProfileConnectionPolicy(device, BluetoothProfile.HEADSET, connectionPolicy);
if (mStatus &&
connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
connect(device);
} else if (mStatus &&
connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
disconnect(device);
}
return mStatus;
}
public int getConnectionPolicy(BluetoothDevice device) {
if(mAdapterService != null) {
int connPolicy;
connPolicy = mAdapterService.getDatabase()
.getProfileConnectionPolicy(device, BluetoothProfile.HEADSET);
Log.d(TAG, "getConnectionPolicy: device=" + device
+ ", connectionPolicy=" + connPolicy);
return connPolicy;
} else {
return BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
}
}
public int getAudioState(BluetoothDevice device) {
if(device == null)
return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
CallDevice mCallDevice;
mCallDevice = mCallDevicesMap.get(device.getAddress());
if (mCallDevice == null) {
Log.w(TAG, "getAudioState: device " + device + " was never connected/connecting");
return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
}
return mCallDevice.scoStatus;
}
private List<BluetoothDevice> getNonIdleAudioDevices() {
if(mCallDevicesMap.size() == 0) {
return new ArrayList<>(0);
}
ArrayList<BluetoothDevice> devices = new ArrayList<>();
for (CallDevice mCallDevice : mCallDevicesMap.values()) {
if (mCallDevice.scoStatus != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
devices.add(mCallDevice.mDevice);
}
}
return devices;
}
public boolean isAudioOn() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
int numConnectedAudioDevices = getNonIdleAudioDevices().size();
Log.d(TAG," isAudioOn: The number of audio connected devices "
+ numConnectedAudioDevices);
return numConnectedAudioDevices > 0;
}
public List<BluetoothDevice> getConnectedDevices() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Log.i(TAG, "getConnectedDevices: ");
if(mCallDevicesMap.size() == 0) {
Log.i(TAG, "no device is Connected:");
return new ArrayList<>(0);
}
List<BluetoothDevice> connectedDevices = new ArrayList<>();
for(CallDevice mCallDevice : mCallDevicesMap.values()) {
if(mCallDevice.deviceConnStatus == BluetoothProfile.STATE_CONNECTED) {
connectedDevices.add(mCallDevice.mDevice);
}
}
Log.i(TAG, "ConnectedDevices: = " + connectedDevices.size());
return connectedDevices;
}
public int getConnectionState(BluetoothDevice device) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if(device == null)
return BluetoothProfile.STATE_DISCONNECTED;
CallDevice mCallDevice;
mCallDevice = mCallDevicesMap.get(device.getAddress());
if(mCallDevice != null)
return mCallDevice.deviceConnStatus;
return BluetoothProfile.STATE_DISCONNECTED;
}
public boolean isVoiceOrCallActive() {
boolean isVoiceActive = isAudioOn() || mVirtualCallStarted;
HeadsetService mHeadsetService = HeadsetService.getHeadsetService();
if(mHeadsetService != null) {
isVoiceActive = isVoiceActive || mHeadsetService.isScoOrCallActive();
}
return isVoiceActive;
}
private void broadcastConnStateChange(BluetoothDevice device, int fromState, int toState) {
Log.d(TAG,"broadcastConnectionState " + device + ": " + fromState + "->" + toState);
HeadsetService mHeadsetService = HeadsetService.getHeadsetService();
if(mHeadsetService == null) {
Log.w(TAG,"broadcastConnectionState: HeadsetService not initialized. Return!");
return;
}
Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, fromState);
intent.putExtra(BluetoothProfile.EXTRA_STATE, toState);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
mHeadsetService.sendBroadcastAsUser(intent, UserHandle.ALL,
BLUETOOTH_CONNECT, Utils.getTempAllowlistBroadcastOptions());
}
private void broadcastAudioState(BluetoothDevice device, int fromState, int toState) {
Log.d(TAG,"broadcastAudioState " + device + ": " + fromState + "->" + toState);
HeadsetService mHeadsetService = HeadsetService.getHeadsetService();
if(mHeadsetService == null) {
Log.d(TAG,"broadcastAudioState: HeadsetService not initialized. Return!");
return;
}
Intent intent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, fromState);
intent.putExtra(BluetoothProfile.EXTRA_STATE, toState);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
mHeadsetService.sendBroadcastAsUser(intent, UserHandle.ALL,
BLUETOOTH_CONNECT, Utils.getTempAllowlistBroadcastOptions());
}
public void onConnStateChange(BluetoothDevice device, Integer state, Integer profile) {
int prevState;
Log.w(TAG, "onConnStateChange: profile: " + profile + " state: "
+ state + " for device " + device);
if(device == null)
return;
CallDevice mCallDevice = mCallDevicesMap.get(device.getAddress());
if(mCallDevice == null) {
if(state == BluetoothProfile.STATE_DISCONNECTED)
return;
if(mCallDevicesMap.size() >= MAX_DEVICES) {
return;
}
mCallDevice = new CallDevice(device, profile, state);
mCallDevicesMap.put(device.getAddress(), mCallDevice);
broadcastConnStateChange(device, BluetoothProfile.STATE_DISCONNECTED, state);
return;
}
int profileIndex = mCallDevice.getProfileIndex(profile);
DeviceProfileMap dMap = DeviceProfileMap.getDeviceProfileMapInstance();
prevState = mCallDevice.deviceConnStatus;
mCallDevice.profileConnStatus[profileIndex] = state;
if(state == BluetoothProfile.STATE_DISCONNECTED) {
dMap.profileConnectionUpdate(device, ApmConst.AudioFeatures.CALL_AUDIO, profile, false);
}
int otherProfileConnectionState = mCallDevice.profileConnStatus[(profileIndex+1)%2];
Log.w(TAG, " otherProfileConnectionState: " + otherProfileConnectionState);
switch(otherProfileConnectionState) {
/*Send Broadcast based on state of other profile*/
case BluetoothProfile.STATE_DISCONNECTED:
broadcastConnStateChange(device, prevState, state);
mCallDevice.deviceConnStatus = state;
if(state == BluetoothProfile.STATE_CONNECTED) {
int supportedProfiles = dMap.getSupportedProfile(device, ApmConst.AudioFeatures.CALL_AUDIO);
if(profile == ApmConst.AudioProfiles.HFP &&
(supportedProfiles & ApmConst.AudioProfiles.BAP_CALL) == ApmConst.AudioProfiles.BAP_CALL) {
Log.w(TAG, "Connect LE Voice after HFP auto connect from remote");
StreamAudioService mStreamService = StreamAudioService.getStreamAudioService();
if(mStreamService != null) {
mStreamService.connectLeStream(device, ApmConst.AudioProfiles.BAP_CALL);
}
} else {
ActiveDeviceManagerService mActiveDeviceManager = ActiveDeviceManagerService.get();
mActiveDeviceManager.setActiveDevice(device, ApmConst.AudioFeatures.CALL_AUDIO);
}
}
break;
case BluetoothProfile.STATE_CONNECTING:
int preferredProfile = dMap.getProfile(device, ApmConst.AudioFeatures.CALL_AUDIO);
boolean isPreferredProfile = (preferredProfile == profile);
if(state == BluetoothProfile.STATE_CONNECTED && isPreferredProfile) {
broadcastConnStateChange(device, prevState, state);
mCallDevice.deviceConnStatus = state;
}
break;
case BluetoothProfile.STATE_DISCONNECTING:
if(state == BluetoothProfile.STATE_CONNECTING ||
state == BluetoothProfile.STATE_CONNECTED) {
broadcastConnStateChange(device, prevState, state);
mCallDevice.deviceConnStatus = state;
}
break;
case BluetoothProfile.STATE_CONNECTED:
if(state == BluetoothProfile.STATE_CONNECTED) {
if(prevState != state) {
broadcastConnStateChange(device, prevState, state);
mCallDevice.deviceConnStatus = state;
}
ActiveDeviceManagerService mActiveDeviceManager =
ActiveDeviceManagerService.get();
mActiveDeviceManager.setActiveDevice(device, ApmConst.AudioFeatures.CALL_AUDIO);
} else if(state == BluetoothProfile.STATE_DISCONNECTED) {
if(prevState != BluetoothProfile.STATE_CONNECTED) {
broadcastConnStateChange(device, prevState, BluetoothProfile.STATE_CONNECTED);
mCallDevice.deviceConnStatus = BluetoothProfile.STATE_CONNECTED;
} else {
ActiveDeviceManagerService mActiveDeviceManager =
ActiveDeviceManagerService.get();
if(device.equals(mActiveDeviceManager.getActiveDevice(ApmConst.AudioFeatures.CALL_AUDIO))) {
mActiveDeviceManager.setActiveDevice(device, ApmConst.AudioFeatures.CALL_AUDIO);
}
}
}
break;
}
if(state == BluetoothProfile.STATE_CONNECTED) {
dMap.profileConnectionUpdate(device, ApmConst.AudioFeatures.CALL_AUDIO, profile, true);
}
}
public void onAudioStateChange(BluetoothDevice device, Integer state) {
int prevStatus;
if(device == null)
return;
CallDevice mCallDevice = mCallDevicesMap.get(device.getAddress());
if(mCallDevice == null) {
return;
}
if(mCallDevice.scoStatus == state)
return;
HeadsetService service = HeadsetService.getHeadsetService();
int profileID = mActiveDeviceManager.getActiveProfile(
ApmConst.AudioFeatures.CALL_AUDIO);
BluetoothDevice mActivedevice = mActiveDeviceManager.getActiveDevice(
ApmConst.AudioFeatures.CALL_AUDIO);
if (service != null) {
if(!(service.shouldCallAudioBeActive() || mVirtualCallStarted)) {
if(ApmConst.AudioProfiles.BAP_CALL == profileID) {
StreamAudioService mStreamAudioService =
StreamAudioService.getStreamAudioService();
if(mStreamAudioService != null) {
Log.w(TAG, "Call not active, disconnect stream");
mStreamAudioService.stopStream(mActivedevice);
}
}
}
}
prevStatus = mCallDevice.scoStatus;
mCallDevice.scoStatus = state;
VolumeManager mVolumeManager = VolumeManager.get();
mVolumeManager.updateStreamState(device, state, ApmConst.AudioFeatures.CALL_AUDIO);
broadcastAudioState(device, prevStatus, state);
if(state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
if(ApmConst.AudioProfiles.HFP != profileID) {
if(service != null) {
service.getHfpA2DPSyncInterface().releaseA2DP(null);
}
}
//mAudioManager.setBluetoothScoOn(false);
} /*else {
mAudioManager.setBluetoothScoOn(true);
}*/
}
public void setAudioParam(String param) {
mAudioManager.setParameters(param);
}
public void setBluetoothScoOn(boolean on) {
mAudioManager.setBluetoothScoOn(on);
}
public AudioManager getAudioManager() {
return mAudioManager;
}
class CallDevice {
BluetoothDevice mDevice;
int[] profileConnStatus = new int[2];
int deviceConnStatus;
int scoStatus;
public static final int SCO_STREAM = 0;
public static final int LE_STREAM = 1;
CallDevice(BluetoothDevice device, int profile, int state) {
mDevice = device;
if(profile == ApmConst.AudioProfiles.HFP) {
profileConnStatus[SCO_STREAM] = state;
profileConnStatus[LE_STREAM] = BluetoothProfile.STATE_DISCONNECTED;
} else {
profileConnStatus[LE_STREAM] = state;
profileConnStatus[SCO_STREAM] = BluetoothProfile.STATE_DISCONNECTED;
}
deviceConnStatus = state;
scoStatus = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;;
}
CallDevice(BluetoothDevice device, int profile) {
this(device, profile, BluetoothProfile.STATE_DISCONNECTED);
}
public int getProfileIndex(int profile) {
if(profile == ApmConst.AudioProfiles.HFP)
return SCO_STREAM;
else
return LE_STREAM;
}
}
}