diff --git a/BGMApp/BGMApp/BGMBackgroundMusicDevice.cpp b/BGMApp/BGMApp/BGMBackgroundMusicDevice.cpp index b44f83cb..e0daa18b 100644 --- a/BGMApp/BGMApp/BGMBackgroundMusicDevice.cpp +++ b/BGMApp/BGMApp/BGMBackgroundMusicDevice.cpp @@ -237,7 +237,14 @@ BGMBackgroundMusicDevice::ResponsibleBundleIDsOf(CACFString inParentBundleID) { "hu.mplayerhq.mplayerosx.extended", { "ch.sttz.mplayerosx.extended.binaries.officialsvn" } }, // Discord - { "com.hnc.Discord", { "com.hnc.Discord.helper" } }, + { "com.hnc.Discord", + { "com.hnc.Discord.helper", + "com.hnc.Discord.helper.Renderer", + "com.hnc.Discord.helper.Plugin" } }, + // Brave + { "com.brave.Browser", + { "com.brave.Browser.helper", + "com.brave.Browser.helper.plugin" } }, // Skype { "com.skype.skype", { "com.skype.skype.Helper" } }, // Google Chrome diff --git a/BGMApp/BGMAppTests/UnitTests/Mocks/MockAudioObjects.cpp b/BGMApp/BGMAppTests/UnitTests/Mocks/MockAudioObjects.cpp index 3e1f0954..3107f48c 100644 --- a/BGMApp/BGMAppTests/UnitTests/Mocks/MockAudioObjects.cpp +++ b/BGMApp/BGMAppTests/UnitTests/Mocks/MockAudioObjects.cpp @@ -17,7 +17,7 @@ // MockAudioObjects.cpp // BGMAppUnitTests // -// Copyright © 2020 Kyle Neideck +// Copyright © 2020, 2025 Kyle Neideck // // Self Include @@ -26,6 +26,9 @@ // PublicUtility Includes #include "CACFString.h" +// STL Includes +#include + // static MockAudioObjects::MockDeviceMap MockAudioObjects::sDevices; @@ -87,9 +90,9 @@ std::shared_ptr MockAudioObjects::GetAudioDevice(CFStringRef in { // Convert inUID to a std::string. UInt32 uidCStringLen = CACFString::GetStringByteLength(inUID) + 1; - char uidCString[uidCStringLen]; - CACFString::GetCString(inUID, uidCString, uidCStringLen); - std::string uid = std::string(uidCString); + std::vector uidCString(uidCStringLen); + CACFString::GetCString(inUID, uidCString.data(), uidCStringLen); + std::string uid = std::string(uidCString.data()); return GetAudioDevice(uid); } diff --git a/BGMDriver/BGMDriver/BGM_Device.cpp b/BGMDriver/BGMDriver/BGM_Device.cpp index 480e2688..8cecf36e 100644 --- a/BGMDriver/BGMDriver/BGM_Device.cpp +++ b/BGMDriver/BGMDriver/BGM_Device.cpp @@ -17,7 +17,7 @@ // BGM_Device.cpp // BGMDriver // -// Copyright © 2016, 2017, 2019 Kyle Neideck +// Copyright © 2016, 2017, 2019, 2025 Kyle Neideck // Copyright © 2017 Andrew Tonner // Copyright © 2019 Gordon Childs // Copyright © 2020 Aleksey Yurkevich @@ -40,6 +40,7 @@ #include "CADispatchQueue.h" #include "CAException.h" #include "CACFArray.h" +#include "CACFDictionary.h" #include "CACFString.h" #include "CADebugMacros.h" #include "CAHostTimeBase.h" @@ -1001,6 +1002,74 @@ void BGM_Device::Device_GetPropertyData(AudioObjectID inObjectID, pid_t inClient }; } +// Validates inAppVolumes for the kAudioDeviceCustomPropertyAppVolumes property as described in +// BGM_Types.h. Throws CAException(kAudioHardwareIllegalOperationError) if invalid. +static void ValidateAppVolumesProperty(const CACFArray& inAppVolumes) +{ + UInt32 theCount = inAppVolumes.GetNumberItems(); + + for(UInt32 i = 0; i < theCount; i++) + { + // Each element must be a CFDictionary. + CFTypeRef theElement = nullptr; + bool didGetValue = inAppVolumes.GetCFType(i, theElement); + ThrowIf(!didGetValue || theElement == nullptr, + CAException(kAudioHardwareIllegalOperationError), + "BGM_Device::ValidateAppVolumesProperty: Could not get element from array"); + ThrowIf(CFGetTypeID(theElement) != CFDictionaryGetTypeID(), + CAException(kAudioHardwareIllegalOperationError), + "BGM_Device::ValidateAppVolumesProperty: Element is not a CFDictionary"); + + CACFDictionary theDict(static_cast(theElement), false); + + // Check for ProcessID. Must be a CFNumber if present. + CFTypeRef thePIDValue = nullptr; + bool hasPID = theDict.GetCFType(CFSTR(kBGMAppVolumesKey_ProcessID), thePIDValue); + if(hasPID && thePIDValue != nullptr) + { + ThrowIf(CFGetTypeID(thePIDValue) != CFNumberGetTypeID(), + CAException(kAudioHardwareIllegalOperationError), + "BGM_Device::ValidateAppVolumesProperty: ProcessID is not a CFNumber"); + } + + // Check for BundleID. Must be a CFString if present. + CFTypeRef theBundleIDValue = nullptr; + bool hasBundleID = theDict.GetCFType(CFSTR(kBGMAppVolumesKey_BundleID), theBundleIDValue); + if(hasBundleID && theBundleIDValue != nullptr) + { + ThrowIf(CFGetTypeID(theBundleIDValue) != CFStringGetTypeID(), + CAException(kAudioHardwareIllegalOperationError), + "BGM_Device::ValidateAppVolumesProperty: BundleID is not a CFString"); + } + + // At least one of ProcessID or BundleID must be present. + ThrowIf(!hasPID && !hasBundleID, + CAException(kAudioHardwareIllegalOperationError), + "BGM_Device::ValidateAppVolumesProperty: Neither ProcessID nor BundleID present"); + + // Check RelativeVolume. Must be a CFNumber in [0, 100] if present. + SInt32 theVolume; + bool hasVolume = theDict.GetSInt32(CFSTR(kBGMAppVolumesKey_RelativeVolume), theVolume); + if(hasVolume) + { + ThrowIf(theVolume < kAppRelativeVolumeMinRawValue || + theVolume > kAppRelativeVolumeMaxRawValue, + CAException(kAudioHardwareIllegalOperationError), + "BGM_Device::ValidateAppVolumesProperty: RelativeVolume out of range"); + } + + // Check PanPosition. Must be a CFNumber in [-100, 100] if present. + SInt32 thePan; + bool hasPan = theDict.GetSInt32(CFSTR(kBGMAppVolumesKey_PanPosition), thePan); + if(hasPan) + { + ThrowIf(thePan < kAppPanLeftRawValue || thePan > kAppPanRightRawValue, + CAException(kAudioHardwareIllegalOperationError), + "BGM_Device::ValidateAppVolumesProperty: PanPosition out of range"); + } + } +} + void BGM_Device::Device_SetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData) { switch(inAddress.mSelector) @@ -1092,6 +1161,8 @@ void BGM_Device::Device_SetPropertyData(AudioObjectID inObjectID, pid_t inClient CACFArray array(arrayRef, false); + ValidateAppVolumesProperty(array); + bool propertyWasChanged = false; CAMutex::Locker theStateLocker(mStateMutex); @@ -1104,7 +1175,7 @@ void BGM_Device::Device_SetPropertyData(AudioObjectID inObjectID, pid_t inClient { Throw(CAException(kAudioHardwareIllegalOperationError)); } - + if(propertyWasChanged) { // Send notification diff --git a/BGMDriver/BGMDriver/DeviceClients/BGM_ClientMap.cpp b/BGMDriver/BGMDriver/DeviceClients/BGM_ClientMap.cpp index 14828bf4..e6717cc9 100644 --- a/BGMDriver/BGMDriver/DeviceClients/BGM_ClientMap.cpp +++ b/BGMDriver/BGMDriver/DeviceClients/BGM_ClientMap.cpp @@ -17,7 +17,7 @@ // BGM_ClientMap.cpp // BGMDriver // -// Copyright © 2016, 2017, 2019 Kyle Neideck +// Copyright © 2016, 2017, 2019, 2025 Kyle Neideck // Copyright © 2017 Andrew Tonner // @@ -264,6 +264,9 @@ std::vector * _Nullable BGM_ClientMap::GetClients(pid_t inAppPid) { } std::vector * _Nullable BGM_ClientMap::GetClients(CACFString inAppBundleID) { + if(!inAppBundleID.IsValid()) { + return nullptr; + } return GetClientsFromMap(mClientMapByBundleIDShadow, inAppBundleID); } @@ -335,12 +338,12 @@ bool BGM_ClientMap::SetClientsRelativeVolume(pid_t searchKey, Float32 inRelative bool BGM_ClientMap::SetClientsRelativeVolume(CACFString searchKey, Float32 inRelativeVolume) { bool didChangeVolume = false; - + CAMutex::Locker theShadowMapsLocker(mShadowMapsMutex); - + auto theSetVolumesInShadowMapsFunc = [&] { // Look up the clients for the key and update their volumes - + auto theClients = GetClients(searchKey); if(theClients != nullptr) { @@ -389,9 +392,9 @@ bool BGM_ClientMap::SetClientsPanPosition(pid_t searchKey, SInt32 inPanPosition) bool BGM_ClientMap::SetClientsPanPosition(CACFString searchKey, SInt32 inPanPosition) { bool didChangePanPosition = false; - + CAMutex::Locker theShadowMapsLocker(mShadowMapsMutex); - + auto theSetPansInShadowMapsFunc = [&] { // Look up the clients for the key and update their pan positions auto theClients = GetClients(searchKey); diff --git a/BGMDriver/BGMDriver/DeviceClients/BGM_ClientMap.h b/BGMDriver/BGMDriver/DeviceClients/BGM_ClientMap.h index 6f4c3e98..1ae1a9ef 100644 --- a/BGMDriver/BGMDriver/DeviceClients/BGM_ClientMap.h +++ b/BGMDriver/BGMDriver/DeviceClients/BGM_ClientMap.h @@ -17,7 +17,7 @@ // BGM_ClientMap.h // BGMDriver // -// Copyright © 2016 Kyle Neideck +// Copyright © 2016, 2025 Kyle Neideck // #ifndef __BGMDriver__BGM_ClientMap__ @@ -131,11 +131,13 @@ class BGM_ClientMap // Returns true if a client for PID inAppPID was found and its relative volume changed. bool SetClientsRelativeVolume(pid_t inAppPID, Float32 inRelativeVolume); // Returns true if a client for bundle ID inAppBundleID was found and its relative volume changed. + // inAppBundleID may contain a null CFStringRef, in which case it returns false. bool SetClientsRelativeVolume(CACFString inAppBundleID, Float32 inRelativeVolume); // Returns true if a client for PID inAppPID was found and its pan position changed. bool SetClientsPanPosition(pid_t inAppPID, SInt32 inPanPosition); // Returns true if a client for bundle ID inAppBundleID was found and its pan position changed. + // inAppBundleID may contain a null CFStringRef, in which case it returns false. bool SetClientsPanPosition(CACFString inAppBundleID, SInt32 inPanPosition); void StartIONonRT(UInt32 inClientID) { UpdateClientIOStateNonRT(inClientID, true); } diff --git a/BGMDriver/BGMDriver/DeviceClients/BGM_Clients.cpp b/BGMDriver/BGMDriver/DeviceClients/BGM_Clients.cpp index 60e1de94..62330939 100644 --- a/BGMDriver/BGMDriver/DeviceClients/BGM_Clients.cpp +++ b/BGMDriver/BGMDriver/DeviceClients/BGM_Clients.cpp @@ -17,7 +17,7 @@ // BGM_Clients.cpp // BGMDriver // -// Copyright © 2016, 2017, 2019 Kyle Neideck +// Copyright © 2016, 2017, 2019, 2025 Kyle Neideck // Copyright © 2017 Andrew Tonner // @@ -26,6 +26,7 @@ // Local Includes #include "BGM_Types.h" +#include "BGM_Utils.h" #include "BGM_PlugIn.h" // PublicUtility Includes @@ -324,26 +325,22 @@ bool BGM_Clients::SetClientsRelativeVolumes(const CACFArray inAppVolumes) // Get the app's PID from the dict pid_t theAppPID; bool didFindPID = theAppVolume.GetSInt32(CFSTR(kBGMAppVolumesKey_ProcessID), theAppPID); - + // Get the app's bundle ID from the dict CACFString theAppBundleID; theAppBundleID.DontAllowRelease(); theAppVolume.GetCACFString(CFSTR(kBGMAppVolumesKey_BundleID), theAppBundleID); - - ThrowIf(!didFindPID && !theAppBundleID.IsValid(), - BGM_InvalidClientRelativeVolumeException(), - "BGM_Clients::SetClientsRelativeVolumes: App volume was sent without PID or bundle ID for app"); - + + BGMAssert(didFindPID || theAppBundleID.IsValid(), + "BGM_Clients::SetClientsRelativeVolumes: No PID or bundle ID"); + (void)didFindPID; // Suppress unused variable warning in release builds. + bool didGetVolume; { SInt32 theRawRelativeVolume; didGetVolume = theAppVolume.GetSInt32(CFSTR(kBGMAppVolumesKey_RelativeVolume), theRawRelativeVolume); - + if (didGetVolume) { - ThrowIf(didGetVolume && (theRawRelativeVolume < kAppRelativeVolumeMinRawValue || theRawRelativeVolume > kAppRelativeVolumeMaxRawValue), - BGM_InvalidClientRelativeVolumeException(), - "BGM_Clients::SetClientsRelativeVolumes: Relative volume for app out of valid range"); - // Apply the volume curve to the raw volume // // mRelativeVolumeCurve uses the default kPow2Over1Curve transfer function, so we also multiply by 4 to @@ -372,10 +369,6 @@ bool BGM_Clients::SetClientsRelativeVolumes(const CACFArray inAppVolumes) SInt32 thePanPosition; didGetPanPosition = theAppVolume.GetSInt32(CFSTR(kBGMAppVolumesKey_PanPosition), thePanPosition); if (didGetPanPosition) { - ThrowIf(didGetPanPosition && (thePanPosition < kAppPanLeftRawValue || thePanPosition > kAppPanRightRawValue), - BGM_InvalidClientPanPositionException(), - "BGM_Clients::SetClientsRelativeVolumes: Pan position for app out of valid range"); - if(mClientMap.SetClientsPanPosition(theAppPID, thePanPosition)) { didChangeAppVolumes = true;