using System.Globalization; using System.Net.Sockets; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; namespace HarmonyPerf.Blog; // Small protocol helpers. The application owns HDC, session recovery, // incremental response parsing, sample recording and UI lifecycle. public static class SmartPerfCommands { public static string CreateToken() => Convert.ToHexString(RandomNumberGenerator.GetBytes(16)).ToLowerInvariant(); public static string Init(string token, bool highPerformance, int intervalMs = 500, string packageName = "") { token = ValidateToken(token); if (intervalMs is < 500 or > 60_000) throw new ArgumentOutOfRangeException(nameof(intervalMs)); var package = packageName.Trim(); if (package.Any(c => !char.IsAsciiLetterOrDigit(c) && c is not '.' and not '_' and not '-')) throw new ArgumentException("Invalid package name.", nameof(packageName)); var app = package.Length == 0 ? "" : $" -PKG {package} -f"; var performance = highPerformance ? " -lockfreq" : ""; return $"init::{token} -SESSIONID 1 -INTERVAL {intervalMs.ToString(CultureInfo.InvariantCulture)}" + $" -c -g -r -d -t -p -net{app}{performance}"; } public static string Start(string token) => $"start::{ValidateToken(token)}"; public static string Keepalive(string token) => $"keepalive::{ValidateToken(token)}"; public static string Stop(string token) => $"stop::{ValidateToken(token)}"; private static string ValidateToken(string token) { ArgumentNullException.ThrowIfNull(token); if (token.Length != 32 || token.Any(c => !Uri.IsHexDigit(c))) throw new ArgumentException("Expected 32 hexadecimal characters.", nameof(token)); return token.ToLowerInvariant(); } // Register an ACK waiter in the receiving loop BEFORE calling this method. // A TCP ReadAsync completion is not a message boundary. public static async Task SendSerializedAsync(NetworkStream stream, SemaphoreSlim writeGate, string command, CancellationToken cancellationToken) { using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5)); using var linked = CancellationTokenSource.CreateLinkedTokenSource( cancellationToken, timeout.Token); var entered = false; try { await writeGate.WaitAsync(linked.Token).ConfigureAwait(false); entered = true; // No newline is added by this daemon's command protocol. await stream.WriteAsync(Encoding.UTF8.GetBytes(command), linked.Token).ConfigureAwait(false); } catch (OperationCanceledException) when (timeout.IsCancellationRequested && !cancellationToken.IsCancellationRequested) { throw new TimeoutException("SmartPerf command write timed out."); } finally { if (entered) writeGate.Release(); } } } public sealed record ScreenState(int BaseTimeoutMs, int? OverrideTimeoutMs, string? PowerState) { public bool IsAwake => PowerState == "AWAKE"; } public static class ScreenCommands { public const int FiniteTimeoutMs = 7_200_000; public const string Read = "hidumper -s PowerManagerService -a '-s'"; public const string Wakeup = "power-shell wakeup"; private static readonly Regex TimeoutPattern = new( @"^[ \t]*ScreenOffTime:[ \t]*Timeout=(?-?\d+)ms(?:[ \t]+OverrideTimeout=(?-?\d+)ms)?[ \t]*\r?$", RegexOptions.Multiline | RegexOptions.CultureInvariant); private static readonly Regex StatePattern = new( @"^[ \t]*Current State:[ \t]*(?[A-Z_]+)(?:[ \t]+Reason:.*)?[ \t]*\r?$", RegexOptions.Multiline | RegexOptions.CultureInvariant); public static ScreenState Parse(string dump) { var matches = TimeoutPattern.Matches(dump); if (matches.Count != 1) throw new FormatException("Expected one screen timeout record."); var baseline = ParseTimeout(matches[0].Groups["base"].Value); var temporary = matches[0].Groups["override"]; int? overridden = temporary.Success ? ParseTimeout(temporary.Value) : null; var states = StatePattern.Matches(dump); if (states.Count > 1) throw new FormatException("Ambiguous power state."); return new(baseline, overridden, states.Count == 1 ? states[0].Groups["state"].Value : null); } public static string Override(int milliseconds) { if (milliseconds < -1) throw new ArgumentOutOfRangeException(nameof(milliseconds)); return $"power-shell timeout -o {milliseconds.ToString(CultureInfo.InvariantCulture)}"; } // Persist original BEFORE any override. Before restore, cancel and join renewals. public static string Restore(ScreenState original) { if (original.BaseTimeoutMs < -1 || original.OverrideTimeoutMs is < -1) throw new ArgumentOutOfRangeException(nameof(original)); return original.OverrideTimeoutMs is int prior ? Override(prior) : "power-shell timeout -r"; } public static bool InfiniteExplicitlyRejected(string output) => output.Trim() == "Override screen off time to -1 failed"; public static void RequireResponse(string output, string expected) { if (!string.Equals(output.Trim(), expected, StringComparison.Ordinal)) throw new InvalidOperationException("Device did not confirm the requested command."); } // A sleeping screen after a user's power-button action may still have a valid override. public static bool SameTimeouts(ScreenState actual, ScreenState expected) => actual.BaseTimeoutMs == expected.BaseTimeoutMs && actual.OverrideTimeoutMs == expected.OverrideTimeoutMs; private static int ParseTimeout(string text) { if (!int.TryParse(text, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out var value) || value < -1) throw new FormatException("Invalid screen timeout value."); return value; } }