diff --git a/Xamarin.MacDev/DeviceCtlOutputParser.cs b/Xamarin.MacDev/DeviceCtlOutputParser.cs index 19e1076..a04e65f 100644 --- a/Xamarin.MacDev/DeviceCtlOutputParser.cs +++ b/Xamarin.MacDev/DeviceCtlOutputParser.cs @@ -47,37 +47,43 @@ public static List ParseDevices (string? json, ICustomLogger Identifier = GetString (device, "identifier"), }; - // deviceProperties - if (device.TryGetProperty ("deviceProperties", out var deviceProps)) { - info.Name = GetString (deviceProps, "name"); - info.BuildVersion = GetString (deviceProps, "osBuildUpdate"); - info.OSVersion = GetString (deviceProps, "osVersionNumber"); - } - - // hardwareProperties - if (device.TryGetProperty ("hardwareProperties", out var hwProps)) { - info.Udid = GetString (hwProps, "udid"); - info.DeviceClass = GetString (hwProps, "deviceType"); - info.HardwareModel = GetString (hwProps, "hardwareModel"); - info.Platform = GetString (hwProps, "platform"); - info.ProductType = GetString (hwProps, "productType"); - info.SerialNumber = GetString (hwProps, "serialNumber"); - - if (hwProps.TryGetProperty ("ecid", out var ecidElement)) { - if (ecidElement.TryGetUInt64 (out var ecid)) - info.UniqueChipID = ecid; - } - - // cpuType.name - if (hwProps.TryGetProperty ("cpuType", out var cpuType)) - info.CpuArchitecture = GetString (cpuType, "name"); - } - - // connectionProperties - if (device.TryGetProperty ("connectionProperties", out var connProps)) { - info.TransportType = GetString (connProps, "transportType"); - info.PairingState = GetString (connProps, "pairingState"); - } + var properties = GetObject (device, "properties"); + var newDeviceProperties = GetObject (properties, "device"); + var stateProperties = GetObject (properties, "state"); + var softwareProperties = GetObject (properties, "software"); + var osBuildVersions = GetObject (softwareProperties, "osBuildVersions"); + var buildVersion = GetObject (osBuildVersions, "buildVersion"); + var osVersionNumber = GetObject (softwareProperties, "osVersionNumber"); + var newHardwareProperties = GetObject (properties, "hardware"); + var newConnectionProperties = GetObject (properties, "connection"); + var deviceProperties = GetObject (device, "deviceProperties"); + var hardwareProperties = GetObject (device, "hardwareProperties"); + var connectionProperties = GetObject (device, "connectionProperties"); + + info.Name = GetString ("name", newDeviceProperties, stateProperties, properties, deviceProperties); + info.BuildVersion = GetString ("name", buildVersion); + if (string.IsNullOrEmpty (info.BuildVersion)) + info.BuildVersion = GetString ("osBuildUpdate", stateProperties, newDeviceProperties, properties, deviceProperties); + info.OSVersion = GetString ("stringValue", osVersionNumber); + if (string.IsNullOrEmpty (info.OSVersion)) + info.OSVersion = GetString ("osVersionNumber", stateProperties, newDeviceProperties, properties, deviceProperties); + info.Udid = GetString ("udid", newHardwareProperties, properties, hardwareProperties); + info.DeviceClass = GetString ("deviceType", newHardwareProperties, properties, hardwareProperties); + info.HardwareModel = GetString ("hardwareModel", newHardwareProperties, properties, hardwareProperties); + info.Platform = GetString ("platform", newHardwareProperties, properties, hardwareProperties); + info.ProductType = GetString ("productType", newHardwareProperties, properties, hardwareProperties); + info.SerialNumber = GetString ("serialNumber", newHardwareProperties, properties, hardwareProperties); + info.UniqueChipID = GetUInt64 ("ecid", newHardwareProperties, properties, hardwareProperties); + + var newCpuType = GetObject (newHardwareProperties, "cpuType"); + var cpuType = GetObject (properties, "cpuType"); + var legacyCpuType = GetObject (hardwareProperties, "cpuType"); + info.CpuArchitecture = GetCpuArchitecture (newCpuType); + if (string.IsNullOrEmpty (info.CpuArchitecture)) + info.CpuArchitecture = GetString ("name", newCpuType, cpuType, legacyCpuType); + + info.TransportType = GetString ("transportType", newConnectionProperties, stateProperties, properties, connectionProperties); + info.PairingState = GetString ("pairingState", newConnectionProperties, stateProperties, properties, connectionProperties); // Fallback: use identifier as UDID if hardware UDID is missing if (string.IsNullOrEmpty (info.Udid)) @@ -106,4 +112,61 @@ static string GetString (JsonElement element, string property) } return ""; } + + static string GetCpuArchitecture (JsonElement? cpuType) + { + if (!cpuType.HasValue || + !cpuType.Value.TryGetProperty ("type", out var typeElement) || + !typeElement.TryGetInt32 (out var type)) + return ""; + + switch (type) { + case 7: + return "i386"; + case 12: + return "arm"; + case 0x01000007: + return "x86_64"; + case 0x0100000c: + if (cpuType.Value.TryGetProperty ("subtype", out var subtypeElement) && + subtypeElement.TryGetUInt64 (out var subtype) && + (subtype & 0xff) == 2) + return "arm64e"; + return "arm64"; + case 0x0200000c: + return "arm64_32"; + default: + return ""; + } + } + + static JsonElement? GetObject (JsonElement? element, string property) + { + if (element.HasValue && element.Value.TryGetProperty (property, out var value) && value.ValueKind == JsonValueKind.Object) + return value; + return null; + } + + static string GetString (string property, params JsonElement? [] elements) + { + foreach (var element in elements) { + if (!element.HasValue) + continue; + var value = GetString (element.Value, property); + if (!string.IsNullOrEmpty (value)) + return value; + } + return ""; + } + + static ulong? GetUInt64 (string property, params JsonElement? [] elements) + { + foreach (var element in elements) { + if (!element.HasValue || !element.Value.TryGetProperty (property, out var value)) + continue; + if (value.TryGetUInt64 (out var result)) + return result; + } + return null; + } } diff --git a/tests/DeviceCtlOutputParserTests.cs b/tests/DeviceCtlOutputParserTests.cs index ee84b80..d081dab 100644 --- a/tests/DeviceCtlOutputParserTests.cs +++ b/tests/DeviceCtlOutputParserTests.cs @@ -61,6 +61,114 @@ public void ParseDevices_ValidJson_ParsesAllFields () Assert.That (device.PairingState, Is.EqualTo ("paired")); } + [Test] + public void ParseDevices_JsonVersion5_ParsesProperties () + { + var json = @"{ + ""info"": { + ""jsonVersion"": 5 + }, + ""result"": { + ""devices"": [ + { + ""_deprecationNotice"": { + ""deprecatedFields"": [ ""hardwareProperties"", ""deviceProperties"", ""connectionProperties"" ], + ""replacement"": ""properties"" + }, + ""deviceProperties"": { + ""name"": ""Deprecated name"", + ""osBuildUpdate"": ""22A1"", + ""osVersionNumber"": ""17.0"" + }, + ""hardwareProperties"": { + ""hardwareModel"": ""D83AP"" + }, + ""identifier"": ""33333333-AAAA-BBBB-CCCC-DDDDDDDDDDDD"", + ""properties"": { + ""connection"": { + ""pairingState"": ""paired"", + ""transportType"": ""localNetwork"" + }, + ""hardware"": { + ""cpuType"": { + ""subtype"": 18446744071562067970, + ""type"": 16777228 + }, + ""deviceType"": ""iPhone"", + ""ecid"": 12345678, + ""platform"": ""iOS"", + ""productType"": ""iPhone16,1"", + ""serialNumber"": ""SERIAL_1"", + ""udid"": ""00008003-012301230123ABCD"" + }, + ""software"": { + ""osBuildVersions"": { + ""buildVersion"": { + ""name"": ""23B85"" + } + }, + ""osVersionNumber"": { + ""stringValue"": ""18.1"" + } + }, + ""state"": { + ""name"": ""Rolf's iPhone 15"" + } + } + } + ] + } + }"; + + var result = DeviceCtlOutputParser.ParseDevices (json); + Assert.That (result.Count, Is.EqualTo (1)); + + var device = result [0]; + Assert.That (device.Name, Is.EqualTo ("Rolf's iPhone 15")); + Assert.That (device.Udid, Is.EqualTo ("00008003-012301230123ABCD")); + Assert.That (device.Identifier, Is.EqualTo ("33333333-AAAA-BBBB-CCCC-DDDDDDDDDDDD")); + Assert.That (device.BuildVersion, Is.EqualTo ("23B85")); + Assert.That (device.OSVersion, Is.EqualTo ("18.1")); + Assert.That (device.DeviceClass, Is.EqualTo ("iPhone")); + Assert.That (device.HardwareModel, Is.EqualTo ("D83AP")); + Assert.That (device.Platform, Is.EqualTo ("iOS")); + Assert.That (device.ProductType, Is.EqualTo ("iPhone16,1")); + Assert.That (device.SerialNumber, Is.EqualTo ("SERIAL_1")); + Assert.That (device.UniqueChipID, Is.EqualTo ((ulong) 12345678)); + Assert.That (device.CpuArchitecture, Is.EqualTo ("arm64e")); + Assert.That (device.TransportType, Is.EqualTo ("localNetwork")); + Assert.That (device.PairingState, Is.EqualTo ("paired")); + } + + [Test] + public void ParseDevices_JsonVersion5_Arm64CpuType_ParsesArchitecture () + { + var json = @"{ + ""info"": { + ""jsonVersion"": 5 + }, + ""result"": { + ""devices"": [ + { + ""identifier"": ""ID-1"", + ""properties"": { + ""hardware"": { + ""cpuType"": { + ""subtype"": 0, + ""type"": 16777228 + } + } + } + } + ] + } + }"; + + var result = DeviceCtlOutputParser.ParseDevices (json); + Assert.That (result.Count, Is.EqualTo (1)); + Assert.That (result [0].CpuArchitecture, Is.EqualTo ("arm64")); + } + [Test] public void ParseDevices_MultipleDevices_ParsesAll () {