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
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Backtrace.Unity.Extensions
/// <summary>
/// Extension for Guid class
/// </summary>
public static class GuidExtensions
public static class GuidHelper
{
/// <summary>
/// Convert long to Guid
Expand All @@ -17,5 +17,11 @@ public static Guid FromLong(long source)
Array.Copy(BitConverter.GetBytes(source), guidData, 8);
return new Guid(guidData);
}

public static bool IsNullOrEmpty(string guid)
{
const string emptyGuid = "00000000-0000-0000-0000-000000000000";
return string.IsNullOrEmpty(guid) || guid == emptyGuid;
}
}
}
35 changes: 2 additions & 33 deletions Runtime/Model/Attributes/MachineAttributeProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,20 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net.NetworkInformation;
using UnityEngine;

namespace Backtrace.Unity.Model.Attributes
{
internal sealed class MachineAttributeProvider : IScopeAttributeProvider
{
private readonly MachineIdStorage _machineIdStorage = new MachineIdStorage();
public void GetAttributes(IDictionary<string, string> attributes)
{
if (attributes == null)
{
return;
}
attributes["guid"] = GenerateMachineId();
attributes["guid"] = _machineIdStorage.GenerateMachineId();
IncludeGraphicCardInformation(attributes);
IncludeOsInformation(attributes);
}
Expand Down Expand Up @@ -83,35 +82,5 @@ private void IncludeGraphicCardInformation(IDictionary<string, string> attribute
attributes["graphic.shader"] = SystemInfo.graphicsShaderLevel.ToString(CultureInfo.InvariantCulture);
attributes["graphic.topUv"] = SystemInfo.graphicsUVStartsAtTop.ToString(CultureInfo.InvariantCulture);
}

private string GenerateMachineId()
{
#if !UNITY_WEBGL && !UNITY_SWITCH
// DeviceUniqueIdentifier will return "Switch" on Nintendo Switch
// try to generate random guid instead
if (SystemInfo.deviceUniqueIdentifier != SystemInfo.unsupportedIdentifier)
{
return SystemInfo.deviceUniqueIdentifier;
}
var networkInterface =
NetworkInterface.GetAllNetworkInterfaces()
.FirstOrDefault(n => n.OperationalStatus == OperationalStatus.Up);

PhysicalAddress physicalAddr = null;
string macAddress = null;
if (networkInterface == null
|| (physicalAddr = networkInterface.GetPhysicalAddress()) == null
|| string.IsNullOrEmpty(macAddress = physicalAddr.ToString()))
{
return Guid.NewGuid().ToString();
}

string hex = macAddress.Replace(":", string.Empty);
var value = Convert.ToInt64(hex, 16);
return GuidExtensions.FromLong(value).ToString();
#else
return Guid.NewGuid().ToString();
#endif
}
}
}
112 changes: 112 additions & 0 deletions Runtime/Model/MachineIdStorage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
using Backtrace.Unity.Extensions;
using System;
using System.Linq;
using System.Net.NetworkInformation;
using UnityEngine;

[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Backtrace.Unity.Tests.Runtime")]
namespace Backtrace.Unity.Model
{
/// <summary>
/// Backtrace Machine Id storage
/// </summary>
internal class MachineIdStorage
{
/// <summary>
/// Player prefs machine identifier key
/// </summary>
internal const string MachineIdentifierKey = "backtrace-machine-id";

/// <summary>
/// Generate unique machine id.
/// </summary>
/// <returns>Unique machine id Guid in a string format</returns>
internal string GenerateMachineId()
{
var storageMachineId = FetchMachineIdFromStorage();
if (!string.IsNullOrEmpty(storageMachineId))
{
return storageMachineId;
}

#if !UNITY_WEBGL && !UNITY_SWITCH
var unityIdentifier = UseUnityIdentifier();
if (!GuidHelper.IsNullOrEmpty(unityIdentifier))
{
StoreMachineId(unityIdentifier);
return unityIdentifier;
}
var networkIdentifier = UseNetworkingIdentifier();
if (!GuidHelper.IsNullOrEmpty(networkIdentifier))
{
StoreMachineId(networkIdentifier);
return networkIdentifier;
}
#endif
var backtraceRandomIdentifier = Guid.NewGuid().ToString();
StoreMachineId(backtraceRandomIdentifier);
return backtraceRandomIdentifier;
}


/// <summary>
/// Fetch a machine id in the internal storage
/// </summary>
/// <returns>machine identifier in the GUID string format</returns>
private string FetchMachineIdFromStorage()
{
return PlayerPrefs.GetString(MachineIdentifierKey);
}

/// <summary>
/// Set a machine id in the internal storage
/// </summary>
/// <param name="machineId">machine identifier</param>
private void StoreMachineId(string machineId)
{
PlayerPrefs.SetString(MachineIdentifierKey, machineId);
}

/// <summary>
/// Use Unity device identifier to generate machine identifier
/// </summary>
/// <returns>Unity machine identifier if the device identifier is supported. Otherwise null</returns>
protected virtual string UseUnityIdentifier()
{
if (SystemInfo.deviceUniqueIdentifier == SystemInfo.unsupportedIdentifier)
{
return null;
}
return SystemInfo.deviceUniqueIdentifier;
}

/// <summary>
/// Use Networking interface to generate machine identifier - MAC number from the networking interface.
/// </summary>
/// <returns>Machine id - MAC in a GUID format. If the networking interface is not available then it returns null.</returns>
protected virtual string UseNetworkingIdentifier()
{
var interfaces = NetworkInterface.GetAllNetworkInterfaces()
.Where(n => n.OperationalStatus == OperationalStatus.Up);

foreach (var @interface in interfaces)
{
var physicalAddress = @interface.GetPhysicalAddress();
if (physicalAddress == null)
{
continue;
}
var macAddress = physicalAddress.ToString();
if (string.IsNullOrEmpty(macAddress))
{
continue;
}
string hex = macAddress.Replace(":", string.Empty);
var value = Convert.ToInt64(hex, 16);
return GuidHelper.FromLong(value).ToString();
}

return null;
}
}
}
11 changes: 11 additions & 0 deletions Runtime/Model/MachineIdStorage.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

98 changes: 98 additions & 0 deletions Tests/Runtime/Client/BacktraceAttributeMachineIdTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
using Backtrace.Unity.Extensions;
using Backtrace.Unity.Model;
using Backtrace.Unity.Tests.Runtime.Client.Mocks;
using NUnit.Framework;
using UnityEngine;

namespace Backtrace.Unity.Tests.Runtime.Client
{
class BacktraceAttributeMachineIdTests
{
[SetUp]
public void Cleanup()
{
PlayerPrefs.DeleteKey(MachineIdStorage.MachineIdentifierKey);
}

[Test]
public void TestMachineAttributes_ShouldUseUnityIdentifier_ShouldReturnUnityIdentitfier()
{
var machineIdStorage = new MachineIdStorageMock();

var machineId = machineIdStorage.GenerateMachineId();

Assert.IsFalse(GuidHelper.IsNullOrEmpty(machineId));
}

[Test]
public void TestMachineAttributes_ShouldUseMac_ShouldReturnNetowrkingIdentifier()
{
var machineIdStorage = new MachineIdStorageMock(false);

var machineId = machineIdStorage.GenerateMachineId();

Assert.IsFalse(GuidHelper.IsNullOrEmpty(machineId));
}

[Test]
public void TestMachineAttributes_ShouldUseRandomMachineId_ShouldReturnRandomMachineId()
{
var machineIdStorage = new MachineIdStorageMock(false, false);

var machineId = machineIdStorage.GenerateMachineId();

Assert.IsFalse(GuidHelper.IsNullOrEmpty(machineId));
}

[Test]
public void TestMachineAttributes_ShouldAlwaysReturnTheSameValueUnityId_IdentifierAreTheSame()
{
var firstMachineIdStorage = new MachineIdStorageMock().GenerateMachineId();
var secGenerationOfMachineIdStorage = new MachineIdStorageMock().GenerateMachineId();

Assert.IsTrue(firstMachineIdStorage == secGenerationOfMachineIdStorage);
}

[Test]
public void TestMachineAttributes_ShouldAlwaysReturnTheSameValueMacId_IdentifierAreTheSame()
{
var firstMachineIdStorage = new MachineIdStorageMock(false).GenerateMachineId();
var secGenerationOfMachineIdStorage = new MachineIdStorageMock(false).GenerateMachineId();

Assert.IsTrue(firstMachineIdStorage == secGenerationOfMachineIdStorage);
}

[Test]
public void TestMachineAttributes_ShouldAlwaysReturnTheSameValueRandomId_IdentifierAreTheSame()
{
var firstMachineIdStorage = new MachineIdStorageMock(false, false).GenerateMachineId();
var secGenerationOfMachineIdStorage = new MachineIdStorageMock(false, false).GenerateMachineId();

Assert.IsTrue(firstMachineIdStorage == secGenerationOfMachineIdStorage);
}

[Test]
public void TestMachineAttributes_ShouldAlwaysGenerateTheSameUntiyAttribute_ShouldReturnTheSameUnityIdentitfier()
{
var machineIdStorage = new MachineIdStorageMock();

var machineId = machineIdStorage.GenerateMachineId();
PlayerPrefs.DeleteKey(MachineIdStorage.MachineIdentifierKey);
var machineIdAfterCleanup = machineIdStorage.GenerateMachineId();

Assert.AreEqual(machineId, machineIdAfterCleanup);
}

[Test]
public void TestMachineAttributes_ShouldAlwaysGenerateTheSameMacAttribute_ShouldReturnTheSameMacIdentitfier()
{
var machineIdStorage = new MachineIdStorageMock(false);

var machineId = machineIdStorage.GenerateMachineId();
PlayerPrefs.DeleteKey(MachineIdStorage.MachineIdentifierKey);
var machineIdAfterCleanup = machineIdStorage.GenerateMachineId();

Assert.AreEqual(machineId, machineIdAfterCleanup);
}
}
}
11 changes: 11 additions & 0 deletions Tests/Runtime/Client/BacktraceAttributeMachineIdTests.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions Tests/Runtime/Client/Mocks.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 39 additions & 0 deletions Tests/Runtime/Client/Mocks/MachineIdStorageMock.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using Backtrace.Unity.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Backtrace.Unity.Tests.Runtime.Client.Mocks
{
internal class MachineIdStorageMock : MachineIdStorage
{
private readonly bool _allowUnityIdentifier;
private readonly bool _allowNetworking;
public MachineIdStorageMock(bool allowUnityIdentifier = true, bool allowNetworking = true) : base()
{
_allowUnityIdentifier = allowUnityIdentifier;
_allowNetworking = allowNetworking;
}


protected override string UseNetworkingIdentifier()
{
if (!_allowNetworking)
{
return null;
}
return base.UseNetworkingIdentifier();
}

protected override string UseUnityIdentifier()
{
if (!_allowUnityIdentifier)
{
return null;
}
return base.UseUnityIdentifier();
}
}
}
11 changes: 11 additions & 0 deletions Tests/Runtime/Client/Mocks/MachineIdStorageMock.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.