From ddafe42a62cf1b9f76fd0e87aa953f5680dafb12 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Mon, 11 Dec 2023 18:03:27 +0800 Subject: [PATCH 01/10] Use PolySharp to generate attributes --- .../AdvancedSharpAdbClient.csproj | 22 +++ .../CallerArgumentExpressionAttribute.cs | 28 --- .../Attributes/CollectionBuilderAttribute.cs | 38 ---- .../ExcludeFromCodeCoverageAttribute.cs | 2 +- .../Polyfills/Attributes/IsExternalInit.cs | 18 -- .../Attributes/NullableAttributes.cs | 180 ------------------ .../Attributes/StackTraceHiddenAttribute.cs | 20 -- 7 files changed, 23 insertions(+), 285 deletions(-) delete mode 100644 AdvancedSharpAdbClient/Polyfills/Attributes/CallerArgumentExpressionAttribute.cs delete mode 100644 AdvancedSharpAdbClient/Polyfills/Attributes/CollectionBuilderAttribute.cs delete mode 100644 AdvancedSharpAdbClient/Polyfills/Attributes/IsExternalInit.cs delete mode 100644 AdvancedSharpAdbClient/Polyfills/Attributes/NullableAttributes.cs delete mode 100644 AdvancedSharpAdbClient/Polyfills/Attributes/StackTraceHiddenAttribute.cs diff --git a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj index 29d6c29d..0b79601f 100644 --- a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj +++ b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj @@ -30,6 +30,23 @@ + + + System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute; + System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute; + System.Diagnostics.CodeAnalysis.MemberNotNullAttribute; + System.Diagnostics.CodeAnalysis.MaybeNullAttribute; + System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute; + System.Diagnostics.CodeAnalysis.NotNullAttribute; + System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute; + System.Diagnostics.CodeAnalysis.NotNullWhenAttribute; + System.Diagnostics.StackTraceHiddenAttribute; + System.Index; + System.Runtime.CompilerServices.CallerArgumentExpressionAttribute; + System.Runtime.CompilerServices.IsExternalInit; + + + 10.0 @@ -46,6 +63,10 @@ + + + + @@ -134,6 +155,7 @@ or '$(TargetFramework)' == 'netcoreapp3.1' or '$(TargetFramework)' == 'netstandard2.1'"> $(DefineConstants);HAS_BUFFERS;HAS_INDEXRANGE + $(PolySharpIncludeGeneratedTypes);System.Runtime.CompilerServices.CollectionBuilderAttribute class. - /// - /// The name of the parameter whose expression should be captured as a string. - public CallerArgumentExpressionAttribute(string parameterName) - { - ParameterName = parameterName; - } - - /// - /// The name of the parameter whose expression should be captured. - /// - public string ParameterName { get; } - } -} -#endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Polyfills/Attributes/CollectionBuilderAttribute.cs b/AdvancedSharpAdbClient/Polyfills/Attributes/CollectionBuilderAttribute.cs deleted file mode 100644 index d61470c1..00000000 --- a/AdvancedSharpAdbClient/Polyfills/Attributes/CollectionBuilderAttribute.cs +++ /dev/null @@ -1,38 +0,0 @@ -#if HAS_BUFFERS && !NET8_0_OR_GREATER -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Runtime.CompilerServices -{ - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, Inherited = false)] - internal sealed class CollectionBuilderAttribute : Attribute - { - /// - /// Initialize the attribute to refer to the method on the type. - /// - /// The type of the builder to use to construct the collection. - /// The name of the method on the builder to use to construct the collection. - /// - /// must refer to a static method that accepts a single parameter of - /// type and returns an instance of the collection being built containing - /// a copy of the data from that span. In future releases of .NET, additional patterns may be supported. - /// - public CollectionBuilderAttribute(Type builderType, string methodName) - { - BuilderType = builderType; - MethodName = methodName; - } - - /// - /// Gets the type of the builder to use to construct the collection. - /// - public Type BuilderType { get; } - - /// - /// Gets the name of the method on the builder to use to construct the collection. - /// - /// This should match the metadata name of the target method. For example, this might be ".ctor" if targeting the type's constructor. - public string MethodName { get; } - } -} -#endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Polyfills/Attributes/ExcludeFromCodeCoverageAttribute.cs b/AdvancedSharpAdbClient/Polyfills/Attributes/ExcludeFromCodeCoverageAttribute.cs index 2ac26093..16ccea03 100644 --- a/AdvancedSharpAdbClient/Polyfills/Attributes/ExcludeFromCodeCoverageAttribute.cs +++ b/AdvancedSharpAdbClient/Polyfills/Attributes/ExcludeFromCodeCoverageAttribute.cs @@ -1,4 +1,4 @@ -#if NET35 +#if NETFRAMEWORK && !NET40_OR_GREATER // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. diff --git a/AdvancedSharpAdbClient/Polyfills/Attributes/IsExternalInit.cs b/AdvancedSharpAdbClient/Polyfills/Attributes/IsExternalInit.cs deleted file mode 100644 index 7f988bb8..00000000 --- a/AdvancedSharpAdbClient/Polyfills/Attributes/IsExternalInit.cs +++ /dev/null @@ -1,18 +0,0 @@ -#if !NET -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.ComponentModel; - -namespace System.Runtime.CompilerServices -{ - /// - /// Reserved to be used by the compiler for tracking metadata. - /// This class should not be used by developers in source code. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - internal static class IsExternalInit - { - } -} -#endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Polyfills/Attributes/NullableAttributes.cs b/AdvancedSharpAdbClient/Polyfills/Attributes/NullableAttributes.cs deleted file mode 100644 index adc36a56..00000000 --- a/AdvancedSharpAdbClient/Polyfills/Attributes/NullableAttributes.cs +++ /dev/null @@ -1,180 +0,0 @@ -#if !NET5_0_OR_GREATER -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Diagnostics.CodeAnalysis -{ -#if !NETCOREAPP3_0_OR_GREATER && !NETSTANDARD2_1_OR_GREATER - /// - /// Specifies that null is allowed as an input even if the corresponding type disallows it. - /// - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] - internal sealed class AllowNullAttribute : Attribute - { } - - /// - /// Specifies that null is disallowed as an input even if the corresponding type allows it. - /// - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] - internal sealed class DisallowNullAttribute : Attribute - { } - - /// - /// Specifies that an output may be null even if the corresponding type disallows it. - /// - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] - internal sealed class MaybeNullAttribute : Attribute - { } - - /// - /// Specifies that an output will not be null even if the corresponding type allows it. Specifies that an input argument was not null when the call returns. - /// - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] - internal sealed class NotNullAttribute : Attribute - { } - - /// - /// Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it. - /// - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - internal sealed class MaybeNullWhenAttribute : Attribute - { - /// - /// Initializes the attribute with the specified return value condition. - /// - /// The return value condition. If the method returns this value, the associated parameter may be null. - public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; - - /// - /// Gets the return value condition. - /// - public bool ReturnValue { get; } - } - - /// - /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. - /// - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - internal sealed class NotNullWhenAttribute : Attribute - { - /// - /// Initializes the attribute with the specified return value condition. - /// - /// The return value condition. If the method returns this value, the associated parameter will not be null. - public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; - - /// - /// Gets the return value condition. - /// - public bool ReturnValue { get; } - } - - /// - /// Specifies that the output will be non-null if the named parameter is non-null. - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] - internal sealed class NotNullIfNotNullAttribute : Attribute - { - /// - /// Initializes the attribute with the associated parameter name. - /// - /// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null. - public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName; - - /// - /// Gets the associated parameter name. - /// - public string ParameterName { get; } - } - - /// - /// Applied to a method that will never return under any circumstance. - /// - [AttributeUsage(AttributeTargets.Method, Inherited = false)] - internal sealed class DoesNotReturnAttribute : Attribute - { } - - /// - /// Specifies that the method will not return if the associated Boolean parameter is passed the specified value. - /// - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - internal sealed class DoesNotReturnIfAttribute : Attribute - { - /// - /// Initializes the attribute with the specified parameter value. - /// - /// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to - /// the associated parameter matches this value. - public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue; - - /// - /// Gets the condition parameter value. - /// - public bool ParameterValue { get; } - } -#endif - - /// - /// Specifies that the method or property will ensure that the listed field and property members have not-null values. - /// - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] - internal sealed class MemberNotNullAttribute : Attribute - { - /// - /// Initializes the attribute with a field or property member. - /// - /// The field or property member that is promised to be not-null. - public MemberNotNullAttribute(string member) => Members = [member]; - - /// - /// Initializes the attribute with the list of field and property members. - /// - /// The list of field and property members that are promised to be not-null. - public MemberNotNullAttribute(params string[] members) => Members = members; - - /// - /// Gets field or property member names. - /// - public string[] Members { get; } - } - - /// - /// Specifies that the method or property will ensure that the listed field and property members have not-null values when returning with the specified return value condition. - /// - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] - internal sealed class MemberNotNullWhenAttribute : Attribute - { - /// - /// Initializes the attribute with the specified return value condition and a field or property member. - /// - /// The return value condition. If the method returns this value, the associated parameter will not be null. - /// The field or property member that is promised to be not-null. - public MemberNotNullWhenAttribute(bool returnValue, string member) - { - ReturnValue = returnValue; - Members = [member]; - } - - /// - /// Initializes the attribute with the specified return value condition and list of field and property members. - /// - /// The return value condition. If the method returns this value, the associated parameter will not be null. - /// The list of field and property members that are promised to be not-null. - public MemberNotNullWhenAttribute(bool returnValue, params string[] members) - { - ReturnValue = returnValue; - Members = members; - } - - /// - /// Gets the return value condition. - /// - public bool ReturnValue { get; } - - /// - /// Gets field or property member names. - /// - public string[] Members { get; } - } -} -#endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Polyfills/Attributes/StackTraceHiddenAttribute.cs b/AdvancedSharpAdbClient/Polyfills/Attributes/StackTraceHiddenAttribute.cs deleted file mode 100644 index 232d640e..00000000 --- a/AdvancedSharpAdbClient/Polyfills/Attributes/StackTraceHiddenAttribute.cs +++ /dev/null @@ -1,20 +0,0 @@ -#if !NET6_0_OR_GREATER -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Diagnostics -{ - /// - /// Types and Methods attributed with StackTraceHidden will be omitted from the stack trace text shown in StackTrace.ToString() - /// and Exception.StackTrace. - /// - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Struct, Inherited = false)] - internal sealed class StackTraceHiddenAttribute : Attribute - { - /// - /// Initializes a new instance of the class. - /// - public StackTraceHiddenAttribute() { } - } -} -#endif \ No newline at end of file From 157539a4215cd688cb7507224b2244bbeeaa4bce Mon Sep 17 00:00:00 2001 From: wherewhere Date: Mon, 11 Dec 2023 18:25:36 +0800 Subject: [PATCH 02/10] Generate Index and Range --- .../AdvancedSharpAdbClient.csproj | 15 +++++++++- .../DeviceCommands/LinuxPath.cs | 20 ++++++------- .../DeviceCommands/Models/AndroidProcess.cs | 28 +++++++++---------- .../Receivers/PackageManagerReceiver.cs | 15 +++++++--- .../Receivers/VersionInfoReceiver.cs | 4 ++- 5 files changed, 50 insertions(+), 32 deletions(-) diff --git a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj index 0b79601f..857e2093 100644 --- a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj +++ b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj @@ -148,13 +148,26 @@ $(DefineConstants);HAS_RUNTIMEINFORMATION + + $(DefineConstants);HAS_RANGE + $(PolySharpIncludeGeneratedTypes);System.Range + + - $(DefineConstants);HAS_BUFFERS;HAS_INDEXRANGE + $(DefineConstants);HAS_BUFFERS $(PolySharpIncludeGeneratedTypes);System.Runtime.CompilerServices.CollectionBuilderAttribute diff --git a/AdvancedSharpAdbClient/DeviceCommands/LinuxPath.cs b/AdvancedSharpAdbClient/DeviceCommands/LinuxPath.cs index 4a6a0066..8ed1ae2e 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/LinuxPath.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/LinuxPath.cs @@ -58,11 +58,7 @@ public static string Combine(params string[] paths) { capacity += paths[i].Length; } -#if HAS_INDEXRANGE char ch = paths[i][^1]; -#else - char ch = paths[i][paths[i].Length - 1]; -#endif if (ch != DirectorySeparatorChar) { capacity++; @@ -81,16 +77,11 @@ public static string Combine(params string[] paths) } else { -#if HAS_INDEXRANGE char ch2 = builder[^1]; -#else - char ch2 = builder[builder.Length - 1]; -#endif if (ch2 != DirectorySeparatorChar) { _ = builder.Append(DirectorySeparatorChar); } - _ = builder.Append(paths[j]); } } @@ -125,11 +116,16 @@ public static string Combine(params string[] paths) { return tpath; } -#if HAS_INDEXRANGE - tpath = tpath[..(tpath.LastIndexOf(DirectorySeparatorChar) + 1)]; + + tpath = +#if HAS_BUFFERS + tpath.AsSpan(0, tpath.LastIndexOf(DirectorySeparatorChar) + 1).ToString(); +#elif HAS_RANGE + tpath[..(tpath.LastIndexOf(DirectorySeparatorChar) + 1)]; #else - tpath = tpath.Substring(0, tpath.LastIndexOf(DirectorySeparatorChar) + 1); + tpath.Substring(0, tpath.LastIndexOf(DirectorySeparatorChar) + 1); #endif + return FixupPath(tpath); } else if (tpath.Length == 1) diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs index 107c105b..84a3c1b8 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs @@ -43,11 +43,11 @@ public AndroidProcess(string line, bool cmdLinePrefix = false) if (cmdLinePrefix) { -#if HAS_INDEXRANGE - string[] cmdLineParts = line[..processNameStart] + string[] cmdLineParts = +#if HAS_RANGE + line[..processNameStart] #else - - string[] cmdLineParts = line.Substring(0, processNameStart) + line.Substring(0, processNameStart) #endif .Split('\0'); @@ -57,11 +57,7 @@ public AndroidProcess(string line, bool cmdLinePrefix = false) } else { -#if HAS_INDEXRANGE pid = int.Parse(cmdLineParts[^1]); -#else - pid = int.Parse(cmdLineParts[cmdLineParts.Length - 1]); -#endif ProcessId = pid; comm = cmdLineParts[0]; @@ -74,10 +70,13 @@ public AndroidProcess(string line, bool cmdLinePrefix = false) if (!parsedCmdLinePrefix) { -#if HAS_INDEXRANGE - pid = int.Parse(line[..processNameStart]); + pid = +#if HAS_BUFFERS + int.Parse(line.AsSpan(0, processNameStart)); +#elif HAS_RANGE + int.Parse(line[..processNameStart]); #else - pid = int.Parse(line.Substring(0, processNameStart)); + int.Parse(line.Substring(0, processNameStart)); #endif ProcessId = pid; @@ -85,10 +84,11 @@ public AndroidProcess(string line, bool cmdLinePrefix = false) Name = comm; } -#if HAS_INDEXRANGE - string[] parts = line[(processNameEnd + 1)..] + string[] parts = +#if HAS_RANGE + line[(processNameEnd + 1)..] #else - string[] parts = line.Substring(processNameEnd + 1) + line.Substring(processNameEnd + 1) #endif .Split(' ', StringSplitOptions.RemoveEmptyEntries); diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/PackageManagerReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/PackageManagerReceiver.cs index 0a4308d0..143e4805 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/PackageManagerReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/PackageManagerReceiver.cs @@ -2,6 +2,7 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // +using System; using System.Collections.Generic; namespace AdvancedSharpAdbClient.DeviceCommands.Receivers @@ -36,10 +37,13 @@ protected override void ProcessNewLines(IEnumerable lines) // package:mwc2015.be // Remove the "package:" prefix -#if HAS_INDEXRANGE - string package = line[8..]; + string package = +#if HAS_BUFFERS + line.AsSpan(8).ToString(); +#elif HAS_RANGE + line[8..]; #else - string package = line.Substring(8); + line.Substring(8); #endif //// If there's a '=' included, use the last instance, //// to accommodate for values like @@ -54,7 +58,10 @@ protected override void ProcessNewLines(IEnumerable lines) } else { -#if HAS_INDEXRANGE +#if HAS_BUFFERS + string path = package.AsSpan(0, separator).ToString(); + string name = package.AsSpan(separator + 1).ToString(); +#elif HAS_RANGE string path = package[..separator]; string name = package[(separator + 1)..]; #else diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/VersionInfoReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/VersionInfoReceiver.cs index 91481939..bbe71dd7 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/VersionInfoReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/VersionInfoReceiver.cs @@ -85,7 +85,9 @@ private void CheckPackagesSection(string line) return !inPackagesSection ? null : line != null && line.Trim().StartsWith("versionName=") -#if HAS_INDEXRANGE +#if HAS_BUFFERS + ? line.Trim().AsSpan(12).ToString().Trim() : null; +#elif HAS_RANGE ? line.Trim()[12..].Trim() : null; #else ? line.Trim().Substring(12).Trim() : null; From f9b94f3eca5c6415dc84885606befa8810070f09 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Mon, 11 Dec 2023 19:44:17 +0800 Subject: [PATCH 03/10] Remove GetOffsetAndLength on non ValueTuple platform --- .../AdbCommandLineClient.Async.cs | 2 +- .../AdbCommandLineClient.cs | 1 - .../AdvancedSharpAdbClient.csproj | 26 ++--- .../DeviceCommands/LinuxPath.cs | 4 +- .../DeviceCommands/Models/AndroidProcess.cs | 18 +-- .../Receivers/PackageManagerReceiver.cs | 9 +- .../Receivers/VersionInfoReceiver.cs | 4 +- .../Extensions/Factories.cs | 1 - .../Interfaces/IAdbClient.cs | 2 +- .../ExcludeFromCodeCoverageAttribute.cs | 25 ---- AdvancedSharpAdbClient/Polyfills/Range.cs | 107 ++++++++++++++++++ 11 files changed, 131 insertions(+), 68 deletions(-) delete mode 100644 AdvancedSharpAdbClient/Polyfills/Attributes/ExcludeFromCodeCoverageAttribute.cs create mode 100644 AdvancedSharpAdbClient/Polyfills/Range.cs diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs index 11edbf1d..83ef8015 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs @@ -33,7 +33,7 @@ public virtual async Task GetVersionAsync(CancellationToken cancellatio return version; } - + /// public virtual async Task StartServerAsync(CancellationToken cancellationToken = default) { diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.cs index 293d4012..38f968a4 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.cs @@ -9,7 +9,6 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text.RegularExpressions; -using System.Threading; namespace AdvancedSharpAdbClient { diff --git a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj index 857e2093..63cded79 100644 --- a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj +++ b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj @@ -42,6 +42,7 @@ System.Diagnostics.CodeAnalysis.NotNullWhenAttribute; System.Diagnostics.StackTraceHiddenAttribute; System.Index; + System.Range; System.Runtime.CompilerServices.CallerArgumentExpressionAttribute; System.Runtime.CompilerServices.IsExternalInit; @@ -126,6 +127,18 @@ $(DefineConstants);HAS_IMAGING + + $(DefineConstants);HAS_VALUETUPLE + + - $(DefineConstants);HAS_RANGE - $(PolySharpIncludeGeneratedTypes);System.Range - - A callback which will receive the event log messages as they are received. /// Optionally, the names of the logs to receive. void RunLogService(DeviceData device, Action messageSink, params LogId[] logNames); - + /// /// Reboots the specified device in to the specified mode. /// diff --git a/AdvancedSharpAdbClient/Polyfills/Attributes/ExcludeFromCodeCoverageAttribute.cs b/AdvancedSharpAdbClient/Polyfills/Attributes/ExcludeFromCodeCoverageAttribute.cs deleted file mode 100644 index 16ccea03..00000000 --- a/AdvancedSharpAdbClient/Polyfills/Attributes/ExcludeFromCodeCoverageAttribute.cs +++ /dev/null @@ -1,25 +0,0 @@ -#if NETFRAMEWORK && !NET40_OR_GREATER -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Diagnostics.CodeAnalysis -{ - /// - /// Specifies that the attributed code should be excluded from code coverage - /// collection.  Placing this attribute on a class/struct excludes all - /// enclosed methods and properties from code coverage collection. - /// - [AttributeUsage( - AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event, - Inherited = false, - AllowMultiple = false - )] - internal sealed class ExcludeFromCodeCoverageAttribute : Attribute - { - /// - /// Initializes a new instance of the class. - /// - public ExcludeFromCodeCoverageAttribute() { } - } -} -#endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Polyfills/Range.cs b/AdvancedSharpAdbClient/Polyfills/Range.cs new file mode 100644 index 00000000..1949a3ce --- /dev/null +++ b/AdvancedSharpAdbClient/Polyfills/Range.cs @@ -0,0 +1,107 @@ +#if !HAS_VALUETUPLE +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace System +{ + /// + /// Represent a range has start and end indexes. + /// + /// + /// Range is used by the C# compiler to support the range syntax. + /// + /// int[] someArray = new int[5] { 1, 2, 3, 4, 5 }; + /// int[] subArray1 = someArray[0..2]; // { 1, 2 } + /// int[] subArray2 = someArray[1..^0]; // { 2, 3, 4, 5 } + /// + /// + internal readonly struct Range : IEquatable + { + /// + /// Represent the inclusive start index of the Range. + /// + public Index Start { get; } + + /// + /// Represent the exclusive end index of the Range. + /// + public Index End { get; } + + /// + /// Construct a Range object using the start and end indexes. + /// + /// Represent the inclusive start index of the range. + /// Represent the exclusive end index of the range. + public Range(Index start, Index end) + { + Start = start; + End = end; + } + + /// + /// Indicates whether the current Range object is equal to another object of the same type. + /// + /// An object to compare with this object + public override bool Equals([NotNullWhen(true)] object? value) => + value is Range r && + r.Start.Equals(Start) && + r.End.Equals(End); + + /// + /// Indicates whether the current Range object is equal to another Range object. + /// + /// An object to compare with this object + public bool Equals(Range other) => other.Start.Equals(Start) && other.End.Equals(End); + + /// + /// Returns the hash code for this instance. + /// + public override int GetHashCode() => HashCode.Combine(Start.GetHashCode(), End.GetHashCode()); + + /// + /// Converts the value of the current Range object to its equivalent string representation. + /// + public override string ToString() => $"{Start}..{End}"; + + /// + /// Create a Range object starting from start index to the end of the collection. + /// + public static Range StartAt(Index start) => new(start, Index.End); + + /// + /// Create a Range object starting from first element in the collection to the end Index. + /// + public static Range EndAt(Index end) => new(Index.Start, end); + + /// + /// Create a Range object starting from first element to the end. + /// + public static Range All => new(Index.Start, Index.End); + +#if !NETFRAMEWORK || NET40_OR_GREATER + /// + /// Calculate the start offset and length of range object using a collection length. + /// + /// The length of the collection that the range will be used with. length has to be a positive value. + /// + /// For performance reason, we don't validate the input length parameter against negative values. + /// It is expected Range will be used with collections which always have non negative length/count. + /// We validate the range is inside the length scope though. + /// + [MethodImpl((MethodImplOptions)256)] + public Tuple GetOffsetAndLength(int length) + { + int start = Start.GetOffset(length); + int end = End.GetOffset(length); + + return (uint)end > (uint)length || (uint)start > (uint)end + ? throw new ArgumentOutOfRangeException(nameof(length)) + : new Tuple(start, end - start); + } +#endif + } +} +#endif \ No newline at end of file From 04020e5122081305b51ee4433a0954e2d485097d Mon Sep 17 00:00:00 2001 From: wherewhere Date: Mon, 11 Dec 2023 22:58:08 +0800 Subject: [PATCH 04/10] Add DisposeAsync in IDeviceMonitor Add GetEnumerator for Element and SyncCommand --- .../Extensions/SyncCommandConverterTests.cs | 4 +- .../SyncServiceTests.Async.cs | 2 +- .../SyncServiceTests.cs | 2 +- AdvancedSharpAdbClient/AdbClient.Async.cs | 6 +- AdvancedSharpAdbClient/AdbClient.cs | 6 +- AdvancedSharpAdbClient/AdbSocket.Async.cs | 2 +- AdvancedSharpAdbClient/AdbSocket.cs | 2 +- .../DeviceCommands/Models/Element.cs | 6 + AdvancedSharpAdbClient/DeviceMonitor.Async.cs | 16 ++- .../Extensions/EnumerableBuilder.cs | 21 ++- .../Extensions/SyncCommandConverter.cs | 10 +- .../Interfaces/IDeviceMonitor.Async.cs | 11 +- .../Logs/LogReader.Async.cs | 3 +- AdvancedSharpAdbClient/Models/ColorData.cs | 2 +- .../Models/FileStatistics.cs | 4 + .../Models/FramebufferHeader.cs | 2 +- AdvancedSharpAdbClient/SyncService.Async.cs | 124 ++++++++---------- AdvancedSharpAdbClient/SyncService.cs | 90 ++++++------- 18 files changed, 171 insertions(+), 142 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/Extensions/SyncCommandConverterTests.cs b/AdvancedSharpAdbClient.Tests/Extensions/SyncCommandConverterTests.cs index 24ab21ad..24ba2b3e 100644 --- a/AdvancedSharpAdbClient.Tests/Extensions/SyncCommandConverterTests.cs +++ b/AdvancedSharpAdbClient.Tests/Extensions/SyncCommandConverterTests.cs @@ -22,7 +22,7 @@ public void GetCommandInvalidCommandTest() => [Fact] public void GetBytesInvalidCommandTest() => - _ = Assert.Throws(() => SyncCommandConverter.GetBytes((SyncCommand)99)); + _ = Assert.Throws(() => ((SyncCommand)99).GetBytes()); [Fact] public void SyncCommandConverterTest() @@ -30,7 +30,7 @@ public void SyncCommandConverterTest() SyncCommand[] commands = Enum.GetValues(); foreach (SyncCommand command in commands) { - byte[] bytes = SyncCommandConverter.GetBytes(command); + byte[] bytes = command.GetBytes(); Assert.Equal(4, bytes.Length); Assert.Equal(command, SyncCommandConverter.GetCommand(bytes)); } diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs index 2547944a..f75c2d0e 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs @@ -191,7 +191,7 @@ public async void PushAsyncTest() byte[] content = await File.ReadAllBytesAsync("Assets/Fstab.bin"); byte[] contentMessage = [ - .. SyncCommandConverter.GetBytes(SyncCommand.DATA), + .. SyncCommand.DATA.GetBytes(), .. BitConverter.GetBytes(content.Length), .. content, ]; diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs index 033048c2..6791a809 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs @@ -142,7 +142,7 @@ public void PushTest() byte[] content = File.ReadAllBytes("Assets/Fstab.bin"); byte[] contentMessage = [ - .. SyncCommandConverter.GetBytes(SyncCommand.DATA), + .. SyncCommand.DATA.GetBytes(), .. BitConverter.GetBytes(content.Length), .. content, ]; diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index b4ec0753..e7175ec5 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -455,7 +455,7 @@ public virtual async Task InstallAsync(DeviceData device, Stream apk, IProgress< _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); byte[] buffer = new byte[32 * 1024]; - int read = 0; + int read; long totalBytesToProcess = apk.Length; long totalBytesRead = 0; @@ -657,7 +657,7 @@ public virtual async Task InstallWriteAsync(DeviceData device, Stream apk, strin _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); byte[] buffer = new byte[32 * 1024]; - int read = 0; + int read; long totalBytesToProcess = apk.Length; long totalBytesRead = 0; @@ -729,7 +729,7 @@ protected virtual async Task InstallWriteAsync(DeviceData device, Stream apk, st _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); byte[] buffer = new byte[32 * 1024]; - int read = 0; + int read; long totalBytesToProcess = apk.Length; long totalBytesRead = 0; diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index fb789e12..39d7200f 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -601,7 +601,7 @@ public void Install(DeviceData device, Stream apk, IProgress FindDescendants() } } + /// + /// Returns an enumerator that iterates through the . + /// + /// An enumerator that can be used to iterate through the . + public IEnumerator GetEnumerator() => FindDescendants().GetEnumerator(); + /// public override bool Equals([NotNullWhen(true)] object? obj) => Equals(obj as Element); diff --git a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs index 23edc8e7..9db786e9 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs @@ -80,9 +80,17 @@ protected virtual async Task DisposeAsyncCore() disposed = true; } -#if NETCOREAPP3_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if NETCOREAPP && !NETCOREAPP3_0_OR_GREATER + /// + /// Asynchronously performs application-defined tasks associated with freeing, releasing, or resetting + /// unmanaged resources asynchronously. + /// + /// A that represents the asynchronous dispose operation. +#else /// - async ValueTask IAsyncDisposable.DisposeAsync() +#endif + public async ValueTask DisposeAsync() { await DisposeAsyncCore().ConfigureAwait(false); Dispose(disposing: false); @@ -90,10 +98,10 @@ async ValueTask IAsyncDisposable.DisposeAsync() } /// - public Task DisposeAsync() => ((IAsyncDisposable)this).DisposeAsync().AsTask(); + public Task AsyncDispose() => DisposeAsync().AsTask(); #else /// - public async Task DisposeAsync() + public async Task AsyncDispose() { await DisposeAsyncCore().ConfigureAwait(false); Dispose(disposing: false); diff --git a/AdvancedSharpAdbClient/Extensions/EnumerableBuilder.cs b/AdvancedSharpAdbClient/Extensions/EnumerableBuilder.cs index e4ceecf9..08d11360 100644 --- a/AdvancedSharpAdbClient/Extensions/EnumerableBuilder.cs +++ b/AdvancedSharpAdbClient/Extensions/EnumerableBuilder.cs @@ -19,7 +19,7 @@ public static class EnumerableBuilder /// /// The data that feeds the struct. /// A new instance of struct. - public static ColorData ColorDataCreate(ReadOnlySpan values) => + public static ColorData ColorDataCreator(ReadOnlySpan values) => new((uint)(values[0] | (values[1] << 8) | (values[2] << 16) | (values[3] << 24)), (uint)(values[4] | (values[5] << 8) | (values[6] << 16) | (values[7] << 24))); @@ -28,7 +28,24 @@ public static ColorData ColorDataCreate(ReadOnlySpan values) => /// /// The data that feeds the struct. /// A new instance of struct. - public static FramebufferHeader FramebufferHeaderCreate(ReadOnlySpan values) => new(values); + public static FramebufferHeader FramebufferHeaderCreator(ReadOnlySpan values) => new(values); + + /// + /// Build a struct. + /// + /// The data that feeds the struct. + /// A new instance of struct. + public static FileStatistics FileStatisticsCreator(ReadOnlySpan values) + { + int index = 0; + return new FileStatistics + { + FileType = (UnixFileType)ReadInt32(in values), + Size = ReadInt32(in values), + Time = DateTimeExtensions.FromUnixTimeSeconds(ReadInt32(in values)) + }; + int ReadInt32(in ReadOnlySpan data) => data[index++] | (data[index++] << 8) | (data[index++] << 16) | (data[index++] << 24); + } } } #endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs b/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs index 8fd9cbb8..d28c6e1a 100644 --- a/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs +++ b/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs @@ -3,6 +3,7 @@ // using System; +using System.Collections.Generic; namespace AdvancedSharpAdbClient { @@ -16,7 +17,7 @@ public static class SyncCommandConverter /// /// The to convert. /// A byte array that represents the . - public static byte[] GetBytes(SyncCommand command) + public static byte[] GetBytes(this SyncCommand command) { if (command == 0) { @@ -42,6 +43,13 @@ or SyncCommand.OKAY return commandBytes; } + /// + /// Returns an enumerator that iterates through the . + /// + /// The to convert. + /// An enumerator that can be used to iterate through the . + public static IEnumerator GetEnumerator(this SyncCommand command) => ((IEnumerable)command.GetBytes()).GetEnumerator(); + /// /// Determines which is represented by this byte array. /// diff --git a/AdvancedSharpAdbClient/Interfaces/IDeviceMonitor.Async.cs b/AdvancedSharpAdbClient/Interfaces/IDeviceMonitor.Async.cs index 90dc5d74..bc4a46a3 100644 --- a/AdvancedSharpAdbClient/Interfaces/IDeviceMonitor.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/IDeviceMonitor.Async.cs @@ -16,12 +16,21 @@ public partial interface IDeviceMonitor /// A which represents the asynchronous operation. Task StartAsync(CancellationToken cancellationToken); +#if NETCOREAPP3_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER + /// + /// Asynchronously performs application-defined tasks associated with freeing, releasing, or resetting + /// unmanaged resources asynchronously. + /// + /// A that represents the asynchronous dispose operation. + ValueTask DisposeAsync() => new(AsyncDispose()); +#endif + /// /// Asynchronously performs application-defined tasks associated with freeing, releasing, or resetting /// unmanaged resources asynchronously. /// /// A that represents the asynchronous dispose operation. - Task DisposeAsync(); + Task AsyncDispose(); } } #endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Logs/LogReader.Async.cs b/AdvancedSharpAdbClient/Logs/LogReader.Async.cs index 9d09e5e5..80e081fd 100644 --- a/AdvancedSharpAdbClient/Logs/LogReader.Async.cs +++ b/AdvancedSharpAdbClient/Logs/LogReader.Async.cs @@ -202,10 +202,9 @@ or LogId.Radio private async Task ReadBytesSafeAsync(int count, CancellationToken cancellationToken = default) { int totalRead = 0; - int read = 0; - byte[] data = new byte[count]; + int read; #if HAS_BUFFERS while ((read = await stream.ReadAsync(data.AsMemory(totalRead, count - totalRead), cancellationToken).ConfigureAwait(false)) > 0) #else diff --git a/AdvancedSharpAdbClient/Models/ColorData.cs b/AdvancedSharpAdbClient/Models/ColorData.cs index d2144708..c5272141 100644 --- a/AdvancedSharpAdbClient/Models/ColorData.cs +++ b/AdvancedSharpAdbClient/Models/ColorData.cs @@ -20,7 +20,7 @@ namespace AdvancedSharpAdbClient.Models /// bytes that contain information for this color are stored. /// The number of bits that contain information for this color. #if HAS_BUFFERS - [CollectionBuilder(typeof(EnumerableBuilder), nameof(EnumerableBuilder.ColorDataCreate))] + [CollectionBuilder(typeof(EnumerableBuilder), nameof(EnumerableBuilder.ColorDataCreator))] #endif public readonly record struct ColorData(uint Offset, uint Length) : IReadOnlyList { diff --git a/AdvancedSharpAdbClient/Models/FileStatistics.cs b/AdvancedSharpAdbClient/Models/FileStatistics.cs index f478c94a..73246b01 100644 --- a/AdvancedSharpAdbClient/Models/FileStatistics.cs +++ b/AdvancedSharpAdbClient/Models/FileStatistics.cs @@ -4,12 +4,16 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; namespace AdvancedSharpAdbClient.Models { /// /// Contains information about a file on the remote device. /// +#if HAS_BUFFERS + [CollectionBuilder(typeof(EnumerableBuilder), nameof(EnumerableBuilder.FileStatisticsCreator))] +#endif public struct FileStatistics : IEquatable { /// diff --git a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs index 3c3e8431..49af26f4 100644 --- a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs +++ b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs @@ -21,7 +21,7 @@ namespace AdvancedSharpAdbClient.Models /// information about the framebuffer. /// #if HAS_BUFFERS - [CollectionBuilder(typeof(EnumerableBuilder), nameof(EnumerableBuilder.FramebufferHeaderCreate))] + [CollectionBuilder(typeof(EnumerableBuilder), nameof(EnumerableBuilder.FramebufferHeaderCreator))] #endif public readonly struct FramebufferHeader : IReadOnlyList { diff --git a/AdvancedSharpAdbClient/SyncService.Async.cs b/AdvancedSharpAdbClient/SyncService.Async.cs index 856906da..0bc37fba 100644 --- a/AdvancedSharpAdbClient/SyncService.Async.cs +++ b/AdvancedSharpAdbClient/SyncService.Async.cs @@ -50,7 +50,7 @@ public virtual async Task PushAsync(Stream stream, string remotePath, int permis // We need 4 bytes of the buffer to send the 'DATA' command, // and an additional X bytes to inform how much data we are // sending. - byte[] dataBytes = SyncCommandConverter.GetBytes(SyncCommand.DATA); + byte[] dataBytes = SyncCommand.DATA.GetBytes(); byte[] lengthBytes = BitConverter.GetBytes(MaxBufferSize); int headerSize = dataBytes.Length + lengthBytes.Length; int reservedHeaderSize = headerSize; @@ -62,27 +62,20 @@ public virtual async Task PushAsync(Stream stream, string remotePath, int permis long totalBytesToProcess = stream.CanSeek ? stream.Length : 0; long totalBytesRead = 0; + int read; // look while there is something to read - while (true) - { - // check if we're canceled - cancellationToken.ThrowIfCancellationRequested(); - - // read up to SYNC_DATA_MAX - int read = + while ((read = #if HAS_BUFFERS - await stream.ReadAsync(buffer.AsMemory(headerSize, maxDataSize), cancellationToken).ConfigureAwait(false); + await stream.ReadAsync(buffer.AsMemory(headerSize, maxDataSize), cancellationToken).ConfigureAwait(false) #else - await stream.ReadAsync(buffer, headerSize, maxDataSize, cancellationToken).ConfigureAwait(false); + await stream.ReadAsync(buffer, headerSize, maxDataSize, cancellationToken).ConfigureAwait(false) #endif + ) > 0) + { + // read up to SYNC_DATA_MAX totalBytesRead += read; - if (read == 0) - { - // we reached the end of the file - break; - } - else if (read != maxDataSize) + if (read != maxDataSize) { // At the end of the line, so we need to recalculate the length of the header lengthBytes = BitConverter.GetBytes(read); @@ -102,6 +95,9 @@ public virtual async Task PushAsync(Stream stream, string remotePath, int permis #endif // Let the caller know about our progress, if requested progress?.Report(new SyncProgressChangedEventArgs(totalBytesRead, totalBytesToProcess)); + + // check if we're canceled + cancellationToken.ThrowIfCancellationRequested(); } // create the DONE message @@ -112,15 +108,13 @@ public virtual async Task PushAsync(Stream stream, string remotePath, int permis // (id, size) SyncCommand result = await Socket.ReadSyncResponseAsync(cancellationToken).ConfigureAwait(false); - if (result == SyncCommand.FAIL) - { - string message = await Socket.ReadSyncStringAsync(cancellationToken).ConfigureAwait(false); - - throw new AdbException(message); - } - else if (result != SyncCommand.OKAY) + switch (result) { - throw new AdbException($"The server sent an invalid response {result}"); + case SyncCommand.FAIL: + string message = await Socket.ReadSyncStringAsync(cancellationToken).ConfigureAwait(false); + throw new AdbException(message); + case not SyncCommand.OKAY: + throw new AdbException($"The server sent an invalid response {result}"); } } @@ -142,20 +136,16 @@ public virtual async Task PullAsync(string remoteFilePath, Stream stream, IProgr while (true) { SyncCommand response = await Socket.ReadSyncResponseAsync(cancellationToken).ConfigureAwait(false); - cancellationToken.ThrowIfCancellationRequested(); - if (response == SyncCommand.DONE) + switch (response) { - break; - } - else if (response == SyncCommand.FAIL) - { - string message = await Socket.ReadSyncStringAsync(cancellationToken).ConfigureAwait(false); - throw new AdbException($"Failed to pull '{remoteFilePath}'. {message}"); - } - else if (response != SyncCommand.DATA) - { - throw new AdbException($"The server sent an invalid response {response}"); + case SyncCommand.DONE: + goto finish; + case SyncCommand.FAIL: + string message = await Socket.ReadSyncStringAsync(cancellationToken).ConfigureAwait(false); + throw new AdbException($"Failed to pull '{remoteFilePath}'. {message}"); + case not SyncCommand.DATA: + throw new AdbException($"The server sent an invalid response {response}"); } // The first 4 bytes contain the length of the data packet @@ -191,7 +181,12 @@ public virtual async Task PullAsync(string remoteFilePath, Stream stream, IProgr // Let the caller know about our progress, if requested progress?.Report(new SyncProgressChangedEventArgs(totalBytesRead, totalBytesToProcess)); + + // check if we're canceled + cancellationToken.ThrowIfCancellationRequested(); } + + finish: return; } /// @@ -229,26 +224,18 @@ public virtual async Task> GetDirectoryListingAsync(string { SyncCommand response = await Socket.ReadSyncResponseAsync(cancellationToken).ConfigureAwait(false); - if (response == 0) + switch (response) { - if (isLocked) - { + case 0 when isLocked: throw new AdbException("The server returned an empty sync response."); - } - else - { + case 0: Reopen(); isLocked = true; goto start; - } - } - else if (response == SyncCommand.DONE) - { - break; - } - else if (response != SyncCommand.DENT) - { - throw new AdbException($"The server returned an invalid sync response {response}."); + case SyncCommand.DONE: + goto finish; + case not SyncCommand.DENT: + throw new AdbException($"The server returned an invalid sync response {response}."); } FileStatistics entry = await ReadStatisticsAsync(cancellationToken).ConfigureAwait(false); @@ -257,6 +244,7 @@ public virtual async Task> GetDirectoryListingAsync(string value.Add(entry); } + finish: return value; } @@ -274,26 +262,18 @@ public virtual async IAsyncEnumerable GetDirectoryAsyncListing(s { SyncCommand response = await Socket.ReadSyncResponseAsync(cancellationToken).ConfigureAwait(false); - if (response == 0) + switch (response) { - if (isLocked) - { + case 0 when isLocked: throw new AdbException("The server returned an empty sync response."); - } - else - { + case 0: Reopen(); isLocked = true; goto start; - } - } - else if (response == SyncCommand.DONE) - { - break; - } - else if (response != SyncCommand.DENT) - { - throw new AdbException($"The server returned an invalid sync response {response}."); + case SyncCommand.DONE: + goto finish; + case not SyncCommand.DENT: + throw new AdbException($"The server returned an invalid sync response {response}."); } FileStatistics entry = await ReadStatisticsAsync(cancellationToken).ConfigureAwait(false); @@ -302,6 +282,9 @@ public virtual async IAsyncEnumerable GetDirectoryAsyncListing(s yield return entry; isLocked = true; } + + finish: + yield break; } #endif @@ -312,19 +295,22 @@ public virtual async IAsyncEnumerable GetDirectoryAsyncListing(s /// A which return a object that contains information about the file. protected async Task ReadStatisticsAsync(CancellationToken cancellationToken = default) { +#if HAS_BUFFERS + Memory statResult = new byte[12]; + _ = await Socket.ReadAsync(statResult, cancellationToken).ConfigureAwait(false); + return EnumerableBuilder.FileStatisticsCreator(statResult.Span); +#else byte[] statResult = new byte[12]; _ = await Socket.ReadAsync(statResult, cancellationToken).ConfigureAwait(false); - int index = 0; - return new FileStatistics { FileType = (UnixFileType)ReadInt32(in statResult), Size = ReadInt32(in statResult), Time = DateTimeExtensions.FromUnixTimeSeconds(ReadInt32(in statResult)) }; - int ReadInt32(in byte[] data) => data[index++] | (data[index++] << 8) | (data[index++] << 16) | (data[index++] << 24); +#endif } } } diff --git a/AdvancedSharpAdbClient/SyncService.cs b/AdvancedSharpAdbClient/SyncService.cs index 38f7a119..d5e8bc5d 100644 --- a/AdvancedSharpAdbClient/SyncService.cs +++ b/AdvancedSharpAdbClient/SyncService.cs @@ -143,7 +143,7 @@ public virtual void Push(Stream stream, string remotePath, int permissions, Date // We need 4 bytes of the buffer to send the 'DATA' command, // and an additional X bytes to inform how much data we are // sending. - byte[] dataBytes = SyncCommandConverter.GetBytes(SyncCommand.DATA); + byte[] dataBytes = SyncCommand.DATA.GetBytes(); byte[] lengthBytes = BitConverter.GetBytes(MaxBufferSize); int headerSize = dataBytes.Length + lengthBytes.Length; int reservedHeaderSize = headerSize; @@ -155,23 +155,20 @@ public virtual void Push(Stream stream, string remotePath, int permissions, Date long totalBytesToProcess = stream.CanSeek ? stream.Length : 0; long totalBytesRead = 0; + int read; // look while there is something to read - while (!isCancelled) - { - // read up to SYNC_DATA_MAX + while (!isCancelled && (read = #if HAS_BUFFERS - int read = stream.Read(buffer.AsSpan(headerSize, maxDataSize)); + stream.Read(buffer.AsSpan(headerSize, maxDataSize)) #else - int read = stream.Read(buffer, headerSize, maxDataSize); + stream.Read(buffer, headerSize, maxDataSize) #endif + ) > 0) + { + // read up to SYNC_DATA_MAX totalBytesRead += read; - if (read == 0) - { - // we reached the end of the file - break; - } - else if (read != maxDataSize) + if (read != maxDataSize) { // At the end of the line, so we need to recalculate the length of the header lengthBytes = BitConverter.GetBytes(read); @@ -201,15 +198,13 @@ public virtual void Push(Stream stream, string remotePath, int permissions, Date // (id, size) SyncCommand result = Socket.ReadSyncResponse(); - if (result == SyncCommand.FAIL) + switch (result) { - string message = Socket.ReadSyncString(); - - throw new AdbException(message); - } - else if (result != SyncCommand.OKAY) - { - throw new AdbException($"The server sent an invalid response {result}"); + case SyncCommand.FAIL: + string message = Socket.ReadSyncString(); + throw new AdbException(message); + case not SyncCommand.OKAY: + throw new AdbException($"The server sent an invalid response {result}"); } } @@ -232,18 +227,15 @@ public virtual void Pull(string remoteFilePath, Stream stream, IProgress @@ -315,26 +309,18 @@ public virtual IEnumerable GetDirectoryListing(string remotePath { SyncCommand response = Socket.ReadSyncResponse(); - if (response == 0) + switch (response) { - if (isLocked) - { + case 0 when isLocked: throw new AdbException("The server returned an empty sync response."); - } - else - { + case 0: Reopen(); isLocked = true; goto start; - } - } - else if (response == SyncCommand.DONE) - { - break; - } - else if (response != SyncCommand.DENT) - { - throw new AdbException($"The server returned an invalid sync response {response}."); + case SyncCommand.DONE: + goto finish; + case not SyncCommand.DENT: + throw new AdbException($"The server returned an invalid sync response {response}."); } FileStatistics entry = ReadStatistics(); @@ -343,6 +329,9 @@ public virtual IEnumerable GetDirectoryListing(string remotePath yield return entry; isLocked = true; } + + finish: + yield break; } /// @@ -373,19 +362,22 @@ public void Dispose() /// A object that contains information about the file. protected FileStatistics ReadStatistics() { +#if HAS_BUFFERS + Span statResult = stackalloc byte[12]; + _ = Socket.Read(statResult); + return EnumerableBuilder.FileStatisticsCreator(statResult); +#else byte[] statResult = new byte[12]; _ = Socket.Read(statResult); - int index = 0; - return new FileStatistics { FileType = (UnixFileType)ReadInt32(in statResult), Size = ReadInt32(in statResult), Time = DateTimeExtensions.FromUnixTimeSeconds(ReadInt32(in statResult)) }; - int ReadInt32(in byte[] data) => data[index++] | (data[index++] << 8) | (data[index++] << 16) | (data[index++] << 24); +#endif } } } From c239f8d2afd0e6a39f8bab9356fdbd17d02161ce Mon Sep 17 00:00:00 2001 From: wherewhere Date: Tue, 12 Dec 2023 00:41:42 +0800 Subject: [PATCH 05/10] Add GetEnumerator for FileStatistics Add operator for ForwardSpec --- .../Logs/LogReader.Async.cs | 46 +++++++++--------- AdvancedSharpAdbClient/Logs/LogReader.cs | 47 +++++++++---------- .../Models/FileStatistics.cs | 24 ++++++++++ AdvancedSharpAdbClient/Models/ForwardSpec.cs | 16 +++++++ 4 files changed, 83 insertions(+), 50 deletions(-) diff --git a/AdvancedSharpAdbClient/Logs/LogReader.Async.cs b/AdvancedSharpAdbClient/Logs/LogReader.Async.cs index 80e081fd..3289d5de 100644 --- a/AdvancedSharpAdbClient/Logs/LogReader.Async.cs +++ b/AdvancedSharpAdbClient/Logs/LogReader.Async.cs @@ -134,38 +134,36 @@ or LogId.Radio } case LogId.Events: + // https://android.googlesource.com/platform/system/core.git/+/master/liblog/logprint.c#547 + EventLogEntry entry = new() { - // https://android.googlesource.com/platform/system/core.git/+/master/liblog/logprint.c#547 - EventLogEntry entry = new() - { - Data = data, - ProcessId = pid, - ThreadId = tid, - TimeStamp = timestamp, - NanoSeconds = nsec, - Id = id - }; + Data = data, + ProcessId = pid, + ThreadId = tid, + TimeStamp = timestamp, + NanoSeconds = nsec, + Id = id + }; - // Use a stream on the data buffer. This will make sure that, - // if anything goes wrong parsing the data, we never go past - // the message boundary itself. + // Use a stream on the data buffer. This will make sure that, + // if anything goes wrong parsing the data, we never go past + // the message boundary itself. #if NETCOREAPP3_0_OR_GREATER - await + await #endif - using (MemoryStream dataStream = new(data)) - { - using BinaryReader reader = new(dataStream); - int priority = reader.ReadInt32(); + using (MemoryStream dataStream = new(data)) + { + using BinaryReader reader = new(dataStream); + int priority = reader.ReadInt32(); - while (dataStream.Position < dataStream.Length) - { - ReadLogEntry(reader, entry.Values); - } + while (dataStream.Position < dataStream.Length) + { + ReadLogEntry(reader, entry.Values); } - - return entry; } + return entry; + default: return new LogEntry { diff --git a/AdvancedSharpAdbClient/Logs/LogReader.cs b/AdvancedSharpAdbClient/Logs/LogReader.cs index 35dbf9d5..38f030c0 100644 --- a/AdvancedSharpAdbClient/Logs/LogReader.cs +++ b/AdvancedSharpAdbClient/Logs/LogReader.cs @@ -140,35 +140,33 @@ or LogId.Radio } case LogId.Events: + // https://android.googlesource.com/platform/system/core.git/+/master/liblog/logprint.c#547 + EventLogEntry entry = new() { - // https://android.googlesource.com/platform/system/core.git/+/master/liblog/logprint.c#547 - EventLogEntry entry = new() - { - Data = data, - ProcessId = pid, - ThreadId = tid, - TimeStamp = timestamp, - NanoSeconds = nsec, - Id = id - }; + Data = data, + ProcessId = pid, + ThreadId = tid, + TimeStamp = timestamp, + NanoSeconds = nsec, + Id = id + }; - // Use a stream on the data buffer. This will make sure that, - // if anything goes wrong parsing the data, we never go past - // the message boundary itself. - using (MemoryStream dataStream = new(data)) - { - using BinaryReader reader = new(dataStream); - int priority = reader.ReadInt32(); + // Use a stream on the data buffer. This will make sure that, + // if anything goes wrong parsing the data, we never go past + // the message boundary itself. + using (MemoryStream dataStream = new(data)) + { + using BinaryReader reader = new(dataStream); + int priority = reader.ReadInt32(); - while (dataStream.Position < dataStream.Length) - { - ReadLogEntry(reader, entry.Values); - } + while (dataStream.Position < dataStream.Length) + { + ReadLogEntry(reader, entry.Values); } - - return entry; } + return entry; + default: return new LogEntry { @@ -205,14 +203,11 @@ protected void ReadLogEntry(BinaryReader reader, ICollection parent) case EventLogType.List: byte listLength = reader.ReadByte(); - List list = []; - for (int i = 0; i < listLength; i++) { ReadLogEntry(reader, list); } - parent.Add(list); break; diff --git a/AdvancedSharpAdbClient/Models/FileStatistics.cs b/AdvancedSharpAdbClient/Models/FileStatistics.cs index 73246b01..dc15ca73 100644 --- a/AdvancedSharpAdbClient/Models/FileStatistics.cs +++ b/AdvancedSharpAdbClient/Models/FileStatistics.cs @@ -3,6 +3,7 @@ // using System; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; @@ -41,6 +42,29 @@ public FileStatistics() { } /// public DateTimeOffset Time { get; init; } + /// + /// Returns an enumerator that iterates through the . + /// + /// An enumerator that can be used to iterate through the . + public readonly IEnumerator GetEnumerator() + { + yield return (byte)FileType; + yield return (byte)((int)FileType >> 8); + yield return (byte)((int)FileType >> 16); + yield return (byte)((int)FileType >> 24); + + yield return (byte)Size; + yield return (byte)(Size >> 8); + yield return (byte)(Size >> 16); + yield return (byte)(Size >> 24); + + long time = Time.ToUnixTimeSeconds(); + yield return (byte)time; + yield return (byte)(time >> 8); + yield return (byte)(time >> 16); + yield return (byte)(time >> 24); + } + /// public override readonly bool Equals([NotNullWhen(true)] object? obj) => obj is FileStatistics other && Equals(other); diff --git a/AdvancedSharpAdbClient/Models/ForwardSpec.cs b/AdvancedSharpAdbClient/Models/ForwardSpec.cs index f61ab3bf..349c0d16 100644 --- a/AdvancedSharpAdbClient/Models/ForwardSpec.cs +++ b/AdvancedSharpAdbClient/Models/ForwardSpec.cs @@ -153,5 +153,21 @@ or ForwardProtocol.LocalReserved or ForwardProtocol.Device => string.Equals(SocketName, other.SocketName), _ => false, }; + + /// + /// Tests whether two objects are equally. + /// + /// The structure that is to the left of the equality operator. + /// The structure that is to the right of the equality operator. + /// This operator returns if the two structures are equally; otherwise . + public static bool operator ==(ForwardSpec left, ForwardSpec right) => left.Equals(right); + + /// + /// Tests whether two objects are different. + /// + /// The structure that is to the left of the inequality operator. + /// The structure that is to the right of the inequality operator. + /// This operator returns if the two structures are unequally; otherwise . + public static bool operator !=(ForwardSpec left, ForwardSpec right) => !left.Equals(right); } } From 03adcc6eb3ff802bab29c11aed88301a992f9b45 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Wed, 13 Dec 2023 21:50:20 +0800 Subject: [PATCH 06/10] Use AwaitByTaskCompleteSource to wait Task synchronously Change FirstDeviceListParsed to Task --- .../Extensions/EnumerableExtensionsTests.cs | 4 +- AdvancedSharpAdbClient/AdbClient.Async.cs | 62 ++--- AdvancedSharpAdbClient/AdbClient.cs | 69 +++--- .../AdbCommandLineClient.Async.cs | 2 +- .../AdbCommandLineClient.cs | 11 +- AdvancedSharpAdbClient/AdbServer.Async.cs | 6 +- AdvancedSharpAdbClient/AdbServer.cs | 14 +- AdvancedSharpAdbClient/AdbSocket.cs | 2 +- .../DeviceCommands/DeviceClient.Async.cs | 8 +- .../DeviceCommands/DeviceClient.cs | 10 +- .../DeviceCommands/DeviceExtensions.cs | 6 +- .../DeviceCommands/LinuxPath.cs | 9 +- .../DeviceCommands/Models/AndroidProcess.cs | 20 ++ .../DeviceCommands/Models/Element.cs | 3 + .../DeviceCommands/PackageManager.Async.cs | 12 +- .../DeviceCommands/PackageManager.cs | 10 +- .../Receivers/EnvironmentVariablesReceiver.cs | 4 + .../Receivers/GetPropReceiver.cs | 4 + .../Receivers/InstallOutputReceiver.cs | 12 + .../Receivers/VersionInfoReceiver.cs | 22 +- AdvancedSharpAdbClient/DeviceMonitor.Async.cs | 53 +++-- AdvancedSharpAdbClient/DeviceMonitor.cs | 94 ++++---- .../Exceptions/JavaException.cs | 17 ++ .../Extensions/Extensions.cs | 125 +--------- .../Logs/LogReader.Async.cs | 25 +- AdvancedSharpAdbClient/Logs/LogReader.cs | 4 + AdvancedSharpAdbClient/Logs/LoggerProvider.cs | 3 + AdvancedSharpAdbClient/Models/DeviceData.cs | 6 +- AdvancedSharpAdbClient/Models/Framebuffer.cs | 17 +- .../Models/FramebufferHeader.cs | 8 +- .../Models/InstallProgress.EventArgs.cs | 10 +- AdvancedSharpAdbClient/Models/ShellStream.cs | 13 +- .../Extensions/EnumerableExtensions.cs | 2 +- .../Polyfills/Extensions/SocketExtensions.cs | 8 +- .../Polyfills/Extensions/StreamExtensions.cs | 1 - .../Polyfills/Extensions/TaskExExtensions.cs | 216 ++++++++++++++++++ .../Properties/GlobalUsings.cs | 1 + .../Receivers/ConsoleOutputReceiver.cs | 20 +- AdvancedSharpAdbClient/SyncService.Async.cs | 2 +- AdvancedSharpAdbClient/SyncService.cs | 2 +- AdvancedSharpAdbClient/TcpSocket.Async.cs | 5 +- 41 files changed, 595 insertions(+), 327 deletions(-) create mode 100644 AdvancedSharpAdbClient/Polyfills/Extensions/TaskExExtensions.cs diff --git a/AdvancedSharpAdbClient.Tests/Extensions/EnumerableExtensionsTests.cs b/AdvancedSharpAdbClient.Tests/Extensions/EnumerableExtensionsTests.cs index 2242e4b0..ed2953d6 100644 --- a/AdvancedSharpAdbClient.Tests/Extensions/EnumerableExtensionsTests.cs +++ b/AdvancedSharpAdbClient.Tests/Extensions/EnumerableExtensionsTests.cs @@ -42,8 +42,8 @@ public void AddRangeTest() public async void TaskToArrayTest() { int[] array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - Task> arrayTask = Extensions.Delay(10).ContinueWith(_ => array.Select(x => x)); - IEnumerable> taskArray = array.Select(x => Extensions.Delay(x).ContinueWith(_ => x)); + Task> arrayTask = TaskExExtensions.Delay(10).ContinueWith(_ => array.Select(x => x)); + IEnumerable> taskArray = array.Select(x => TaskExExtensions.Delay(x).ContinueWith(_ => x)); Assert.Equal(array, await taskArray.ToArrayAsync()); Assert.Equal(array, await arrayTask.ToArrayAsync()); } diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index e7175ec5..64bfc503 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -20,7 +20,7 @@ public partial class AdbClient /// public virtual async Task GetAdbVersionAsync(CancellationToken cancellationToken = default) { - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); await socket.SendAdbRequestAsync("host:version", cancellationToken).ConfigureAwait(false); _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); @@ -32,7 +32,7 @@ public virtual async Task GetAdbVersionAsync(CancellationToken cancellation /// public virtual async Task KillAdbAsync(CancellationToken cancellationToken = default) { - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); await socket.SendAdbRequestAsync("host:kill", cancellationToken).ConfigureAwait(false); // The host will immediately close the connection after the kill @@ -42,7 +42,7 @@ public virtual async Task KillAdbAsync(CancellationToken cancellationToken = def /// public virtual async Task> GetDevicesAsync(CancellationToken cancellationToken = default) { - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); await socket.SendAdbRequestAsync("host:devices-l", cancellationToken).ConfigureAwait(false); _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); @@ -57,7 +57,7 @@ public virtual async Task CreateForwardAsync(DeviceData device, string loca { EnsureDevice(device); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); string rebind = allowRebind ? string.Empty : "norebind:"; await socket.SendAdbRequestAsync($"host-serial:{device.Serial}:forward:{rebind}{local};{remote}", cancellationToken).ConfigureAwait(false); @@ -73,7 +73,7 @@ public virtual async Task CreateReverseForwardAsync(DeviceData device, stri { EnsureDevice(device); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); string rebind = allowRebind ? string.Empty : "norebind:"; @@ -91,7 +91,7 @@ public virtual async Task RemoveReverseForwardAsync(DeviceData device, string re { EnsureDevice(device); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); await socket.SendAdbRequestAsync($"reverse:killforward:{remote}", cancellationToken).ConfigureAwait(false); @@ -103,7 +103,7 @@ public virtual async Task RemoveAllReverseForwardsAsync(DeviceData device, Cance { EnsureDevice(device); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); await socket.SendAdbRequestAsync($"reverse:killforward-all", cancellationToken).ConfigureAwait(false); @@ -115,7 +115,7 @@ public virtual async Task RemoveForwardAsync(DeviceData device, int localPort, C { EnsureDevice(device); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); await socket.SendAdbRequestAsync($"host-serial:{device.Serial}:killforward:tcp:{localPort}", cancellationToken).ConfigureAwait(false); _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); } @@ -125,7 +125,7 @@ public virtual async Task RemoveAllForwardsAsync(DeviceData device, Cancellation { EnsureDevice(device); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); await socket.SendAdbRequestAsync($"host-serial:{device.Serial}:killforward-all", cancellationToken).ConfigureAwait(false); _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); } @@ -135,7 +135,7 @@ public virtual async Task> ListForwardAsync(DeviceData { EnsureDevice(device); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); await socket.SendAdbRequestAsync($"host-serial:{device.Serial}:list-forward", cancellationToken).ConfigureAwait(false); _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); string data = await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); @@ -149,7 +149,7 @@ public virtual async Task> ListReverseForwardAsync(Devi { EnsureDevice(device); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); await socket.SendAdbRequestAsync($"reverse:list-forward", cancellationToken).ConfigureAwait(false); @@ -164,7 +164,7 @@ public virtual async Task> ListReverseForwardAsync(Devi public virtual async Task ExecuteServerCommandAsync(string target, string command, Encoding encoding, CancellationToken cancellationToken = default) { ExceptionExtensions.ThrowIfNull(encoding); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); await ExecuteServerCommandAsync(target, command, socket, encoding, cancellationToken); } @@ -190,7 +190,7 @@ public virtual async Task ExecuteRemoteCommandAsync(string command, DeviceData d EnsureDevice(device); ExceptionExtensions.ThrowIfNull(encoding); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken); await ExecuteServerCommandAsync("shell", command, socket, encoding, cancellationToken); @@ -200,7 +200,7 @@ public virtual async Task ExecuteRemoteCommandAsync(string command, DeviceData d public virtual async Task ExecuteServerCommandAsync(string target, string command, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken = default) { ExceptionExtensions.ThrowIfNull(encoding); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); await ExecuteServerCommandAsync(target, command, socket, receiver, encoding, cancellationToken); } @@ -256,7 +256,7 @@ public virtual async Task ExecuteRemoteCommandAsync(string command, DeviceData d EnsureDevice(device); ExceptionExtensions.ThrowIfNull(encoding); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken); await ExecuteServerCommandAsync("shell", command, socket, receiver, encoding, cancellationToken); @@ -282,7 +282,7 @@ public virtual async Task RunLogServiceAsync(DeviceData device, Action // The 'log' service has been deprecated, see // https://android.googlesource.com/platform/system/core/+/7aa39a7b199bb9803d3fd47246ee9530b4a96177 - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); StringBuilder request = new StringBuilder().Append("shell:logcat -B"); @@ -330,7 +330,7 @@ public virtual async Task RebootAsync(string into, DeviceData device, Cancellati { EnsureDevice(device); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); await socket.SendAdbRequestAsync($"reboot:{into}", cancellationToken).ConfigureAwait(false); @@ -342,7 +342,7 @@ public virtual async Task PairAsync(DnsEndPoint endpoint, string code, C { ExceptionExtensions.ThrowIfNull(endpoint); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); await socket.SendAdbRequestAsync($"host:pair:{code}:{endpoint.Host}:{endpoint.Port}", cancellationToken).ConfigureAwait(false); _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); @@ -354,7 +354,7 @@ public virtual async Task ConnectAsync(DnsEndPoint endpoint, Cancellatio { ExceptionExtensions.ThrowIfNull(endpoint); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); await socket.SendAdbRequestAsync($"host:connect:{endpoint.Host}:{endpoint.Port}", cancellationToken).ConfigureAwait(false); _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); @@ -366,7 +366,7 @@ public virtual async Task DisconnectAsync(DnsEndPoint endpoint, Cancella { ExceptionExtensions.ThrowIfNull(endpoint); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); await socket.SendAdbRequestAsync($"host:disconnect:{endpoint.Host}:{endpoint.Port}", cancellationToken).ConfigureAwait(false); _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); @@ -390,7 +390,7 @@ protected async Task RootAsync(string request, DeviceData device, CancellationTo { EnsureDevice(device); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); await socket.SendAdbRequestAsync(request, cancellationToken).ConfigureAwait(false); @@ -417,7 +417,7 @@ protected async Task RootAsync(string request, DeviceData device, CancellationTo { // Give adbd some time to kill itself and come back up. // We can't use wait-for-device because devices (e.g. adb over network) might not come back. - await Extensions.Delay(3000, cancellationToken).ConfigureAwait(false); + await TaskExExtensions.Delay(3000, cancellationToken).ConfigureAwait(false); } } @@ -448,7 +448,7 @@ public virtual async Task InstallAsync(DeviceData device, Stream apk, IProgress< // do last to override any user specified value _ = requestBuilder.Append($" -S {apk.Length}"); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); await socket.SendAdbRequestAsync(requestBuilder.ToString(), cancellationToken).ConfigureAwait(false); @@ -537,7 +537,7 @@ void OnSplitSyncProgressChanged(string? sender, double args) } int i = 0; - await Extensions.WhenAll(splitAPKs.Select(splitAPK => InstallWriteAsync(device, splitAPK, $"{nameof(splitAPK)}{i++}", session, OnSplitSyncProgressChanged, cancellationToken))).ConfigureAwait(false); + await TaskExExtensions.WhenAll(splitAPKs.Select(splitAPK => InstallWriteAsync(device, splitAPK, $"{nameof(splitAPK)}{i++}", session, OnSplitSyncProgressChanged, cancellationToken))).ConfigureAwait(false); progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Installing)); await InstallCommitAsync(device, session, cancellationToken).ConfigureAwait(false); @@ -581,7 +581,7 @@ void OnSyncProgressChanged(string? sender, double args) } int i = 0; - await Extensions.WhenAll(splitAPKs.Select(splitAPK => InstallWriteAsync(device, splitAPK, $"{nameof(splitAPK)}{i++}", session, OnSyncProgressChanged, cancellationToken))).ConfigureAwait(false); + await TaskExExtensions.WhenAll(splitAPKs.Select(splitAPK => InstallWriteAsync(device, splitAPK, $"{nameof(splitAPK)}{i++}", session, OnSyncProgressChanged, cancellationToken))).ConfigureAwait(false); progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Installing)); await InstallCommitAsync(device, session, cancellationToken).ConfigureAwait(false); @@ -608,7 +608,7 @@ public virtual async Task InstallCreateAsync(DeviceData device, string? } } - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); await socket.SendAdbRequestAsync(requestBuilder.ToString(), cancellationToken).ConfigureAwait(false); @@ -650,7 +650,7 @@ public virtual async Task InstallWriteAsync(DeviceData device, Stream apk, strin .AppendFormat(" -S {0}", apk.Length) .AppendFormat(" {0} {1}.apk", session, apkName); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); await socket.SendAdbRequestAsync(requestBuilder.ToString(), cancellationToken).ConfigureAwait(false); @@ -722,7 +722,7 @@ protected virtual async Task InstallWriteAsync(DeviceData device, Stream apk, st .AppendFormat(" -S {0}", apk.Length) .AppendFormat(" {0} {1}.apk", session, apkName); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); await socket.SendAdbRequestAsync(requestBuilder.ToString(), cancellationToken).ConfigureAwait(false); @@ -766,7 +766,7 @@ protected virtual async Task InstallWriteAsync(DeviceData device, Stream apk, st /// public virtual async Task InstallCommitAsync(DeviceData device, string session, CancellationToken cancellationToken = default) { - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); await socket.SendAdbRequestAsync($"exec:cmd package 'install-commit' {session}", cancellationToken).ConfigureAwait(false); @@ -797,7 +797,7 @@ public virtual async Task UninstallAsync(DeviceData device, string packageName, _ = requestBuilder.AppendFormat(" {0}", packageName); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); await socket.SendAdbRequestAsync(requestBuilder.ToString(), cancellationToken).ConfigureAwait(false); @@ -816,7 +816,7 @@ public virtual async Task> GetFeatureSetAsync(DeviceData dev { EnsureDevice(device); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); await socket.SendAdbRequestAsync($"host-serial:{device.Serial}:features", cancellationToken).ConfigureAwait(false); _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); string features = await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index 39d7200f..6cd703d0 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -30,6 +30,9 @@ namespace AdvancedSharpAdbClient /// public partial class AdbClient : IAdbClient { + /// + /// The of s that represent a new line. + /// private static readonly char[] separator = Extensions.NewLineSeparator; /// @@ -52,7 +55,7 @@ public partial class AdbClient : IAdbClient /// /// The to create . /// - protected readonly Func adbSocketFactory; + protected readonly Func AdbSocketFactory; /// /// Initializes a new instance of the class. @@ -96,7 +99,7 @@ public AdbClient(EndPoint endPoint, Func adbSocketFactory) } EndPoint = endPoint; - this.adbSocketFactory = adbSocketFactory ?? throw new ArgumentNullException(nameof(adbSocketFactory)); + this.AdbSocketFactory = adbSocketFactory ?? throw new ArgumentNullException(nameof(adbSocketFactory)); } /// @@ -120,7 +123,7 @@ public AdbClient(Func adbSocketFactory) } /// - /// Get or set default encoding. + /// Gets or sets default encoding. /// public static Encoding Encoding { get; set; } = Encoding.UTF8; @@ -169,7 +172,7 @@ public static byte[] CreateAdbForwardRequest(string address, int port) /// public virtual int GetAdbVersion() { - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); socket.SendAdbRequest("host:version"); _ = socket.ReadAdbResponse(); @@ -181,7 +184,7 @@ public virtual int GetAdbVersion() /// public virtual void KillAdb() { - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); socket.SendAdbRequest("host:kill"); // The host will immediately close the connection after the kill @@ -191,7 +194,7 @@ public virtual void KillAdb() /// public virtual IEnumerable GetDevices() { - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); socket.SendAdbRequest("host:devices-l"); _ = socket.ReadAdbResponse(); @@ -206,7 +209,7 @@ public virtual int CreateForward(DeviceData device, string local, string remote, { EnsureDevice(device); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); string rebind = allowRebind ? string.Empty : "norebind:"; socket.SendAdbRequest($"host-serial:{device.Serial}:forward:{rebind}{local};{remote}"); @@ -222,7 +225,7 @@ public virtual int CreateReverseForward(DeviceData device, string remote, string { EnsureDevice(device); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); socket.SetDevice(device); string rebind = allowRebind ? string.Empty : "norebind:"; @@ -240,7 +243,7 @@ public virtual void RemoveReverseForward(DeviceData device, string remote) { EnsureDevice(device); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); socket.SetDevice(device); socket.SendAdbRequest($"reverse:killforward:{remote}"); @@ -252,7 +255,7 @@ public virtual void RemoveAllReverseForwards(DeviceData device) { EnsureDevice(device); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); socket.SetDevice(device); socket.SendAdbRequest($"reverse:killforward-all"); @@ -264,7 +267,7 @@ public virtual void RemoveForward(DeviceData device, int localPort) { EnsureDevice(device); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); socket.SendAdbRequest($"host-serial:{device.Serial}:killforward:tcp:{localPort}"); _ = socket.ReadAdbResponse(); } @@ -274,7 +277,7 @@ public virtual void RemoveAllForwards(DeviceData device) { EnsureDevice(device); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); socket.SendAdbRequest($"host-serial:{device.Serial}:killforward-all"); _ = socket.ReadAdbResponse(); } @@ -284,7 +287,7 @@ public IEnumerable ListForward(DeviceData device) { EnsureDevice(device); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); socket.SendAdbRequest($"host-serial:{device.Serial}:list-forward"); _ = socket.ReadAdbResponse(); string data = socket.ReadString(); @@ -298,7 +301,7 @@ public IEnumerable ListReverseForward(DeviceData device) { EnsureDevice(device); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); socket.SetDevice(device); socket.SendAdbRequest($"reverse:list-forward"); @@ -313,7 +316,7 @@ public IEnumerable ListReverseForward(DeviceData device) public virtual void ExecuteServerCommand(string target, string command, Encoding encoding) { ExceptionExtensions.ThrowIfNull(encoding); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); ExecuteServerCommand(target, command, socket, encoding); } @@ -339,7 +342,7 @@ public virtual void ExecuteRemoteCommand(string command, DeviceData device, Enco EnsureDevice(device); ExceptionExtensions.ThrowIfNull(encoding); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); socket.SetDevice(device); ExecuteServerCommand("shell", command, socket, encoding); @@ -349,7 +352,7 @@ public virtual void ExecuteRemoteCommand(string command, DeviceData device, Enco public virtual void ExecuteServerCommand(string target, string command, IShellOutputReceiver receiver, Encoding encoding) { ExceptionExtensions.ThrowIfNull(encoding); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); ExecuteServerCommand(target, command, socket, receiver, encoding); } @@ -397,7 +400,7 @@ public virtual void ExecuteRemoteCommand(string command, DeviceData device, IShe { EnsureDevice(device); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); socket.SetDevice(device); ExecuteServerCommand("shell", command, socket, receiver, encoding); @@ -407,7 +410,7 @@ public virtual void ExecuteRemoteCommand(string command, DeviceData device, IShe public Framebuffer CreateRefreshableFramebuffer(DeviceData device) { EnsureDevice(device); - return new Framebuffer(device, this, adbSocketFactory); + return new Framebuffer(device, this, AdbSocketFactory); } /// @@ -430,7 +433,7 @@ public virtual void RunLogService(DeviceData device, Action messageSin // The 'log' service has been deprecated, see // https://android.googlesource.com/platform/system/core/+/7aa39a7b199bb9803d3fd47246ee9530b4a96177 - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); socket.SetDevice(device); StringBuilder request = new StringBuilder().Append("shell:logcat -B"); @@ -475,7 +478,7 @@ public virtual void Reboot(string into, DeviceData device) { EnsureDevice(device); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); socket.SetDevice(device); socket.SendAdbRequest($"reboot:{into}"); @@ -487,7 +490,7 @@ public string Pair(DnsEndPoint endpoint, string code) { ExceptionExtensions.ThrowIfNull(endpoint); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); socket.SendAdbRequest($"host:pair:{code}:{endpoint.Host}:{endpoint.Port}"); _ = socket.ReadAdbResponse(); @@ -499,7 +502,7 @@ public string Connect(DnsEndPoint endpoint) { ExceptionExtensions.ThrowIfNull(endpoint); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); socket.SendAdbRequest($"host:connect:{endpoint.Host}:{endpoint.Port}"); _ = socket.ReadAdbResponse(); @@ -511,7 +514,7 @@ public string Disconnect(DnsEndPoint endpoint) { ExceptionExtensions.ThrowIfNull(endpoint); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); socket.SendAdbRequest($"host:disconnect:{endpoint.Host}:{endpoint.Port}"); _ = socket.ReadAdbResponse(); @@ -533,7 +536,7 @@ protected void Root(string request, DeviceData device) { EnsureDevice(device); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); socket.SetDevice(device); socket.SendAdbRequest(request); _ = socket.ReadAdbResponse(); @@ -562,7 +565,7 @@ protected void Root(string request, DeviceData device) #if HAS_PROCESS && !WINDOWS_UWP Thread.Sleep(3000); #else - Extensions.Delay(3000).Wait(); + TaskExExtensions.Delay(3000).AwaitByTaskCompleteSource(); #endif } } @@ -594,7 +597,7 @@ public void Install(DeviceData device, Stream apk, IProgress GetFeatureSet(DeviceData device) { EnsureDevice(device); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); socket.SendAdbRequest($"host-serial:{device.Serial}:features"); _ = socket.ReadAdbResponse(); string features = socket.ReadString(); diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs index 83ef8015..bb570b55 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs @@ -79,7 +79,7 @@ public virtual Task CheckFileExistsAsync(string adbPath, CancellationToken #if WINDOWS_UWP StorageFile.GetFileFromPathAsync(adbPath).AsTask(cancellationToken).ContinueWith(x => x.Result != null && x.Result.IsOfType(StorageItemTypes.File)); #else - Extensions.FromResult(File.Exists(adbPath)); + TaskExExtensions.FromResult(File.Exists(adbPath)); #endif /// diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.cs index 38f968a4..1f15aa30 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.cs @@ -22,12 +22,15 @@ public partial class AdbCommandLineClient : IAdbCommandLineClient /// protected const string AdbVersionPattern = "^.*(\\d+)\\.(\\d+)\\.(\\d+)$"; + /// + /// The of s that represent a new line. + /// private static readonly char[] separator = Extensions.NewLineSeparator; /// /// The logger to use when logging messages. /// - protected readonly ILogger logger; + private readonly ILogger logger; /// /// Initializes a new instance of the class. @@ -123,7 +126,7 @@ public virtual void StartServer() /// public virtual bool CheckFileExists(string adbPath) => #if WINDOWS_UWP - StorageFile.GetFileFromPathAsync(adbPath).GetResults() is StorageFile file && file.IsOfType(StorageItemTypes.File); + StorageFile.GetFileFromPathAsync(adbPath).AwaitByTaskCompleteSource() is StorageFile file && file.IsOfType(StorageItemTypes.File); #else File.Exists(adbPath); #endif @@ -265,6 +268,10 @@ protected virtual int RunProcess(string filename, string command, ICollection + /// Gets a for parsing the adb version. + /// + /// The for parsing the adb version. private static Regex AdbVersionRegex() => new(AdbVersionPattern); #endif } diff --git a/AdvancedSharpAdbClient/AdbServer.Async.cs b/AdvancedSharpAdbClient/AdbServer.Async.cs index 0ca5e40d..c9ace8f7 100644 --- a/AdvancedSharpAdbClient/AdbServer.Async.cs +++ b/AdvancedSharpAdbClient/AdbServer.Async.cs @@ -21,7 +21,7 @@ public virtual async Task StartServerAsync(string adbPath, bo AdbServerStatus serverStatus = await GetStatusAsync(cancellationToken).ConfigureAwait(false); Version? commandLineVersion = null; - IAdbCommandLineClient commandLineClient = adbCommandLineClientFactory(adbPath); + IAdbCommandLineClient commandLineClient = AdbCommandLineClientFactory(adbPath); if (await commandLineClient.CheckFileExistsAsync(adbPath, cancellationToken).ConfigureAwait(false)) { @@ -76,7 +76,7 @@ public virtual Task RestartServerAsync(string adbPath, Cancel /// public virtual async Task StopServerAsync(CancellationToken cancellationToken = default) { - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); await socket.SendAdbRequestAsync("host:kill", cancellationToken).ConfigureAwait(false); // The host will immediately close the connection after the kill @@ -89,7 +89,7 @@ public virtual async Task GetStatusAsync(CancellationToken canc // Try to connect to a running instance of the adb server try { - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); await socket.SendAdbRequestAsync("host:version", cancellationToken).ConfigureAwait(false); AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); string version = await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); diff --git a/AdvancedSharpAdbClient/AdbServer.cs b/AdvancedSharpAdbClient/AdbServer.cs index 714d7120..6d16e6d9 100644 --- a/AdvancedSharpAdbClient/AdbServer.cs +++ b/AdvancedSharpAdbClient/AdbServer.cs @@ -45,14 +45,14 @@ public partial class AdbServer : IAdbServer /// /// The to create . /// - protected readonly Func adbSocketFactory; + protected readonly Func AdbSocketFactory; /// /// Gets or sets a function that returns a new instance of a class that implements the /// interface, that can be used to interact with the /// adb.exe command line client. /// - protected readonly Func adbCommandLineClientFactory; + protected readonly Func AdbCommandLineClientFactory; /// /// Initializes a new instance of the class. @@ -117,8 +117,8 @@ public AdbServer(EndPoint endPoint, Func adbSocketFactory, } EndPoint = endPoint; - this.adbSocketFactory = adbSocketFactory ?? throw new ArgumentNullException(nameof(adbSocketFactory)); - this.adbCommandLineClientFactory = adbCommandLineClientFactory ?? throw new ArgumentNullException(nameof(adbCommandLineClientFactory)); + this.AdbSocketFactory = adbSocketFactory ?? throw new ArgumentNullException(nameof(adbSocketFactory)); + this.AdbCommandLineClientFactory = adbCommandLineClientFactory ?? throw new ArgumentNullException(nameof(adbCommandLineClientFactory)); } /// @@ -173,7 +173,7 @@ public virtual StartServerResult StartServer(string adbPath, bool restartServerI AdbServerStatus serverStatus = GetStatus(); Version? commandLineVersion = null; - IAdbCommandLineClient commandLineClient = adbCommandLineClientFactory(adbPath); + IAdbCommandLineClient commandLineClient = AdbCommandLineClientFactory(adbPath); if (commandLineClient.CheckFileExists(adbPath)) { @@ -227,7 +227,7 @@ public virtual StartServerResult RestartServer(string adbPath) => /// public virtual void StopServer() { - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); socket.SendAdbRequest("host:kill"); // The host will immediately close the connection after the kill @@ -240,7 +240,7 @@ public virtual AdbServerStatus GetStatus() // Try to connect to a running instance of the adb server try { - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); socket.SendAdbRequest("host:version"); AdbResponse response = socket.ReadAdbResponse(); string version = socket.ReadString(); diff --git a/AdvancedSharpAdbClient/AdbSocket.cs b/AdvancedSharpAdbClient/AdbSocket.cs index 9fa0a53c..5d53fbed 100644 --- a/AdvancedSharpAdbClient/AdbSocket.cs +++ b/AdvancedSharpAdbClient/AdbSocket.cs @@ -26,7 +26,7 @@ public partial class AdbSocket : IAdbSocket /// /// The logger to use when logging messages. /// - protected readonly ILogger logger; + private readonly ILogger logger; /// /// Initializes a new instance of the class. diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.Async.cs index 282632c4..51a2ee06 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.Async.cs @@ -233,7 +233,7 @@ public virtual async Task IsAppInForegroundAsync(string packageName, Cance } /// - /// Get the of the app. + /// Gets the of the app. /// /// The package name of the app to check. /// A which can be used to cancel the asynchronous operation. @@ -253,7 +253,7 @@ public virtual async Task GetAppStatusAsync(string packageName, Cance } /// - /// Get element by xpath asynchronously. You can specify the waiting time in timeout. + /// Gets element by xpath asynchronously. You can specify the waiting time in timeout. /// /// The xpath of the elements. /// A which can be used to cancel the asynchronous operation. @@ -302,7 +302,7 @@ public virtual async Task GetAppStatusAsync(string packageName, Cance } /// - /// Get elements by xpath asynchronously. You can specify the waiting time in timeout. + /// Gets elements by xpath asynchronously. You can specify the waiting time in timeout. /// /// The xpath of the elements. /// A which can be used to cancel the asynchronous operation. @@ -359,7 +359,7 @@ static IEnumerable FindElements(IAdbClient client, DeviceData device, X #if NETCOREAPP3_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER /// - /// Get elements by xpath asynchronously. You can specify the waiting time in timeout. + /// Gets elements by xpath asynchronously. You can specify the waiting time in timeout. /// /// The xpath of the elements. /// A which can be used to cancel the asynchronous operation. diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.cs index d5963db5..06c83e90 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.cs @@ -247,7 +247,7 @@ public virtual bool IsAppInForeground(string packageName) } /// - /// Get the of the app. + /// Gets the of the app. /// /// The package name of the app to check. /// The of the app. Foreground, stopped or running in background. @@ -266,7 +266,7 @@ public virtual AppStatus GetAppStatus(string packageName) } /// - /// Get element by xpath. You can specify the waiting time in timeout. + /// Gets element by xpath. You can specify the waiting time in timeout. /// /// The xpath of the element. /// The timeout for waiting the element. @@ -305,7 +305,7 @@ public virtual AppStatus GetAppStatus(string packageName) } /// - /// Get elements by xpath. You can specify the waiting time in timeout. + /// Gets elements by xpath. You can specify the waiting time in timeout. /// /// The xpath of the elements. /// The timeout for waiting the elements. @@ -419,6 +419,10 @@ public virtual void Deconstruct(out IAdbClient client, out DeviceData device) [GeneratedRegex("<\\?xml(.?)*")] private static partial Regex GetXmlRegex(); #else + /// + /// Gets a for parsing the xml. + /// + /// The for parsing the xml. private static Regex GetXmlRegex() => new("<\\?xml(.?)*"); #endif } diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs index a0dac92a..b1231189 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs @@ -66,7 +66,7 @@ public static void Swipe(this IAdbClient client, DeviceData device, Point first, new DeviceClient(client, device).Swipe(first, second, speed); /// - /// Get the of the app. + /// Gets the of the app. /// /// An instance of a class that implements the interface. /// The device on which to clear the input text. @@ -76,7 +76,7 @@ public static AppStatus GetAppStatus(this IAdbClient client, DeviceData device, new DeviceClient(client, device).GetAppStatus(packageName); /// - /// Get element by xpath. You can specify the waiting time in timeout. + /// Gets element by xpath. You can specify the waiting time in timeout. /// /// An instance of a class that implements the interface. /// The device on which to clear the input text. @@ -88,7 +88,7 @@ public static AppStatus GetAppStatus(this IAdbClient client, DeviceData device, new DeviceClient(client, device).FindElement(xpath, timeout); /// - /// Get elements by xpath. You can specify the waiting time in timeout. + /// Gets elements by xpath. You can specify the waiting time in timeout. /// /// An instance of a class that implements the interface. /// The device on which to clear the input text. diff --git a/AdvancedSharpAdbClient/DeviceCommands/LinuxPath.cs b/AdvancedSharpAdbClient/DeviceCommands/LinuxPath.cs index 0196c1eb..5336a840 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/LinuxPath.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/LinuxPath.cs @@ -26,7 +26,10 @@ public static partial class LinuxPath /// private const string EscapePattern = "([\\\\()*+?\"'#/\\s])"; - private static readonly char[] InvalidCharacters = ['|', '\\', '?', '*', '<', '\"', ':', '>']; + /// + /// The of s which are invalid in a path. + /// + public static readonly char[] InvalidCharacters = ['|', '\\', '?', '*', '<', '\"', ':', '>']; /// /// Combine the specified paths to form one path. @@ -246,6 +249,10 @@ private static string FixupPath(string path) [GeneratedRegex(EscapePattern)] private static partial Regex EscapeRegex(); #else + /// + /// Gets a to escape filenames for shell command consumption. + /// + /// The to escape filenames for shell command consumption. private static Regex EscapeRegex() => new(EscapePattern); #endif } diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs index 9eca31f2..32498e26 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs @@ -602,12 +602,32 @@ public AndroidProcess(string line, bool cmdLinePrefix = false) /// A that represents this . public override string ToString() => $"{Name} ({ProcessId})"; + /// + /// Converts the string representation of a number to its 32-bit signed integer equivalent. + /// + /// A string containing a number to convert. + /// A 32-bit signed integer equivalent to the number contained in . private static int ParseInt(string value) => value == "-" ? 0 : int.Parse(value); + /// + /// Converts the string representation of a number to its 32-bit unsigned integer equivalent. + /// + /// A string representing the number to convert. + /// A 32-bit unsigned integer equivalent to the number contained in . private static uint ParseUInt(string value) => value == "-" ? 0 : uint.Parse(value); + /// + /// Converts the string representation of a number to its 64-bit signed integer equivalent. + /// + /// A string containing a number to convert. + /// A 64-bit signed integer equivalent to the number contained in . private static long ParseLong(string value) => value == "-" ? 0 : long.Parse(value); + /// + /// Converts the string representation of a number to its 64-bit unsigned integer equivalent. + /// + /// A string that represents the number to convert. + /// A 64-bit unsigned integer equivalent to the number contained in . private static ulong ParseULong(string value) => value == "-" ? 0 : ulong.Parse(value); } } diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/Element.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/Element.cs index 6a57c627..727783ea 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/Element.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/Element.cs @@ -16,6 +16,9 @@ namespace AdvancedSharpAdbClient.DeviceCommands.Models /// public class Element : IEquatable { + /// + /// The of s that separate the coordinates of the element. + /// private static readonly char[] separator = ['[', ']', ',', ' ']; /// diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs index 3a9abb57..6533dc2c 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs @@ -162,7 +162,7 @@ void OnSplitSyncProgressChanged(string? sender, SyncProgressChangedEventArgs arg progress?.Report(new InstallProgressEventArgs(0, splitRemoteFilePaths.Length + 1, PackageInstallProgressState.PostInstall)); int count = 0; - await Extensions.WhenAll(splitRemoteFilePaths.Select(async x => + await TaskExExtensions.WhenAll(splitRemoteFilePaths.Select(async x => { count++; await RemoveRemotePackageAsync(x, cancellationToken).ConfigureAwait(false); @@ -233,7 +233,7 @@ void OnSyncProgressChanged(string? sender, SyncProgressChangedEventArgs args) progress?.Report(new InstallProgressEventArgs(0, splitRemoteFilePaths.Length, PackageInstallProgressState.PostInstall)); int count = 0; - await Extensions.WhenAll(splitRemoteFilePaths.Select(async x => + await TaskExExtensions.WhenAll(splitRemoteFilePaths.Select(async x => { count++; await RemoveRemotePackageAsync(x, cancellationToken).ConfigureAwait(false); @@ -275,7 +275,7 @@ public virtual async Task InstallMultipleRemotePackageAsync(string baseRemoteFil progress?.Report(new InstallProgressEventArgs(1, splitRemoteFileCount + 1, PackageInstallProgressState.WriteSession)); int count = 0; - await Extensions.WhenAll(splitRemoteFilePaths.Select(async (splitRemoteFilePath) => + await TaskExExtensions.WhenAll(splitRemoteFilePaths.Select(async (splitRemoteFilePath) => { await WriteInstallSessionAsync(session, $"split{count++}", splitRemoteFilePath, cancellationToken).ConfigureAwait(false); progress?.Report(new InstallProgressEventArgs(count, splitRemoteFileCount + 1, PackageInstallProgressState.WriteSession)); @@ -320,7 +320,7 @@ public virtual async Task InstallMultipleRemotePackageAsync(IEnumerable progress?.Report(new InstallProgressEventArgs(0, splitRemoteFileCount, PackageInstallProgressState.WriteSession)); int count = 0; - await Extensions.WhenAll(splitRemoteFilePaths.Select(async (splitRemoteFilePath) => + await TaskExExtensions.WhenAll(splitRemoteFilePaths.Select(async (splitRemoteFilePath) => { await WriteInstallSessionAsync(session, $"split{count++}", splitRemoteFilePath, cancellationToken).ConfigureAwait(false); progress?.Report(new InstallProgressEventArgs(count, splitRemoteFileCount, PackageInstallProgressState.WriteSession)); @@ -408,7 +408,7 @@ protected virtual Task GetFileStreamAsync(string path, CancellationToken #if WINDOWS_UWP StorageFile.GetFileFromPathAsync(path).AsTask(cancellationToken).ContinueWith(x => x.Result.OpenReadAsync().AsTask(cancellationToken)).Unwrap().ContinueWith(x => x.Result.AsStream()); #else - Extensions.FromResult(File.OpenRead(path)); + TaskExExtensions.FromResult(File.OpenRead(path)); #endif /// @@ -436,7 +436,7 @@ protected virtual async Task SyncPackageToDeviceAsync(string localFilePa logger.LogDebug("Uploading {0} onto device '{1}'", packageFileName, Device.Serial); - using (ISyncService sync = syncServiceFactory(AdbClient, Device)) + using (ISyncService sync = SyncServiceFactory(AdbClient, Device)) { #if NETCOREAPP3_0_OR_GREATER await diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs index 70db1c20..611f44d0 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs @@ -29,14 +29,14 @@ public partial class PackageManager /// /// The logger to use when logging messages. /// - protected readonly ILogger logger; + private readonly ILogger logger; /// /// A function which returns a new instance of a class /// that implements the interface, /// that can be used to transfer files to and from a given device. /// - protected readonly Func syncServiceFactory; + protected readonly Func SyncServiceFactory; /// /// Initializes a new instance of the class. @@ -57,7 +57,7 @@ public PackageManager(IAdbClient client, DeviceData device, FuncA read-only on the specified path. protected virtual Stream GetFileStream(string path) => #if WINDOWS_UWP - StorageFile.GetFileFromPathAsync(path).GetResults().OpenReadAsync().GetResults().AsStream(); + StorageFile.GetFileFromPathAsync(path).AwaitByTaskCompleteSource().OpenReadAsync().AwaitByTaskCompleteSource().AsStream(); #else File.OpenRead(path); #endif @@ -484,7 +484,7 @@ protected virtual string SyncPackageToDevice(string localFilePath, Action + /// Gets a which can be used to parse the output of the printenv command. + /// + /// The which can be used to parse the output of the printenv command. private static Regex EnvRegex() => new(EnvPattern); #endif } diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs index 8117f9fe..3c6722ca 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs @@ -74,6 +74,10 @@ public override bool AddOutput(string line) [GeneratedRegex(GetPropPattern)] private static partial Regex GetPropRegex(); #else + /// + /// Gets a which can be used to parse the getprop output. + /// + /// The which can be used to parse the getprop output. private static Regex GetPropRegex() => new(GetPropPattern); #endif } diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs index 909bf6ef..9bbcd962 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs @@ -134,10 +134,22 @@ public override bool AddOutput(string line) [GeneratedRegex(ErrorPattern, RegexOptions.IgnoreCase)] private static partial Regex ErrorRegex(); #else + /// + /// Gets a that matches output of the success. + /// + /// The that matches output of the success. private static Regex SuccessRegex() => new(SuccessPattern, RegexOptions.IgnoreCase); + /// + /// Gets a that matches output that indicates a failure. + /// + /// The that matches output that indicates a failure. private static Regex FailureRegex() => new(FailurePattern, RegexOptions.IgnoreCase); + /// + /// Gets a that matches output that indicates a error. + /// + /// The that matches output that indicates a error. private static Regex ErrorRegex() => new(ErrorPattern, RegexOptions.IgnoreCase); #endif } diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/VersionInfoReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/VersionInfoReceiver.cs index 73398d33..b03280ad 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/VersionInfoReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/VersionInfoReceiver.cs @@ -15,12 +15,12 @@ public partial class VersionInfoReceiver : InfoOutputReceiver /// /// The name of the version code property. /// - private const string versionCode = "VersionCode"; + private const string VersionCode = nameof(VersionCode); /// /// The name of the version name property. /// - private const string versionName = "VersionName"; + private const string VersionName = nameof(VersionName); /// /// A regular expression that can be used to parse the version code. @@ -37,21 +37,25 @@ public partial class VersionInfoReceiver : InfoOutputReceiver /// public VersionInfoReceiver() { - AddPropertyParser(versionCode, GetVersionCode); - AddPropertyParser(versionName, GetVersionName); + AddPropertyParser(VersionCode, GetVersionCode); + AddPropertyParser(VersionName, GetVersionName); } /// /// Gets the version code of the specified package. /// public VersionInfo VersionInfo => - GetPropertyValue(versionName) is string name - ? new VersionInfo(GetPropertyValue(versionCode), name) + GetPropertyValue(VersionName) is string name + ? new VersionInfo(GetPropertyValue(VersionCode), name) : default; + /// + /// Checks whether we're in the packages section of the dumpsys package output. + /// + /// The line to be checked. private void CheckPackagesSection(string line) { - // This method check whether we're in the packages section of the dumpsys package output. + // This method checks whether we're in the packages section of the dumpsys package output. // See gapps.txt for what the output looks for. Each section starts with a header // which looks like: // @@ -120,6 +124,10 @@ private void CheckPackagesSection(string line) [GeneratedRegex(VersionCodePattern)] private static partial Regex VersionCodeRegex(); #else + /// + /// Gets a that can be used to parse the version code. + /// + /// The that can be used to parse the version code. private static Regex VersionCodeRegex() => new(VersionCodePattern); #endif } diff --git a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs index 9db786e9..e6b5d445 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs @@ -17,30 +17,36 @@ public partial class DeviceMonitor /// is used to block the method until the /// has processed the first list of devices. /// - protected readonly ManualResetEvent firstDeviceListParsed = new(false); + protected TaskCompletionSource? FirstDeviceListParsed; /// - /// A that can be used to cancel the . + /// A that can be used to cancel the . /// - protected readonly CancellationTokenSource monitorTaskCancellationTokenSource = new(); + protected readonly CancellationTokenSource MonitorTaskCancellationTokenSource = new(); /// /// The that monitors the and waits for device notifications. /// - protected Task? monitorTask; + protected Task? MonitorTask; /// - [MemberNotNull(nameof(monitorTask))] + [MemberNotNull(nameof(MonitorTask))] public virtual async Task StartAsync(CancellationToken cancellationToken = default) { - if (monitorTask == null) + if (MonitorTask == null) { - _ = firstDeviceListParsed.Reset(); - - monitorTask = DeviceMonitorLoopAsync(monitorTaskCancellationTokenSource.Token); - - // Wait for the worker thread to have read the first list of devices. - _ = await Extensions.Run(firstDeviceListParsed.WaitOne, cancellationToken).ConfigureAwait(false); + try + { + FirstDeviceListParsed = new TaskCompletionSource(); + cancellationToken.Register(() => FirstDeviceListParsed.SetCanceled()); + MonitorTask = DeviceMonitorLoopAsync(MonitorTaskCancellationTokenSource.Token); + // Wait for the worker thread to have read the first list of devices. + _ = await FirstDeviceListParsed.Task.ConfigureAwait(false); + } + finally + { + FirstDeviceListParsed = null; + } } } @@ -53,18 +59,18 @@ protected virtual async Task DisposeAsyncCore() // First kill the monitor task, which has a dependency on the socket, // then close the socket. - if (monitorTask != null) + if (MonitorTask != null) { IsRunning = false; // Stop the thread. The tread will keep waiting for updated information from adb // eternally, so we need to forcefully abort it here. - monitorTaskCancellationTokenSource.Cancel(); - await monitorTask.ConfigureAwait(false); + MonitorTaskCancellationTokenSource.Cancel(); + await MonitorTask.ConfigureAwait(false); #if HAS_PROCESS - monitorTask.Dispose(); + MonitorTask.Dispose(); #endif - monitorTask = null; + MonitorTask = null; } // Close the connection to adb. To be done after the monitor task exited. @@ -74,8 +80,7 @@ protected virtual async Task DisposeAsyncCore() Socket = null!; } - firstDeviceListParsed.Dispose(); - monitorTaskCancellationTokenSource.Dispose(); + MonitorTaskCancellationTokenSource.Dispose(); disposed = true; } @@ -117,7 +122,7 @@ public async Task AsyncDispose() protected virtual async Task DeviceMonitorLoopAsync(CancellationToken cancellationToken = default) { IsRunning = true; - await Extensions.Yield(); + await TaskExExtensions.Yield(); // Set up the connection to track the list of devices. await InitializeSocketAsync(cancellationToken).ConfigureAwait(false); @@ -128,8 +133,7 @@ protected virtual async Task DeviceMonitorLoopAsync(CancellationToken cancellati { string value = await Socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); ProcessIncomingDeviceData(value); - - firstDeviceListParsed.Set(); + FirstDeviceListParsed?.TrySetResult(null); } catch (TaskCanceledException ex) { @@ -220,6 +224,11 @@ protected virtual async Task DeviceMonitorLoopAsync(CancellationToken cancellati while (!cancellationToken.IsCancellationRequested); } + /// + /// Initializes the and sends the host:track-devices command to the adb server. + /// + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. private async Task InitializeSocketAsync(CancellationToken cancellationToken) { // Set up the connection to track the list of devices. diff --git a/AdvancedSharpAdbClient/DeviceMonitor.cs b/AdvancedSharpAdbClient/DeviceMonitor.cs index 15c8dbe8..02ef64d0 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.cs @@ -38,19 +38,25 @@ public partial class DeviceMonitor : IDeviceMonitor , IAsyncDisposable #endif { + /// + /// The of s that represent a new line. + /// private static readonly char[] separator = Extensions.NewLineSeparator; + /// + /// The that indicates whether this instance has been disposed. + /// private bool disposed = false; /// /// The logger to use when logging messages. /// - protected readonly ILogger logger; + private readonly ILogger logger; /// /// The list of devices currently connected to the Android Debug Bridge. /// - protected readonly List devices; + protected readonly List devices = []; #if !HAS_TASK /// @@ -58,23 +64,23 @@ public partial class DeviceMonitor : IDeviceMonitor /// is used to block the method until the /// has processed the first list of devices. /// - protected readonly ManualResetEvent firstDeviceListParsed = new(false); + protected readonly ManualResetEvent FirstDeviceListParsed = new(false); /// /// When the method is called, this /// is used to block until the is finished. /// - protected readonly ManualResetEvent monitorLoopFinished = new(false); + protected readonly ManualResetEvent MonitorLoopFinished = new(false); /// - /// A that can be used to cancel the . + /// A that can be used to cancel the . /// - protected bool isMonitorThreadCancel = false; + protected bool IsMonitorThreadCancel = false; /// /// The that monitors the and waits for device notifications. /// - protected Thread? monitorThread; + protected Thread? MonitorThread; #endif /// @@ -119,7 +125,6 @@ public DeviceMonitor(EndPoint endPoint, ILogger? logger = null) public DeviceMonitor(IAdbSocket socket, ILogger? logger = null) { Socket = socket ?? throw new ArgumentNullException(nameof(socket)); - devices = []; Devices = devices.AsReadOnly(); this.logger = logger ?? LoggerProvider.CreateLogger(); } @@ -142,37 +147,42 @@ public DeviceMonitor(IAdbSocket socket, ILogger? logger = null) /// [MemberNotNull( #if HAS_TASK - nameof(monitorTask) + nameof(MonitorTask) #else - nameof(monitorThread) + nameof(MonitorThread) #endif )] public virtual void Start() { #if HAS_TASK - if (monitorTask == null) + if (MonitorTask == null) { - _ = firstDeviceListParsed.Reset(); - - monitorTask = DeviceMonitorLoopAsync(monitorTaskCancellationTokenSource.Token); - - // Wait for the worker thread to have read the first list of devices. - _ = firstDeviceListParsed.WaitOne(); + try + { + FirstDeviceListParsed = new TaskCompletionSource(); + MonitorTask = DeviceMonitorLoopAsync(MonitorTaskCancellationTokenSource.Token); + // Wait for the worker thread to have read the first list of devices. + _ = FirstDeviceListParsed.Task.AwaitByTaskCompleteSource(); + } + finally + { + FirstDeviceListParsed = null; + } } #else - if (monitorThread == null) + if (MonitorThread == null) { - _ = firstDeviceListParsed.Reset(); + _ = FirstDeviceListParsed.Reset(); - monitorThread = new Thread(DeviceMonitorLoop) + MonitorThread = new Thread(DeviceMonitorLoop) { Name = nameof(DeviceMonitorLoop), IsBackground = true }; - monitorThread.Start(); + MonitorThread.Start(); // Wait for the worker thread to have read the first list of devices. - _ = firstDeviceListParsed.WaitOne(); + _ = FirstDeviceListParsed.WaitOne(); } #endif } @@ -186,18 +196,18 @@ protected virtual void Dispose(bool disposing) #if HAS_TASK // First kill the monitor task, which has a dependency on the socket, // then close the socket. - if (monitorTask != null) + if (MonitorTask != null) { IsRunning = false; // Stop the thread. The tread will keep waiting for updated information from adb // eternally, so we need to forcefully abort it here. - monitorTaskCancellationTokenSource.Cancel(); - monitorTask.Wait(); + MonitorTaskCancellationTokenSource.Cancel(); + MonitorTask.AwaitByTaskCompleteSource(); #if HAS_PROCESS - monitorTask.Dispose(); + MonitorTask.Dispose(); #endif - monitorTask = null; + MonitorTask = null; } // Close the connection to adb. To be done after the monitor task exited. @@ -207,22 +217,21 @@ protected virtual void Dispose(bool disposing) Socket = null!; } - firstDeviceListParsed.Dispose(); - monitorTaskCancellationTokenSource.Dispose(); + MonitorTaskCancellationTokenSource.Dispose(); #else // First kill the monitor task, which has a dependency on the socket, // then close the socket. - if (monitorThread != null) + if (MonitorThread != null) { IsRunning = false; // Stop the thread. The tread will keep waiting for updated information from adb // eternally, so we need to forcefully abort it here. - isMonitorThreadCancel = true; + IsMonitorThreadCancel = true; Socket?.Close(); - _ = monitorLoopFinished.WaitOne(); + _ = MonitorLoopFinished.WaitOne(); - monitorThread = null; + MonitorThread = null; } // Close the connection to adb. To be done after the monitor task exited. @@ -232,7 +241,7 @@ protected virtual void Dispose(bool disposing) Socket = null!; } - firstDeviceListParsed.Close(); + FirstDeviceListParsed.Close(); #endif disposed = true; } @@ -251,7 +260,7 @@ public void Dispose() protected virtual void DeviceMonitorLoop() { IsRunning = true; - monitorLoopFinished.Reset(); + MonitorLoopFinished.Reset(); try { @@ -265,13 +274,13 @@ protected virtual void DeviceMonitorLoop() string value = Socket.ReadString(); ProcessIncomingDeviceData(value); - firstDeviceListParsed.Set(); + FirstDeviceListParsed.Set(); } catch (AdbException adbException) { if (adbException.InnerException is SocketException ex) { - if (isMonitorThreadCancel) + if (IsMonitorThreadCancel) { // The DeviceMonitor is shutting down (disposing) and Dispose() // has called Socket.Close(). This exception is expected, @@ -299,15 +308,18 @@ protected virtual void DeviceMonitorLoop() } } } - while (!isMonitorThreadCancel); - isMonitorThreadCancel = false; + while (!IsMonitorThreadCancel); + IsMonitorThreadCancel = false; } finally { - monitorLoopFinished.Set(); + MonitorLoopFinished.Set(); } } - + + /// + /// Initializes the and sends the host:track-devices command to the adb server. + /// private void InitializeSocket() { // Set up the connection to track the list of devices. diff --git a/AdvancedSharpAdbClient/Exceptions/JavaException.cs b/AdvancedSharpAdbClient/Exceptions/JavaException.cs index 83d63312..85f29218 100644 --- a/AdvancedSharpAdbClient/Exceptions/JavaException.cs +++ b/AdvancedSharpAdbClient/Exceptions/JavaException.cs @@ -11,11 +11,24 @@ namespace AdvancedSharpAdbClient.Exceptions [Serializable] public partial class JavaException : Exception { + /// + /// Unknown error message. + /// private const string UnknownError = "An error occurred in Java"; + /// + /// The output of the Java exception. + /// private const string ExceptionOutput = "java.lang."; + + /// + /// The pattern of the Java exception. + /// private const string ExceptionPattern = @"java.lang.(\w+Exception):\s+(.*)?"; + /// + /// The of s that represent a new line. + /// private static readonly char[] separator = Extensions.NewLineSeparator; /// @@ -125,6 +138,10 @@ public static JavaException Parse(IEnumerable lines) [GeneratedRegex(ExceptionPattern, RegexOptions.IgnoreCase)] private static partial Regex ExceptionRegex(); #else + /// + /// Gets a that can be used to parse the Java exception. + /// + /// The that can be used to parse the Java exception. private static Regex ExceptionRegex() => new(ExceptionPattern, RegexOptions.IgnoreCase); #endif } diff --git a/AdvancedSharpAdbClient/Extensions/Extensions.cs b/AdvancedSharpAdbClient/Extensions/Extensions.cs index d6dd2604..7d6463e2 100644 --- a/AdvancedSharpAdbClient/Extensions/Extensions.cs +++ b/AdvancedSharpAdbClient/Extensions/Extensions.cs @@ -12,10 +12,6 @@ using System.Runtime.InteropServices; using System.Threading; -#if NET40 -using Microsoft.Runtime.CompilerServices; -#endif - namespace AdvancedSharpAdbClient { /// @@ -24,6 +20,9 @@ namespace AdvancedSharpAdbClient [EditorBrowsable(EditorBrowsableState.Never)] internal static class Extensions { + /// + /// The of s that represent a new line. + /// public static char[] NewLineSeparator { get; } = ['\r', '\n']; /// @@ -47,114 +46,6 @@ public static DnsEndPoint CreateDnsEndPoint(string host, int port) } #if HAS_TASK -#if NETFRAMEWORK && !NET46_OR_GREATER - /// - /// Singleton cached task that's been completed successfully. - /// - internal static readonly Task s_cachedCompleted = -#if NET45_OR_GREATER - Task. -#else - TaskEx. -#endif - FromResult(null); - - /// - /// Gets a task that's already been completed successfully. - /// - public static Task CompletedTask => s_cachedCompleted; -#else - public static Task CompletedTask => Task.CompletedTask; -#endif - - /// - /// Creates a task that completes after a specified number of milliseconds. - /// - /// The number of milliseconds to wait before completing the returned task, or -1 to wait indefinitely. - /// A cancellation token to observe while waiting for the task to complete. - /// A task that represents the time delay. - /// The argument is less than -1. - public static Task Delay(int dueTime, CancellationToken cancellationToken = default) => -#if NETFRAMEWORK && !NET45_OR_GREATER - TaskEx -#else - Task -#endif - .Delay(dueTime, cancellationToken); - - /// - /// Queues the specified work to run on the thread pool and returns a proxy for the - /// returned by function. A cancellation token allows the work to be cancelled if it has not yet started. - /// - /// The type of the result returned by the proxy task. - /// The work to execute asynchronously. - /// A cancellation token that can be used to cancel the work if it has not yet started. - /// A that represents a proxy for the - /// returned by . - /// The parameter was . - public static Task Run(Func function, CancellationToken cancellationToken = default) => -#if NETFRAMEWORK && !NET45_OR_GREATER - TaskEx -#else - Task -#endif - .Run(function, cancellationToken); - - /// - /// Creates a task that will complete when all of the objects in an enumerable collection have completed. - /// - /// The tasks to wait on for completion. - /// A task that represents the completion of all of the supplied tasks. - public static Task WhenAll(IEnumerable tasks) => -#if NETFRAMEWORK && !NET45_OR_GREATER - TaskEx -#else - Task -#endif - .WhenAll(tasks); - - /// - /// Creates a task that will complete when all of the objects in an enumerable collection have completed. - /// - /// The type of the completed task. - /// The tasks to wait on for completion. - /// A task that represents the completion of all of the supplied tasks. - public static Task WhenAll(IEnumerable> tasks) => -#if NETFRAMEWORK && !NET45_OR_GREATER - TaskEx -#else - Task -#endif - .WhenAll(tasks); - - /// - /// Creates an awaitable task that asynchronously yields back to the current context when awaited. - /// - /// A context that, when awaited, will asynchronously transition back into the current context at the time of the await. - /// If the current is non-null, it is treated as the current context. Otherwise, the task scheduler - /// that is associated with the currently executing task is treated as the current context. - public static YieldAwaitable Yield() => -#if NETFRAMEWORK && !NET45_OR_GREATER - TaskEx -#else - Task -#endif - .Yield(); - - /// - /// Creates a System.Threading.Tasks.Task`1 that's completed successfully with the specified result. - /// - /// The type of the result returned by the task. - /// The result to store into the completed task. - /// The successfully completed task. - public static Task FromResult(TResult result) => -#if NETFRAMEWORK && !NET45_OR_GREATER - TaskEx -#else - Task -#endif - .FromResult(result); - #if !NET7_0_OR_GREATER /// /// Reads a line of characters asynchronously and returns the data as a string. @@ -168,10 +59,9 @@ public static Task FromResult(TResult result) => { using CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.Register(reader.Close); #if NET35 - await Yield(); - return reader.ReadLine(); + return await Task.Factory.StartNew(() => reader.ReadLine(), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default); #else - return await reader.ReadLineAsync(); + return await reader.ReadLineAsync().ConfigureAwait(false); #endif } @@ -187,10 +77,9 @@ public static async Task ReadToEndAsync(this TextReader reader, Cancella { using CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.Register(reader.Close); #if NET35 - await Yield(); - return reader.ReadToEnd(); + return await Task.Factory.StartNew(() => reader.ReadToEnd(), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).ConfigureAwait(false); #else - return await reader.ReadToEndAsync(); + return await reader.ReadToEndAsync().ConfigureAwait(false); #endif } #endif diff --git a/AdvancedSharpAdbClient/Logs/LogReader.Async.cs b/AdvancedSharpAdbClient/Logs/LogReader.Async.cs index 3289d5de..2628b9c1 100644 --- a/AdvancedSharpAdbClient/Logs/LogReader.Async.cs +++ b/AdvancedSharpAdbClient/Logs/LogReader.Async.cs @@ -176,27 +176,46 @@ or LogId.Radio }; } } + + /// + /// Asynchronously reads a from the stream. + /// + /// A which can be used to cancel the asynchronous operation. + /// A which return the value. private async Task ReadUInt16Async(CancellationToken cancellationToken = default) { byte[]? data = await ReadBytesSafeAsync(2, cancellationToken).ConfigureAwait(false); - return data == null ? null : BitConverter.ToUInt16(data, 0); } + /// + /// Asynchronously reads a from the stream. + /// + /// A which can be used to cancel the asynchronous operation. + /// A which return the value. private async Task ReadUInt32Async(CancellationToken cancellationToken = default) { byte[]? data = await ReadBytesSafeAsync(4, cancellationToken).ConfigureAwait(false); - return data == null ? null : BitConverter.ToUInt32(data, 0); } + /// + /// Asynchronously reads a from the stream. + /// + /// A which can be used to cancel the asynchronous operation. + /// A which return the value. private async Task ReadInt32Async(CancellationToken cancellationToken = default) { byte[]? data = await ReadBytesSafeAsync(4, cancellationToken).ConfigureAwait(false); - return data == null ? null : BitConverter.ToInt32(data, 0); } + /// + /// Asynchronously bytes from the stream, making sure that the requested number of bytes + /// + /// The number of bytes to read. + /// A which can be used to cancel the asynchronous operation. + /// A which return the array. private async Task ReadBytesSafeAsync(int count, CancellationToken cancellationToken = default) { int totalRead = 0; diff --git a/AdvancedSharpAdbClient/Logs/LogReader.cs b/AdvancedSharpAdbClient/Logs/LogReader.cs index 38f030c0..120b263a 100644 --- a/AdvancedSharpAdbClient/Logs/LogReader.cs +++ b/AdvancedSharpAdbClient/Logs/LogReader.cs @@ -223,6 +223,7 @@ protected void ReadLogEntry(BinaryReader reader, ICollection parent) /// /// Reads a from the stream. /// + /// The that was read. protected ushort? ReadUInt16() { byte[]? data = ReadBytesSafe(2); @@ -238,6 +239,7 @@ protected void ReadLogEntry(BinaryReader reader, ICollection parent) /// /// Reads a from the stream. /// + /// The that was read. protected uint? ReadUInt32() { byte[]? data = ReadBytesSafe(4); @@ -253,6 +255,7 @@ protected void ReadLogEntry(BinaryReader reader, ICollection parent) /// /// Reads a from the stream. /// + /// The that was read. protected int? ReadInt32() { byte[]? data = ReadBytesSafe(4); @@ -269,6 +272,7 @@ protected void ReadLogEntry(BinaryReader reader, ICollection parent) /// Reads bytes from the stream, making sure that the requested number of bytes /// /// The number of bytes to read. + /// The bytes that were read. protected byte[]? ReadBytesSafe(int count) { int totalRead = 0; diff --git a/AdvancedSharpAdbClient/Logs/LoggerProvider.cs b/AdvancedSharpAdbClient/Logs/LoggerProvider.cs index 2f8c10ba..cc537f48 100644 --- a/AdvancedSharpAdbClient/Logs/LoggerProvider.cs +++ b/AdvancedSharpAdbClient/Logs/LoggerProvider.cs @@ -9,6 +9,9 @@ namespace AdvancedSharpAdbClient.Logs /// public static class LoggerProvider { + /// + /// The current log provider. + /// private static ILoggerFactory? _loggerFactory = null; /// diff --git a/AdvancedSharpAdbClient/Models/DeviceData.cs b/AdvancedSharpAdbClient/Models/DeviceData.cs index 6bced8e0..9a2df33b 100644 --- a/AdvancedSharpAdbClient/Models/DeviceData.cs +++ b/AdvancedSharpAdbClient/Models/DeviceData.cs @@ -143,7 +143,7 @@ public override int GetHashCode() public override string ToString() => Serial; /// - /// Get the device state from the string value. + /// Gets the device state from the string value. /// /// The device state string. /// The device state. @@ -178,6 +178,10 @@ public static DeviceState GetStateFromString(string state) [GeneratedRegex(DeviceDataRegexString, RegexOptions.IgnoreCase)] private static partial Regex DeviceDataRegex(); #else + /// + /// Gets a that can be used to parse the device information that is returned by the Android Debut Bridge. + /// + /// The that can be used to parse the device information that is returned by the Android Debut Bridge. private static Regex DeviceDataRegex() => new(DeviceDataRegexString, RegexOptions.IgnoreCase); #endif } diff --git a/AdvancedSharpAdbClient/Models/Framebuffer.cs b/AdvancedSharpAdbClient/Models/Framebuffer.cs index 139fd1b6..6226ddde 100644 --- a/AdvancedSharpAdbClient/Models/Framebuffer.cs +++ b/AdvancedSharpAdbClient/Models/Framebuffer.cs @@ -17,14 +17,25 @@ namespace AdvancedSharpAdbClient.Models /// The to create . public class Framebuffer(DeviceData device, EndPoint endPoint, Func adbSocketFactory) : IDisposable { + /// + /// The of s which contains the framebuffer header. + /// private byte[] headerData = new byte[FramebufferHeader.MaxLength]; + + /// + /// The which indicates whether the header has been initialized. + /// private bool headerInitialized; + + /// + /// The that indicates whether this instance has been disposed. + /// private bool disposed = false; /// /// The to create . /// - protected readonly Func adbSocketFactory = adbSocketFactory; + protected readonly Func AdbSocketFactory = adbSocketFactory; /// /// Initializes a new instance of the class. @@ -93,7 +104,7 @@ public virtual void Refresh(bool reset = false) { EnsureNotDisposed(); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); // Select the target device socket.SetDevice(Device); @@ -145,7 +156,7 @@ public virtual async Task RefreshAsync(bool reset = false, CancellationToken can { EnsureNotDisposed(); - using IAdbSocket socket = adbSocketFactory(EndPoint); + using IAdbSocket socket = AdbSocketFactory(EndPoint); // Select the target device await socket.SetDeviceAsync(Device, cancellationToken).ConfigureAwait(false); diff --git a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs index 49af26f4..b2bbfe38 100644 --- a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs +++ b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs @@ -356,7 +356,7 @@ private PixelFormat StandardizePixelFormat(byte[] buffer) throw new ArgumentOutOfRangeException($"The alpha length {Alpha.Length} is not supported"); } - // Get the index at which the red, bue, green and alpha values are stored. + // Gets the index at which the red, bue, green and alpha values are stored. int redIndex = (int)Red.Offset / 8; int blueIndex = (int)Blue.Offset / 8; int greenIndex = (int)Green.Offset / 8; @@ -388,7 +388,7 @@ private PixelFormat StandardizePixelFormat(byte[] buffer) } } - // Return RGB or RGBA, function of the presence of an alpha channel. + // Returns RGB or RGBA, function of the presence of an alpha channel. return Alpha.Length == 0 ? PixelFormat.Format32bppRgb : PixelFormat.Format32bppArgb; } else if (Bpp == 24) @@ -563,6 +563,10 @@ public IEnumerator GetEnumerator() /// IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + /// + /// Gets an which enumerates the values of this . + /// + /// The values of the . private IEnumerable GetEnumerable() { yield return Version; diff --git a/AdvancedSharpAdbClient/Models/InstallProgress.EventArgs.cs b/AdvancedSharpAdbClient/Models/InstallProgress.EventArgs.cs index 00760cff..bc3b974b 100644 --- a/AdvancedSharpAdbClient/Models/InstallProgress.EventArgs.cs +++ b/AdvancedSharpAdbClient/Models/InstallProgress.EventArgs.cs @@ -13,12 +13,12 @@ namespace AdvancedSharpAdbClient.Models public class InstallProgressEventArgs(PackageInstallProgressState state) : EventArgs { /// - /// Get the state of the installation. + /// Gets the state of the installation. /// public PackageInstallProgressState State { get; } = state; /// - /// Get the number of packages which is finished operation. + /// Gets the number of packages which is finished operation. /// Used only in , /// and /// state. @@ -26,7 +26,7 @@ public class InstallProgressEventArgs(PackageInstallProgressState state) : Event public int PackageFinished { get; init; } /// - /// Get the number of packages required for this operation. + /// Gets the number of packages required for this operation. /// Used only in , /// and /// state. @@ -34,7 +34,7 @@ public class InstallProgressEventArgs(PackageInstallProgressState state) : Event public int PackageRequired { get; init; } /// - /// Get the upload percentage (from to ) completed. + /// Gets the upload percentage (from to ) completed. /// Used only in state. /// public double UploadProgress { get; init; } @@ -45,7 +45,7 @@ public class InstallProgressEventArgs(PackageInstallProgressState state) : Event /// /// The number of packages which is finished operation. /// The number of packages required for this operation. - /// Get the upload percentage (from to ) completed. + /// Gets the upload percentage (from to ) completed. public InstallProgressEventArgs(int packageUploaded, int packageRequired, double uploadProgress) : this(PackageInstallProgressState.Uploading) { PackageFinished = packageUploaded; diff --git a/AdvancedSharpAdbClient/Models/ShellStream.cs b/AdvancedSharpAdbClient/Models/ShellStream.cs index 2fe5a3f3..221cbf7b 100644 --- a/AdvancedSharpAdbClient/Models/ShellStream.cs +++ b/AdvancedSharpAdbClient/Models/ShellStream.cs @@ -15,7 +15,14 @@ namespace AdvancedSharpAdbClient.Models /// public class ShellStream : Stream { + /// + /// The value which indicates whether the should close the stream when closed. + /// private readonly bool closeStream; + + /// + /// The byte which is pending to be read. + /// private byte? pendingByte; /// @@ -349,9 +356,6 @@ public override async ValueTask ReadAsync(Memory buffer, Cancellation #endif #if HAS_TASK -#if NET8_0_OR_GREATER -#pragma warning disable CA1835 -#endif /// public #if !NETFRAMEWORK || NET45_OR_GREATER @@ -451,9 +455,6 @@ async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToke return read; } -#if NET8_0_OR_GREATER -#pragma warning restore CA1835 -#endif #endif /// diff --git a/AdvancedSharpAdbClient/Polyfills/Extensions/EnumerableExtensions.cs b/AdvancedSharpAdbClient/Polyfills/Extensions/EnumerableExtensions.cs index 302de322..5f27961d 100644 --- a/AdvancedSharpAdbClient/Polyfills/Extensions/EnumerableExtensions.cs +++ b/AdvancedSharpAdbClient/Polyfills/Extensions/EnumerableExtensions.cs @@ -61,7 +61,7 @@ public static Task ToArrayAsync(this TaskAn to create an array from. /// An array that contains the elements from the input sequence. public static Task ToArrayAsync(this IEnumerable> source) => - Extensions.WhenAll(source); + TaskExExtensions.WhenAll(source); #endif } } diff --git a/AdvancedSharpAdbClient/Polyfills/Extensions/SocketExtensions.cs b/AdvancedSharpAdbClient/Polyfills/Extensions/SocketExtensions.cs index ba5f1b76..dcf8b522 100644 --- a/AdvancedSharpAdbClient/Polyfills/Extensions/SocketExtensions.cs +++ b/AdvancedSharpAdbClient/Polyfills/Extensions/SocketExtensions.cs @@ -70,13 +70,11 @@ public static Task ReceiveAsync(this Socket socket, byte[] buffer, int offs // This will cause an ObjectDisposedException to bubble up via TrySetResult, which we can catch // and convert to a TaskCancelledException - which is the exception we expect. CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.Register(socket.Close); - TaskCompletionSource taskCompletionSource = new(socket); IAsyncResult asyncResult = socket.BeginReceive(buffer, offset, size, socketFlags, iar => { // this is the callback - TaskCompletionSource taskCompletionSource = (TaskCompletionSource)iar.AsyncState; Socket socket = (Socket)taskCompletionSource.Task.AsyncState; @@ -100,7 +98,7 @@ public static Task ReceiveAsync(this Socket socket, byte[] buffer, int offs return taskCompletionSource.Task; #else - return Extensions.Run(() => socket.Receive(buffer, offset, size, socketFlags), cancellationToken); + return Task.Factory.StartNew(() => socket.Receive(buffer, offset, size, socketFlags), cancellationToken, TaskCreationOptions.None, TaskScheduler.Default); #endif } @@ -157,13 +155,11 @@ public static Task SendAsync(this Socket socket, byte[] buffer, int offset, // This will cause an ObjectDisposedException to bubble up via TrySetResult, which we can catch // and convert to a TaskCancelledException - which is the exception we expect. CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.Register(socket.Dispose); - TaskCompletionSource taskCompletionSource = new(socket); _ = socket.BeginSend(buffer, offset, size, socketFlags, iar => { // this is the callback - TaskCompletionSource taskCompletionSource = (TaskCompletionSource)iar.AsyncState; Socket socket = (Socket)taskCompletionSource.Task.AsyncState; @@ -187,7 +183,7 @@ public static Task SendAsync(this Socket socket, byte[] buffer, int offset, return taskCompletionSource.Task; #else - return Extensions.Run(() => socket.Send(buffer, offset, size, socketFlags), cancellationToken); + return Task.Factory.StartNew(() => socket.Send(buffer, offset, size, socketFlags), cancellationToken, TaskCreationOptions.None, TaskScheduler.Default); #endif } } diff --git a/AdvancedSharpAdbClient/Polyfills/Extensions/StreamExtensions.cs b/AdvancedSharpAdbClient/Polyfills/Extensions/StreamExtensions.cs index f0169bca..47747cb6 100644 --- a/AdvancedSharpAdbClient/Polyfills/Extensions/StreamExtensions.cs +++ b/AdvancedSharpAdbClient/Polyfills/Extensions/StreamExtensions.cs @@ -36,7 +36,6 @@ public static Task ReadAsync(this Stream stream, byte[] buffer, int offset, // This will cause an ObjectDisposedException to bubble up via TrySetResult, which we can catch // and convert to a TaskCancelledException - which is the exception we expect. CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.Register(stream.Close); - TaskCompletionSource taskCompletionSource = new(stream); IAsyncResult asyncResult = stream.BeginRead(buffer, offset, count, iar => diff --git a/AdvancedSharpAdbClient/Polyfills/Extensions/TaskExExtensions.cs b/AdvancedSharpAdbClient/Polyfills/Extensions/TaskExExtensions.cs new file mode 100644 index 00000000..40a9059d --- /dev/null +++ b/AdvancedSharpAdbClient/Polyfills/Extensions/TaskExExtensions.cs @@ -0,0 +1,216 @@ +#if HAS_TASK +// +// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. +// + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Threading; + +#if NET40 +using Microsoft.Runtime.CompilerServices; +#endif + +namespace AdvancedSharpAdbClient.Polyfills +{ + /// + /// Provides extension methods for the class. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public static class TaskExExtensions + { +#if NETFRAMEWORK && !NET46_OR_GREATER + /// + /// Singleton cached task that's been completed successfully. + /// + internal static readonly Task s_cachedCompleted = +#if NET45_OR_GREATER + Task. +#else + TaskEx. +#endif + FromResult(null); + + /// + /// Gets a task that's already been completed successfully. + /// + public static Task CompletedTask => s_cachedCompleted; +#else + /// + /// Gets a task that's already been completed successfully. + /// + public static Task CompletedTask => Task.CompletedTask; +#endif + + /// + /// Creates a task that completes after a specified number of milliseconds. + /// + /// The number of milliseconds to wait before completing the returned task, or -1 to wait indefinitely. + /// A cancellation token to observe while waiting for the task to complete. + /// A task that represents the time delay. + /// The argument is less than -1. + public static Task Delay(int dueTime, CancellationToken cancellationToken = default) => +#if NETFRAMEWORK && !NET45_OR_GREATER + TaskEx +#else + Task +#endif + .Delay(dueTime, cancellationToken); + + /// + /// Queues the specified work to run on the thread pool and returns a proxy for the + /// returned by function. A cancellation token allows the work to be cancelled if it has not yet started. + /// + /// The type of the result returned by the proxy task. + /// The work to execute asynchronously. + /// A cancellation token that can be used to cancel the work if it has not yet started. + /// A that represents a proxy for the + /// returned by . + /// The parameter was . + public static Task Run(Func function, CancellationToken cancellationToken = default) => +#if NETFRAMEWORK && !NET45_OR_GREATER + TaskEx +#else + Task +#endif + .Run(function, cancellationToken); + + /// + /// Creates a task that will complete when all of the objects in an enumerable collection have completed. + /// + /// The tasks to wait on for completion. + /// A task that represents the completion of all of the supplied tasks. + public static Task WhenAll(IEnumerable tasks) => +#if NETFRAMEWORK && !NET45_OR_GREATER + TaskEx +#else + Task +#endif + .WhenAll(tasks); + + /// + /// Creates a task that will complete when all of the objects in an enumerable collection have completed. + /// + /// The type of the completed task. + /// The tasks to wait on for completion. + /// A task that represents the completion of all of the supplied tasks. + public static Task WhenAll(IEnumerable> tasks) => +#if NETFRAMEWORK && !NET45_OR_GREATER + TaskEx +#else + Task +#endif + .WhenAll(tasks); + + /// + /// Creates an awaitable task that asynchronously yields back to the current context when awaited. + /// + /// A context that, when awaited, will asynchronously transition back into the current context at the time of the await. + /// If the current is non-null, it is treated as the current context. Otherwise, the task scheduler + /// that is associated with the currently executing task is treated as the current context. + public static YieldAwaitable Yield() => +#if NETFRAMEWORK && !NET45_OR_GREATER + TaskEx +#else + Task +#endif + .Yield(); + + /// + /// Creates a that's completed successfully with the specified result. + /// + /// The type of the result returned by the task. + /// The result to store into the completed task. + /// The successfully completed task. + public static Task FromResult(TResult result) => +#if NETFRAMEWORK && !NET45_OR_GREATER + TaskEx +#else + Task +#endif + .FromResult(result); + +#if WINDOWS_UWP + /// + /// Wait a synchronously by and return the result. + /// + /// The type of the result returned by the task. + /// The to wait. + /// A cancellation token that can be used to cancel the work if it has not yet started. + /// The result of the completed task. + public static TResult AwaitByTaskCompleteSource(this IAsyncOperation function, CancellationToken cancellationToken = default) + { + TaskCompletionSource taskCompletionSource = new(); + Task task = taskCompletionSource.Task; + _ = Task.Run(async () => + { + try + { + TResult result = await function.AsTask(cancellationToken).ConfigureAwait(false); + taskCompletionSource.SetResult(result); + } + catch (Exception e) + { + taskCompletionSource.SetException(e); + } + }, cancellationToken); + TResult taskResult = task.Result; + return taskResult; + } +#endif + + /// + /// Wait a synchronously by . + /// + /// The to wait. + /// A cancellation token that can be used to cancel the work if it has not yet started. + public static void AwaitByTaskCompleteSource(this Task function, CancellationToken cancellationToken = default) + { + TaskCompletionSource taskCompletionSource = new(); + Task task = taskCompletionSource.Task; + _ = Run(async () => + { + try + { + await function.ConfigureAwait(false); + taskCompletionSource.SetResult(null); + } + catch (Exception e) + { + taskCompletionSource.SetException(e); + } + }, cancellationToken); + _ = task.Result; + } + + /// + /// Wait a synchronously by and return the result. + /// + /// The type of the result returned by the task. + /// The to wait. + /// A cancellation token that can be used to cancel the work if it has not yet started. + /// The result of the completed task. + public static TResult AwaitByTaskCompleteSource(this Task function, CancellationToken cancellationToken = default) + { + TaskCompletionSource taskCompletionSource = new(); + Task task = taskCompletionSource.Task; + _ = Run(async () => + { + try + { + TResult result = await function.ConfigureAwait(false); + taskCompletionSource.SetResult(result); + } + catch (Exception e) + { + taskCompletionSource.SetException(e); + } + }, cancellationToken); + TResult taskResult = task.Result; + return taskResult; + } + } +} +#endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Properties/GlobalUsings.cs b/AdvancedSharpAdbClient/Properties/GlobalUsings.cs index 05610a52..b1a251c0 100644 --- a/AdvancedSharpAdbClient/Properties/GlobalUsings.cs +++ b/AdvancedSharpAdbClient/Properties/GlobalUsings.cs @@ -36,6 +36,7 @@ #endif #if WINDOWS10_0_17763_0_OR_GREATER +global using Windows.Foundation; global using Buffer = System.Buffer; global using DateTime = System.DateTime; global using Point = System.Drawing.Point; diff --git a/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs b/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs index 250efec1..5d543522 100644 --- a/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs +++ b/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs @@ -24,18 +24,18 @@ public partial class ConsoleOutputReceiver(ILogger? logge /// /// The logger to use when logging messages. /// - protected readonly ILogger logger = logger ?? LoggerProvider.CreateLogger(); + private readonly ILogger logger = logger ?? LoggerProvider.CreateLogger(); /// /// A which receives all output from the device. /// - protected readonly StringBuilder output = new(); + protected readonly StringBuilder Output = new(); /// /// Gets a that represents the current . /// /// A that represents the current . - public override string ToString() => TrimLines ? output.ToString().Trim() : output.ToString(); + public override string ToString() => TrimLines ? Output.ToString().Trim() : Output.ToString(); /// protected override void ThrowOnError(string line) @@ -98,7 +98,7 @@ protected override void ProcessNewLines(IEnumerable lines) { continue; } - output.AppendLine(line); + Output.AppendLine(line); logger.LogDebug(line); } } @@ -113,10 +113,22 @@ protected override void ProcessNewLines(IEnumerable lines) [GeneratedRegex("(permission|access) denied$", DefaultRegexOptions)] private static partial Regex DeniedRegex(); #else + /// + /// Gets a which matches lines which indicate that the command was aborted. + /// + /// The which matches lines which indicate that the command was aborted. private static Regex AbortingRegex() => new("Aborting.$", DefaultRegexOptions); + /// + /// Gets a which matches lines which indicate that the applet was not found. + /// + /// The which matches lines which indicate that the applet was not found. private static Regex AppletRegex() => new("applet not found$", DefaultRegexOptions); + /// + /// Gets a which matches lines which indicate that the permission to execute the command was denied. + /// + /// The which matches lines which indicate that the permission to execute the command was denied. private static Regex DeniedRegex() => new("(permission|access) denied$", DefaultRegexOptions); #endif } diff --git a/AdvancedSharpAdbClient/SyncService.Async.cs b/AdvancedSharpAdbClient/SyncService.Async.cs index 0bc37fba..a21a235c 100644 --- a/AdvancedSharpAdbClient/SyncService.Async.cs +++ b/AdvancedSharpAdbClient/SyncService.Async.cs @@ -124,7 +124,7 @@ public virtual async Task PullAsync(string remoteFilePath, Stream stream, IProgr ExceptionExtensions.ThrowIfNull(remoteFilePath); ExceptionExtensions.ThrowIfNull(stream); - // Get file information, including the file size, used to calculate the total amount of bytes to receive. + // Gets file information, including the file size, used to calculate the total amount of bytes to receive. FileStatistics stat = await StatAsync(remoteFilePath, cancellationToken).ConfigureAwait(false); long totalBytesToProcess = stat.Size; long totalBytesRead = 0; diff --git a/AdvancedSharpAdbClient/SyncService.cs b/AdvancedSharpAdbClient/SyncService.cs index d5e8bc5d..7eb00b9f 100644 --- a/AdvancedSharpAdbClient/SyncService.cs +++ b/AdvancedSharpAdbClient/SyncService.cs @@ -214,7 +214,7 @@ public virtual void Pull(string remoteFilePath, Stream stream, IProgress Socket.Connect(endPoint), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default); } #endif Socket.Blocking = true; @@ -47,7 +46,7 @@ public virtual Task ReconnectAsync(bool isForce, CancellationToken cancellationT else { // Already connected - nothing to do. - return Extensions.CompletedTask; + return TaskExExtensions.CompletedTask; } } From a040020e00c223cabec48438f076b7b6f36212ca Mon Sep 17 00:00:00 2001 From: wherewhere Date: Wed, 13 Dec 2023 23:29:20 +0800 Subject: [PATCH 07/10] Switch thread when DeviceMonitorLoopAsync start Using Task.Factory.StartNew instead of Task.Run --- AdvancedSharpAdbClient/AdbClient.Async.cs | 1 - AdvancedSharpAdbClient/DeviceMonitor.Async.cs | 32 +++++++++++++++- .../Extensions/Extensions.cs | 24 ++++++++---- .../Polyfills/Extensions/TaskExExtensions.cs | 38 ++----------------- AdvancedSharpAdbClient/TcpSocket.Async.cs | 5 +-- 5 files changed, 50 insertions(+), 50 deletions(-) diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index 64bfc503..750e4908 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -221,7 +221,6 @@ public virtual async Task ExecuteServerCommandAsync(string target, string comman try { - cancellationToken.Register(socket.Dispose); using StreamReader reader = new(socket.GetShellStream(), encoding); // Previously, we would loop while reader.Peek() >= 0. Turns out that this would // break too soon in certain cases (about every 10 loops, so it appears to be a timing diff --git a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs index e6b5d445..770df9ec 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs @@ -4,8 +4,10 @@ // using System; +using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Net.Sockets; +using System.Runtime.CompilerServices; using System.Threading; namespace AdvancedSharpAdbClient @@ -38,7 +40,7 @@ public virtual async Task StartAsync(CancellationToken cancellationToken = defau try { FirstDeviceListParsed = new TaskCompletionSource(); - cancellationToken.Register(() => FirstDeviceListParsed.SetCanceled()); + using CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.Register(() => FirstDeviceListParsed.SetCanceled()); MonitorTask = DeviceMonitorLoopAsync(MonitorTaskCancellationTokenSource.Token); // Wait for the worker thread to have read the first list of devices. _ = await FirstDeviceListParsed.Task.ConfigureAwait(false); @@ -122,7 +124,7 @@ public async Task AsyncDispose() protected virtual async Task DeviceMonitorLoopAsync(CancellationToken cancellationToken = default) { IsRunning = true; - await TaskExExtensions.Yield(); + await this; // Set up the connection to track the list of devices. await InitializeSocketAsync(cancellationToken).ConfigureAwait(false); @@ -235,6 +237,32 @@ private async Task InitializeSocketAsync(CancellationToken cancellationToken) await Socket.SendAdbRequestAsync("host:track-devices", cancellationToken).ConfigureAwait(false); _ = await Socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); } + + /// + /// Gets an awaiter used to switch to background thread. + /// + /// An awaiter instance. + private ThreadSwitcher GetAwaiter() => new(); + + /// + /// A helper type for switch thread by . This type is not intended to be used directly from your code. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + private readonly struct ThreadSwitcher() : INotifyCompletion + { + /// + /// Gets a value that indicates whether the asynchronous operation has completed. + /// + public bool IsCompleted { get; } = false; + + /// + /// Ends the await on the completed task. + /// + public void GetResult() { } + + /// + public void OnCompleted(Action continuation) => _ = Task.Factory.StartNew(continuation, default, TaskCreationOptions.None, TaskScheduler.Default); + } } } #endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Extensions/Extensions.cs b/AdvancedSharpAdbClient/Extensions/Extensions.cs index 7d6463e2..096b86aa 100644 --- a/AdvancedSharpAdbClient/Extensions/Extensions.cs +++ b/AdvancedSharpAdbClient/Extensions/Extensions.cs @@ -55,13 +55,17 @@ public static DnsEndPoint CreateDnsEndPoint(string host, int port) /// A value task that represents the asynchronous read operation. The value of the /// TResult parameter contains the next line from the text reader, or is null if /// all of the characters have been read. - public static async Task ReadLineAsync(this TextReader reader, CancellationToken cancellationToken) + public static Task ReadLineAsync(this TextReader reader, CancellationToken cancellationToken) { - using CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.Register(reader.Close); #if NET35 - return await Task.Factory.StartNew(() => reader.ReadLine(), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default); + return Task.Factory.StartNew(() => reader.ReadLine(), default, TaskCreationOptions.None, TaskScheduler.Default); #else - return await reader.ReadLineAsync().ConfigureAwait(false); + CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.Register(reader.Close); + return reader.ReadLineAsync().ContinueWith(x => + { + cancellationTokenRegistration.Dispose(); + return x.Result; + }); #endif } @@ -73,13 +77,17 @@ public static DnsEndPoint CreateDnsEndPoint(string host, int port) /// A task that represents the asynchronous read operation. The value of the TResult /// parameter contains a string with the characters from the current position to /// the end of the stream. - public static async Task ReadToEndAsync(this TextReader reader, CancellationToken cancellationToken) + public static Task ReadToEndAsync(this TextReader reader, CancellationToken cancellationToken) { - using CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.Register(reader.Close); #if NET35 - return await Task.Factory.StartNew(() => reader.ReadToEnd(), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).ConfigureAwait(false); + return Task.Factory.StartNew(() => reader.ReadToEnd(), cancellationToken, TaskCreationOptions.None, TaskScheduler.Default); #else - return await reader.ReadToEndAsync().ConfigureAwait(false); + CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.Register(reader.Close); + return reader.ReadToEndAsync().ContinueWith(x => + { + cancellationTokenRegistration.Dispose(); + return x.Result; + }); #endif } #endif diff --git a/AdvancedSharpAdbClient/Polyfills/Extensions/TaskExExtensions.cs b/AdvancedSharpAdbClient/Polyfills/Extensions/TaskExExtensions.cs index 40a9059d..0a18a673 100644 --- a/AdvancedSharpAdbClient/Polyfills/Extensions/TaskExExtensions.cs +++ b/AdvancedSharpAdbClient/Polyfills/Extensions/TaskExExtensions.cs @@ -59,24 +59,6 @@ public static Task Delay(int dueTime, CancellationToken cancellationToken = defa #endif .Delay(dueTime, cancellationToken); - /// - /// Queues the specified work to run on the thread pool and returns a proxy for the - /// returned by function. A cancellation token allows the work to be cancelled if it has not yet started. - /// - /// The type of the result returned by the proxy task. - /// The work to execute asynchronously. - /// A cancellation token that can be used to cancel the work if it has not yet started. - /// A that represents a proxy for the - /// returned by . - /// The parameter was . - public static Task Run(Func function, CancellationToken cancellationToken = default) => -#if NETFRAMEWORK && !NET45_OR_GREATER - TaskEx -#else - Task -#endif - .Run(function, cancellationToken); - /// /// Creates a task that will complete when all of the objects in an enumerable collection have completed. /// @@ -104,20 +86,6 @@ public static Task WhenAll(IEnumerable> tasks) #endif .WhenAll(tasks); - /// - /// Creates an awaitable task that asynchronously yields back to the current context when awaited. - /// - /// A context that, when awaited, will asynchronously transition back into the current context at the time of the await. - /// If the current is non-null, it is treated as the current context. Otherwise, the task scheduler - /// that is associated with the currently executing task is treated as the current context. - public static YieldAwaitable Yield() => -#if NETFRAMEWORK && !NET45_OR_GREATER - TaskEx -#else - Task -#endif - .Yield(); - /// /// Creates a that's completed successfully with the specified result. /// @@ -144,7 +112,7 @@ public static TResult AwaitByTaskCompleteSource(this IAsyncOperation taskCompletionSource = new(); Task task = taskCompletionSource.Task; - _ = Task.Run(async () => + _ = Task.Factory.StartNew(async () => { try { @@ -170,7 +138,7 @@ public static void AwaitByTaskCompleteSource(this Task function, CancellationTok { TaskCompletionSource taskCompletionSource = new(); Task task = taskCompletionSource.Task; - _ = Run(async () => + _ = Task.Factory.StartNew(async () => { try { @@ -196,7 +164,7 @@ public static TResult AwaitByTaskCompleteSource(this Task func { TaskCompletionSource taskCompletionSource = new(); Task task = taskCompletionSource.Task; - _ = Run(async () => + _ = Task.Factory.StartNew(async () => { try { diff --git a/AdvancedSharpAdbClient/TcpSocket.Async.cs b/AdvancedSharpAdbClient/TcpSocket.Async.cs index 14678093..557afe04 100644 --- a/AdvancedSharpAdbClient/TcpSocket.Async.cs +++ b/AdvancedSharpAdbClient/TcpSocket.Async.cs @@ -26,10 +26,7 @@ public virtual async Task ConnectAsync(EndPoint endPoint, CancellationToken canc #if NET6_0_OR_GREATER await Socket.ConnectAsync(endPoint, cancellationToken).ConfigureAwait(false); #else - using (CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.Register(Socket.Close)) - { - await Task.Factory.StartNew(() => Socket.Connect(endPoint), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default); - } + await Task.Factory.StartNew(() => Socket.Connect(endPoint), cancellationToken, TaskCreationOptions.None, TaskScheduler.Default).ConfigureAwait(false); #endif Socket.Blocking = true; } From dc772134215352cea4d1d2ac62b0f7b0cc7645d6 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Thu, 14 Dec 2023 19:52:51 +0800 Subject: [PATCH 08/10] Change target frameworks --- .github/ISSUE_TEMPLATE/bug_report.yaml | 2 ++ .../AdvancedSharpAdbClient.csproj | 25 ++++++++----------- .../Extensions/Extensions.cs | 20 +++++++-------- README.md | 6 ++--- 4 files changed, 26 insertions(+), 27 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index a8c5d798..8a43aa8d 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -32,6 +32,7 @@ body: description: Specify the version of AdvancedSharpAdbClient you're using. options: - "Latest Source" + - "2.5.8" - "2.5.7" - "2.5.6" - "2.5.5" @@ -49,6 +50,7 @@ body: multiple: true options: - ".NET Framework 2.0" + - ".NET Framework 3.0" - ".NET Framework 3.5" - ".NET Framework 4.0" - ".NET Framework 4.5.x" diff --git a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj index 63cded79..5958db9d 100644 --- a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj +++ b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj @@ -18,14 +18,14 @@ $(NoWarn);NU1603;NU1605;NU1902;NU1903 net6.0;net8.0;netcoreapp2.1;netcoreapp3.1;netstandard1.3;netstandard2.0;netstandard2.1 - $(TargetFrameworks);net2.0-client;net3.5-client;net4.0-client;net4.5.2;net4.6.2;net4.8.1;net6.0-windows10.0.17763.0;net8.0-windows10.0.17763.0 + $(TargetFrameworks);net2.0-client;net3.5-client;net4.0-client;net4.5;net4.6.1;net4.8;net6.0-windows10.0.17763.0;net8.0-windows10.0.17763.0 $(TargetFrameworks);netcore5.0;uap10.0;uap10.0.15138.0 net8.0;netcoreapp3.1;netstandard1.3;netstandard2.0;netstandard2.1 - $(TargetFrameworks);net2.0-client;net3.5-client;net4.5.2;net4.8.1;net8.0-windows10.0.17763.0 + $(TargetFrameworks);net2.0-client;net3.5-client;net4.5;net4.8;net8.0-windows10.0.17763.0 @@ -93,9 +93,8 @@ - @@ -118,10 +117,9 @@ or '$(TargetFramework)' == 'net3.0-client' or '$(TargetFramework)' == 'net3.5-client' or '$(TargetFramework)' == 'net4.0-client' - or '$(TargetFramework)' == 'net4.5.2' - or '$(TargetFramework)' == 'net4.6.2' - or '$(TargetFramework)' == 'net4.7.2' - or '$(TargetFramework)' == 'net4.8.1' + or '$(TargetFramework)' == 'net4.5' + or '$(TargetFramework)' == 'net4.6.1' + or '$(TargetFramework)' == 'net4.8' or '$(TargetFramework)' == 'net6.0-windows10.0.17763.0' or '$(TargetFramework)' == 'net8.0-windows10.0.17763.0'"> $(DefineConstants);HAS_IMAGING @@ -131,8 +129,8 @@ and '$(TargetFramework)' != 'net3.0-client' and '$(TargetFramework)' != 'net3.5-client' and '$(TargetFramework)' != 'net4.0-client' - and '$(TargetFramework)' != 'net4.5.2' - and '$(TargetFramework)' != 'net4.6.2' + and '$(TargetFramework)' != 'net4.5' + and '$(TargetFramework)' != 'net4.6.1' and '$(TargetFramework)' != 'netstandard1.3' and '$(TargetFramework)' != 'netcore5.0' and '$(TargetFramework)' != 'uap10.0'"> @@ -153,9 +151,8 @@ and '$(TargetFramework)' != 'net3.0-client' and '$(TargetFramework)' != 'net3.5-client' and '$(TargetFramework)' != 'net4.0-client' - and '$(TargetFramework)' != 'net4.5.2' - and '$(TargetFramework)' != 'net4.6.2' - and '$(TargetFramework)' != 'net4.7.2' + and '$(TargetFramework)' != 'net4.5' + and '$(TargetFramework)' != 'net4.6.1' and '$(TargetFramework)' != 'netcore5.0' and '$(TargetFramework)' != 'uap10.0'"> $(DefineConstants);HAS_RUNTIMEINFORMATION diff --git a/AdvancedSharpAdbClient/Extensions/Extensions.cs b/AdvancedSharpAdbClient/Extensions/Extensions.cs index 096b86aa..65d5c1fc 100644 --- a/AdvancedSharpAdbClient/Extensions/Extensions.cs +++ b/AdvancedSharpAdbClient/Extensions/Extensions.cs @@ -55,13 +55,13 @@ public static DnsEndPoint CreateDnsEndPoint(string host, int port) /// A value task that represents the asynchronous read operation. The value of the /// TResult parameter contains the next line from the text reader, or is null if /// all of the characters have been read. - public static Task ReadLineAsync(this TextReader reader, CancellationToken cancellationToken) + public static Task ReadLineAsync(this TextReader reader, CancellationToken cancellationToken) { #if NET35 - return Task.Factory.StartNew(() => reader.ReadLine(), default, TaskCreationOptions.None, TaskScheduler.Default); + return Task.Factory.StartNew(reader.ReadLine, default, TaskCreationOptions.None, TaskScheduler.Default); #else CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.Register(reader.Close); - return reader.ReadLineAsync().ContinueWith(x => + return reader.ReadLineAsync().ContinueWith(x => { cancellationTokenRegistration.Dispose(); return x.Result; @@ -80,7 +80,7 @@ public static Task ReadLineAsync(this TextReader reader, CancellationTok public static Task ReadToEndAsync(this TextReader reader, CancellationToken cancellationToken) { #if NET35 - return Task.Factory.StartNew(() => reader.ReadToEnd(), cancellationToken, TaskCreationOptions.None, TaskScheduler.Default); + return Task.Factory.StartNew(reader.ReadToEnd, cancellationToken, TaskCreationOptions.None, TaskScheduler.Default); #else CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.Register(reader.Close); return reader.ReadToEndAsync().ContinueWith(x => @@ -130,10 +130,10 @@ public static void Dispose(this WaitHandle waitHandle) #endif public static bool IsWindowsPlatform() => -#if HAS_RUNTIMEINFORMATION - RuntimeInformation.IsOSPlatform(OSPlatform.Windows); -#elif NETCORE +#if NETCORE && !UAP10_0_15138_0 true; +#elif !NETFRAMEWORK || NET48_OR_GREATER + RuntimeInformation.IsOSPlatform(OSPlatform.Windows); #else Environment.OSVersion.Platform is PlatformID.Win32S @@ -144,15 +144,15 @@ or PlatformID.WinCE #endif public static bool IsUnixPlatform() => -#if HAS_RUNTIMEINFORMATION +#if NETCORE && !UAP10_0_15138_0 + false; +#elif !NETFRAMEWORK || NET48_OR_GREATER RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX) #if NETCOREAPP3_0_OR_GREATER || RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD) #endif ; -#elif NETCORE - false; #else Environment.OSVersion.Platform is PlatformID.Unix diff --git a/README.md b/README.md index 2c07d7e1..5f2527c8 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,9 @@ Added important features. - .NET Framework 2.0 (Without Task) - .NET Framework 3.5 - .NET Framework 4.0 (Need [Microsoft.Bcl.Async](https://www.nuget.org/packages/Microsoft.Bcl.Async)) -- .NET Framework 4.5.2 -- .NET Framework 4.6.2 -- .NET Framework 4.8.1 +- .NET Framework 4.5 +- .NET Framework 4.6.1 +- .NET Framework 4.8 - .NET Standard 1.3 - .NET Standard 2.0 - .NET Standard 2.1 From e71739d8dd8f441f260544cbdf227544d708f86a Mon Sep 17 00:00:00 2001 From: wherewhere Date: Thu, 14 Dec 2023 20:21:20 +0800 Subject: [PATCH 09/10] Fix DeviceMonitorLoopAsync blocking Add IAsyncDisposable with Task --- AdvancedSharpAdbClient/DeviceMonitor.Async.cs | 15 ++++++++---- AdvancedSharpAdbClient/DeviceMonitor.cs | 2 +- .../Interfaces/IDeviceMonitor.Async.cs | 16 ------------- .../Interfaces/IDeviceMonitor.cs | 3 +++ .../Polyfills/Interfaces/IAsyncDisposable.cs | 23 +++++++++++++++++++ 5 files changed, 37 insertions(+), 22 deletions(-) create mode 100644 AdvancedSharpAdbClient/Polyfills/Interfaces/IAsyncDisposable.cs diff --git a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs index 770df9ec..606cbe0f 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs @@ -44,6 +44,7 @@ public virtual async Task StartAsync(CancellationToken cancellationToken = defau MonitorTask = DeviceMonitorLoopAsync(MonitorTaskCancellationTokenSource.Token); // Wait for the worker thread to have read the first list of devices. _ = await FirstDeviceListParsed.Task.ConfigureAwait(false); + await this; // Switch to the background thread to avoid blocking the caller. } finally { @@ -97,7 +98,7 @@ protected virtual async Task DisposeAsyncCore() #else /// #endif - public async ValueTask DisposeAsync() + async ValueTask System.IAsyncDisposable.DisposeAsync() { await DisposeAsyncCore().ConfigureAwait(false); Dispose(disposing: false); @@ -105,10 +106,10 @@ public async ValueTask DisposeAsync() } /// - public Task AsyncDispose() => DisposeAsync().AsTask(); + public Task DisposeAsync() => ((System.IAsyncDisposable)this).DisposeAsync().AsTask(); #else /// - public async Task AsyncDispose() + public async Task DisposeAsync() { await DisposeAsyncCore().ConfigureAwait(false); Dispose(disposing: false); @@ -124,7 +125,7 @@ public async Task AsyncDispose() protected virtual async Task DeviceMonitorLoopAsync(CancellationToken cancellationToken = default) { IsRunning = true; - await this; + await this; // Switch to the background thread, so that the loop can continue to run. // Set up the connection to track the list of devices. await InitializeSocketAsync(cancellationToken).ConfigureAwait(false); @@ -135,7 +136,11 @@ protected virtual async Task DeviceMonitorLoopAsync(CancellationToken cancellati { string value = await Socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); ProcessIncomingDeviceData(value); - FirstDeviceListParsed?.TrySetResult(null); + if (FirstDeviceListParsed != null) + { + FirstDeviceListParsed?.TrySetResult(null); + await this; // Switch to the background thread to avoid blocking the caller. + } } catch (TaskCanceledException ex) { diff --git a/AdvancedSharpAdbClient/DeviceMonitor.cs b/AdvancedSharpAdbClient/DeviceMonitor.cs index 02ef64d0..5e582d74 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.cs @@ -35,7 +35,7 @@ namespace AdvancedSharpAdbClient /// public partial class DeviceMonitor : IDeviceMonitor #if NETCOREAPP3_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER - , IAsyncDisposable + , System.IAsyncDisposable #endif { /// diff --git a/AdvancedSharpAdbClient/Interfaces/IDeviceMonitor.Async.cs b/AdvancedSharpAdbClient/Interfaces/IDeviceMonitor.Async.cs index bc4a46a3..54c8795c 100644 --- a/AdvancedSharpAdbClient/Interfaces/IDeviceMonitor.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/IDeviceMonitor.Async.cs @@ -15,22 +15,6 @@ public partial interface IDeviceMonitor /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. Task StartAsync(CancellationToken cancellationToken); - -#if NETCOREAPP3_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER - /// - /// Asynchronously performs application-defined tasks associated with freeing, releasing, or resetting - /// unmanaged resources asynchronously. - /// - /// A that represents the asynchronous dispose operation. - ValueTask DisposeAsync() => new(AsyncDispose()); -#endif - - /// - /// Asynchronously performs application-defined tasks associated with freeing, releasing, or resetting - /// unmanaged resources asynchronously. - /// - /// A that represents the asynchronous dispose operation. - Task AsyncDispose(); } } #endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Interfaces/IDeviceMonitor.cs b/AdvancedSharpAdbClient/Interfaces/IDeviceMonitor.cs index a5716b9b..2be55762 100644 --- a/AdvancedSharpAdbClient/Interfaces/IDeviceMonitor.cs +++ b/AdvancedSharpAdbClient/Interfaces/IDeviceMonitor.cs @@ -11,6 +11,9 @@ namespace AdvancedSharpAdbClient /// Provides a common interface for any class that allows you to monitor the list of devices that are currently connected to the adb server. /// public partial interface IDeviceMonitor : IDisposable +#if HAS_TASK + , Polyfills.IAsyncDisposable +#endif { /// /// Occurs when the status of one of the connected devices has changed. diff --git a/AdvancedSharpAdbClient/Polyfills/Interfaces/IAsyncDisposable.cs b/AdvancedSharpAdbClient/Polyfills/Interfaces/IAsyncDisposable.cs new file mode 100644 index 00000000..58d5e1a6 --- /dev/null +++ b/AdvancedSharpAdbClient/Polyfills/Interfaces/IAsyncDisposable.cs @@ -0,0 +1,23 @@ +#if HAS_TASK +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel; + +namespace AdvancedSharpAdbClient.Polyfills +{ + /// + /// Provides a mechanism for releasing unmanaged resources asynchronously. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public interface IAsyncDisposable + { + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting + /// unmanaged resources asynchronously. + /// + /// A task that represents the asynchronous dispose operation. + Task DisposeAsync(); + } +} +#endif \ No newline at end of file From 7cff24ed292593cc4b01e0419139f4f98e42fba1 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Thu, 14 Dec 2023 20:54:16 +0800 Subject: [PATCH 10/10] Rename IAsyncDisposable to IDisposableWithTask --- AdvancedSharpAdbClient/DeviceMonitor.Async.cs | 20 +++++-------------- AdvancedSharpAdbClient/DeviceMonitor.cs | 2 +- .../Interfaces/IDeviceMonitor.cs | 2 +- ...ncDisposable.cs => IDisposableWithTask.cs} | 2 +- 4 files changed, 8 insertions(+), 18 deletions(-) rename AdvancedSharpAdbClient/Polyfills/Interfaces/{IAsyncDisposable.cs => IDisposableWithTask.cs} (94%) diff --git a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs index 606cbe0f..29c4df99 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs @@ -44,7 +44,6 @@ public virtual async Task StartAsync(CancellationToken cancellationToken = defau MonitorTask = DeviceMonitorLoopAsync(MonitorTaskCancellationTokenSource.Token); // Wait for the worker thread to have read the first list of devices. _ = await FirstDeviceListParsed.Task.ConfigureAwait(false); - await this; // Switch to the background thread to avoid blocking the caller. } finally { @@ -88,17 +87,9 @@ protected virtual async Task DisposeAsyncCore() disposed = true; } -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER -#if NETCOREAPP && !NETCOREAPP3_0_OR_GREATER - /// - /// Asynchronously performs application-defined tasks associated with freeing, releasing, or resetting - /// unmanaged resources asynchronously. - /// - /// A that represents the asynchronous dispose operation. -#else +#if NETCOREAPP3_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER /// -#endif - async ValueTask System.IAsyncDisposable.DisposeAsync() + async ValueTask IAsyncDisposable.DisposeAsync() { await DisposeAsyncCore().ConfigureAwait(false); Dispose(disposing: false); @@ -106,13 +97,12 @@ async ValueTask System.IAsyncDisposable.DisposeAsync() } /// - public Task DisposeAsync() => ((System.IAsyncDisposable)this).DisposeAsync().AsTask(); + public Task DisposeAsync() => ((IAsyncDisposable)this).DisposeAsync().AsTask(); #else /// public async Task DisposeAsync() { await DisposeAsyncCore().ConfigureAwait(false); - Dispose(disposing: false); Dispose(); } #endif @@ -138,8 +128,8 @@ protected virtual async Task DeviceMonitorLoopAsync(CancellationToken cancellati ProcessIncomingDeviceData(value); if (FirstDeviceListParsed != null) { - FirstDeviceListParsed?.TrySetResult(null); - await this; // Switch to the background thread to avoid blocking the caller. + // Switch to the background thread to avoid blocking the caller. + _ = Task.Factory.StartNew(() => FirstDeviceListParsed?.TrySetResult(null), default, TaskCreationOptions.None, TaskScheduler.Default); } } catch (TaskCanceledException ex) diff --git a/AdvancedSharpAdbClient/DeviceMonitor.cs b/AdvancedSharpAdbClient/DeviceMonitor.cs index 5e582d74..02ef64d0 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.cs @@ -35,7 +35,7 @@ namespace AdvancedSharpAdbClient /// public partial class DeviceMonitor : IDeviceMonitor #if NETCOREAPP3_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER - , System.IAsyncDisposable + , IAsyncDisposable #endif { /// diff --git a/AdvancedSharpAdbClient/Interfaces/IDeviceMonitor.cs b/AdvancedSharpAdbClient/Interfaces/IDeviceMonitor.cs index 2be55762..21c6f6ba 100644 --- a/AdvancedSharpAdbClient/Interfaces/IDeviceMonitor.cs +++ b/AdvancedSharpAdbClient/Interfaces/IDeviceMonitor.cs @@ -12,7 +12,7 @@ namespace AdvancedSharpAdbClient /// public partial interface IDeviceMonitor : IDisposable #if HAS_TASK - , Polyfills.IAsyncDisposable + , IDisposableWithTask #endif { /// diff --git a/AdvancedSharpAdbClient/Polyfills/Interfaces/IAsyncDisposable.cs b/AdvancedSharpAdbClient/Polyfills/Interfaces/IDisposableWithTask.cs similarity index 94% rename from AdvancedSharpAdbClient/Polyfills/Interfaces/IAsyncDisposable.cs rename to AdvancedSharpAdbClient/Polyfills/Interfaces/IDisposableWithTask.cs index 58d5e1a6..c6c4d442 100644 --- a/AdvancedSharpAdbClient/Polyfills/Interfaces/IAsyncDisposable.cs +++ b/AdvancedSharpAdbClient/Polyfills/Interfaces/IDisposableWithTask.cs @@ -10,7 +10,7 @@ namespace AdvancedSharpAdbClient.Polyfills /// Provides a mechanism for releasing unmanaged resources asynchronously. /// [EditorBrowsable(EditorBrowsableState.Never)] - public interface IAsyncDisposable + public interface IDisposableWithTask { /// /// Performs application-defined tasks associated with freeing, releasing, or resetting