Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 94 additions & 31 deletions Xamarin.MacDev/DeviceCtlOutputParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,37 +47,43 @@ public static List<PhysicalDeviceInfo> 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))
Expand Down Expand Up @@ -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;
}
}
108 changes: 108 additions & 0 deletions tests/DeviceCtlOutputParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 ()
{
Expand Down
Loading