Reduce amount of data being sent via auto-update

This commit is contained in:
Kamil Trzciński 2022-12-19 09:10:56 +01:00
parent a979c29906
commit 72564c3145
8 changed files with 250 additions and 72 deletions

View file

@ -178,12 +178,6 @@ namespace CommonHelpers
public static void WithSentry(Action action, string? dsn = null)
{
if (File.Exists("DisableCheckForUpdates.txt"))
{
action();
return;
}
// Overwrite DSN
if (dsn != null)
{
@ -201,35 +195,6 @@ namespace CommonHelpers
get { return Assembly.GetEntryAssembly()?.GetName().Name ?? "unknown"; }
}
public static String ID
{
get
{
#if PRODUCTION_BUILD
return "";
#else
try
{
using (var registryKey = Registry.CurrentUser.CreateSubKey(@"Software\SteamDeckTools", true))
{
var machineID = registryKey?.GetValue("MachineID") as string;
if (machineID is null)
{
registryKey?.SetValue("MachineID", Guid.NewGuid().ToString());
machineID = registryKey?.GetValue("MachineID") as string;
}
return machineID ?? "undefined";
}
}
catch (Exception)
{
return "exception";
}
#endif
}
}
public static String ProductVersion
{
get => Application.ProductVersion;
@ -244,6 +209,22 @@ namespace CommonHelpers
}
}
public static bool HasFile(String name)
{
var currentProcess = Process.GetCurrentProcess();
var currentDir = Path.GetDirectoryName(currentProcess.MainModule?.FileName);
if (currentDir is null)
return false;
var uninstallExe = Path.Combine(currentDir, name);
return File.Exists(uninstallExe);
}
public static bool AcceptedTerms
{
get { return HasFile("Uninstall.exe"); }
}
private static System.Timers.Timer? updateTimer;
public static void RunUpdater(string Title, bool user = false, int recheckIntervalHours = 24)

View file

@ -7,7 +7,7 @@ namespace CommonHelpers
public static class Log
{
#if PRODUCTION_BUILD
internal static String SENTRY_DSN = null; // "https://3c93e3c3b47b40ffba72d9cb333fc6d7@o4504334913830912.ingest.sentry.io/4504334914879488";
internal static String SENTRY_DSN = "https://3c93e3c3b47b40ffba72d9cb333fc6d7@o4504334913830912.ingest.sentry.io/4504334914879488";
#else
internal static String SENTRY_DSN = "https://d9204614b2cd47468bfa1ea2ab55da4e@o4504334914355200.ingest.sentry.io/4504334915469312";
#endif
@ -23,7 +23,7 @@ namespace CommonHelpers
{
var env = Instance.IsProductionBuild ? "prod" : "dev";
var build = Instance.IsDEBUG ? "debug" : "release";
var deploy = File.Exists("Uninstaller.exe") ? "setup" : "zip";
var deploy = File.Exists("Uninstall.exe") ? "setup" : "zip";
o.BeforeSend += Sentry_BeforeSend;
o.Dsn = Log.SENTRY_DSN;
@ -31,7 +31,6 @@ namespace CommonHelpers
o.IsGlobalModeEnabled = true;
o.Environment = String.Format("{0}:{1}_{2}", Instance.ApplicationName, build, deploy);
o.DefaultTags.Add("App", Instance.ApplicationName);
o.DefaultTags.Add("ID", Instance.ID);
o.DefaultTags.Add("Build", build);
o.DefaultTags.Add("Deploy", deploy);
@ -46,6 +45,11 @@ namespace CommonHelpers
private static SentryEvent? Sentry_BeforeSend(SentryEvent arg)
{
if (Instance.HasFile("DisableCheckForUpdates.txt") || Instance.HasFile("DisableSentryTracking.txt"))
return null;
if (!Instance.AcceptedTerms)
return null;
if (LogFileFolder == null)
{
var documentsFolder = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

View file

@ -0,0 +1,116 @@
using System;
using System.Runtime.InteropServices;
using System.Security.AccessControl;
using System.Text;
using Microsoft.Win32;
using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
namespace ExternalHelpers
{
// Taken from: https://stackoverflow.com/a/45548823
public static class RegistryUtils
{
[DllImport("advapi32.dll", EntryPoint = "RegQueryInfoKey", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
private static extern int RegQueryInfoKey(
UIntPtr hkey,
out StringBuilder lpClass,
ref uint lpcbClass,
IntPtr lpReserved,
out uint lpcSubKeys,
out uint lpcbMaxSubKeyLen,
out uint lpcbMaxClassLen,
out uint lpcValues,
out uint lpcbMaxValueNameLen,
out uint lpcbMaxValueLen,
out uint lpcbSecurityDescriptor,
ref FILETIME lpftLastWriteTime);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern int RegCloseKey(UIntPtr hKey);
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
private static extern int RegOpenKeyEx(
UIntPtr hKey,
string subKey,
int ulOptions,
int samDesired,
out UIntPtr hkResult);
private static DateTimeOffset ToDateTime(FILETIME ft)
{
IntPtr buf = IntPtr.Zero;
try
{
long[] longArray = new long[1];
int cb = Marshal.SizeOf(ft);
buf = Marshal.AllocHGlobal(cb);
Marshal.StructureToPtr(ft, buf, false);
Marshal.Copy(buf, longArray, 0, 1);
return DateTimeOffset.FromFileTime(longArray[0]);
}
finally
{
if (buf != IntPtr.Zero) Marshal.FreeHGlobal(buf);
}
}
public static DateTimeOffset? GetDateModified(RegistryHive registryHive, string path)
{
var lastModified = new FILETIME();
var lpcbClass = new uint();
var lpReserved = new IntPtr();
UIntPtr key = UIntPtr.Zero;
try
{
try
{
var hive = new UIntPtr(unchecked((uint)registryHive));
if (RegOpenKeyEx(hive, path, 0, (int)RegistryRights.ReadKey, out key) != 0)
{
return null;
}
uint lpcbSubKeys;
uint lpcbMaxKeyLen;
uint lpcbMaxClassLen;
uint lpcValues;
uint maxValueName;
uint maxValueLen;
uint securityDescriptor;
StringBuilder sb;
if (RegQueryInfoKey(
key,
out sb,
ref lpcbClass,
lpReserved,
out lpcbSubKeys,
out lpcbMaxKeyLen,
out lpcbMaxClassLen,
out lpcValues,
out maxValueName,
out maxValueLen,
out securityDescriptor,
ref lastModified) != 0)
{
return null;
}
var result = ToDateTime(lastModified);
return result;
}
finally
{
if (key != UIntPtr.Zero)
RegCloseKey(key);
}
}
catch (Exception)
{
return null;
}
}
}
}

View file

@ -3,6 +3,13 @@ https://creativecommons.org/licenses/by-nc/4.0/
By running this project you agree to the following licenses and policies:
This project might connect to remote server and share the following information unless it is disabled
by creation of the `DisableCheckForUpdates.txt` in the root folder of the project:
- During updates checking it might share version, type of installation and installation time of the application
- Exceptions raised by the application might be logged using sentry.io: this includes OS version, exception message,
application version, stack trace and loaded libraries
This project requires the following dependencies:
- Microsoft Visual C++ Redistributable - https://aka.ms/vs/17/release/vc_redist.x64.exe
@ -10,13 +17,6 @@ This project requires the following dependencies:
- Rivatuner Statistics Server - https://www.guru3d.com/files-details/rtss-rivatuner-statistics-server-download.html
- ViGEmBus - https://github.com/ViGEm/ViGEmBus/releases
This project might connect to remote server and share the following information unless it is disabled
by creation of the `DisableCheckForUpdates.txt` in the root folder of the project:
- During updates checking it might share version, type of installation and unique identifier of installation for statistical purposes
- Exceptions raised by the application might be logged using sentry.io: this includes OS version, exception message,
application version, stack trace, loaded libraries, unique identifier of installation to correlate distinct exceptions.
This project uses the following components:
- https://github.com/mKenfenheuer/hidapi.net/
@ -27,3 +27,4 @@ This project uses the following components:
- Copyright (C) LibreHardwareMonitor and Contributors.
- http://stackoverflow.com/questions/14306048/controling-volume-mixer
- https://stackoverflow.com/questions/22903429/c-sharp-not-supported
- https://stackoverflow.com/a/45548823

View file

@ -4,6 +4,8 @@ using System.Reflection;
using System.Web;
using AutoUpdaterDotNET;
using CommonHelpers;
using ExternalHelpers;
using Microsoft.Win32;
namespace Updater
{
@ -53,7 +55,7 @@ namespace Updater
Instance.RunOnce(null, "Global\\SteamDeckToolsAutoUpdater");
if (File.Exists("DisableCheckForUpdates.txt"))
if (Instance.HasFile("DisableCheckForUpdates.txt"))
{
if (userCheck || cmdLine)
{
@ -79,7 +81,7 @@ namespace Updater
AutoUpdater.LetUserSelectRemindLater = true;
AutoUpdater.ShowRemindLaterButton = true;
AutoUpdater.HttpUserAgent = String.Format("AutoUpdater/{0}/{1}/{2}",
Instance.ID,
InstallationTime,
Instance.ProductVersionWithSha,
Instance.IsProductionBuild ? "prod" : "dev");
AutoUpdater.PersistenceProvider = persistence;
@ -102,14 +104,34 @@ namespace Updater
TrackProcess("PerformanceOverlay", usedTools);
TrackProcess("SteamController", usedTools);
var todayMatch = DateTimeOffset.UtcNow.DayOfYear;
var runToday = Settings.Default.GetRunTimes("Today", todayMatch) + 1;
var thisWeekMatch = DateTimeOffset.UtcNow.DayOfYear / 7;
var runThisWeek = Settings.Default.GetRunTimes("ThisWeek", thisWeekMatch) + 1;
AutoUpdater.ParseUpdateInfoEvent += delegate
{
Settings.Default.SetRunTimes("Today", todayMatch, runToday);
Settings.Default.SetRunTimes("ThisWeek", thisWeekMatch, runThisWeek);
};
// This method requests an auto-update from remote server. It includes the following information:
// Type of installation: prod/dev, release/debug, setup/zip
// Version of application: 0.5.40+12345cdef
// Installation time: when the application was installed to track the age
// Used Tools: which application of suite are running, like: FanControl,PerformanceOverlay
// Updates Today/ThisWeek: amount of times update run today and this week
var updateURL = String.Format(
"https://steam-deck-tools.ayufan.dev/docs/updates/{0}_{1}.xml?version={2}&id={3}&env={4}&apps={5}",
"https://steam-deck-tools.ayufan.dev/updates/{4}_{0}_{1}.xml?version={2}&installTime={3}&env={4}&apps={5}&updatesToday={6}&updatesThisWeek={7}",
Instance.IsDEBUG ? "debug" : "release",
IsUsingInstaller ? "setup" : "zip",
HttpUtility.UrlEncode(Instance.ProductVersionWithSha),
HttpUtility.UrlEncode(Instance.ID),
InstallationTime,
Instance.IsProductionBuild ? "prod" : "dev",
HttpUtility.UrlEncode(String.Join(",", usedTools))
HttpUtility.UrlEncode(String.Join(",", usedTools)),
runToday,
runThisWeek
);
AutoUpdater.Start(updateURL);
@ -186,6 +208,41 @@ namespace Updater
}
}
public static string InstallationTime
{
get
{
try
{
using (var registryKey = Registry.CurrentUser.CreateSubKey(@"Software\SteamDeckTools", true))
{
var installationTime = registryKey?.GetValue("InstallationTime") as string;
if (installationTime is null)
{
var previousTime = RegistryUtils.GetDateModified(
RegistryHive.CurrentUser, @"Software\SteamDeckTools");
Log.TraceLine("PreviousTime: {0}", previousTime);
previousTime ??= DateTimeOffset.UtcNow;
registryKey?.SetValue("InstallationTime", previousTime.Value.ToUnixTimeMilliseconds());
installationTime = registryKey?.GetValue("InstallationTime") as string;
}
if (!Instance.AcceptedTerms)
{
return "";
}
return installationTime ?? "";
}
}
catch (Exception e)
{
return "";
}
}
}
private static IEnumerable<Process> FindProcesses(String processFilerName)
{
var currentProcess = Process.GetCurrentProcess();

26
Updater/Settings.cs Normal file
View file

@ -0,0 +1,26 @@
using CommonHelpers;
namespace Updater
{
internal sealed class Settings : BaseSettings
{
public static readonly Settings Default = new Settings();
public Settings() : base("Settings")
{
}
public int GetRunTimes(string key, Int64 match)
{
if (Get<Int64>(key + "Match", 0) != match)
return 0;
return Get<int>(key + "Counter", 0);
}
public void SetRunTimes(string key, Int64 match, int value)
{
Set(key + "Match", match);
Set(key + "Counter", value);
}
}
}

View file

@ -16,6 +16,7 @@
<ItemGroup>
<ProjectReference Include="..\CommonHelpers\CommonHelpers.csproj" />
<ProjectReference Include="..\ExternalHelpers\ExternalHelpers.csproj" />
</ItemGroup>
<ItemGroup>

View file

@ -1,36 +1,28 @@
# Privacy
This project might connect to remote server and share the following information unless it is disabled
by creation of the `DisableCheckForUpdates.txt` in the root folder of the project:
## Error Tracking
To aid the application development this project uses [Sentry.io](https://sentry.io/)
to track all exceptions raised by the application. It is essential to identify bugs
and fix them with minimal user intervention superseeding the need to user
[troubleshooting](troubleshooting.md).
[troubleshooting](troubleshooting.md). The Sentry is configured to not track PII.
As part of Sentry error tracking the following information is being sent and is logged
including, but not only:
- Windows Version
- .NET Framework Version
- Exception Stack Trace
- Application Version
- Type of installation
- Installation ID
Additionally for statistic purposes the installation ID, application version
and which SteamDeckTools apps are used might be tracked as part of Update
process to see active user-base vs version used.
The installation ID is one time generated GUID that is persisted on the first start
of an application.
Application for auto-update purposes checks for latest release on a start,
or every 24 hours.
All information being sent can be seen in a publically available source code
of this application.
including: Windows Version, .NET Framework Version, Exception Stack Trace, Application Version,
Type of installation.
You can see exact exceptions being sent in `Documents/SteamDeckTools/Logs`
- if it is empty it means nothing was sent.
## Auto-update
Application for auto-update purposes checks for latest release on a start,
or every 24 hours. As part of auto-update it sends information about installation
time of the application, application version, which SteamDeckTools apps are used.
## Disable it
Create `DisableCheckForUpdates.txt` file. To validate that this is working,