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

Commit 419379d8 authored by Jean-Michel Trivi's avatar Jean-Michel Trivi
Browse files

AudioService: ringMyCar carkit/headphones rings in vibrate

Update the AudioManager.shouldNotificationSoundPlay() behavior
to enforce ringtone playback on "private" devices such as
carkits or BT headphones even when the device is in
vibrate mode.
Availability of such "always ring" devices is evaluated
in AudioDeviceInventory whenever devices are connected/
disconnected, to allow shouldNotificationSoundPlay()
to query that information in a lock-free manner.

Bug: 415311394
Flag: com.android.media.audio.ring_my_car
Test: receive call in vibrate mode with HFP/carkit

Change-Id: Ia72085d496e2785031889c9e844ec363b5c2cc50
parent d47841c5
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -3110,6 +3110,10 @@ public class AudioDeviceBroker {
        return mDeviceInventory.findBtDeviceStateForAddress(address, deviceType);
    }

    boolean hasAlwaysRingDevice() {
        return mDeviceInventory.hasAlwaysRingDevice();
    }

    void addAudioDeviceWithCategoryInInventoryIfNeeded(@NonNull String address,
            @AudioDeviceCategory int btAudioDeviceCategory) {
        mDeviceInventory.addAudioDeviceWithCategoryInInventoryIfNeeded(address,
+54 −0
Original line number Diff line number Diff line
@@ -533,6 +533,39 @@ public class AudioDeviceInventory {
        }
    }

    /**
     * Whether there's a connected device that should always ring (outside of silent mode)
     * Writes are protected by mDevicesLock, reads are atomic in {@link #hasAlwaysRingDevice()}
     */
    private final AtomicBoolean mAlwaysRingDeviceConnected = new AtomicBoolean(false);

    /**
     * @return whether there's a connected device that should always ring (outside of silent mode)
     */
    boolean hasAlwaysRingDevice() {
        return mAlwaysRingDeviceConnected.get();
    }

    /**
     * Holds the list of native device types that are candidates for "always ring"
     */
    private static final Set<Integer> ALWAYS_RING_DEVICE_CANDIDATES;
    /**
     * Holds the list of device categories that are "always ring"
     */
    private static final Set<Integer> ALWAYS_RING_CATEGORIES;

    static {
        ALWAYS_RING_DEVICE_CANDIDATES = new HashSet<>();
        ALWAYS_RING_DEVICE_CANDIDATES.addAll(DEVICE_OUT_ALL_SCO_SET);
        ALWAYS_RING_DEVICE_CANDIDATES.add(DEVICE_OUT_BLE_HEADSET);

        ALWAYS_RING_CATEGORIES = new HashSet<>();
        ALWAYS_RING_CATEGORIES.add(AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES);
        ALWAYS_RING_CATEGORIES.add(AudioManager.AUDIO_DEVICE_CATEGORY_CARKIT);
    }


    /**
     * List of devices actually connected to AudioPolicy (through AudioSystem).
     * Each entry is a String, generated from {@link DeviceInfo#getKey()}.
@@ -863,6 +896,7 @@ public class AudioDeviceInventory {
                    mDeviceBroker.onSetBtScoActiveDevice(null, false /*deviceSwitch*/);
                }
            }
            updateAlwaysRingDeviceConnected();

            mAppliedStrategyRolesInt.clear();
            mAppliedPresetRolesInt.clear();
@@ -3247,6 +3281,25 @@ public class AudioDeviceInventory {
                        new Exception());
            }
        }
        updateAlwaysRingDeviceConnected();
    }

    @GuardedBy("mDevicesLock")
    private void updateAlwaysRingDeviceConnected() {
        boolean foundAlwaysRingDevice = false;
        for (DeviceInfo devInfo : mConnectedDevices.values()) {
            // device type is candidate?
            if (ALWAYS_RING_DEVICE_CANDIDATES.contains(devInfo.mDeviceType)) {
                AdiDeviceState ads = findBtDeviceStateForAddress(
                        devInfo.mDeviceAddress, devInfo.mDeviceType);
                // device category is a match?
                if (ads != null && ALWAYS_RING_CATEGORIES.contains(ads.getAudioDeviceCategory())) {
                    foundAlwaysRingDevice = true;
                    break;
                }
            }
        }
        mAlwaysRingDeviceConnected.set(foundAlwaysRingDevice);
    }

    /**
@@ -3271,6 +3324,7 @@ public class AudioDeviceInventory {
                        new Exception());
            }
        }
        updateAlwaysRingDeviceConnected();
    }

    //----------------------------------------------------------
+28 −11
Original line number Diff line number Diff line
@@ -6696,12 +6696,12 @@ public class AudioService extends IAudioService.Stub
    /* package */ void updateRingerModeMutedStreams() {
        synchronized (mSettingsLock) {
            muteRingerModeStreams();
            updateStreamMuteFromRingerMode();
        }
    }
    @GuardedBy("mSettingsLock")
    private void muteRingerModeStreams() {
    private void updateStreamMuteFromRingerMode() {
        // Mute stream if not previously muted by ringer mode and (ringer mode
        // is not RINGER_MODE_NORMAL OR stream is zen muted) and stream is affected by ringer mode.
        // Unmute stream if previously muted by ringer/zen mode and ringer mode
@@ -6722,7 +6722,7 @@ public class AudioService extends IAudioService.Stub
                || mBtCommDeviceActive.get() == BT_COMM_DEVICE_ACTIVE_BLE_SPEAKER);
        // Ask audio policy engine to force use Bluetooth SCO/BLE channel if needed
        final String eventSource =
                "muteRingerModeStreams() from u/pid:" + Binder.getCallingUid()
                "updateStreamMuteFromRingerMode() from u/pid:" + Binder.getCallingUid()
                        + "/" + Binder.getCallingPid();
        int forceUse = AudioSystem.FORCE_NONE;
        if (shouldRingSco) {
@@ -6773,15 +6773,15 @@ public class AudioService extends IAudioService.Stub
                    }
                }
                sRingerAndZenModeMutedStreams &= ~(1 << streamType);
                vss.mute(false, "muteRingerModeStreams");
                vss.mute(false, "updateStreamMuteFromRingerMode");
            } else {
                // mute
                sRingerAndZenModeMutedStreams |= (1 << streamType);
                vss.mute(true, "muteRingerModeStreams");
                vss.mute(true, "updateStreamMuteFromRingerMode");
            }
        }
        sMuteLogger.enqueue(new AudioServiceEvents.RingerZenMutedStreamsEvent(
                sRingerAndZenModeMutedStreams, "muteRingerModeStreams"));
                sRingerAndZenModeMutedStreams, "updateStreamMuteFromRingerMode"));
    }
    private boolean isAlarm(int streamType) {
@@ -6807,7 +6807,7 @@ public class AudioService extends IAudioService.Stub
        synchronized(mSettingsLock) {
            change = mRingerMode != ringerMode;
            mRingerMode = ringerMode;
            muteRingerModeStreams();
            updateStreamMuteFromRingerMode();
        }
        // Post a persist ringer mode msg
@@ -15494,8 +15494,25 @@ public class AudioService extends IAudioService.Stub
        // AudioAttributes of the notification record is 0 (non-zero volume implies
        // not silenced by SILENT or VIBRATE ringer mode)
        final int stream = AudioAttributes.toLegacyStreamType(aa);
        final boolean mutingFromVolume = getStreamVolume(stream) == 0;
        if (mutingFromVolume) {
        boolean maybeMutingFromVolume = getStreamVolume(stream) == 0;
        // Exception with ringMyCar
        //       if in vibrate mode,
        //       and if an HFP device is available and is of type carkit
        //            or headphones,
        //       then ringtones should play even when the stream is muted.
        if (ringMyCar()
                && maybeMutingFromVolume
                && (getRingerModeExternal() == RINGER_MODE_VIBRATE)
                && (stream == AudioSystem.STREAM_RING)) {
            final boolean hasAlwaysRingDevice = mDeviceBroker.hasAlwaysRingDevice();
            if (hasAlwaysRingDevice) {
                Slog.i(TAG, "shouldNotificationSoundPlay found always ring device");
            }
            maybeMutingFromVolume = !hasAlwaysRingDevice;
        }
        if (maybeMutingFromVolume) {
            Slog.i(TAG, "shouldNotificationSoundPlay false: muted stream:" + stream
                        + " attr:" + aa);
            return false;