Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f9570f3f authored by Jakub Pawlowski's avatar Jakub Pawlowski
Browse files

AudioService: Hearing Aid output management

This patch makes Hearing Aid output active/inactive when the connection
state, reported from Bluetooth stack is changed.

Bug: 64038649
Test: mm
Change-Id: Iaeee39febab3c340a77e02afe3e0a573fb9736f9
parent aad36148
Loading
Loading
Loading
Loading
+191 −25
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -256,6 +257,7 @@ public class AudioService extends IAudioService.Stub
    private static final int MSG_SET_A2DP_SINK_CONNECTION_STATE = 102;
    private static final int MSG_A2DP_DEVICE_CONFIG_CHANGE = 103;
    private static final int MSG_DISABLE_AUDIO_FOR_UID = 104;
    private static final int MSG_SET_HEARING_AID_CONNECTION_STATE = 105;
    // end of messages handled under wakelock

    private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
@@ -265,6 +267,8 @@ public class AudioService extends IAudioService.Stub
    // retry delay in case of failure to indicate system ready to AudioFlinger
    private static final int INDICATE_SYSTEM_READY_RETRY_DELAY_MS = 1000;

    private static final int BT_HEARING_AID_GAIN_MIN = -128;

    /** @see AudioSystemThread */
    private AudioSystemThread mAudioSystemThread;
    /** @see AudioHandler */
@@ -573,7 +577,7 @@ public class AudioService extends IAudioService.Stub
            AudioSystem.DEVICE_OUT_HDMI_ARC |
            AudioSystem.DEVICE_OUT_SPDIF |
            AudioSystem.DEVICE_OUT_AUX_LINE;
    int mFullVolumeDevices = 0;
    int mFullVolumeDevices = AudioSystem.DEVICE_OUT_HEARING_AID;

    private final boolean mMonitorRotation;

@@ -590,6 +594,10 @@ public class AudioService extends IAudioService.Stub

    private final MediaFocusControl mMediaFocusControl;

    // Reference to BluetoothA2dp to query for volume.
    private BluetoothHearingAid mHearingAid;
    // lock always taken synchronized on mConnectedDevices
    private final Object mHearingAidLock = new Object();
    // Reference to BluetoothA2dp to query for AbsoluteVolume.
    private BluetoothA2dp mA2dp;
    // lock always taken synchronized on mConnectedDevices
@@ -849,6 +857,8 @@ public class AudioService extends IAudioService.Stub
        if (adapter != null) {
            adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
                                    BluetoothProfile.A2DP);
            adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
                                    BluetoothProfile.HEARING_AID);
        }

        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_HDMI_CEC)) {
@@ -1607,6 +1617,20 @@ public class AudioService extends IAudioService.Stub
                }
            }

            // Check if volume update should be send to Hearing Aid
            if ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0) {
                synchronized (mHearingAidLock) {
                    if (mHearingAid != null) {
                        //hearing aid expect volume value in range -128dB to 0dB
                        int gainDB = (int)AudioSystem.getStreamVolumeDB(streamType, newIndex/10,
                                AudioSystem.DEVICE_OUT_HEARING_AID);
                        if (gainDB < BT_HEARING_AID_GAIN_MIN)
                            gainDB = BT_HEARING_AID_GAIN_MIN;
                        mHearingAid.setVolume(gainDB);
                    }
                }
            }

            // Check if volume update should be sent to Hdmi system audio.
            if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
                setSystemAudioVolume(oldIndex, newIndex, getStreamMaxVolume(streamType), flags);
@@ -1852,6 +1876,19 @@ public class AudioService extends IAudioService.Stub
                }
            }

            if ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0) {
                synchronized (mHearingAidLock) {
                    if (mHearingAid != null) {
                        //hearing aid expect volume value in range -128dB to 0dB
                        int gainDB = (int)AudioSystem.getStreamVolumeDB(streamType, index/10, AudioSystem.DEVICE_OUT_HEARING_AID);
                        if (gainDB < BT_HEARING_AID_GAIN_MIN)
                            gainDB = BT_HEARING_AID_GAIN_MIN;
                        mHearingAid.setVolume(gainDB);

                    }
                }
            }

            if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
                setSystemAudioVolume(oldIndex, index, getStreamMaxVolume(streamType), flags);
            }
@@ -3609,6 +3646,30 @@ public class AudioService extends IAudioService.Stub
                }
                break;

            case BluetoothProfile.HEARING_AID:
                synchronized (mConnectedDevices) {
                    synchronized (mHearingAidLock) {
                        mHearingAid = (BluetoothHearingAid) proxy;
                        deviceList = mHearingAid.getConnectedDevices();
                        if (deviceList.size() > 0) {
                            btDevice = deviceList.get(0);
                            int state = mHearingAid.getConnectionState(btDevice);
                            int intState = (state == BluetoothHearingAid.STATE_CONNECTED) ? 1 : 0;
                            int delay = checkSendBecomingNoisyIntent(
                                    AudioSystem.DEVICE_OUT_HEARING_AID, intState,
                                    AudioSystem.DEVICE_NONE);
                            queueMsgUnderWakeLock(mAudioHandler,
                                    MSG_SET_HEARING_AID_CONNECTION_STATE,
                                    state,
                                    0 /* arg2 unused */,
                                    btDevice,
                                    delay);
                        }
                    }
                }

                break;

            default:
                break;
            }
@@ -3628,6 +3689,10 @@ public class AudioService extends IAudioService.Stub
                disconnectHeadset();
                break;

            case BluetoothProfile.HEARING_AID:
                disconnectHearingAid();
                break;

            default:
                break;
            }
@@ -3638,6 +3703,7 @@ public class AudioService extends IAudioService.Stub
        disconnectA2dp();
        disconnectA2dpSink();
        disconnectHeadset();
        disconnectHearingAid();
    }

    void disconnectA2dp() {
@@ -3689,6 +3755,29 @@ public class AudioService extends IAudioService.Stub
        }
    }

    void disconnectHearingAid() {
        synchronized (mConnectedDevices) {
            synchronized (mHearingAidLock) {
                ArraySet<String> toRemove = null;
                // Disconnect ALL DEVICE_OUT_HEARING_AID devices
                for (int i = 0; i < mConnectedDevices.size(); i++) {
                    DeviceListSpec deviceSpec = mConnectedDevices.valueAt(i);
                    if (deviceSpec.mDeviceType == AudioSystem.DEVICE_OUT_HEARING_AID) {
                        toRemove = toRemove != null ? toRemove : new ArraySet<String>();
                        toRemove.add(deviceSpec.mDeviceAddress);
                    }
                }
                if (toRemove != null) {
                    int delay = checkSendBecomingNoisyIntent(AudioSystem.DEVICE_OUT_HEARING_AID,
                            0, AudioSystem.DEVICE_NONE);
                    for (int i = 0; i < toRemove.size(); i++) {
                        makeHearingAidDeviceUnavailable(toRemove.valueAt(i) /*, delay*/);
                    }
                }
            }
        }
    }

    private void onCheckMusicActive(String caller) {
        synchronized (mSafeMediaVolumeState) {
            if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_INACTIVE) {
@@ -4200,7 +4289,8 @@ public class AudioService extends IAudioService.Stub
            handler.sendMessageAtTime(handler.obtainMessage(msg, arg1, arg2, obj), time);
            if (msg == MSG_SET_WIRED_DEVICE_CONNECTION_STATE ||
                    msg == MSG_SET_A2DP_SRC_CONNECTION_STATE ||
                    msg == MSG_SET_A2DP_SINK_CONNECTION_STATE) {
                    msg == MSG_SET_A2DP_SINK_CONNECTION_STATE ||
                    msg == MSG_SET_HEARING_AID_CONNECTION_STATE) {
                mLastDeviceConnectMsgTime = time;
            }
        }
@@ -4303,6 +4393,33 @@ public class AudioService extends IAudioService.Stub
    @Override
    public void setHearingAidDeviceConnectionState(BluetoothDevice device, int state)
    {
        Log.i(TAG, "setBluetoothHearingAidDeviceConnectionState");

        setBluetoothHearingAidDeviceConnectionState(
                device, state,  false /* suppressNoisyIntent */, AudioSystem.DEVICE_NONE);
    }

    public int setBluetoothHearingAidDeviceConnectionState(
            BluetoothDevice device, int state, boolean suppressNoisyIntent,
            int musicDevice)
    {
        int delay;
        synchronized (mConnectedDevices) {
            if (!suppressNoisyIntent) {
                int intState = (state == BluetoothHearingAid.STATE_CONNECTED) ? 1 : 0;
                delay = checkSendBecomingNoisyIntent(AudioSystem.DEVICE_OUT_HEARING_AID,
                        intState, musicDevice);
            } else {
                delay = 0;
            }
            queueMsgUnderWakeLock(mAudioHandler,
                    MSG_SET_HEARING_AID_CONNECTION_STATE,
                    state,
                    0 /* arg2 unused */,
                    device,
                    delay);
        }
        return delay;
    }

    public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state, int profile)
@@ -5244,6 +5361,11 @@ public class AudioService extends IAudioService.Stub
                    mAudioEventWakeLock.release();
                    break;

                case MSG_SET_HEARING_AID_CONNECTION_STATE:
                    onSetHearingAidConnectionState((BluetoothDevice)msg.obj, msg.arg1);
                    mAudioEventWakeLock.release();
                    break;

                case MSG_A2DP_DEVICE_CONFIG_CHANGE:
                    onBluetoothA2dpDeviceConfigChange((BluetoothDevice)msg.obj);
                    mAudioEventWakeLock.release();
@@ -5436,14 +5558,8 @@ public class AudioService extends IAudioService.Stub
                AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "");
        mConnectedDevices.remove(
                makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address));
        synchronized (mCurAudioRoutes) {
        // Remove A2DP routes as well
            if (mCurAudioRoutes.bluetoothName != null) {
                mCurAudioRoutes.bluetoothName = null;
                sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
                        SENDMSG_NOOP, 0, 0, null, 0);
            }
        }
        setCurrentAudioRouteName(null);
    }

    // must be called synchronized on mConnectedDevices
@@ -5478,6 +5594,28 @@ public class AudioService extends IAudioService.Stub
                makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address));
    }

    // must be called synchronized on mConnectedDevices
    private void makeHearingAidDeviceAvailable(String address, String name, String eventSource) {
        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID,
                AudioSystem.DEVICE_STATE_AVAILABLE, address, name);
        mConnectedDevices.put(
                makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, address),
                new DeviceListSpec(AudioSystem.DEVICE_OUT_HEARING_AID, name,
                                   address));
        sendMsg(mAudioHandler, MSG_ACCESSORY_PLUG_MEDIA_UNMUTE, SENDMSG_QUEUE,
                AudioSystem.DEVICE_OUT_HEARING_AID, 0, null, 0);
    }

    // must be called synchronized on mConnectedDevices
    private void makeHearingAidDeviceUnavailable(String address) {
        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID,
                AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "");
        mConnectedDevices.remove(
                makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, address));
        // Remove Hearing Aid routes as well
        setCurrentAudioRouteName(null);
    }

    // must be called synchronized on mConnectedDevices
    private void cancelA2dpDeviceTimeout() {
        mAudioHandler.removeMessages(MSG_BTA2DP_DOCK_TIMEOUT);
@@ -5519,13 +5657,7 @@ public class AudioService extends IAudioService.Stub
                } else {
                    makeA2dpDeviceUnavailableNow(address);
                }
                synchronized (mCurAudioRoutes) {
                    if (mCurAudioRoutes.bluetoothName != null) {
                        mCurAudioRoutes.bluetoothName = null;
                        sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
                                SENDMSG_NOOP, 0, 0, null, 0);
                    }
                }
                setCurrentAudioRouteName(null);
            } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
                if (btDevice.isBluetoothDock()) {
                    // this could be a reconnection after a transient disconnection
@@ -5541,14 +5673,7 @@ public class AudioService extends IAudioService.Stub
                }
                makeA2dpDeviceAvailable(address, btDevice.getName(),
                        "onSetA2dpSinkConnectionState");
                synchronized (mCurAudioRoutes) {
                    String name = btDevice.getAliasName();
                    if (!TextUtils.equals(mCurAudioRoutes.bluetoothName, name)) {
                        mCurAudioRoutes.bluetoothName = name;
                        sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
                                SENDMSG_NOOP, 0, 0, null, 0);
                    }
                }
                setCurrentAudioRouteName(btDevice.getAliasName());
            }
        }
    }
@@ -5579,6 +5704,46 @@ public class AudioService extends IAudioService.Stub
        }
    }

    private void onSetHearingAidConnectionState(BluetoothDevice btDevice, int state)
    {
        if (DEBUG_DEVICES) {
            Log.d(TAG, "onSetHearingAidConnectionState btDevice=" + btDevice+", state=" + state);
        }
        if (btDevice == null) {
            return;
        }
        String address = btDevice.getAddress();
        if (!BluetoothAdapter.checkBluetoothAddress(address)) {
            address = "";
        }

        synchronized (mConnectedDevices) {
            final String key = makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID,
                                                 btDevice.getAddress());
            final DeviceListSpec deviceSpec = mConnectedDevices.get(key);
            boolean isConnected = deviceSpec != null;

            if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
                makeHearingAidDeviceUnavailable(address);
                setCurrentAudioRouteName(null);
            } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
                makeHearingAidDeviceAvailable(address, btDevice.getName(),
                        "onSetHearingAidConnectionState");
                setCurrentAudioRouteName(btDevice.getAliasName());
            }
        }
    }

    private void setCurrentAudioRouteName(String name){
        synchronized (mCurAudioRoutes) {
            if (!TextUtils.equals(mCurAudioRoutes.bluetoothName, name)) {
                mCurAudioRoutes.bluetoothName = name;
                sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
                        SENDMSG_NOOP, 0, 0, null, 0);
            }
        }
    }

    private void onBluetoothA2dpDeviceConfigChange(BluetoothDevice btDevice)
    {
        if (DEBUG_DEVICES) {
@@ -5714,6 +5879,7 @@ public class AudioService extends IAudioService.Stub

        if (mAudioHandler.hasMessages(MSG_SET_A2DP_SRC_CONNECTION_STATE) ||
                mAudioHandler.hasMessages(MSG_SET_A2DP_SINK_CONNECTION_STATE) ||
                mAudioHandler.hasMessages(MSG_SET_HEARING_AID_CONNECTION_STATE) ||
                mAudioHandler.hasMessages(MSG_SET_WIRED_DEVICE_CONNECTION_STATE)) {
            synchronized (mLastDeviceConnectMsgTime) {
                long time = SystemClock.uptimeMillis();