1717// BGM_Device.cpp
1818// BGMDriver
1919//
20- // Copyright © 2016, 2017, 2019 Kyle Neideck
20+ // Copyright © 2016, 2017, 2019, 2025 Kyle Neideck
2121// Copyright © 2017 Andrew Tonner
2222// Copyright © 2019 Gordon Childs
2323// Copyright © 2020 Aleksey Yurkevich
4040#include " CADispatchQueue.h"
4141#include " CAException.h"
4242#include " CACFArray.h"
43+ #include " CACFDictionary.h"
4344#include " CACFString.h"
4445#include " CADebugMacros.h"
4546#include " CAHostTimeBase.h"
@@ -1001,6 +1002,74 @@ void BGM_Device::Device_GetPropertyData(AudioObjectID inObjectID, pid_t inClient
10011002 };
10021003}
10031004
1005+ // Validates inAppVolumes for the kAudioDeviceCustomPropertyAppVolumes property as described in
1006+ // BGM_Types.h. Throws CAException(kAudioHardwareIllegalOperationError) if invalid.
1007+ static void ValidateAppVolumesProperty (const CACFArray& inAppVolumes)
1008+ {
1009+ UInt32 theCount = inAppVolumes.GetNumberItems ();
1010+
1011+ for (UInt32 i = 0 ; i < theCount; i++)
1012+ {
1013+ // Each element must be a CFDictionary.
1014+ CFTypeRef theElement = nullptr ;
1015+ bool didGetValue = inAppVolumes.GetCFType (i, theElement);
1016+ ThrowIf (!didGetValue || theElement == nullptr ,
1017+ CAException (kAudioHardwareIllegalOperationError ),
1018+ " BGM_Device::ValidateAppVolumesProperty: Could not get element from array" );
1019+ ThrowIf (CFGetTypeID (theElement) != CFDictionaryGetTypeID (),
1020+ CAException (kAudioHardwareIllegalOperationError ),
1021+ " BGM_Device::ValidateAppVolumesProperty: Element is not a CFDictionary" );
1022+
1023+ CACFDictionary theDict (static_cast <CFDictionaryRef>(theElement), false );
1024+
1025+ // Check for ProcessID. Must be a CFNumber if present.
1026+ CFTypeRef thePIDValue = nullptr ;
1027+ bool hasPID = theDict.GetCFType (CFSTR (kBGMAppVolumesKey_ProcessID ), thePIDValue);
1028+ if (hasPID && thePIDValue != nullptr )
1029+ {
1030+ ThrowIf (CFGetTypeID (thePIDValue) != CFNumberGetTypeID (),
1031+ CAException (kAudioHardwareIllegalOperationError ),
1032+ " BGM_Device::ValidateAppVolumesProperty: ProcessID is not a CFNumber" );
1033+ }
1034+
1035+ // Check for BundleID. Must be a CFString if present.
1036+ CFTypeRef theBundleIDValue = nullptr ;
1037+ bool hasBundleID = theDict.GetCFType (CFSTR (kBGMAppVolumesKey_BundleID ), theBundleIDValue);
1038+ if (hasBundleID && theBundleIDValue != nullptr )
1039+ {
1040+ ThrowIf (CFGetTypeID (theBundleIDValue) != CFStringGetTypeID (),
1041+ CAException (kAudioHardwareIllegalOperationError ),
1042+ " BGM_Device::ValidateAppVolumesProperty: BundleID is not a CFString" );
1043+ }
1044+
1045+ // At least one of ProcessID or BundleID must be present.
1046+ ThrowIf (!hasPID && !hasBundleID,
1047+ CAException (kAudioHardwareIllegalOperationError ),
1048+ " BGM_Device::ValidateAppVolumesProperty: Neither ProcessID nor BundleID present" );
1049+
1050+ // Check RelativeVolume. Must be a CFNumber in [0, 100] if present.
1051+ SInt32 theVolume;
1052+ bool hasVolume = theDict.GetSInt32 (CFSTR (kBGMAppVolumesKey_RelativeVolume ), theVolume);
1053+ if (hasVolume)
1054+ {
1055+ ThrowIf (theVolume < kAppRelativeVolumeMinRawValue ||
1056+ theVolume > kAppRelativeVolumeMaxRawValue ,
1057+ CAException (kAudioHardwareIllegalOperationError ),
1058+ " BGM_Device::ValidateAppVolumesProperty: RelativeVolume out of range" );
1059+ }
1060+
1061+ // Check PanPosition. Must be a CFNumber in [-100, 100] if present.
1062+ SInt32 thePan;
1063+ bool hasPan = theDict.GetSInt32 (CFSTR (kBGMAppVolumesKey_PanPosition ), thePan);
1064+ if (hasPan)
1065+ {
1066+ ThrowIf (thePan < kAppPanLeftRawValue || thePan > kAppPanRightRawValue ,
1067+ CAException (kAudioHardwareIllegalOperationError ),
1068+ " BGM_Device::ValidateAppVolumesProperty: PanPosition out of range" );
1069+ }
1070+ }
1071+ }
1072+
10041073void BGM_Device::Device_SetPropertyData (AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void * inQualifierData, UInt32 inDataSize, const void * inData)
10051074{
10061075 switch (inAddress.mSelector )
@@ -1092,6 +1161,8 @@ void BGM_Device::Device_SetPropertyData(AudioObjectID inObjectID, pid_t inClient
10921161
10931162 CACFArray array (arrayRef, false );
10941163
1164+ ValidateAppVolumesProperty (array);
1165+
10951166 bool propertyWasChanged = false ;
10961167
10971168 CAMutex::Locker theStateLocker (mStateMutex );
@@ -1104,7 +1175,7 @@ void BGM_Device::Device_SetPropertyData(AudioObjectID inObjectID, pid_t inClient
11041175 {
11051176 Throw (CAException (kAudioHardwareIllegalOperationError ));
11061177 }
1107-
1178+
11081179 if (propertyWasChanged)
11091180 {
11101181 // Send notification
0 commit comments