I would like to get all services that contain specific properties.
AppleDeviceManagementHIDEventService <class AppleDeviceManagementHIDEventService, id 0x100000a79, registered, matched, active, busy 0 (0 ms), retain 7>
| | | | | | {
| | | | | | "IOMatchedAtBoot" = Yes
| | | | | | "LowBatteryNotificationPercentage" = 2
| | | | | | "PrimaryUsagePage" = 65280
| | | | | | "BatteryFaultNotificationType" = "TPBatteryFault"
| | | | | | "HasBattery" = Yes
| | | | | | "VendorID" = 76
| | | | | | "VersionNumber" = 0
| | | | | | "Built-In" = No
| | | | | | "DeviceAddress" = "10-94-bb-ab-b9-53"
| | | | | | "WakeReason" = "Host (0x01)"
| | | | | | "Product" = "Magic Trackpad"
| | | | | | "SerialNumber" = "10-94-bb-ab-b9-53"
| | | | | | "Transport" = "Bluetooth"
| | | | | | "BatteryLowNotificationType" = "TPLowBattery"
| | | | | | "ProductID" = 613
| | | | | | "DeviceUsagePairs" = ({"DeviceUsagePage"=65280,"DeviceUsage"=11},{"DeviceUsagePage"=65280,"DeviceUsage"=20})
| | | | | | "IOPersonalityPublisher" = "com.apple.driver.AppleTopCaseHIDEventDriver"
| | | | | | "MTFW Version" = 920
| | | | | | "BD_ADDR" = <1094bbabb953>
| | | | | | "BatteryPercent" = 42
| | | | | | "BatteryStatusNotificationType" = "BatteryStatusChanged"
| | | | | | "CriticallyLowBatteryNotificationPercentage" = 1
| | | | | | "ReportInterval" = 11250
| | | | | | "RadioFW Version" = 368
| | | | | | "VendorIDSource" = 1
| | | | | | "STFW Version" = 2144
| | | | | | "CFBundleIdentifier" = "com.apple.driver.AppleTopCaseHIDEventDriver"
| | | | | | "IOProviderClass" = "IOHIDInterface"
| | | | | | "LocationID" = 1001109843
| | | | | | "BluetoothDevice" = Yes
| | | | | | "IOClass" = "AppleDeviceManagementHIDEventService"
| | | | | | "HIDServiceSupport" = No
| | | | | | "CFBundleIdentifierKernel" = "com.apple.driver.AppleTopCaseHIDEventDriver"
| | | | | | "ProductIDArray" = (613)
| | | | | | "BatteryStatusFlags" = 0
| | | | | | "ColorID" = 5
| | | | | | "IOMatchCategory" = "IODefaultMatchCategory"
| | | | | | "CountryCode" = 0
| | | | | | "IOProbeScore" = 7175
| | | | | | "PrimaryUsage" = 11
| | | | | | "IOGeneralInterest" = "IOCommand is not serializable"
| | | | | | "BTFW Version" = 368
| | | | | | }
Eg. every service that contains the child property "BatteryPercent"
.
I know that I can get specific service by using IOServiceNameMatching
and then IOServiceGetMatchingServices
, but this seems to not be working on the properties inside the service. Is it possible to that? I want to match by BatteryPercent and then also get ProductName.
EDIT: This is the code I'm currently using to get the trackpad battery.
func getTrackpadBattery() -> (String, Int) {
var serialPortIterator = io_iterator_t()
var object : io_object_t
var percent: Int = 0
var productName: String = ""
let masterPort: mach_port_t = kIOMainPortDefault
let matchingDict : CFDictionary = IOServiceMatching("AppleDeviceManagementHIDEventService")
let kernResult = IOServiceGetMatchingServices(masterPort, matchingDict, &serialPortIterator)
if KERN_SUCCESS == kernResult {
repeat {
object = IOIteratorNext(serialPortIterator)
if object != 0 {
let percentProperty = IORegistryEntryCreateCFProperty(object, "BatteryPercent" as CFString, kCFAllocatorDefault, 0)
if (percentProperty != nil) {
percent = (percentProperty?.takeRetainedValue() as? Int)!
productName = (IORegistryEntryCreateCFProperty(object, "Product" as CFString, kCFAllocatorDefault, 0).takeRetainedValue() as? String)!
}
}
} while percent == 0
IOObjectRelease(object)
}
IOObjectRelease(serialPortIterator)
return (productName, percent)
}
(Sorry, I don't really know Swift, so my example is in Objective-C and I've attempted to provide it in Swift too but it might not be right.)
The kIOPropertyExistsMatchKey
matching key should be able to do what you're after. Use it in your matching dictionary to constrain the match results to IOKit services which have the property given. For example:
CFDictionaryRef match_dict =
(__bridge_retained CFDictionaryRef)
@{ @kIOPropertyExistsMatchKey : @"BatteryPercent" };
io_service_t service = IOServiceGetMatchingService(
kIOMasterPortDefault, match_dict);
Note that IOKit properties are for the most part not standardised, so unless you know that a specific service (super-)class publishes the property in a way that your code can process, services could export just about any data via properties with arbitrary names, which does not necessarily map to the meaning you were expecting. So you might want to constrain the registry entries you process in this way to a specific allow-list of IOService classes. (By adding the "IOProviderClass"
/kIOProviderClassKey
matching key to your match dictionary, which is also what IOServiceMatching()
spits out.) At minimum you might want to warn the user when interpreting data from an unknown service class.
In Swift (may be wrong):
For returning all I/O registry entries with the "BatteryPercent"
property, try something like:
[…]
let matchingDict = [ kIOPropertyExistsMatchKey: "BatteryPercent" ] as NSDictionary
let kernResult = IOServiceGetMatchingServices(masterPort, matchingDict, &iterator)
[…]
(You'll need to import Foundation
to be able to use NSDictionary
.)
For only returning those objects with a specific class (here: AppleDeviceManagementHIDEventService
) and the "BatteryPercent"
property:
[…]
let matchingDict = IOServiceMatching("AppleDeviceManagementHIDEventService")
CFDictionarySetValue(matchingDict, kIOPropertyExistsMatchKey, "BatteryPercent")
let kernResult = IOServiceGetMatchingServices(masterPort, matchingDict, &iterator)
[…]