diff --git a/Runtime/Common/GuidHelper.cs b/Runtime/Common/GuidHelper.cs
index 4338cbb5..8472e419 100644
--- a/Runtime/Common/GuidHelper.cs
+++ b/Runtime/Common/GuidHelper.cs
@@ -1,4 +1,6 @@
using System;
+using System.Security.Cryptography;
+using System.Text;
namespace Backtrace.Unity.Extensions
{
@@ -23,5 +25,20 @@ public static bool IsNullOrEmpty(string guid)
const string emptyGuid = "00000000-0000-0000-0000-000000000000";
return string.IsNullOrEmpty(guid) || guid == emptyGuid;
}
+
+ ///
+ /// Converts a random string into a guid representation.
+ ///
+ public static Guid FromString(string value)
+ {
+ if (string.IsNullOrEmpty(value))
+ {
+ return Guid.Empty;
+ }
+ // to make sure we're supporting old version of Unity that can use .NET 3.5
+ // we're using an older API to generate a GUID.
+ MD5 md5 = new MD5CryptoServiceProvider();
+ return new Guid(md5.ComputeHash(Encoding.UTF8.GetBytes(value)));
+ }
}
}
diff --git a/Runtime/Model/Attributes/MachineAttributeProvider.cs b/Runtime/Model/Attributes/MachineAttributeProvider.cs
index 80426746..7c5dd9dd 100644
--- a/Runtime/Model/Attributes/MachineAttributeProvider.cs
+++ b/Runtime/Model/Attributes/MachineAttributeProvider.cs
@@ -1,5 +1,4 @@
using Backtrace.Unity.Common;
-using Backtrace.Unity.Extensions;
using System;
using System.Collections.Generic;
using System.Globalization;
diff --git a/Tests/Runtime/Client/Mocks.meta b/Runtime/Model/DataProvider.meta
similarity index 77%
rename from Tests/Runtime/Client/Mocks.meta
rename to Runtime/Model/DataProvider.meta
index a4d1551e..29d34515 100644
--- a/Tests/Runtime/Client/Mocks.meta
+++ b/Runtime/Model/DataProvider.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
-guid: fd956ac43783a6b4bbc9603939fa6a8b
+guid: 2925498833be55643891f75e96ad9ded
folderAsset: yes
DefaultImporter:
externalObjects: {}
diff --git a/Runtime/Model/DataProvider/IMachineIdentifierProvider.cs b/Runtime/Model/DataProvider/IMachineIdentifierProvider.cs
new file mode 100644
index 00000000..b438c581
--- /dev/null
+++ b/Runtime/Model/DataProvider/IMachineIdentifierProvider.cs
@@ -0,0 +1,8 @@
+[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Backtrace.Unity.Tests.Runtime")]
+namespace Backtrace.Unity.Model.DataProvider
+{
+ internal interface IMachineIdentifierProvider
+ {
+ string Get();
+ }
+}
diff --git a/Runtime/Model/DataProvider/IMachineIdentifierProvider.cs.meta b/Runtime/Model/DataProvider/IMachineIdentifierProvider.cs.meta
new file mode 100644
index 00000000..123ed6b9
--- /dev/null
+++ b/Runtime/Model/DataProvider/IMachineIdentifierProvider.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: e94db95360eabe94a8f901670f630ab9
\ No newline at end of file
diff --git a/Runtime/Model/DataProvider/ISessionStorageDataProvider.cs b/Runtime/Model/DataProvider/ISessionStorageDataProvider.cs
new file mode 100644
index 00000000..eeec0974
--- /dev/null
+++ b/Runtime/Model/DataProvider/ISessionStorageDataProvider.cs
@@ -0,0 +1,9 @@
+[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Backtrace.Unity.Tests.Runtime")]
+namespace Backtrace.Unity.Model.DataProvider
+{
+ internal interface ISessionStorageDataProvider
+ {
+ void SetString(string key, string value);
+ string GetString(string key);
+ }
+}
diff --git a/Runtime/Model/DataProvider/ISessionStorageDataProvider.cs.meta b/Runtime/Model/DataProvider/ISessionStorageDataProvider.cs.meta
new file mode 100644
index 00000000..672e4599
--- /dev/null
+++ b/Runtime/Model/DataProvider/ISessionStorageDataProvider.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 79226154fe224d84b9f0014d5c8a4206
\ No newline at end of file
diff --git a/Runtime/Model/DataProvider/NetworkIdentifierProvider.cs b/Runtime/Model/DataProvider/NetworkIdentifierProvider.cs
new file mode 100644
index 00000000..4ac56a69
--- /dev/null
+++ b/Runtime/Model/DataProvider/NetworkIdentifierProvider.cs
@@ -0,0 +1,35 @@
+using Backtrace.Unity.Extensions;
+using System;
+using System.Linq;
+using System.Net.NetworkInformation;
+
+namespace Backtrace.Unity.Model.DataProvider
+{
+ internal class NetworkIdentifierProvider : IMachineIdentifierProvider
+ {
+ public string Get()
+ {
+ 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;
+ }
+ }
+}
diff --git a/Runtime/Model/DataProvider/NetworkIdentifierProvider.cs.meta b/Runtime/Model/DataProvider/NetworkIdentifierProvider.cs.meta
new file mode 100644
index 00000000..288aecb1
--- /dev/null
+++ b/Runtime/Model/DataProvider/NetworkIdentifierProvider.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 92eddf6f5fcf68d489027eabf6b0d935
\ No newline at end of file
diff --git a/Runtime/Model/DataProvider/SessionStorageDataProvider.cs b/Runtime/Model/DataProvider/SessionStorageDataProvider.cs
new file mode 100644
index 00000000..6ac95287
--- /dev/null
+++ b/Runtime/Model/DataProvider/SessionStorageDataProvider.cs
@@ -0,0 +1,17 @@
+using UnityEngine;
+
+namespace Backtrace.Unity.Model.DataProvider
+{
+ internal class SessionStorageDataProvider : ISessionStorageDataProvider
+ {
+ public string GetString(string key)
+ {
+ return PlayerPrefs.GetString(key);
+ }
+
+ public void SetString(string key, string value)
+ {
+ PlayerPrefs.SetString(key, value);
+ }
+ }
+}
diff --git a/Runtime/Model/DataProvider/SessionStorageDataProvider.cs.meta b/Runtime/Model/DataProvider/SessionStorageDataProvider.cs.meta
new file mode 100644
index 00000000..411c7d00
--- /dev/null
+++ b/Runtime/Model/DataProvider/SessionStorageDataProvider.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 61658e4a3b261f940a5497ad0125d81f
\ No newline at end of file
diff --git a/Runtime/Model/DataProvider/UnityMachineIdentifierProvider.cs b/Runtime/Model/DataProvider/UnityMachineIdentifierProvider.cs
new file mode 100644
index 00000000..df1f70e8
--- /dev/null
+++ b/Runtime/Model/DataProvider/UnityMachineIdentifierProvider.cs
@@ -0,0 +1,35 @@
+using Backtrace.Unity.Extensions;
+using System;
+using UnityEngine;
+
+namespace Backtrace.Unity.Model.DataProvider
+{
+ internal class UnityMachineIdentifierProvider : IMachineIdentifierProvider
+ {
+ private readonly string _deviceUniqueIdentifier;
+ internal UnityMachineIdentifierProvider() : this(SystemInfo.deviceUniqueIdentifier) { }
+
+ internal UnityMachineIdentifierProvider(string machineIdentifier)
+ {
+ _deviceUniqueIdentifier = machineIdentifier;
+ }
+ public string Get()
+ {
+ if (!IsValidIdentifier())
+ {
+ return null;
+ }
+
+ if (Guid.TryParse(_deviceUniqueIdentifier, out Guid unityUuidGuid))
+ {
+ return unityUuidGuid.ToString();
+ }
+ return GuidHelper.FromString(_deviceUniqueIdentifier).ToString();
+ }
+
+ private bool IsValidIdentifier()
+ {
+ return _deviceUniqueIdentifier != SystemInfo.unsupportedIdentifier && !string.IsNullOrEmpty(_deviceUniqueIdentifier);
+ }
+ }
+}
diff --git a/Runtime/Model/DataProvider/UnityMachineIdentifierProvider.cs.meta b/Runtime/Model/DataProvider/UnityMachineIdentifierProvider.cs.meta
new file mode 100644
index 00000000..2e862da4
--- /dev/null
+++ b/Runtime/Model/DataProvider/UnityMachineIdentifierProvider.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: ed39481d15e8b2249875498830f31c6f
\ No newline at end of file
diff --git a/Runtime/Model/MachineIdStorage.cs b/Runtime/Model/MachineIdStorage.cs
index 07dfc973..2a199314 100644
--- a/Runtime/Model/MachineIdStorage.cs
+++ b/Runtime/Model/MachineIdStorage.cs
@@ -1,8 +1,6 @@
using Backtrace.Unity.Extensions;
+using Backtrace.Unity.Model.DataProvider;
using System;
-using System.Linq;
-using System.Net.NetworkInformation;
-using UnityEngine;
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Backtrace.Unity.Tests.Runtime")]
namespace Backtrace.Unity.Model
@@ -17,6 +15,19 @@ internal class MachineIdStorage
///
internal const string MachineIdentifierKey = "backtrace-machine-id";
+ private readonly ISessionStorageDataProvider _sessionStorageDataProvider;
+ private readonly IMachineIdentifierProvider[] _machineIdentifierDataProviders;
+
+ internal MachineIdStorage() : this(
+ new IMachineIdentifierProvider[] { new UnityMachineIdentifierProvider(), new NetworkIdentifierProvider() },
+ new SessionStorageDataProvider())
+ { }
+ internal MachineIdStorage(IMachineIdentifierProvider[] machineIdentifierDataProviders, ISessionStorageDataProvider sessionStorageDataProvider)
+ {
+ _machineIdentifierDataProviders = machineIdentifierDataProviders;
+ _sessionStorageDataProvider = sessionStorageDataProvider;
+ }
+
///
/// Generate unique machine id.
///
@@ -30,17 +41,14 @@ internal string GenerateMachineId()
}
#if !UNITY_WEBGL && !UNITY_SWITCH
- var unityIdentifier = UseUnityIdentifier();
- if (!GuidHelper.IsNullOrEmpty(unityIdentifier))
+ foreach (var machineIdentifierProvider in _machineIdentifierDataProviders)
{
- StoreMachineId(unityIdentifier);
- return unityIdentifier;
- }
- var networkIdentifier = UseNetworkingIdentifier();
- if (!GuidHelper.IsNullOrEmpty(networkIdentifier))
- {
- StoreMachineId(networkIdentifier);
- return networkIdentifier;
+ var identifier = machineIdentifierProvider.Get();
+ if (!GuidHelper.IsNullOrEmpty(identifier))
+ {
+ StoreMachineId(identifier);
+ return identifier;
+ }
}
#endif
var backtraceRandomIdentifier = Guid.NewGuid().ToString();
@@ -55,7 +63,17 @@ internal string GenerateMachineId()
/// machine identifier in the GUID string format
private string FetchMachineIdFromStorage()
{
- return PlayerPrefs.GetString(MachineIdentifierKey);
+ var storedMachineId = _sessionStorageDataProvider.GetString(MachineIdentifierKey);
+ // in the previous version of the SDK, the stored machine id could be invalid
+ // to fix the problem, we want to verify if the id is valid and if isn't, fix it.
+ if (string.IsNullOrEmpty(storedMachineId) || Guid.TryParse(storedMachineId, out Guid _))
+ {
+ return storedMachineId;
+ }
+
+ var machineId = GuidHelper.FromString(storedMachineId).ToString();
+ StoreMachineId(machineId);
+ return machineId;
}
///
@@ -64,49 +82,7 @@ private string FetchMachineIdFromStorage()
/// machine identifier
private void StoreMachineId(string machineId)
{
- PlayerPrefs.SetString(MachineIdentifierKey, machineId);
- }
-
- ///
- /// Use Unity device identifier to generate machine identifier
- ///
- /// Unity machine identifier if the device identifier is supported. Otherwise null
- protected virtual string UseUnityIdentifier()
- {
- if (SystemInfo.deviceUniqueIdentifier == SystemInfo.unsupportedIdentifier)
- {
- return null;
- }
- return SystemInfo.deviceUniqueIdentifier;
- }
-
- ///
- /// Use Networking interface to generate machine identifier - MAC number from the networking interface.
- ///
- /// Machine id - MAC in a GUID format. If the networking interface is not available then it returns null.
- 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;
+ _sessionStorageDataProvider.SetString(MachineIdentifierKey, machineId);
}
}
}
diff --git a/Tests/Runtime/Client/BacktraceAttributeMachineIdTests.cs b/Tests/Runtime/Client/BacktraceAttributeMachineIdTests.cs
index db7aac69..e0ffa2fa 100644
--- a/Tests/Runtime/Client/BacktraceAttributeMachineIdTests.cs
+++ b/Tests/Runtime/Client/BacktraceAttributeMachineIdTests.cs
@@ -1,6 +1,6 @@
using Backtrace.Unity.Extensions;
using Backtrace.Unity.Model;
-using Backtrace.Unity.Tests.Runtime.Client.Mocks;
+using Backtrace.Unity.Model.DataProvider;
using NUnit.Framework;
using UnityEngine;
@@ -17,7 +17,7 @@ public void Cleanup()
[Test]
public void TestMachineAttributes_ShouldUseUnityIdentifier_ShouldReturnUnityIdentitfier()
{
- var machineIdStorage = new MachineIdStorageMock();
+ var machineIdStorage = new MachineIdStorage();
var machineId = machineIdStorage.GenerateMachineId();
@@ -27,17 +27,19 @@ public void TestMachineAttributes_ShouldUseUnityIdentifier_ShouldReturnUnityIden
[Test]
public void TestMachineAttributes_ShouldUseMac_ShouldReturnNetowrkingIdentifier()
{
- var machineIdStorage = new MachineIdStorageMock(false);
+ var networkIdentifierDataProvider = new NetworkIdentifierProvider();
+ var expectedMachineId = networkIdentifierDataProvider.Get();
+ var machineIdStorage = new MachineIdStorage(new IMachineIdentifierProvider[] { networkIdentifierDataProvider }, new SessionStorageDataProvider());
var machineId = machineIdStorage.GenerateMachineId();
- Assert.IsFalse(GuidHelper.IsNullOrEmpty(machineId));
+ Assert.IsFalse(GuidHelper.IsNullOrEmpty(expectedMachineId));
}
[Test]
public void TestMachineAttributes_ShouldUseRandomMachineId_ShouldReturnRandomMachineId()
{
- var machineIdStorage = new MachineIdStorageMock(false, false);
+ var machineIdStorage = new MachineIdStorage(new IMachineIdentifierProvider[0], new SessionStorageDataProvider());
var machineId = machineIdStorage.GenerateMachineId();
@@ -45,28 +47,36 @@ public void TestMachineAttributes_ShouldUseRandomMachineId_ShouldReturnRandomMac
}
[Test]
- public void TestMachineAttributes_ShouldAlwaysReturnTheSameValueUnityId_IdentifierAreTheSame()
+ public void TestMachineAttributes_ShouldConvertInvalidIdIntoGuid_ValidIdIsAlwaysUsed()
{
- var firstMachineIdStorage = new MachineIdStorageMock().GenerateMachineId();
- var secGenerationOfMachineIdStorage = new MachineIdStorageMock().GenerateMachineId();
+ var invalidValue = "randomValue";
+ PlayerPrefs.SetString(MachineIdStorage.MachineIdentifierKey, invalidValue);
+ var expectedGuid = GuidHelper.FromString(invalidValue).ToString();
- Assert.IsTrue(firstMachineIdStorage == secGenerationOfMachineIdStorage);
+ var machineId = new MachineIdStorage().GenerateMachineId();
+
+ Assert.IsTrue(expectedGuid == machineId);
}
[Test]
- public void TestMachineAttributes_ShouldAlwaysReturnTheSameValueMacId_IdentifierAreTheSame()
+ public void TestMachineAttributes_ShouldRetrieveValueFromStorage_IdentifierIsStored()
{
- var firstMachineIdStorage = new MachineIdStorageMock(false).GenerateMachineId();
- var secGenerationOfMachineIdStorage = new MachineIdStorageMock(false).GenerateMachineId();
+ // make sure it's always empty
+ PlayerPrefs.DeleteKey(MachineIdStorage.MachineIdentifierKey);
- Assert.IsTrue(firstMachineIdStorage == secGenerationOfMachineIdStorage);
+ var machineId = new MachineIdStorage().GenerateMachineId();
+
+ var storage = new SessionStorageDataProvider();
+ var storedMachineId = storage.GetString(MachineIdStorage.MachineIdentifierKey);
+
+ Assert.IsTrue(machineId == storedMachineId);
}
[Test]
- public void TestMachineAttributes_ShouldAlwaysReturnTheSameValueRandomId_IdentifierAreTheSame()
+ public void TestMachineAttributes_ShouldAlwaysReturnTheSameValueUnityId_IdentifierAreTheSame()
{
- var firstMachineIdStorage = new MachineIdStorageMock(false, false).GenerateMachineId();
- var secGenerationOfMachineIdStorage = new MachineIdStorageMock(false, false).GenerateMachineId();
+ var firstMachineIdStorage = new MachineIdStorage().GenerateMachineId();
+ var secGenerationOfMachineIdStorage = new MachineIdStorage().GenerateMachineId();
Assert.IsTrue(firstMachineIdStorage == secGenerationOfMachineIdStorage);
}
@@ -74,7 +84,7 @@ public void TestMachineAttributes_ShouldAlwaysReturnTheSameValueRandomId_Identif
[Test]
public void TestMachineAttributes_ShouldAlwaysGenerateTheSameUntiyAttribute_ShouldReturnTheSameUnityIdentitfier()
{
- var machineIdStorage = new MachineIdStorageMock();
+ var machineIdStorage = new MachineIdStorage();
var machineId = machineIdStorage.GenerateMachineId();
PlayerPrefs.DeleteKey(MachineIdStorage.MachineIdentifierKey);
@@ -86,7 +96,7 @@ public void TestMachineAttributes_ShouldAlwaysGenerateTheSameUntiyAttribute_Shou
[Test]
public void TestMachineAttributes_ShouldAlwaysGenerateTheSameMacAttribute_ShouldReturnTheSameMacIdentitfier()
{
- var machineIdStorage = new MachineIdStorageMock(false);
+ var machineIdStorage = new MachineIdStorage(new IMachineIdentifierProvider[] { new NetworkIdentifierProvider() }, new SessionStorageDataProvider());
var machineId = machineIdStorage.GenerateMachineId();
PlayerPrefs.DeleteKey(MachineIdStorage.MachineIdentifierKey);
diff --git a/Tests/Runtime/Client/Mocks/MachineIdStorageMock.cs b/Tests/Runtime/Client/Mocks/MachineIdStorageMock.cs
deleted file mode 100644
index 4a5760a4..00000000
--- a/Tests/Runtime/Client/Mocks/MachineIdStorageMock.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-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();
- }
- }
-}
diff --git a/Tests/Runtime/Client/Mocks/MachineIdStorageMock.cs.meta b/Tests/Runtime/Client/Mocks/MachineIdStorageMock.cs.meta
deleted file mode 100644
index fda396ef..00000000
--- a/Tests/Runtime/Client/Mocks/MachineIdStorageMock.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: d5e8f08a85cb6094ab6e27c6a018641e
-MonoImporter:
- externalObjects: {}
- serializedVersion: 2
- defaultReferences: []
- executionOrder: 0
- icon: {instanceID: 0}
- userData:
- assetBundleName:
- assetBundleVariant: