improved built-in device detection + improved parsing on macOS 12

parsing of log messages on macOS 12 improved
internal / build-in mic/camera detection improved
This commit is contained in:
Patrick Wardle 2022-12-08 10:58:15 -10:00
parent dac55f007a
commit 150dc44410
2 changed files with 182 additions and 127 deletions

View File

@ -34,7 +34,6 @@
CDC60991263CBD36006D1332 /* AVMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = CDC60990263CBD36006D1332 /* AVMonitor.m */; };
CDC60994263CBEE7006D1332 /* Client.m in Sources */ = {isa = PBXBuildFile; fileRef = CDC60993263CBEE7006D1332 /* Client.m */; };
CDCBF0DE26499DFF001D9F9A /* Event.m in Sources */ = {isa = PBXBuildFile; fileRef = CDCBF0DD26499DFF001D9F9A /* Event.m */; };
CDE09AF729198BA800561CFF /* OverSight Installer.app in Resources */ = {isa = PBXBuildFile; fileRef = CDE09AF629198BA800561CFF /* OverSight Installer.app */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@ -98,7 +97,6 @@
CDC60993263CBEE7006D1332 /* Client.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Client.m; sourceTree = "<group>"; };
CDCBF0DC26499DFF001D9F9A /* Event.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Event.h; sourceTree = "<group>"; };
CDCBF0DD26499DFF001D9F9A /* Event.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Event.m; sourceTree = "<group>"; };
CDE09AF629198BA800561CFF /* OverSight Installer.app */ = {isa = PBXFileReference; lastKnownFileType = wrapper.application; path = "OverSight Installer.app"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -218,7 +216,6 @@
CDE09AF12919829000561CFF /* Uninstaller */ = {
isa = PBXGroup;
children = (
CDE09AF629198BA800561CFF /* OverSight Installer.app */,
);
path = Uninstaller;
sourceTree = "<group>";
@ -296,7 +293,6 @@
buildActionMask = 2147483647;
files = (
CD8FD5D523BAE2D200EFE0FB /* Preferences.xib in Resources */,
CDE09AF729198BA800561CFF /* OverSight Installer.app in Resources */,
CD6836682391DB6F00CF19C1 /* security.plist in Resources */,
CD2F800C24455333009C3D77 /* AboutWindow.xib in Resources */,
7D16D6951F64E43300DB3161 /* UpdateWindow.xib in Resources */,

View File

@ -157,118 +157,158 @@ extern os_log_t logHandle;
//start logging
[self.logMonitor start:[NSPredicate predicateWithFormat:@"subsystem=='com.apple.SystemStatus'"] level:Log_Level_Default callback:^(OSLogEvent* logEvent) {
//flags
BOOL audioAttributionsList = NO;
BOOL cameraAttributionsList = NO;
//new audio attributions
NSMutableArray* newAudioAttributions = nil;
//new camera attributions
NSMutableArray* newCameraAttributions = nil;
//dbg msg
os_log_debug(logHandle, "log message from 'com.apple.SystemStatus'");
//only interested on "Server data changed..." msgs
if(YES != [logEvent.composedMessage containsString:@"Server data changed for media domain"])
{
return;
}
//dbg msg
//os_log_debug(logHandle, "new (video) client msg: %{public}@", logEvent.composedMessage);
//split on newlines
// ...and then parse out audio/camera attributions
for(NSString* __strong line in [logEvent.composedMessage componentsSeparatedByString:@"\n"])
{
//pid
NSNumber* pid = 0;
//trim
line = [line stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
//audioAttributions list?
if(YES == [line hasPrefix:@"audioAttributions = "])
{
//set flag
audioAttributionsList = YES;
//init
newAudioAttributions = [NSMutableArray array];
//unset (other) list
cameraAttributionsList = NO;
//next
continue;
}
//cameraAttributions list?
if(YES == [line hasPrefix:@"cameraAttributions = "])
{
//set flag
cameraAttributionsList = YES;
//init
newCameraAttributions = [NSMutableArray array];
//unset (other) list
audioAttributionsList = NO;
//next
continue;
}
//audit token of item?
if(YES == [line containsString:@"<BSAuditToken:"])
{
//pid extraction regex
NSRegularExpression* regex = nil;
//match
NSTextCheckingResult* match = nil;
//init regex
regex = [NSRegularExpression regularExpressionWithPattern:@"(?<=PID: )[0-9]*" options:0 error:nil];
//match/extract pid
match = [regex firstMatchInString:line options:0 range:NSMakeRange(0, line.length)];
if( (nil == match) ||
(NSNotFound == match.range.location))
{
//ignore
continue;
}
//extract pid
pid = @([[line substringWithRange:[match rangeAtIndex:0]] intValue]);
//in audio list?
if(YES == audioAttributionsList)
{
//add
[newAudioAttributions addObject:[NSNumber numberWithInt:[pid intValue]]];
}
//in camera list?
else if(YES == cameraAttributionsList)
{
//add
[newCameraAttributions addObject:[NSNumber numberWithInt:[pid intValue]]];
}
//next
continue;
}
}
os_log_debug(logHandle, "received log message from 'com.apple.SystemStatus': %{public}@", logEvent.composedMessage);
//sync to process
@synchronized (self) {
//flags
BOOL audioAttributionsList = NO;
BOOL cameraAttributionsList = NO;
//new audio attributions
NSMutableArray* newAudioAttributions = nil;
//new camera attributions
NSMutableArray* newCameraAttributions = nil;
//only interested on "Server data changed..." msgs
if(YES != [logEvent.composedMessage containsString:@"Server data changed for media domain"])
{
return;
}
//split on newlines
// ...and then parse out audio/camera attributions
for(NSString* __strong line in [logEvent.composedMessage componentsSeparatedByString:@"\n"])
{
//pid
NSNumber* pid = 0;
//trim
line = [line stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
//'audioAttributions' list?
if( (YES == [line hasPrefix:@"audioAttributions = "]) ||
(YES == [line hasPrefix:@"audioRecordingAttributions = "]) )
{
//dbg msg
os_log_debug(logHandle, "found 'audio attributions'");
//set flag
audioAttributionsList = YES;
//init
newAudioAttributions = [NSMutableArray array];
//unset (other) list
cameraAttributionsList = NO;
//next
continue;
}
//'cameraAttributions' list?
if( (YES == [line hasPrefix:@"cameraAttributions = "]) ||
(YES == [line hasPrefix:@"cameraCaptureAttributions = "]) )
{
//dbg msg
os_log_debug(logHandle, "found 'camera attributions'");
//set flag
cameraAttributionsList = YES;
//init
newCameraAttributions = [NSMutableArray array];
//unset (other) list
audioAttributionsList = NO;
//next
continue;
}
//audit token of item?
if(YES == [line containsString:@"<BSAuditToken:"])
{
//dbg msg
os_log_debug(logHandle, "line has audit token...");
//pid extraction regex
NSRegularExpression* regex = nil;
//match
NSTextCheckingResult* match = nil;
//init regex
regex = [NSRegularExpression regularExpressionWithPattern:@"(?<=PID: )[0-9]*" options:0 error:nil];
//match/extract pid
match = [regex firstMatchInString:line options:0 range:NSMakeRange(0, line.length)];
if( (nil == match) ||
(NSNotFound == match.range.location))
{
//dbg msg
os_log_debug(logHandle, "no match on regex");
//ignore
continue;
}
//extract pid
pid = @([[line substringWithRange:[match rangeAtIndex:0]] intValue]);
//dbg msg
os_log_debug(logHandle, "pid: %@", pid);
//in audio list?
if(YES == audioAttributionsList)
{
//dbg msg
os_log_debug(logHandle, "...for audio");
//add
[newAudioAttributions addObject:[NSNumber numberWithInt:[pid intValue]]];
}
//in camera list?
else if(YES == cameraAttributionsList)
{
//dbg msg
os_log_debug(logHandle, "...for camera");
//add
[newCameraAttributions addObject:[NSNumber numberWithInt:[pid intValue]]];
}
//next
continue;
}
}
//macOS 12: off events trigger the removal of the list
// so then we'll just pass in an empty list in that case
if(12 == NSProcessInfo.processInfo.operatingSystemVersion.majorVersion)
{
//nil?
if(nil == newAudioAttributions)
{
//init blank
newAudioAttributions = [NSMutableArray array];
}
//nil?
if(nil == newCameraAttributions)
{
//init blank
newCameraAttributions = [NSMutableArray array];
}
}
//process attibutions
[self processAttributions:newAudioAttributions newCameraAttributions:newCameraAttributions];
}
}//sync
//(re)enumerate active devices
// delayed need as device deactiavation
@ -305,15 +345,20 @@ extern os_log_t logHandle;
//event
__block Event* event = nil;
//dbg msg
os_log_debug(logHandle, "method '%s' invoked", __PRETTY_FUNCTION__);
//diff audio differences
if(nil != newAudioAttributions)
if( (nil != newAudioAttributions) &&
(nil != self.audioAttributions) )
{
//diff
audioDifferences = [newAudioAttributions differenceFromArray:self.audioAttributions];
}
//diff camera differences
if(nil != newCameraAttributions)
if( (nil != newCameraAttributions) &&
(nil != self.cameraAttributions) )
{
//diff
cameraDifferences = [newCameraAttributions differenceFromArray:self.cameraAttributions];
@ -325,6 +370,9 @@ extern os_log_t logHandle;
// handle (lookup mic, send event)
if(YES == audioDifferences.hasChanges)
{
//dbg msg
os_log_debug(logHandle, "new audio event");
//cancel prev timer
if(nil != self.audioEventTimer)
{
@ -389,7 +437,7 @@ extern os_log_t logHandle;
}
//dbg msg
os_log_debug(logHandle, "device: %{public}@/%{public}@ is on", microphone.manufacturer, microphone.localizedName);
os_log_debug(logHandle, "audio device: %{public}@/%{public}@ is on", microphone.manufacturer, microphone.localizedName);
//save
activeMic = microphone;
@ -428,6 +476,9 @@ extern os_log_t logHandle;
// handle (lookup camera, send event)
if(YES == cameraDifferences.hasChanges)
{
//dbg msg
os_log_debug(logHandle, "new camera event");
//cancel prev timer
if(nil != self.cameraEventTimer)
{
@ -528,7 +579,7 @@ extern os_log_t logHandle;
}
});
//start audio timer
//start camera timer
dispatch_resume(self.cameraEventTimer);
} //camera event
@ -741,15 +792,19 @@ bail:
for(AVCaptureDevice* currentMic in [AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio])
{
//dbg msg
os_log_debug(logHandle, "device: %{public}@/%{public}@", currentMic.manufacturer, currentMic.localizedName);
os_log_debug(logHandle, "device: %{public}@/%{public}@/%{public}@", currentMic.manufacturer, currentMic.localizedName, currentMic.uniqueID);
//is "BuiltInMicrophoneDevice" ?
if( (YES == [currentMic.manufacturer isEqualToString:@"Apple Inc."]) &&
(YES == [currentMic.uniqueID isEqualToString:@"BuiltInMicrophoneDevice"]) )
//is device apple + built in mic?
if(YES == [currentMic.manufacturer isEqualToString:@"Apple Inc."])
{
//found
builtInMic = currentMic;
break;
//is built in mic?
if( (YES == [currentMic.uniqueID isEqualToString:@"BuiltInMicrophoneDevice"]) ||
(YES == [currentMic.localizedName isEqualToString:@"Built-in Microphone"]) )
{
//found
builtInMic = currentMic;
break;
}
}
}
@ -777,15 +832,19 @@ bail:
for(AVCaptureDevice* currentCamera in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo])
{
//dbg msg
os_log_debug(logHandle, "device: %{public}@/%{public}@", currentCamera.manufacturer, currentCamera.localizedName);
os_log_debug(logHandle, "device: %{public}@/%{public}@/%{public}@", currentCamera.manufacturer, currentCamera.localizedName, currentCamera.uniqueID);
//check if apple && 'FaceTime HD Camera'
if( (YES == [currentCamera.manufacturer isEqualToString:@"Apple Inc."]) &&
(YES == [currentCamera.uniqueID isEqualToString:@"FaceTime HD Camera"]) )
//is device apple + built in camera?
if(YES == [currentCamera.manufacturer isEqualToString:@"Apple Inc."])
{
//found
builtInCamera = currentCamera;
break;
//is built in camera?
if( (YES == [currentCamera.uniqueID isEqualToString:@"FaceTime HD Camera"]) ||
(YES == [currentCamera.localizedName isEqualToString:@"FaceTime-HD-camera"]) )
{
//found
builtInCamera = currentCamera;
break;
}
}
}