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

Commit 840f2f4f authored by Jean-Michel Trivi's avatar Jean-Michel Trivi Committed by Android (Google) Code Review
Browse files

Merge "AudioService: manage single audio device type in APM"

parents 1614da7f 788417d1
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -903,6 +903,18 @@ public class AudioSystem
        }
    }

    /**
     * Returns a human readable name for a given device type
     * @param device a native device type, NOT an AudioDeviceInfo type
     * @return a string describing the device type
     */
    public static @NonNull String getDeviceName(int device) {
        if ((device & DEVICE_BIT_IN) != 0) {
            return getInputDeviceName(device);
        }
        return getOutputDeviceName(device);
    }

    // phone state, match audio_mode???
    public static final int PHONE_STATE_OFFCALL = 0;
    public static final int PHONE_STATE_RINGING = 1;
+6 −14
Original line number Diff line number Diff line
@@ -600,19 +600,11 @@ import java.io.PrintWriter;
        sendMsgNoDelay(MSG_REPORT_NEW_ROUTES, SENDMSG_NOOP);
    }

    /*package*/ void cancelA2dpDockTimeout() {
        mBrokerHandler.removeMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT);
    }

    /*package*/ void postA2dpActiveDeviceChange(
                    @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) {
        sendLMsgNoDelay(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE, SENDMSG_QUEUE, btDeviceInfo);
    }

    /*package*/ boolean hasScheduledA2dpDockTimeout() {
        return mBrokerHandler.hasMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT);
    }

    // must be called synchronized on mConnectedDevices
    /*package*/ boolean hasScheduledA2dpSinkConnectionState(BluetoothDevice btDevice) {
        return (mBrokerHandler.hasMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED,
@@ -621,8 +613,8 @@ import java.io.PrintWriter;
                        new BtHelper.BluetoothA2dpDeviceInfo(btDevice)));
    }

    /*package*/ void setA2dpDockTimeout(String address, int a2dpCodec, int delayMs) {
        sendILMsg(MSG_IL_BTA2DP_DOCK_TIMEOUT, SENDMSG_QUEUE, a2dpCodec, address, delayMs);
    /*package*/ void setA2dpTimeout(String address, int a2dpCodec, int delayMs) {
        sendILMsg(MSG_IL_BTA2DP_TIMEOUT, SENDMSG_QUEUE, a2dpCodec, address, delayMs);
    }

    /*package*/ void setAvrcpAbsoluteVolumeSupported(boolean supported) {
@@ -781,7 +773,7 @@ import java.io.PrintWriter;
                        }
                    }
                    break;
                case MSG_IL_BTA2DP_DOCK_TIMEOUT:
                case MSG_IL_BTA2DP_TIMEOUT:
                    // msg.obj  == address of BTA2DP device
                    synchronized (mDeviceStateLock) {
                        mDeviceInventory.onMakeA2dpDeviceUnavailableNow((String) msg.obj, msg.arg1);
@@ -945,7 +937,7 @@ import java.io.PrintWriter;
    private static final int MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE = 7;
    private static final int MSG_IL_SET_HEARING_AID_CONNECTION_STATE = 8;
    private static final int MSG_BT_HEADSET_CNCT_FAILED = 9;
    private static final int MSG_IL_BTA2DP_DOCK_TIMEOUT = 10;
    private static final int MSG_IL_BTA2DP_TIMEOUT = 10;
    private static final int MSG_L_A2DP_DEVICE_CONFIG_CHANGE = 11;
    private static final int MSG_BROADCAST_AUDIO_BECOMING_NOISY = 12;
    private static final int MSG_REPORT_NEW_ROUTES = 13;
@@ -981,7 +973,7 @@ import java.io.PrintWriter;
            case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED:
            case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE:
            case MSG_IL_SET_HEARING_AID_CONNECTION_STATE:
            case MSG_IL_BTA2DP_DOCK_TIMEOUT:
            case MSG_IL_BTA2DP_TIMEOUT:
            case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
            case MSG_TOGGLE_HDMI:
            case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
@@ -1071,7 +1063,7 @@ import java.io.PrintWriter;
                case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED:
                case MSG_IL_SET_HEARING_AID_CONNECTION_STATE:
                case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
                case MSG_IL_BTA2DP_DOCK_TIMEOUT:
                case MSG_IL_BTA2DP_TIMEOUT:
                case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
                case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
                    if (sLastDeviceConnectMsgTime >= time) {
+87 −41
Original line number Diff line number Diff line
@@ -59,10 +59,21 @@ public class AudioDeviceInventory {

    private static final String TAG = "AS.AudioDeviceInventory";

    // Actual list of connected devices
    // lock to synchronize all access to mConnectedDevices and mApmConnectedDevices
    private final Object mDevicesLock = new Object();

    // List of connected devices
    // Key for map created from DeviceInfo.makeDeviceListKey()
    @GuardedBy("mDevicesLock")
    private final LinkedHashMap<String, DeviceInfo> mConnectedDevices = new LinkedHashMap<>();

    // List of devices actually connected to AudioPolicy (through AudioSystem), only one
    // by device type, which is used as the key, value is the DeviceInfo generated key.
    // For the moment only for A2DP sink devices.
    // TODO: extend to all device types
    @GuardedBy("mDevicesLock")
    private final ArrayMap<Integer, String> mApmConnectedDevices = new ArrayMap<>();

    // List of preferred devices for strategies
    private final ArrayMap<Integer, AudioDeviceAddress> mPreferredDevices = new ArrayMap<>();

@@ -94,25 +105,30 @@ public class AudioDeviceInventory {
     */
    private static class DeviceInfo {
        final int mDeviceType;
        final String mDeviceName;
        final String mDeviceAddress;
        final @NonNull String mDeviceName;
        final @NonNull String mDeviceAddress;
        int mDeviceCodecFormat;

        DeviceInfo(int deviceType, String deviceName, String deviceAddress, int deviceCodecFormat) {
            mDeviceType = deviceType;
            mDeviceName = deviceName;
            mDeviceAddress = deviceAddress;
            mDeviceName = deviceName == null ? "" : deviceName;
            mDeviceAddress = deviceAddress == null ? "" : deviceAddress;
            mDeviceCodecFormat = deviceCodecFormat;
        }

        @Override
        public String toString() {
            return "[DeviceInfo: type:0x" + Integer.toHexString(mDeviceType)
                    + " name:" + mDeviceName
                    + " (" + AudioSystem.getDeviceName(mDeviceType)
                    + ") name:" + mDeviceName
                    + " addr:" + mDeviceAddress
                    + " codec: " + Integer.toHexString(mDeviceCodecFormat) + "]";
        }

        String getKey() {
            return makeDeviceListKey(mDeviceType, mDeviceAddress);
        }

        /**
         * Generate a unique key for the mConnectedDevices List by composing the device "type"
         * and the "address" associated with a specific instance of that device type
@@ -147,6 +163,14 @@ public class AudioDeviceInventory {
        pw.println("\n" + prefix + "Preferred devices for strategy:");
        mPreferredDevices.forEach((strategy, device) -> {
            pw.println("  " + prefix + "strategy:" + strategy + " device:" + device); });
        pw.println("\n" + prefix + "Connected devices:");
        mConnectedDevices.forEach((key, deviceInfo) -> {
            pw.println("  " + prefix + deviceInfo.toString()); });
        pw.println("\n" + prefix + "APM Connected device (A2DP sink only):");
        mApmConnectedDevices.forEach((keyType, valueAddress) -> {
            pw.println("  " + prefix + " type:0x" + Integer.toHexString(keyType)
                    + " (" + AudioSystem.getDeviceName(keyType)
                    + ") addr:" + valueAddress); });
    }

    //------------------------------------------------------------
@@ -158,7 +182,8 @@ public class AudioDeviceInventory {
     */
    // Always executed on AudioDeviceBroker message queue
    /*package*/ void onRestoreDevices() {
        synchronized (mConnectedDevices) {
        synchronized (mDevicesLock) {
            //TODO iterate on mApmConnectedDevices instead once it handles all device types
            for (DeviceInfo di : mConnectedDevices.values()) {
                AudioSystem.setDeviceConnectionState(
                        di.mDeviceType,
@@ -168,7 +193,6 @@ public class AudioDeviceInventory {
                        di.mDeviceCodecFormat);
            }
        }

        synchronized (mPreferredDevices) {
            mPreferredDevices.forEach((strategy, device) -> {
                AudioSystem.setPreferredDeviceForStrategy(strategy, device); });
@@ -187,6 +211,9 @@ public class AudioDeviceInventory {
                    + state + " vol=" + a2dpVolume);
        }
        String address = btDevice.getAddress();
        if (address == null) {
            address = "";
        }
        if (!BluetoothAdapter.checkBluetoothAddress(address)) {
            address = "";
        }
@@ -198,7 +225,7 @@ public class AudioDeviceInventory {
                        + " codec=" + a2dpCodec
                        + " vol=" + a2dpVolume));

        synchronized (mConnectedDevices) {
        synchronized (mDevicesLock) {
            final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
                    btDevice.getAddress());
            final DeviceInfo di = mConnectedDevices.get(key);
@@ -238,7 +265,7 @@ public class AudioDeviceInventory {
            address = "";
        }

        synchronized (mConnectedDevices) {
        synchronized (mDevicesLock) {
            final String key = DeviceInfo.makeDeviceListKey(
                    AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address);
            final DeviceInfo di = mConnectedDevices.get(key);
@@ -261,7 +288,7 @@ public class AudioDeviceInventory {
        AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
                "onSetHearingAidConnectionState addr=" + address));

        synchronized (mConnectedDevices) {
        synchronized (mDevicesLock) {
            final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID,
                    btDevice.getAddress());
            final DeviceInfo di = mConnectedDevices.get(key);
@@ -297,7 +324,7 @@ public class AudioDeviceInventory {
                "onBluetoothA2dpActiveDeviceChange addr=" + address
                    + " event=" + BtHelper.a2dpDeviceEventToString(event)));

        synchronized (mConnectedDevices) {
        synchronized (mDevicesLock) {
            if (mDeviceBroker.hasScheduledA2dpSinkConnectionState(btDevice)) {
                AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
                        "A2dp config change ignored (scheduled connection change)"));
@@ -340,7 +367,7 @@ public class AudioDeviceInventory {
    }

    /*package*/ void onMakeA2dpDeviceUnavailableNow(String address, int a2dpCodec) {
        synchronized (mConnectedDevices) {
        synchronized (mDevicesLock) {
            makeA2dpDeviceUnavailableNow(address, a2dpCodec);
        }
    }
@@ -377,7 +404,7 @@ public class AudioDeviceInventory {
                            AudioDeviceInventory.WiredDeviceConnectionState wdcs) {
        AudioService.sDeviceLogger.log(new AudioServiceEvents.WiredDevConnectEvent(wdcs));

        synchronized (mConnectedDevices) {
        synchronized (mDevicesLock) {
            if ((wdcs.mState == AudioService.CONNECTION_STATE_DISCONNECTED)
                    && DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(wdcs.mType)) {
                mDeviceBroker.setBluetoothA2dpOnInt(true,
@@ -405,7 +432,7 @@ public class AudioDeviceInventory {
    }

    /*package*/ void onToggleHdmi() {
        synchronized (mConnectedDevices) {
        synchronized (mDevicesLock) {
            // Is HDMI connected?
            final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HDMI, "");
            final DeviceInfo di = mConnectedDevices.get(key);
@@ -472,7 +499,7 @@ public class AudioDeviceInventory {
                    + Integer.toHexString(device) + " address:" + address
                    + " name:" + deviceName + ")");
        }
        synchronized (mConnectedDevices) {
        synchronized (mDevicesLock) {
            final String deviceKey = DeviceInfo.makeDeviceListKey(device, address);
            if (AudioService.DEBUG_DEVICES) {
                Slog.i(TAG, "deviceKey:" + deviceKey);
@@ -511,7 +538,7 @@ public class AudioDeviceInventory {


    /*package*/ void disconnectA2dp() {
        synchronized (mConnectedDevices) {
        synchronized (mDevicesLock) {
            final ArraySet<String> toRemove = new ArraySet<>();
            // Disconnect ALL DEVICE_OUT_BLUETOOTH_A2DP devices
            mConnectedDevices.values().forEach(deviceInfo -> {
@@ -531,7 +558,7 @@ public class AudioDeviceInventory {
    }

    /*package*/ void disconnectA2dpSink() {
        synchronized (mConnectedDevices) {
        synchronized (mDevicesLock) {
            final ArraySet<String> toRemove = new ArraySet<>();
            // Disconnect ALL DEVICE_IN_BLUETOOTH_A2DP devices
            mConnectedDevices.values().forEach(deviceInfo -> {
@@ -544,7 +571,7 @@ public class AudioDeviceInventory {
    }

    /*package*/ void disconnectHearingAid() {
        synchronized (mConnectedDevices) {
        synchronized (mDevicesLock) {
            final ArraySet<String> toRemove = new ArraySet<>();
            // Disconnect ALL DEVICE_OUT_HEARING_AID devices
            mConnectedDevices.values().forEach(deviceInfo -> {
@@ -568,7 +595,7 @@ public class AudioDeviceInventory {
    // from AudioSystem
    /*package*/ int checkSendBecomingNoisyIntent(int device,
            @AudioService.ConnectionState int state, int musicDevice) {
        synchronized (mConnectedDevices) {
        synchronized (mDevicesLock) {
            return checkSendBecomingNoisyIntentInt(device, state, musicDevice);
        }
    }
@@ -595,7 +622,7 @@ public class AudioDeviceInventory {
        if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK) {
            throw new IllegalArgumentException("invalid profile " + profile);
        }
        synchronized (mConnectedDevices) {
        synchronized (mDevicesLock) {
            if (profile == BluetoothProfile.A2DP && !suppressNoisyIntent) {
                @AudioService.ConnectionState int asState =
                        (state == BluetoothA2dp.STATE_CONNECTED)
@@ -635,7 +662,7 @@ public class AudioDeviceInventory {

    /*package*/ int setWiredDeviceConnectionState(int type, @AudioService.ConnectionState int state,
                                                  String address, String name, String caller) {
        synchronized (mConnectedDevices) {
        synchronized (mDevicesLock) {
            int delay = checkSendBecomingNoisyIntentInt(type, state, AudioSystem.DEVICE_NONE);
            mDeviceBroker.postSetWiredDeviceConnectionState(
                    new WiredDeviceConnectionState(type, state, address, name, caller),
@@ -648,7 +675,7 @@ public class AudioDeviceInventory {
            @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
            boolean suppressNoisyIntent, int musicDevice) {
        int delay;
        synchronized (mConnectedDevices) {
        synchronized (mDevicesLock) {
            if (!suppressNoisyIntent) {
                int intState = (state == BluetoothHearingAid.STATE_CONNECTED) ? 1 : 0;
                delay = checkSendBecomingNoisyIntentInt(AudioSystem.DEVICE_OUT_HEARING_AID,
@@ -665,39 +692,58 @@ public class AudioDeviceInventory {
    //-------------------------------------------------------------------
    // Internal utilities

    @GuardedBy("mConnectedDevices")
    @GuardedBy("mDevicesLock")
    private void makeA2dpDeviceAvailable(String address, String name, String eventSource,
            int a2dpCodec) {
        // enable A2DP before notifying A2DP connection to avoid unnecessary processing in
        // audio policy manager
        mDeviceBroker.setBluetoothA2dpOnInt(true, eventSource);
        // at this point there could be another A2DP device already connected in APM, but it
        // doesn't matter as this new one will overwrite the previous one
        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
                AudioSystem.DEVICE_STATE_AVAILABLE, address, name, a2dpCodec);
        // Reset A2DP suspend state each time a new sink is connected
        AudioSystem.setParameters("A2dpSuspended=false");
        mConnectedDevices.put(
                DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address),
                new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name,
                        address, a2dpCodec));

        final DeviceInfo di = new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name,
                address, a2dpCodec);
        final String diKey = di.getKey();
        mConnectedDevices.put(diKey, di);
        // on a connection always overwrite the device seen by AudioPolicy, see comment above when
        // calling AudioSystem
        mApmConnectedDevices.put(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, diKey);

        mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
        setCurrentAudioRouteNameIfPossible(name);
    }

    @GuardedBy("mConnectedDevices")
    @GuardedBy("mDevicesLock")
    private void makeA2dpDeviceUnavailableNow(String address, int a2dpCodec) {
        if (address == null) {
            return;
        }
        final String deviceToRemoveKey =
                DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address);

        mConnectedDevices.remove(deviceToRemoveKey);
        if (!mApmConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)
                .equals(deviceToRemoveKey)) {
            // removing A2DP device not currently used by AudioPolicy, log but don't act on it
            AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
                    "A2DP device " + address + " made unavailable, was not used")).printLog(TAG));
            return;
        }

        // device to remove was visible by APM, update APM
        mDeviceBroker.setAvrcpAbsoluteVolumeSupported(false);
        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
                AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "", a2dpCodec);
        mConnectedDevices.remove(
                DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address));
        mApmConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
        // Remove A2DP routes as well
        setCurrentAudioRouteNameIfPossible(null);
    }

    @GuardedBy("mConnectedDevices")
    @GuardedBy("mDevicesLock")
    private void makeA2dpDeviceUnavailableLater(String address, int delayMs) {
        // prevent any activity on the A2DP audio output to avoid unwanted
        // reconnection of the sink.
@@ -711,11 +757,11 @@ public class AudioDeviceInventory {
        // the device will be made unavailable later, so consider it disconnected right away
        mConnectedDevices.remove(deviceKey);
        // send the delayed message to make the device unavailable later
        mDeviceBroker.setA2dpDockTimeout(address, a2dpCodec, delayMs);
        mDeviceBroker.setA2dpTimeout(address, a2dpCodec, delayMs);
    }


    @GuardedBy("mConnectedDevices")
    @GuardedBy("mDevicesLock")
    private void makeA2dpSrcAvailable(String address) {
        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP,
                AudioSystem.DEVICE_STATE_AVAILABLE, address, "",
@@ -726,7 +772,7 @@ public class AudioDeviceInventory {
                        address, AudioSystem.AUDIO_FORMAT_DEFAULT));
    }

    @GuardedBy("mConnectedDevices")
    @GuardedBy("mDevicesLock")
    private void makeA2dpSrcUnavailable(String address) {
        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP,
                AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "",
@@ -735,7 +781,7 @@ public class AudioDeviceInventory {
                DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address));
    }

    @GuardedBy("mConnectedDevices")
    @GuardedBy("mDevicesLock")
    private void makeHearingAidDeviceAvailable(
            String address, String name, int streamType, String eventSource) {
        final int hearingAidVolIndex = mDeviceBroker.getVssVolumeForDevice(streamType,
@@ -755,7 +801,7 @@ public class AudioDeviceInventory {
        setCurrentAudioRouteNameIfPossible(name);
    }

    @GuardedBy("mConnectedDevices")
    @GuardedBy("mDevicesLock")
    private void makeHearingAidDeviceUnavailable(String address) {
        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID,
                AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "",
@@ -766,7 +812,7 @@ public class AudioDeviceInventory {
        setCurrentAudioRouteNameIfPossible(null);
    }

    @GuardedBy("mConnectedDevices")
    @GuardedBy("mDevicesLock")
    private void setCurrentAudioRouteNameIfPossible(String name) {
        synchronized (mCurAudioRoutes) {
            if (TextUtils.equals(mCurAudioRoutes.bluetoothName, name)) {
@@ -779,7 +825,7 @@ public class AudioDeviceInventory {
        }
    }

    @GuardedBy("mConnectedDevices")
    @GuardedBy("mDevicesLock")
    private boolean isCurrentDeviceConnected() {
        return mConnectedDevices.values().stream().anyMatch(deviceInfo ->
            TextUtils.equals(deviceInfo.mDeviceName, mCurAudioRoutes.bluetoothName));
@@ -807,7 +853,7 @@ public class AudioDeviceInventory {
    // must be called before removing the device from mConnectedDevices
    // musicDevice argument is used when not AudioSystem.DEVICE_NONE instead of querying
    // from AudioSystem
    @GuardedBy("mConnectedDevices")
    @GuardedBy("mDevicesLock")
    private int checkSendBecomingNoisyIntentInt(int device,
            @AudioService.ConnectionState int state, int musicDevice) {
        if (state != AudioService.CONNECTION_STATE_DISCONNECTED) {
@@ -1015,7 +1061,7 @@ public class AudioDeviceInventory {
    public boolean isA2dpDeviceConnected(@NonNull BluetoothDevice device) {
        final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
                device.getAddress());
        synchronized (mConnectedDevices) {
        synchronized (mDevicesLock) {
            return (mConnectedDevices.get(key) != null);
        }
    }