From d48fc791a657132c3ce5f6e62aba6e537e619482 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Fri, 22 Sep 2023 17:02:49 +0800 Subject: [PATCH 01/66] Update AdvancedSharpAdbClient.Tests C# version --- .github/workflows/build-and-test.yml | 5 +- .../AdbClientTests.Async.cs | 438 ++++++---------- .../AdbClientTests.cs | 478 ++++++------------ .../AdbCommandLineClientTests.Async.cs | 2 - .../AdbCommandLineClientTests.cs | 2 - .../AdbSocketTests.Async.cs | 10 +- .../AdbSocketTests.cs | 8 +- .../DeviceExtensionsTests.Async.cs | 2 + .../DeviceCommands/DeviceExtensionsTests.cs | 2 + .../PackageManagerTests.Async.cs | 4 + .../DeviceCommands/PackageManagerTests.cs | 6 +- .../Dummys/DeviceMonitorSink.cs | 8 +- .../Dummys/DummyTcpSocket.cs | 1 + .../Dummys/TracingAdbSocket.cs | 6 +- .../Extensions/FactoriesLocker.cs | 40 ++ .../Extensions/UtilitiesTests.cs | 2 +- .../Logs/ShellStreamTests.cs | 1 - .../Models/FramebufferTests.cs | 16 +- .../SocketBasedTests.cs | 8 +- .../SyncServiceTests.Async.cs | 30 +- .../SyncServiceTests.cs | 40 +- 21 files changed, 440 insertions(+), 669 deletions(-) create mode 100644 AdvancedSharpAdbClient.Tests/Extensions/FactoriesLocker.cs diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index c130b1c3..bfd7d215 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -3,7 +3,7 @@ name: build and test on: [push, pull_request] env: - DOTNET_VERSION: '6.0.x' # The .NET SDK version to use + DOTNET_VERSION: '8.0.x' # The .NET SDK version to use jobs: build-and-test: @@ -16,10 +16,11 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Setup .NET Core + - name: Setup .NET Core App uses: actions/setup-dotnet@v3 with: dotnet-version: ${{env.DOTNET_VERSION}} + dotnet-quality: 'preview' - name: Install dependencies run: dotnet restore -p:FullTargets=false diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs index e675c304..6ba0b93e 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs @@ -3,8 +3,8 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Drawing.Imaging; using System.Drawing; +using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Net; @@ -14,7 +14,6 @@ using System.Threading.Tasks; using System.Xml; using Xunit; -using System.Data.Common; namespace AdvancedSharpAdbClient.Tests { @@ -26,18 +25,10 @@ public partial class AdbClientTests [Fact] public async void GetAdbVersionAsyncTest() { - string[] responseMessages = new string[] - { - "0020" - }; - - string[] requests = new string[] - { - "host:version" - }; + string[] responseMessages = ["0020"]; + string[] requests = ["host:version"]; int version = 0; - await RunTestAsync( OkResponse, responseMessages, @@ -54,10 +45,7 @@ await RunTestAsync( [Fact] public async void KillAdbAsyncTest() { - string[] requests = new string[] - { - "host:kill" - }; + string[] requests = ["host:kill"]; await RunTestAsync( NoResponses, @@ -72,18 +60,10 @@ await RunTestAsync( [Fact] public async void GetDevicesAsyncTest() { - string[] responseMessages = new string[] - { - "169.254.109.177:5555 device product:VS Emulator 5\" KitKat (4.4) XXHDPI Phone model:5__KitKat__4_4__XXHDPI_Phone device:donatello\n" - }; - - string[] requests = new string[] - { - "host:devices-l" - }; + string[] responseMessages = ["169.254.109.177:5555 device product:VS Emulator 5\" KitKat (4.4) XXHDPI Phone model:5__KitKat__4_4__XXHDPI_Phone device:donatello\n"]; + string[] requests = ["host:devices-l"]; IEnumerable devices = null; - await RunTestAsync( OkResponse, responseMessages, @@ -144,15 +124,8 @@ await RunCreateForwardAsyncTest( [Fact] public async void CreateDuplicateForwardAsyncTest() { - AdbResponse[] responses = new AdbResponse[] - { - AdbResponse.FromError("cannot rebind existing socket") - }; - - string[] requests = new string[] - { - "host-serial:169.254.109.177:5555:forward:norebind:tcp:1;tcp:2" - }; + AdbResponse[] responses = [AdbResponse.FromError("cannot rebind existing socket")]; + string[] requests = ["host-serial:169.254.109.177:5555:forward:norebind:tcp:1;tcp:2"]; _ = await Assert.ThrowsAsync(() => RunTestAsync( @@ -168,10 +141,7 @@ public async void CreateDuplicateForwardAsyncTest() [Fact] public async void RemoveForwardAsyncTest() { - string[] requests = new string[] - { - "host-serial:169.254.109.177:5555:killforward:tcp:1" - }; + string[] requests = ["host-serial:169.254.109.177:5555:killforward:tcp:1"]; await RunTestAsync( OkResponse, @@ -186,17 +156,17 @@ await RunTestAsync( [Fact] public async void RemoveReverseForwardAsyncTest() { - string[] requests = new string[] - { + string[] requests = + [ "host:transport:169.254.109.177:5555", "reverse:killforward:localabstract:test" - }; + ]; - AdbResponse[] responses = new AdbResponse[] - { + AdbResponse[] responses = + [ AdbResponse.OK, AdbResponse.OK, - }; + ]; await RunTestAsync( responses, @@ -211,10 +181,7 @@ await RunTestAsync( [Fact] public async void RemoveAllForwardsAsyncTest() { - string[] requests = new string[] - { - "host-serial:169.254.109.177:5555:killforward-all" - }; + string[] requests = ["host-serial:169.254.109.177:5555:killforward-all"]; await RunTestAsync( OkResponse, @@ -229,17 +196,17 @@ await RunTestAsync( [Fact] public async void RemoveAllReversesAsyncTest() { - string[] requests = new string[] - { + string[] requests = + [ "host:transport:169.254.109.177:5555", "reverse:killforward-all" - }; + ]; - AdbResponse[] responses = new AdbResponse[] - { + AdbResponse[] responses = + [ AdbResponse.OK, AdbResponse.OK, - }; + ]; await RunTestAsync( responses, @@ -254,18 +221,10 @@ await RunTestAsync( [Fact] public async void ListForwardAsyncTest() { - string[] responseMessages = new string[] - { - "169.254.109.177:5555 tcp:1 tcp:2\n169.254.109.177:5555 tcp:3 tcp:4\n169.254.109.177:5555 tcp:5 local:/socket/1\n" - }; - - string[] requests = new string[] - { - "host-serial:169.254.109.177:5555:list-forward" - }; + string[] responseMessages = ["169.254.109.177:5555 tcp:1 tcp:2\n169.254.109.177:5555 tcp:3 tcp:4\n169.254.109.177:5555 tcp:5 local:/socket/1\n"]; + string[] requests = ["host-serial:169.254.109.177:5555:list-forward"]; ForwardData[] forwards = null; - await RunTestAsync( OkResponse, responseMessages, @@ -285,24 +244,21 @@ await RunTestAsync( [Fact] public async void ListReverseForwardAsyncTest() { - string[] responseMessages = new string[] - { - "(reverse) localabstract:scrcpy tcp:100\n(reverse) localabstract: scrcpy2 tcp:100\n(reverse) localabstract: scrcpy3 tcp:100\n" - }; - AdbResponse[] responses = new AdbResponse[] - { + string[] responseMessages = ["(reverse) localabstract:scrcpy tcp:100\n(reverse) localabstract: scrcpy2 tcp:100\n(reverse) localabstract: scrcpy3 tcp:100\n"]; + + AdbResponse[] responses = + [ AdbResponse.OK, AdbResponse.OK, - }; + ]; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:169.254.109.177:5555", "reverse:list-forward" - }; + ]; ForwardData[] forwards = null; - await RunTestAsync( responses, responseMessages, @@ -328,19 +284,19 @@ public async void ExecuteRemoteCommandAsyncTest() State = DeviceState.Online }; - AdbResponse[] responses = new AdbResponse[] - { + AdbResponse[] responses = + [ AdbResponse.OK, AdbResponse.OK - }; + ]; - string[] responseMessages = Array.Empty(); + string[] responseMessages = []; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:169.254.109.177:5555", "shell:echo Hello, World" - }; + ]; byte[] streamData = Encoding.ASCII.GetBytes("Hello, World\r\n"); await using MemoryStream shellStream = new(streamData); @@ -369,19 +325,19 @@ public async void ExecuteRemoteCommandAsyncUnresponsiveTest() State = DeviceState.Online }; - AdbResponse[] responses = new AdbResponse[] - { + AdbResponse[] responses = + [ AdbResponse.OK, AdbResponse.OK - }; + ]; - string[] responseMessages = Array.Empty(); + string[] responseMessages = []; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:169.254.109.177:5555", "shell:echo Hello, World" - }; + ]; ConsoleOutputReceiver receiver = new(); @@ -419,9 +375,13 @@ public async void GetFrameBufferAsyncTest() Framebuffer framebuffer = null; - Factories.AdbSocketFactory = (endPoint) => socket; - framebuffer = await TestClient.GetFrameBufferAsync(device); - + using (FactoriesLocker locker = await FactoriesLocker.WaitAsync()) + { + Factories.AdbSocketFactory = (endPoint) => socket; + framebuffer = await TestClient.GetFrameBufferAsync(device); + Factories.Reset(); + } + Assert.NotNull(framebuffer); Assert.Equal(device, framebuffer.Device); Assert.Equal(16, framebuffer.Data.Length); @@ -474,25 +434,25 @@ public async void RunLogServiceAsyncTest() State = DeviceState.Online }; - AdbResponse[] responses = new AdbResponse[] - { + AdbResponse[] responses = + [ AdbResponse.OK, AdbResponse.OK - }; + ]; - string[] responseMessages = Array.Empty(); + string[] responseMessages = []; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:169.254.109.177:5555", "shell:logcat -B -b system" - }; + ]; ConsoleOutputReceiver receiver = new(); await using Stream stream = File.OpenRead("Assets/logcat.bin"); await using ShellStream shellStream = new(stream, false); - Collection logs = new(); + Collection logs = []; Action sink = logs.Add; await RunTestAsync( @@ -511,14 +471,14 @@ await RunTestAsync( [Fact] public async void RebootAsyncTest() { - string[] requests = new string[] - { + string[] requests = + [ "host:transport:169.254.109.177:5555", "reboot:" - }; + ]; await RunTestAsync( - new AdbResponse[] { AdbResponse.OK, AdbResponse.OK }, + [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, () => TestClient.RebootAsync(Device)); @@ -662,8 +622,8 @@ public async void ConnectAsyncHostEndpointNullTest() => [Fact] public async void DisconnectAsyncTest() { - string[] requests = new string[] { "host:disconnect:localhost:5555" }; - string[] responseMessages = new string[] { "disconnected 127.0.0.1:5555" }; + string[] requests = ["host:disconnect:localhost:5555"]; + string[] responseMessages = ["disconnected 127.0.0.1:5555"]; await RunTestAsync( OkResponse, @@ -684,11 +644,11 @@ public async void RootAsyncTest() State = DeviceState.Online }; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:009d1cd696d5194a", "root:" - }; + ]; byte[] expectedData = new byte[1024]; byte[] expectedString = Encoding.UTF8.GetBytes("adbd cannot run as root in production builds\n"); @@ -696,12 +656,12 @@ public async void RootAsyncTest() _ = await Assert.ThrowsAsync(() => RunTestAsync( - new AdbResponse[] { AdbResponse.OK, AdbResponse.OK }, + [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, Array.Empty<(SyncCommand, string)>(), Array.Empty(), - new byte[][] { expectedData }, + [expectedData], Array.Empty(), () => TestClient.RootAsync(device))); } @@ -718,11 +678,11 @@ public async void UnrootAsyncTest() State = DeviceState.Online }; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:009d1cd696d5194a", "unroot:" - }; + ]; byte[] expectedData = new byte[1024]; byte[] expectedString = Encoding.UTF8.GetBytes("adbd not running as root\n"); @@ -730,12 +690,12 @@ public async void UnrootAsyncTest() _ = await Assert.ThrowsAsync(() => RunTestAsync( - new AdbResponse[] { AdbResponse.OK, AdbResponse.OK }, + [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, Array.Empty<(SyncCommand, string)>(), Array.Empty(), - new byte[][] { expectedData }, + [expectedData], Array.Empty(), () => TestClient.UnrootAsync(device))); } @@ -752,14 +712,14 @@ public async void InstallAsyncTest() State = DeviceState.Online }; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:009d1cd696d5194a", "exec:cmd package 'install' -S 205774" - }; + ]; // The app data is sent in chunks of 32 kb - Collection applicationDataChuncks = new(); + Collection applicationDataChunks = []; await using (Stream stream = File.OpenRead("Assets/testapp.apk")) { @@ -775,7 +735,7 @@ public async void InstallAsyncTest() else { buffer = buffer.Take(read).ToArray(); - applicationDataChuncks.Add(buffer); + applicationDataChunks.Add(buffer); } } } @@ -785,17 +745,13 @@ public async void InstallAsyncTest() await using (Stream stream = File.OpenRead("Assets/testapp.apk")) { await RunTestAsync( - new AdbResponse[] - { - AdbResponse.OK, - AdbResponse.OK, - }, + [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, Array.Empty<(SyncCommand, string)>(), Array.Empty(), - new byte[][] { response }, - applicationDataChuncks.ToArray(), + [response], + applicationDataChunks.ToArray(), () => TestClient.InstallAsync(device, stream)); } } @@ -812,23 +768,18 @@ public async void InstallCreateAsyncTest() State = DeviceState.Online }; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:009d1cd696d5194a", "exec:cmd package 'install-create' -p com.google.android.gms" - }; + ]; byte[] streamData = Encoding.ASCII.GetBytes("Success: created install session [936013062]\r\n"); await using MemoryStream shellStream = new(streamData); string session = string.Empty; - await RunTestAsync( - new AdbResponse[] - { - AdbResponse.OK, - AdbResponse.OK, - }, + [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, shellStream, @@ -849,14 +800,14 @@ public async void InstallWriteAsyncTest() State = DeviceState.Online }; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:009d1cd696d5194a", "exec:cmd package 'install-write' -S 205774 936013062 base.apk" - }; + ]; // The app data is sent in chunks of 32 kb - Collection applicationDataChuncks = new(); + Collection applicationDataChuncks = []; await using (Stream stream = File.OpenRead("Assets/testapp.apk")) { @@ -882,16 +833,12 @@ public async void InstallWriteAsyncTest() await using (Stream stream = File.OpenRead("Assets/testapp.apk")) { await RunTestAsync( - new AdbResponse[] - { - AdbResponse.OK, - AdbResponse.OK, - }, + [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, Array.Empty<(SyncCommand, string)>(), Array.Empty(), - new byte[][] { response }, + [response], applicationDataChuncks.ToArray(), () => TestClient.InstallWriteAsync(device, stream, "base", "936013062")); } @@ -909,21 +856,17 @@ public async void InstallCommitAsyncTest() State = DeviceState.Online }; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:009d1cd696d5194a", "exec:cmd package 'install-commit' 936013062" - }; + ]; byte[] streamData = Encoding.ASCII.GetBytes("Success\r\n"); await using MemoryStream shellStream = new(streamData); await RunTestAsync( - new AdbResponse[] - { - AdbResponse.OK, - AdbResponse.OK, - }, + [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, shellStream, @@ -942,23 +885,12 @@ public async void GetFeatureSetAsyncTest() State = DeviceState.Online }; - string[] requests = new string[] - { - "host-serial:009d1cd696d5194a:features" - }; - - string[] responses = new string[] - { - "sendrecv_v2_brotli,remount_shell,sendrecv_v2,abb_exec,fixed_push_mkdir,fixed_push_symlink_timestamp,abb,shell_v2,cmd,ls_v2,apex,stat_v2\r\n" - }; + string[] requests = ["host-serial:009d1cd696d5194a:features"]; + string[] responses = ["sendrecv_v2_brotli,remount_shell,sendrecv_v2,abb_exec,fixed_push_mkdir,fixed_push_symlink_timestamp,abb,shell_v2,cmd,ls_v2,apex,stat_v2\r\n"]; IEnumerable features = null; - await RunTestAsync( - new AdbResponse[] - { - AdbResponse.OK, - }, + [AdbResponse.OK], responses, requests, async () => features = await TestClient.GetFeatureSetAsync(device)); @@ -980,11 +912,11 @@ public async void DumpScreenStringAsyncTest() State = DeviceState.Online }; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:009d1cd696d5194a", "shell:uiautomator dump /dev/tty" - }; + ]; string dump = File.ReadAllText(@"Assets/dumpscreen.txt"); string cleanDump = File.ReadAllText(@"Assets/dumpscreen_clean.txt"); @@ -992,13 +924,8 @@ public async void DumpScreenStringAsyncTest() await using MemoryStream shellStream = new(streamData); string xml = string.Empty; - await RunTestAsync( - new AdbResponse[] - { - AdbResponse.OK, - AdbResponse.OK, - }, + [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, shellStream, @@ -1019,11 +946,11 @@ public async void DumpScreenStringAsyncMIUITest() State = DeviceState.Online }; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:009d1cd696d5194a", "shell:uiautomator dump /dev/tty" - }; + ]; string miuiDump = File.ReadAllText(@"Assets/dumpscreen_miui.txt"); string cleanMIUIDump = File.ReadAllText(@"Assets/dumpscreen_miui_clean.txt"); @@ -1031,13 +958,8 @@ public async void DumpScreenStringAsyncMIUITest() await using MemoryStream miuiStream = new(miuiStreamData); string miuiXml = string.Empty; - await RunTestAsync( - new AdbResponse[] - { - AdbResponse.OK, - AdbResponse.OK, - }, + [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, miuiStream, @@ -1058,22 +980,18 @@ public async void DumpScreenStringAsyncEmptyTest() State = DeviceState.Online }; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:009d1cd696d5194a", "shell:uiautomator dump /dev/tty" - }; + ]; byte[] emptyStreamData = Encoding.UTF8.GetBytes(string.Empty); await using MemoryStream emptyStream = new(emptyStreamData); - string emptyXml = string.Empty; + string emptyXml = string.Empty; await RunTestAsync( - new AdbResponse[] - { - AdbResponse.OK, - AdbResponse.OK, - }, + [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, emptyStream, @@ -1094,11 +1012,11 @@ public async void DumpScreenStringAsyncErrorTest() State = DeviceState.Online }; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:009d1cd696d5194a", "shell:uiautomator dump /dev/tty" - }; + ]; string errorXml = File.ReadAllText(@"Assets/dumpscreen_error.txt"); byte[] errorStreamData = Encoding.UTF8.GetBytes(errorXml); @@ -1106,11 +1024,7 @@ public async void DumpScreenStringAsyncErrorTest() await Assert.ThrowsAsync(() => RunTestAsync( - new AdbResponse[] - { - AdbResponse.OK, - AdbResponse.OK, - }, + [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, errorStream, @@ -1129,24 +1043,19 @@ public async void DumpScreenAsyncTest() State = DeviceState.Online }; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:009d1cd696d5194a", "shell:uiautomator dump /dev/tty" - }; + ]; string dump = File.ReadAllText(@"Assets/dumpscreen.txt"); byte[] streamData = Encoding.UTF8.GetBytes(dump); await using MemoryStream shellStream = new(streamData); XmlDocument xml = null; - await RunTestAsync( - new AdbResponse[] - { - AdbResponse.OK, - AdbResponse.OK, - }, + [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, shellStream, @@ -1171,11 +1080,11 @@ public async void ClickAsyncTest() State = DeviceState.Online }; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:009d1cd696d5194a", "shell:input tap 100 100" - }; + ]; byte[] streamData = Encoding.UTF8.GetBytes(@"java.lang.SecurityException: Injecting to another application requires INJECT_EVENTS permission at android.os.Parcel.createExceptionOrNull(Parcel.java:2373) @@ -1203,11 +1112,7 @@ at android.os.Binder.execTransactInternal(Binder.java:1165) JavaException exception = await Assert.ThrowsAsync(() => RunTestAsync( - new AdbResponse[] - { - AdbResponse.OK, - AdbResponse.OK, - }, + [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, shellStream, @@ -1250,22 +1155,18 @@ public async void ClickCordsAsyncTest() State = DeviceState.Online }; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:009d1cd696d5194a", "shell:input tap 100 100" - }; - + ]; + byte[] streamData = Encoding.UTF8.GetBytes(@"Error: Injecting to another application requires INJECT_EVENTS permission"); await using MemoryStream shellStream = new(streamData); _ = await Assert.ThrowsAsync(() => RunTestAsync( - new AdbResponse[] - { - AdbResponse.OK, - AdbResponse.OK, - }, + [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, shellStream, @@ -1284,22 +1185,18 @@ public async void FindElementAsyncTest() State = DeviceState.Online }; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:009d1cd696d5194a", "shell:uiautomator dump /dev/tty" - }; + ]; string dump = File.ReadAllText(@"Assets/dumpscreen.txt"); byte[] streamData = Encoding.UTF8.GetBytes(dump); await using MemoryStream shellStream = new(streamData); await RunTestAsync( - new AdbResponse[] - { - AdbResponse.OK, - AdbResponse.OK, - }, + [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, shellStream, @@ -1325,22 +1222,18 @@ public async void FindElementsAsyncTest() State = DeviceState.Online }; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:009d1cd696d5194a", "shell:uiautomator dump /dev/tty" - }; + ]; string dump = File.ReadAllText(@"Assets/dumpscreen.txt"); byte[] streamData = Encoding.UTF8.GetBytes(dump); await using MemoryStream shellStream = new(streamData); await RunTestAsync( - new AdbResponse[] - { - AdbResponse.OK, - AdbResponse.OK, - }, + [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, shellStream, @@ -1358,15 +1251,8 @@ await RunTestAsync( private Task RunConnectAsyncTest(Func test, string connectString) { - string[] requests = new string[] - { - $"host:connect:{connectString}" - }; - - string[] responseMessages = new string[] - { - $"connected to {connectString}" - }; + string[] requests = [$"host:connect:{connectString}"]; + string[] responseMessages = [$"connected to {connectString}"]; return RunTestAsync( OkResponse, @@ -1377,15 +1263,8 @@ private Task RunConnectAsyncTest(Func test, string connectString) private Task RunPairAsyncTest(Func test, string connectString, string code) { - string[] requests = new string[] - { - $"host:pair:{code}:{connectString}" - }; - - string[] responseMessages = new string[] - { - $"Successfully paired to {connectString} [guid=adb-996198a3-xPRwsQ]" - }; + string[] requests = [$"host:pair:{code}:{connectString}"]; + string[] responseMessages = [$"Successfully paired to {connectString} [guid=adb-996198a3-xPRwsQ]"]; return RunTestAsync( OkResponse, @@ -1396,23 +1275,15 @@ private Task RunPairAsyncTest(Func test, string connectString, string code private Task RunCreateReverseAsyncTest(Func test, string reverseString) { - string[] requests = new string[] - { + string[] requests = + [ "host:transport:169.254.109.177:5555", $"reverse:forward:{reverseString}", - }; + ]; return RunTestAsync( - new AdbResponse[] - { - AdbResponse.OK, - AdbResponse.OK, - AdbResponse.OK - }, - new string[] - { - null - }, + [AdbResponse.OK, AdbResponse.OK, AdbResponse.OK], + [null], requests, () => test(Device)); } @@ -1425,15 +1296,8 @@ private Task RunCreateForwardAsyncTest(Func test, string forwa }; return RunTestAsync( - new AdbResponse[] - { - AdbResponse.OK, - AdbResponse.OK - }, - new string[] - { - null - }, + [AdbResponse.OK, AdbResponse.OK], + [null], requests, () => test(Device)); } diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs index a6df31e0..4710da56 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs @@ -3,8 +3,8 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Drawing.Imaging; using System.Drawing; +using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Net; @@ -12,7 +12,6 @@ using System.Text; using System.Xml; using Xunit; -using System.Text.RegularExpressions; namespace AdvancedSharpAdbClient.Tests { @@ -85,18 +84,10 @@ public void CreateAdbForwardRequestTest() [Fact] public void GetAdbVersionTest() { - string[] responseMessages = new string[] - { - "0020" - }; - - string[] requests = new string[] - { - "host:version" - }; + string[] responseMessages = ["0020"]; + string[] requests = ["host:version"]; int version = 0; - RunTest( OkResponse, responseMessages, @@ -113,10 +104,7 @@ public void GetAdbVersionTest() [Fact] public void KillAdbTest() { - string[] requests = new string[] - { - "host:kill" - }; + string[] requests = ["host:kill"]; RunTest( NoResponses, @@ -131,18 +119,10 @@ public void KillAdbTest() [Fact] public void GetDevicesTest() { - string[] responseMessages = new string[] - { - "169.254.109.177:5555 device product:VS Emulator 5\" KitKat (4.4) XXHDPI Phone model:5__KitKat__4_4__XXHDPI_Phone device:donatello\n" - }; - - string[] requests = new string[] - { - "host:devices-l" - }; + string[] responseMessages = ["169.254.109.177:5555 device product:VS Emulator 5\" KitKat (4.4) XXHDPI Phone model:5__KitKat__4_4__XXHDPI_Phone device:donatello\n"]; + string[] requests = ["host:devices-l"]; IEnumerable devices = null; - RunTest( OkResponse, responseMessages, @@ -167,10 +147,7 @@ public void GetDevicesTest() [Fact] public void SetDeviceTest() { - string[] requests = new string[] - { - "host:transport:169.254.109.177:5555" - }; + string[] requests = ["host:transport:169.254.109.177:5555"]; RunTest( OkResponse, @@ -185,14 +162,11 @@ public void SetDeviceTest() [Fact] public void SetInvalidDeviceTest() { - string[] requests = new string[] - { - "host:transport:169.254.109.177:5555" - }; + string[] requests = ["host:transport:169.254.109.177:5555"]; _ = Assert.Throws(() => RunTest( - new AdbResponse[] { AdbResponse.FromError("device not found") }, + [AdbResponse.FromError("device not found")], NoResponseMessages, requests, () => Socket.SetDevice(Device))); @@ -204,14 +178,11 @@ public void SetInvalidDeviceTest() [Fact] public void SetDeviceOtherException() { - string[] requests = new string[] - { - "host:transport:169.254.109.177:5555" - }; + string[] requests = ["host:transport:169.254.109.177:5555"]; _ = Assert.Throws(() => RunTest( - new AdbResponse[] { AdbResponse.FromError("Too many cats.") }, + [AdbResponse.FromError("Too many cats.")], NoResponseMessages, requests, () => Socket.SetDevice(Device))); @@ -259,15 +230,8 @@ public void CreateSocketForwardTest() => [Fact] public void CreateDuplicateForwardTest() { - AdbResponse[] responses = new AdbResponse[] - { - AdbResponse.FromError("cannot rebind existing socket") - }; - - string[] requests = new string[] - { - "host-serial:169.254.109.177:5555:forward:norebind:tcp:1;tcp:2" - }; + AdbResponse[] responses = [AdbResponse.FromError("cannot rebind existing socket")]; + string[] requests = ["host-serial:169.254.109.177:5555:forward:norebind:tcp:1;tcp:2"]; _ = Assert.Throws(() => RunTest( @@ -283,10 +247,7 @@ public void CreateDuplicateForwardTest() [Fact] public void RemoveForwardTest() { - string[] requests = new string[] - { - "host-serial:169.254.109.177:5555:killforward:tcp:1" - }; + string[] requests = ["host-serial:169.254.109.177:5555:killforward:tcp:1"]; RunTest( OkResponse, @@ -301,17 +262,17 @@ public void RemoveForwardTest() [Fact] public void RemoveReverseForwardTest() { - string[] requests = new string[] - { + string[] requests = + [ "host:transport:169.254.109.177:5555", "reverse:killforward:localabstract:test" - }; + ]; - AdbResponse[] responses = new AdbResponse[] - { + AdbResponse[] responses = + [ AdbResponse.OK, AdbResponse.OK, - }; + ]; RunTest( responses, @@ -326,10 +287,7 @@ public void RemoveReverseForwardTest() [Fact] public void RemoveAllForwardsTest() { - string[] requests = new string[] - { - "host-serial:169.254.109.177:5555:killforward-all" - }; + string[] requests = ["host-serial:169.254.109.177:5555:killforward-all"]; RunTest( OkResponse, @@ -344,17 +302,17 @@ public void RemoveAllForwardsTest() [Fact] public void RemoveAllReversesTest() { - string[] requests = new string[] - { + string[] requests = + [ "host:transport:169.254.109.177:5555", "reverse:killforward-all" - }; + ]; - AdbResponse[] responses = new AdbResponse[] - { + AdbResponse[] responses = + [ AdbResponse.OK, AdbResponse.OK, - }; + ]; RunTest( responses, @@ -369,18 +327,10 @@ public void RemoveAllReversesTest() [Fact] public void ListForwardTest() { - string[] responseMessages = new string[] - { - "169.254.109.177:5555 tcp:1 tcp:2\n169.254.109.177:5555 tcp:3 tcp:4\n169.254.109.177:5555 tcp:5 local:/socket/1\n" - }; - - string[] requests = new string[] - { - "host-serial:169.254.109.177:5555:list-forward" - }; + string[] responseMessages = ["169.254.109.177:5555 tcp:1 tcp:2\n169.254.109.177:5555 tcp:3 tcp:4\n169.254.109.177:5555 tcp:5 local:/socket/1\n"]; + string[] requests = ["host-serial:169.254.109.177:5555:list-forward"]; ForwardData[] forwards = null; - RunTest( OkResponse, responseMessages, @@ -400,24 +350,21 @@ public void ListForwardTest() [Fact] public void ListReverseForwardTest() { - string[] responseMessages = new string[] - { - "(reverse) localabstract:scrcpy tcp:100\n(reverse) localabstract: scrcpy2 tcp:100\n(reverse) localabstract: scrcpy3 tcp:100\n" - }; - AdbResponse[] responses = new AdbResponse[] - { + string[] responseMessages = ["(reverse) localabstract:scrcpy tcp:100\n(reverse) localabstract: scrcpy2 tcp:100\n(reverse) localabstract: scrcpy3 tcp:100\n"]; + + AdbResponse[] responses = + [ AdbResponse.OK, AdbResponse.OK, - }; + ]; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:169.254.109.177:5555", "reverse:list-forward" - }; + ]; ForwardData[] forwards = null; - RunTest( responses, responseMessages, @@ -443,19 +390,19 @@ public void ExecuteRemoteCommandTest() State = DeviceState.Online }; - AdbResponse[] responses = new AdbResponse[] - { + AdbResponse[] responses = + [ AdbResponse.OK, AdbResponse.OK - }; + ]; - string[] responseMessages = Array.Empty(); + string[] responseMessages = []; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:169.254.109.177:5555", "shell:echo Hello, World" - }; + ]; byte[] streamData = Encoding.ASCII.GetBytes("Hello, World\r\n"); using MemoryStream shellStream = new(streamData); @@ -484,19 +431,19 @@ public void ExecuteRemoteCommandUnresponsiveTest() State = DeviceState.Online }; - AdbResponse[] responses = new AdbResponse[] - { + AdbResponse[] responses = + [ AdbResponse.OK, AdbResponse.OK - }; + ]; - string[] responseMessages = Array.Empty(); + string[] responseMessages = []; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:169.254.109.177:5555", "shell:echo Hello, World" - }; + ]; ConsoleOutputReceiver receiver = new(); @@ -544,8 +491,12 @@ public void GetFrameBufferTest() Framebuffer framebuffer = null; - Factories.AdbSocketFactory = (endPoint) => socket; - framebuffer = TestClient.GetFrameBuffer(device); + using (FactoriesLocker locker = FactoriesLocker.Wait()) + { + Factories.AdbSocketFactory = (endPoint) => socket; + framebuffer = TestClient.GetFrameBuffer(device); + Factories.Reset(); + } Assert.NotNull(framebuffer); Assert.Equal(device, framebuffer.Device); @@ -596,25 +547,25 @@ public void RunLogServiceTest() State = DeviceState.Online }; - AdbResponse[] responses = new AdbResponse[] - { + AdbResponse[] responses = + [ AdbResponse.OK, AdbResponse.OK - }; + ]; - string[] responseMessages = Array.Empty(); + string[] responseMessages = []; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:169.254.109.177:5555", "shell:logcat -B -b system" - }; + ]; ConsoleOutputReceiver receiver = new(); using Stream stream = File.OpenRead("Assets/logcat.bin"); using ShellStream shellStream = new(stream, false); - Collection logs = new(); + Collection logs = []; Action sink = logs.Add; RunTest( @@ -633,14 +584,14 @@ public void RunLogServiceTest() [Fact] public void RebootTest() { - string[] requests = new string[] - { + string[] requests = + [ "host:transport:169.254.109.177:5555", "reboot:" - }; + ]; RunTest( - new AdbResponse[] { AdbResponse.OK, AdbResponse.OK }, + [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, () => TestClient.Reboot(Device)); @@ -784,8 +735,8 @@ public void ConnectHostEndpointNullTest() => [Fact] public void DisconnectTest() { - string[] requests = new string[] { "host:disconnect:localhost:5555" }; - string[] responseMessages = new string[] { "disconnected 127.0.0.1:5555" }; + string[] requests = ["host:disconnect:localhost:5555"]; + string[] responseMessages = ["disconnected 127.0.0.1:5555"]; RunTest( OkResponse, @@ -806,11 +757,11 @@ public void RootTest() State = DeviceState.Online }; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:009d1cd696d5194a", "root:" - }; + ]; byte[] expectedData = new byte[1024]; byte[] expectedString = Encoding.UTF8.GetBytes("adbd cannot run as root in production builds\n"); @@ -818,12 +769,12 @@ public void RootTest() _ = Assert.Throws(() => RunTest( - new AdbResponse[] { AdbResponse.OK, AdbResponse.OK }, + [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, Array.Empty<(SyncCommand, string)>(), Array.Empty(), - new byte[][] { expectedData }, + [expectedData], Array.Empty(), () => TestClient.Root(device))); } @@ -840,11 +791,11 @@ public void UnrootTest() State = DeviceState.Online }; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:009d1cd696d5194a", "unroot:" - }; + ]; byte[] expectedData = new byte[1024]; byte[] expectedString = Encoding.UTF8.GetBytes("adbd not running as root\n"); @@ -852,12 +803,12 @@ public void UnrootTest() _ = Assert.Throws(() => RunTest( - new AdbResponse[] { AdbResponse.OK, AdbResponse.OK }, + [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, Array.Empty<(SyncCommand, string)>(), Array.Empty(), - new byte[][] { expectedData }, + [expectedData], Array.Empty(), () => TestClient.Unroot(device))); } @@ -874,14 +825,14 @@ public void InstallTest() State = DeviceState.Online }; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:009d1cd696d5194a", "exec:cmd package 'install' -S 205774" - }; + ]; // The app data is sent in chunks of 32 kb - Collection applicationDataChuncks = new(); + Collection applicationDataChunks = []; using (Stream stream = File.OpenRead("Assets/testapp.apk")) { @@ -897,7 +848,7 @@ public void InstallTest() else { buffer = buffer.Take(read).ToArray(); - applicationDataChuncks.Add(buffer); + applicationDataChunks.Add(buffer); } } } @@ -907,17 +858,13 @@ public void InstallTest() using (Stream stream = File.OpenRead("Assets/testapp.apk")) { RunTest( - new AdbResponse[] - { - AdbResponse.OK, - AdbResponse.OK, - }, + [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, Array.Empty<(SyncCommand, string)>(), Array.Empty(), - new byte[][] { response }, - applicationDataChuncks.ToArray(), + [response], + applicationDataChunks.ToArray(), () => TestClient.Install(device, stream)); } } @@ -934,23 +881,18 @@ public void InstallCreateTest() State = DeviceState.Online }; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:009d1cd696d5194a", "exec:cmd package 'install-create' -p com.google.android.gms" - }; + ]; byte[] streamData = Encoding.ASCII.GetBytes("Success: created install session [936013062]\r\n"); using MemoryStream shellStream = new(streamData); string session = string.Empty; - RunTest( - new AdbResponse[] - { - AdbResponse.OK, - AdbResponse.OK, - }, + [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, shellStream, @@ -971,14 +913,14 @@ public void InstallWriteTest() State = DeviceState.Online }; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:009d1cd696d5194a", "exec:cmd package 'install-write' -S 205774 936013062 base.apk" - }; + ]; // The app data is sent in chunks of 32 kb - Collection applicationDataChuncks = new(); + Collection applicationDataChunks = []; using (Stream stream = File.OpenRead("Assets/testapp.apk")) { @@ -994,7 +936,7 @@ public void InstallWriteTest() else { buffer = buffer.Take(read).ToArray(); - applicationDataChuncks.Add(buffer); + applicationDataChunks.Add(buffer); } } } @@ -1004,17 +946,13 @@ public void InstallWriteTest() using (Stream stream = File.OpenRead("Assets/testapp.apk")) { RunTest( - new AdbResponse[] - { - AdbResponse.OK, - AdbResponse.OK, - }, + [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, Array.Empty<(SyncCommand, string)>(), Array.Empty(), - new byte[][] { response }, - applicationDataChuncks.ToArray(), + [response], + applicationDataChunks.ToArray(), () => TestClient.InstallWrite(device, stream, "base", "936013062")); } } @@ -1031,21 +969,17 @@ public void InstallCommitTest() State = DeviceState.Online }; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:009d1cd696d5194a", "exec:cmd package 'install-commit' 936013062" - }; + ]; byte[] streamData = Encoding.ASCII.GetBytes("Success\r\n"); using MemoryStream shellStream = new(streamData); RunTest( - new AdbResponse[] - { - AdbResponse.OK, - AdbResponse.OK, - }, + [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, shellStream, @@ -1064,23 +998,12 @@ public void GetFeatureSetTest() State = DeviceState.Online }; - string[] requests = new string[] - { - "host-serial:009d1cd696d5194a:features" - }; - - string[] responses = new string[] - { - "sendrecv_v2_brotli,remount_shell,sendrecv_v2,abb_exec,fixed_push_mkdir,fixed_push_symlink_timestamp,abb,shell_v2,cmd,ls_v2,apex,stat_v2\r\n" - }; + string[] requests = ["host-serial:009d1cd696d5194a:features"]; + string[] responses = ["sendrecv_v2_brotli,remount_shell,sendrecv_v2,abb_exec,fixed_push_mkdir,fixed_push_symlink_timestamp,abb,shell_v2,cmd,ls_v2,apex,stat_v2\r\n"]; IEnumerable features = null; - RunTest( - new AdbResponse[] - { - AdbResponse.OK, - }, + [AdbResponse.OK], responses, requests, () => features = TestClient.GetFeatureSet(device)); @@ -1102,11 +1025,11 @@ public void DumpScreenStringTest() State = DeviceState.Online }; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:009d1cd696d5194a", "shell:uiautomator dump /dev/tty" - }; + ]; string dump = File.ReadAllText(@"Assets/dumpscreen.txt"); string cleanDump = File.ReadAllText(@"Assets/dumpscreen_clean.txt"); @@ -1114,13 +1037,8 @@ public void DumpScreenStringTest() using MemoryStream shellStream = new(streamData); string xml = string.Empty; - RunTest( - new AdbResponse[] - { - AdbResponse.OK, - AdbResponse.OK, - }, + [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, shellStream, @@ -1141,11 +1059,11 @@ public void DumpScreenStringMIUITest() State = DeviceState.Online }; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:009d1cd696d5194a", "shell:uiautomator dump /dev/tty" - }; + ]; string miuidump = File.ReadAllText(@"Assets/dumpscreen_miui.txt"); string cleanMIUIDump = File.ReadAllText(@"Assets/dumpscreen_miui_clean.txt"); @@ -1153,13 +1071,8 @@ public void DumpScreenStringMIUITest() using MemoryStream miuiStream = new(miuiStreamData); string miuiXml = string.Empty; - RunTest( - new AdbResponse[] - { - AdbResponse.OK, - AdbResponse.OK, - }, + [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, miuiStream, @@ -1180,26 +1093,22 @@ public void DumpScreenStringEmptyTest() State = DeviceState.Online }; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:009d1cd696d5194a", "shell:uiautomator dump /dev/tty" - }; + ]; byte[] emptyStreamData = Encoding.UTF8.GetBytes(string.Empty); using MemoryStream emptyStream = new(emptyStreamData); - string emptyXml = string.Empty; + string emptyXml = string.Empty; RunTest( - new AdbResponse[] - { - AdbResponse.OK, - AdbResponse.OK, - }, - NoResponseMessages, - requests, - emptyStream, - () => emptyXml = TestClient.DumpScreenString(device)); + [AdbResponse.OK, AdbResponse.OK], + NoResponseMessages, + requests, + emptyStream, + () => emptyXml = TestClient.DumpScreenString(device)); Assert.True(string.IsNullOrEmpty(emptyXml)); } @@ -1216,11 +1125,11 @@ public void DumpScreenStringErrorTest() State = DeviceState.Online }; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:009d1cd696d5194a", "shell:uiautomator dump /dev/tty" - }; + ]; string errorXml = File.ReadAllText(@"Assets/dumpscreen_error.txt"); byte[] errorStreamData = Encoding.UTF8.GetBytes(errorXml); @@ -1228,15 +1137,11 @@ public void DumpScreenStringErrorTest() Assert.Throws(() => RunTest( - new AdbResponse[] - { - AdbResponse.OK, - AdbResponse.OK, - }, - NoResponseMessages, - requests, - errorStream, - () => TestClient.DumpScreenString(device))); + [AdbResponse.OK, AdbResponse.OK], + NoResponseMessages, + requests, + errorStream, + () => TestClient.DumpScreenString(device))); } /// @@ -1251,24 +1156,19 @@ public void DumpScreenTest() State = DeviceState.Online }; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:009d1cd696d5194a", "shell:uiautomator dump /dev/tty" - }; + ]; string dump = File.ReadAllText(@"Assets/dumpscreen.txt"); byte[] streamData = Encoding.UTF8.GetBytes(dump); using MemoryStream shellStream = new(streamData); XmlDocument xml = null; - RunTest( - new AdbResponse[] - { - AdbResponse.OK, - AdbResponse.OK, - }, + [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, shellStream, @@ -1293,11 +1193,11 @@ public void ClickTest() State = DeviceState.Online }; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:009d1cd696d5194a", "shell:input tap 100 100" - }; + ]; byte[] streamData = Encoding.UTF8.GetBytes(@"java.lang.SecurityException: Injecting to another application requires INJECT_EVENTS permission at android.os.Parcel.createExceptionOrNull(Parcel.java:2373) @@ -1325,11 +1225,7 @@ at android.os.Binder.execTransactInternal(Binder.java:1165) JavaException exception = Assert.Throws(() => RunTest( - new AdbResponse[] - { - AdbResponse.OK, - AdbResponse.OK, - }, + [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, shellStream, @@ -1372,22 +1268,18 @@ public void ClickCordsTest() State = DeviceState.Online }; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:009d1cd696d5194a", "shell:input tap 100 100" - }; + ]; byte[] streamData = Encoding.UTF8.GetBytes(@"Error: Injecting to another application requires INJECT_EVENTS permission"); using MemoryStream shellStream = new(streamData); _ = Assert.Throws(() => RunTest( - new AdbResponse[] - { - AdbResponse.OK, - AdbResponse.OK, - }, + [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, shellStream, @@ -1406,22 +1298,18 @@ public void FindElementTest() State = DeviceState.Online }; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:009d1cd696d5194a", "shell:uiautomator dump /dev/tty" - }; + ]; string dump = File.ReadAllText(@"Assets/dumpscreen.txt"); byte[] streamData = Encoding.UTF8.GetBytes(dump); using MemoryStream shellStream = new(streamData); RunTest( - new AdbResponse[] - { - AdbResponse.OK, - AdbResponse.OK, - }, + [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, shellStream, @@ -1447,22 +1335,18 @@ public void FindElementsTest() State = DeviceState.Online }; - string[] requests = new string[] - { + string[] requests = + [ "host:transport:009d1cd696d5194a", "shell:uiautomator dump /dev/tty" - }; + ]; string dump = File.ReadAllText(@"Assets/dumpscreen.txt"); byte[] streamData = Encoding.UTF8.GetBytes(dump); using MemoryStream shellStream = new(streamData); RunTest( - new AdbResponse[] - { - AdbResponse.OK, - AdbResponse.OK, - }, + [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, shellStream, @@ -1480,15 +1364,8 @@ public void FindElementsTest() private void RunConnectTest(Action test, string connectString) { - string[] requests = new string[] - { - $"host:connect:{connectString}" - }; - - string[] responseMessages = new string[] - { - $"connected to {connectString}" - }; + string[] requests = [$"host:connect:{connectString}"]; + string[] responseMessages = [$"connected to {connectString}"]; RunTest( OkResponse, @@ -1499,15 +1376,8 @@ private void RunConnectTest(Action test, string connectString) private void RunPairTest(Action test, string connectString, string code) { - string[] requests = new string[] - { - $"host:pair:{code}:{connectString}" - }; - - string[] responseMessages = new string[] - { - $"Successfully paired to {connectString} [guid=adb-996198a3-xPRwsQ]" - }; + string[] requests = [$"host:pair:{code}:{connectString}"]; + string[] responseMessages = [$"Successfully paired to {connectString} [guid=adb-996198a3-xPRwsQ]"]; RunTest( OkResponse, @@ -1518,44 +1388,26 @@ private void RunPairTest(Action test, string connectString, string code) private void RunCreateReverseTest(Action test, string reverseString) { - string[] requests = new string[] - { + string[] requests = + [ "host:transport:169.254.109.177:5555", $"reverse:forward:{reverseString}", - }; + ]; RunTest( - new AdbResponse[] - { - AdbResponse.OK, - AdbResponse.OK, - AdbResponse.OK - }, - new string[] - { - null - }, + [AdbResponse.OK, AdbResponse.OK, AdbResponse.OK], + [null], requests, () => test(Device)); } private void RunCreateForwardTest(Action test, string forwardString) { - string[] requests = new string[] - { - $"host-serial:169.254.109.177:5555:forward:{forwardString}" - }; + string[] requests = [$"host-serial:169.254.109.177:5555:forward:{forwardString}"]; RunTest( - new AdbResponse[] - { - AdbResponse.OK, - AdbResponse.OK - }, - new string[] - { - null - }, + [AdbResponse.OK, AdbResponse.OK], + [null], requests, () => test(Device)); } diff --git a/AdvancedSharpAdbClient.Tests/AdbCommandLineClientTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbCommandLineClientTests.Async.cs index 6430112b..ff6a965c 100644 --- a/AdvancedSharpAdbClient.Tests/AdbCommandLineClientTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbCommandLineClientTests.Async.cs @@ -13,7 +13,6 @@ public async void GetVersionAsyncTest() { Version = new Version(1, 0, 32) }; - Assert.Equal(new Version(1, 0, 32), await commandLine.GetVersionAsync()); } @@ -34,7 +33,6 @@ public async void GetOutdatedVersionAsyncTest() { Version = new Version(1, 0, 1) }; - _ = await Assert.ThrowsAsync(() => commandLine.GetVersionAsync()); } diff --git a/AdvancedSharpAdbClient.Tests/AdbCommandLineClientTests.cs b/AdvancedSharpAdbClient.Tests/AdbCommandLineClientTests.cs index 74d33fcb..103592f8 100644 --- a/AdvancedSharpAdbClient.Tests/AdbCommandLineClientTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbCommandLineClientTests.cs @@ -16,7 +16,6 @@ public void GetVersionTest() { Version = new Version(1, 0, 32) }; - Assert.Equal(new Version(1, 0, 32), commandLine.GetVersion()); } @@ -37,7 +36,6 @@ public void GetOutdatedVersionTest() { Version = new Version(1, 0, 1) }; - _ = Assert.Throws(commandLine.GetVersion); } diff --git a/AdvancedSharpAdbClient.Tests/AdbSocketTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbSocketTests.Async.cs index 8d650767..cedda4d1 100644 --- a/AdvancedSharpAdbClient.Tests/AdbSocketTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbSocketTests.Async.cs @@ -14,23 +14,23 @@ public partial class AdbSocketTests public async void SendSyncDATARequestAsyncTest() => await RunTestAsync( (socket) => socket.SendSyncRequestAsync(SyncCommand.DATA, 2, CancellationToken.None), - new byte[] { (byte)'D', (byte)'A', (byte)'T', (byte)'A', 2, 0, 0, 0 }); + [(byte)'D', (byte)'A', (byte)'T', (byte)'A', 2, 0, 0, 0]); [Fact] public async void SendSyncSENDRequestAsyncTest() => await RunTestAsync( (socket) => socket.SendSyncRequestAsync(SyncCommand.SEND, "/test", CancellationToken.None), - new byte[] { (byte)'S', (byte)'E', (byte)'N', (byte)'D', 5, 0, 0, 0, (byte)'/', (byte)'t', (byte)'e', (byte)'s', (byte)'t' }); + [(byte)'S', (byte)'E', (byte)'N', (byte)'D', 5, 0, 0, 0, (byte)'/', (byte)'t', (byte)'e', (byte)'s', (byte)'t']); [Fact] public async void SendSyncDENTRequestAsyncTest() => await RunTestAsync( (socket) => socket.SendSyncRequestAsync(SyncCommand.DENT, "/data", 633, CancellationToken.None), - new byte[] { (byte)'D', (byte)'E', (byte)'N', (byte)'T', 9, 0, 0, 0, (byte)'/', (byte)'d', (byte)'a', (byte)'t', (byte)'a', (byte)',', (byte)'6', (byte)'3', (byte)'3' }); + [(byte)'D', (byte)'E', (byte)'N', (byte)'T', 9, 0, 0, 0, (byte)'/', (byte)'d', (byte)'a', (byte)'t', (byte)'a', (byte)',', (byte)'6', (byte)'3', (byte)'3']); [Fact] public async void SendSyncNullRequestAsyncTest() => - _ = await Assert.ThrowsAsync(() => RunTestAsync((socket) => socket.SendSyncRequestAsync(SyncCommand.DATA, null, CancellationToken.None), Array.Empty())); + _ = await Assert.ThrowsAsync(() => RunTestAsync((socket) => socket.SendSyncRequestAsync(SyncCommand.DATA, null, CancellationToken.None), [])); [Fact] public async void ReadSyncResponseAsync() @@ -117,7 +117,7 @@ public async void ReadAsyncTest() data[i] = (byte)i; } - await tcpSocket.InputStream.WriteAsync(data, 0, 101); + await tcpSocket.InputStream.WriteAsync(data.AsMemory(0, 101)); tcpSocket.InputStream.Position = 0; // Buffer has a capacity of 101, but we'll only want to read 100 bytes diff --git a/AdvancedSharpAdbClient.Tests/AdbSocketTests.cs b/AdvancedSharpAdbClient.Tests/AdbSocketTests.cs index a6b11ba9..c5f6170a 100644 --- a/AdvancedSharpAdbClient.Tests/AdbSocketTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbSocketTests.cs @@ -50,23 +50,23 @@ public void IsOkayTest() public void SendSyncDATARequestTest() => RunTest( (socket) => socket.SendSyncRequest(SyncCommand.DATA, 2), - new byte[] { (byte)'D', (byte)'A', (byte)'T', (byte)'A', 2, 0, 0, 0 }); + [(byte)'D', (byte)'A', (byte)'T', (byte)'A', 2, 0, 0, 0]); [Fact] public void SendSyncSENDRequestTest() => RunTest( (socket) => socket.SendSyncRequest(SyncCommand.SEND, "/test"), - new byte[] { (byte)'S', (byte)'E', (byte)'N', (byte)'D', 5, 0, 0, 0, (byte)'/', (byte)'t', (byte)'e', (byte)'s', (byte)'t' }); + [(byte)'S', (byte)'E', (byte)'N', (byte)'D', 5, 0, 0, 0, (byte)'/', (byte)'t', (byte)'e', (byte)'s', (byte)'t']); [Fact] public void SendSyncDENTRequestTest() => RunTest( (socket) => socket.SendSyncRequest(SyncCommand.DENT, "/data", 633), - new byte[] { (byte)'D', (byte)'E', (byte)'N', (byte)'T', 9, 0, 0, 0, (byte)'/', (byte)'d', (byte)'a', (byte)'t', (byte)'a', (byte)',', (byte)'6', (byte)'3', (byte)'3' }); + [(byte)'D', (byte)'E', (byte)'N', (byte)'T', 9, 0, 0, 0, (byte)'/', (byte)'d', (byte)'a', (byte)'t', (byte)'a', (byte)',', (byte)'6', (byte)'3', (byte)'3']); [Fact] public void SendSyncNullRequestTest() => - _ = Assert.Throws(() => RunTest((socket) => socket.SendSyncRequest(SyncCommand.DATA, null), Array.Empty())); + _ = Assert.Throws(() => RunTest((socket) => socket.SendSyncRequest(SyncCommand.DATA, null), [])); [Fact] public void ReadSyncResponse() diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs index 8d5487a4..7c013e47 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs @@ -21,6 +21,8 @@ public async void StatAsyncTest() ISyncService mock = Substitute.For(); mock.StatAsync("/test", Arg.Any()).Returns(tcs.Task); + using FactoriesLocker locker = await FactoriesLocker.WaitAsync(); + Factories.SyncServiceFactory = (c, d) => mock; DeviceData device = new(); diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.cs index 1d4978c7..b155e6a6 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.cs @@ -20,6 +20,8 @@ public void StatTest() ISyncService mock = Substitute.For(); mock.Stat("/test").Returns(stats); + using FactoriesLocker locker = FactoriesLocker.Wait(); + Factories.SyncServiceFactory = (c, d) => mock; DeviceData device = new(); diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs index 209b8f2c..381b856a 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs @@ -37,6 +37,8 @@ public async void InstallPackageAsyncTest() { DummySyncService syncService = new(); + using FactoriesLocker locker = await FactoriesLocker.WaitAsync(); + Factories.SyncServiceFactory = (c, d) => syncService; DummyAdbClient adbClient = new(); @@ -123,6 +125,8 @@ public async void InstallMultiplePackageAsyncTest() { DummySyncService syncService = new(); + using FactoriesLocker locker = await FactoriesLocker.WaitAsync(); + Factories.SyncServiceFactory = (c, d) => syncService; DummyAdbClient adbClient = new(); diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs index 5e7a5381..3710528d 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs @@ -66,6 +66,8 @@ public void InstallPackageTest() { DummySyncService syncService = new(); + using FactoriesLocker locker = FactoriesLocker.Wait(); + Factories.SyncServiceFactory = (c, d) => syncService; DummyAdbClient adbClient = new(); @@ -104,7 +106,7 @@ public void InstallMultipleRemotePackageTest() adbClient.Commands["pm install-write 936013062 base.apk \"/data/base.apk\""] = "Success"; adbClient.Commands["pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\""] = "Success"; adbClient.Commands["pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\""] = "Success"; - adbClient.Commands["pm install-commit 936013062"] = "Success" ; + adbClient.Commands["pm install-commit 936013062"] = "Success"; DeviceData device = new() { @@ -152,6 +154,8 @@ public void InstallMultiplePackageTest() { DummySyncService syncService = new(); + using FactoriesLocker locker = FactoriesLocker.Wait(); + Factories.SyncServiceFactory = (c, d) => syncService; DummyAdbClient adbClient = new(); diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DeviceMonitorSink.cs b/AdvancedSharpAdbClient.Tests/Dummys/DeviceMonitorSink.cs index 884e7e3c..57bbe1db 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DeviceMonitorSink.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DeviceMonitorSink.cs @@ -14,10 +14,10 @@ public DeviceMonitorSink(DeviceMonitor monitor) Monitor.DeviceConnected += OnDeviceConnected; Monitor.DeviceDisconnected += OnDeviceDisconnected; - ChangedEvents = new Collection(); - NotifiedEvents = new Collection(); - ConnectedEvents = new Collection(); - DisconnectedEvents = new Collection(); + ChangedEvents = []; + NotifiedEvents = []; + ConnectedEvents = []; + DisconnectedEvents = []; } public void ResetSignals() diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyTcpSocket.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyTcpSocket.cs index 6d388205..3be32d3b 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyTcpSocket.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyTcpSocket.cs @@ -26,6 +26,7 @@ internal class DummyTcpSocket : ITcpSocket public void Close() => Connected = false; public void Connect(EndPoint endPoint) => Connected = true; + public Task ConnectAsync(EndPoint endPoint) { Connected = true; diff --git a/AdvancedSharpAdbClient.Tests/Dummys/TracingAdbSocket.cs b/AdvancedSharpAdbClient.Tests/Dummys/TracingAdbSocket.cs index 936ad99d..c6c8c0aa 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/TracingAdbSocket.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/TracingAdbSocket.cs @@ -10,12 +10,8 @@ namespace AdvancedSharpAdbClient.Tests { - internal class TracingAdbSocket : AdbSocket, IDummyAdbSocket + internal class TracingAdbSocket(EndPoint endPoint) : AdbSocket(endPoint), IDummyAdbSocket { - public TracingAdbSocket(EndPoint endPoint) : base(endPoint) - { - } - public Stream ShellStream { get; set; } public bool DoDispose { get; set; } diff --git a/AdvancedSharpAdbClient.Tests/Extensions/FactoriesLocker.cs b/AdvancedSharpAdbClient.Tests/Extensions/FactoriesLocker.cs new file mode 100644 index 00000000..c04988f2 --- /dev/null +++ b/AdvancedSharpAdbClient.Tests/Extensions/FactoriesLocker.cs @@ -0,0 +1,40 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace AdvancedSharpAdbClient.Tests +{ + /// + /// Locker for the class. + /// + public class FactoriesLocker : IDisposable + { + public static SemaphoreSlim SlimLocker { get; } = new(1, 1); + + public static FactoriesLocker Wait() + { + SlimLocker.Wait(); + return new FactoriesLocker(); + } + + public static async Task WaitAsync() + { + await SlimLocker.WaitAsync(); + return new FactoriesLocker(); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + SlimLocker.Release(); + } + } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/AdvancedSharpAdbClient.Tests/Extensions/UtilitiesTests.cs b/AdvancedSharpAdbClient.Tests/Extensions/UtilitiesTests.cs index 1af7da56..325a6753 100644 --- a/AdvancedSharpAdbClient.Tests/Extensions/UtilitiesTests.cs +++ b/AdvancedSharpAdbClient.Tests/Extensions/UtilitiesTests.cs @@ -28,7 +28,7 @@ public void IsNullOrWhiteSpaceTest() [Fact] public void JoinTest() => - Assert.Equal("Hello World!", Utilities.Join(" ", new string[] { "Hello", "World!" })); + Assert.Equal("Hello World!", Utilities.Join(" ", ["Hello", "World!"])); [Fact] public void FromUnixTimeSecondsTest() diff --git a/AdvancedSharpAdbClient.Tests/Logs/ShellStreamTests.cs b/AdvancedSharpAdbClient.Tests/Logs/ShellStreamTests.cs index 09a47fcc..13a2bff3 100644 --- a/AdvancedSharpAdbClient.Tests/Logs/ShellStreamTests.cs +++ b/AdvancedSharpAdbClient.Tests/Logs/ShellStreamTests.cs @@ -1,7 +1,6 @@ using System; using System.IO; using System.Text; -using System.Threading.Tasks; using Xunit; namespace AdvancedSharpAdbClient.Logs.Tests diff --git a/AdvancedSharpAdbClient.Tests/Models/FramebufferTests.cs b/AdvancedSharpAdbClient.Tests/Models/FramebufferTests.cs index e0d25840..a07615c3 100644 --- a/AdvancedSharpAdbClient.Tests/Models/FramebufferTests.cs +++ b/AdvancedSharpAdbClient.Tests/Models/FramebufferTests.cs @@ -46,8 +46,12 @@ public void RefreshTest() using Framebuffer framebuffer = new(device); - Factories.AdbSocketFactory = (endPoint) => socket; - framebuffer.Refresh(); + using (FactoriesLocker locker = FactoriesLocker.Wait()) + { + Factories.AdbSocketFactory = (endPoint) => socket; + framebuffer.Refresh(); + Factories.Reset(); + } Assert.NotNull(framebuffer); Assert.Equal(device, framebuffer.Device); @@ -109,8 +113,12 @@ public async void RefreshAsyncTest() using Framebuffer framebuffer = new(device); - Factories.AdbSocketFactory = (endPoint) => socket; - await framebuffer.RefreshAsync(); + using (FactoriesLocker locker = await FactoriesLocker.WaitAsync()) + { + Factories.AdbSocketFactory = (endPoint) => socket; + await framebuffer.RefreshAsync(); + Factories.Reset(); + } Assert.NotNull(framebuffer); Assert.Equal(device, framebuffer.Device); diff --git a/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs b/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs index f025d646..dcac2be8 100644 --- a/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs +++ b/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs @@ -15,6 +15,8 @@ public class SocketBasedTests protected SocketBasedTests(bool integrationTest, bool doDispose) { + using FactoriesLocker locker = FactoriesLocker.Wait(); + // this.EndPoint = AdbClient.Instance.EndPoint; #if DEBUG // Use the tracing adb socket factory to run the tests on an actual device. @@ -46,9 +48,9 @@ protected SocketBasedTests(bool integrationTest, bool doDispose) Factories.Reset(); } - protected static AdbResponse[] NoResponses { get; } = Array.Empty(); - protected static AdbResponse[] OkResponse { get; } = new AdbResponse[] { AdbResponse.OK }; - protected static string[] NoResponseMessages { get; } = Array.Empty(); + protected static AdbResponse[] NoResponses { get; } = []; + protected static AdbResponse[] OkResponse { get; } = [AdbResponse.OK]; + protected static string[] NoResponseMessages { get; } = []; protected static DeviceData Device { get; } = new() { Serial = "169.254.109.177:5555", diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs index 5aed6b66..1337dc61 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs @@ -25,8 +25,8 @@ await RunTestAsync( NoResponseMessages, Requests("host:transport:169.254.109.177:5555", "sync:"), SyncRequests(SyncCommand.STAT, "/fstab.donatello"), - new SyncCommand[] { SyncCommand.STAT }, - new byte[][] { new byte[] { 160, 129, 0, 0, 85, 2, 0, 0, 0, 0, 0, 0 } }, + new[] { SyncCommand.STAT }, + new byte[][] { [160, 129, 0, 0, 85, 2, 0, 0, 0, 0, 0, 0] }, null, async () => { @@ -56,13 +56,13 @@ await RunTestAsync( ResponseMessages(".", "..", "sdcard0", "emulated"), Requests("host:transport:169.254.109.177:5555", "sync:"), SyncRequests(SyncCommand.LIST, "/storage"), - new SyncCommand[] { SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DONE }, + new[] { SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DONE }, new byte[][] { - new byte[] { 233, 65, 0, 0, 0, 0, 0, 0, 152, 130, 56, 86 }, - new byte[] { 237, 65, 0, 0, 0, 0, 0, 0, 152, 130, 56, 86 }, - new byte[] { 255, 161, 0, 0, 24, 0, 0, 0, 152, 130, 56, 86 }, - new byte[] { 109, 65, 0, 0, 0, 0, 0, 0, 152, 130, 56, 86 } + [233, 65, 0, 0, 0, 0, 0, 0, 152, 130, 56, 86], + [237, 65, 0, 0, 0, 0, 0, 0, 152, 130, 56, 86], + [255, 161, 0, 0, 24, 0, 0, 0, 152, 130, 56, 86], + [109, 65, 0, 0, 0, 0, 0, 0, 152, 130, 56, 86] }, null, async () => @@ -121,7 +121,7 @@ await RunTestAsync( new SyncCommand[] { SyncCommand.STAT, SyncCommand.DATA, SyncCommand.DONE }, new byte[][] { - new byte[] { 160, 129, 0, 0, 85, 2, 0, 0, 0, 0, 0, 0 }, + [160, 129, 0, 0, 85, 2, 0, 0, 0, 0, 0, 0], contentLength, content }, @@ -147,10 +147,12 @@ public async void PushAsyncTest() Stream stream = File.OpenRead("Assets/fstab.bin"); byte[] content = File.ReadAllBytes("Assets/fstab.bin"); - List contentMessage = new(); - contentMessage.AddRange(SyncCommandConverter.GetBytes(SyncCommand.DATA)); - contentMessage.AddRange(BitConverter.GetBytes(content.Length)); - contentMessage.AddRange(content); + List contentMessage = + [ + .. SyncCommandConverter.GetBytes(SyncCommand.DATA), + .. BitConverter.GetBytes(content.Length), + .. content, + ]; await RunTestAsync( OkResponses(2), @@ -159,11 +161,11 @@ await RunTestAsync( SyncRequests( SyncCommand.SEND, "/sdcard/test,644", SyncCommand.DONE, "1446505200"), - new SyncCommand[] { SyncCommand.OKAY }, + new[] { SyncCommand.OKAY }, null, new byte[][] { - contentMessage.ToArray() + [.. contentMessage] }, async () => { diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs index 42589feb..56582c2f 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs @@ -36,8 +36,8 @@ public void StatTest() NoResponseMessages, Requests("host:transport:169.254.109.177:5555", "sync:"), SyncRequests(SyncCommand.STAT, "/fstab.donatello"), - new SyncCommand[] { SyncCommand.STAT }, - new byte[][] { new byte[] { 160, 129, 0, 0, 85, 2, 0, 0, 0, 0, 0, 0 } }, + new[] { SyncCommand.STAT }, + new byte[][] { [160, 129, 0, 0, 85, 2, 0, 0, 0, 0, 0, 0] }, null, () => { @@ -67,13 +67,13 @@ public void GetListingTest() ResponseMessages(".", "..", "sdcard0", "emulated"), Requests("host:transport:169.254.109.177:5555", "sync:"), SyncRequests(SyncCommand.LIST, "/storage"), - new SyncCommand[] { SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DONE }, + new[] { SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DONE }, new byte[][] { - new byte[] { 233, 65, 0, 0, 0, 0, 0, 0, 152, 130, 56, 86 }, - new byte[] { 237, 65, 0, 0, 0, 0, 0, 0, 152, 130, 56, 86 }, - new byte[] { 255, 161, 0, 0, 24, 0, 0, 0, 152, 130, 56, 86 }, - new byte[] { 109, 65, 0, 0, 0, 0, 0, 0, 152, 130, 56, 86 } + [233, 65, 0, 0, 0, 0, 0, 0, 152, 130, 56, 86], + [237, 65, 0, 0, 0, 0, 0, 0, 152, 130, 56, 86], + [255, 161, 0, 0, 24, 0, 0, 0, 152, 130, 56, 86], + [109, 65, 0, 0, 0, 0, 0, 0, 152, 130, 56, 86] }, null, () => @@ -129,13 +129,12 @@ public void PullTest() ResponseMessages(), Requests("host:transport:169.254.109.177:5555", "sync:"), SyncRequests(SyncCommand.STAT, "/fstab.donatello").Union(SyncRequests(SyncCommand.RECV, "/fstab.donatello")), - new SyncCommand[] { SyncCommand.STAT, SyncCommand.DATA, SyncCommand.DONE }, - new byte[][] - { - new byte[] { 160, 129, 0, 0, 85, 2, 0, 0, 0, 0, 0, 0 }, + [SyncCommand.STAT, SyncCommand.DATA, SyncCommand.DONE], + [ + [160, 129, 0, 0, 85, 2, 0, 0, 0, 0, 0, 0], contentLength, content - }, + ], null, () => { @@ -158,10 +157,12 @@ public void PushTest() Stream stream = File.OpenRead("Assets/fstab.bin"); byte[] content = File.ReadAllBytes("Assets/fstab.bin"); - List contentMessage = new(); - contentMessage.AddRange(SyncCommandConverter.GetBytes(SyncCommand.DATA)); - contentMessage.AddRange(BitConverter.GetBytes(content.Length)); - contentMessage.AddRange(content); + List contentMessage = + [ + .. SyncCommandConverter.GetBytes(SyncCommand.DATA), + .. BitConverter.GetBytes(content.Length), + .. content, + ]; RunTest( OkResponses(2), @@ -170,12 +171,9 @@ public void PushTest() SyncRequests( SyncCommand.SEND, "/sdcard/test,644", SyncCommand.DONE, "1446505200"), - new SyncCommand[] { SyncCommand.OKAY }, + [SyncCommand.OKAY], null, - new byte[][] - { - contentMessage.ToArray() - }, + [[.. contentMessage]], () => { using SyncService service = new(Socket, device); From 9d22002afd41f6e9ec1be5f9e26b74c02a159854 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Fri, 22 Sep 2023 17:43:37 +0800 Subject: [PATCH 02/66] Update AdvancedSharpAdbClient C# version --- AdvancedSharpAdbClient/AdbClient.Async.cs | 34 ++++++++---------- AdvancedSharpAdbClient/AdbClient.cs | 36 +++++++++---------- .../AdbCommandLineClient.Async.cs | 2 +- .../AdbCommandLineClient.cs | 2 +- AdvancedSharpAdbClient/AdbServer.cs | 19 +++------- AdvancedSharpAdbClient/AdbSocket.cs | 2 +- .../DeviceCommands/DeviceExtensions.Async.cs | 8 ++--- .../DeviceCommands/DeviceExtensions.cs | 4 +-- .../DeviceCommands/PackageManager.Async.cs | 8 ++--- .../DeviceCommands/PackageManager.cs | 6 ++-- AdvancedSharpAdbClient/DeviceMonitor.cs | 2 +- .../Extensions/SyncCommandConverter.cs | 2 +- .../Extensions/Utilities.cs | 15 ++++++++ .../Logs/AndroidLogEntry.cs | 2 +- .../Logs/LogReader.Async.cs | 2 +- AdvancedSharpAdbClient/Logs/LogReader.cs | 2 +- AdvancedSharpAdbClient/Models/ForwardData.cs | 2 +- AdvancedSharpAdbClient/SyncService.Async.cs | 2 +- Directory.Build.props | 11 +++++- 19 files changed, 85 insertions(+), 76 deletions(-) diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index c3c6bbe9..5ac7d873 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -51,7 +51,7 @@ public async Task> GetDevicesAsync(CancellationToken can await socket.ReadAdbResponseAsync(cancellationToken); string reply = await socket.ReadStringAsync(cancellationToken); - string[] data = reply.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries); + string[] data = reply.Split(separator, StringSplitOptions.RemoveEmptyEntries); return data.Select(DeviceData.CreateFromAdbData); } @@ -149,7 +149,7 @@ public async Task> ListForwardAsync(DeviceData device, string data = await socket.ReadStringAsync(cancellationToken); - string[] parts = data.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); + string[] parts = data.Split(separator, StringSplitOptions.RemoveEmptyEntries); return parts.Select(ForwardData.FromString); } @@ -167,7 +167,7 @@ public async Task> ListReverseForwardAsync(DeviceData d string data = await socket.ReadStringAsync(cancellationToken); - string[] parts = data.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); + string[] parts = data.Split(separator, StringSplitOptions.RemoveEmptyEntries); return parts.Select(ForwardData.FromString); } @@ -235,7 +235,7 @@ public async Task GetFrameBufferAsync(DeviceData device, Cancellati /// public Task RunLogServiceAsync(DeviceData device, Action messageSink, params LogId[] logNames) => RunLogServiceAsync(device, messageSink, default, logNames); - + /// public async Task RunLogServiceAsync(DeviceData device, Action messageSink, CancellationToken cancellationToken, params LogId[] logNames) { @@ -262,7 +262,7 @@ public async Task RunLogServiceAsync(DeviceData device, Action message #if NETCOREAPP3_0_OR_GREATER await #endif - using Stream stream = socket.GetShellStream(); + using Stream stream = socket.GetShellStream(); LogReader reader = new(stream); while (!cancellationToken.IsCancellationRequested) @@ -564,8 +564,8 @@ public async Task InstallCreateAsync(DeviceData device, string packageNa throw new AdbException(await reader.ReadToEndAsync(cancellationToken)); } - int arr = result.IndexOf("]") - 1 - result.IndexOf("["); - string session = result.Substring(result.IndexOf("[") + 1, arr); + int arr = result.IndexOf(']') - 1 - result.IndexOf('['); + string session = result.Substring(result.IndexOf('[') + 1, arr); return session; } @@ -649,7 +649,7 @@ public async Task> GetFeatureSetAsync(DeviceData device, Can AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken); string features = await socket.ReadStringAsync(cancellationToken); - IEnumerable featureList = features.Trim().Split(new char[] { '\n', ',' }); + IEnumerable featureList = features.Trim().Split('\n', ','); return featureList; } @@ -671,11 +671,7 @@ public async Task DumpScreenStringAsync(DeviceData device, CancellationT return xmlString; } Match xmlMatch = GetXMLRegex().Match(xmlString); - if (!xmlMatch.Success) - { - throw new XmlException("An error occurred while receiving xml: " + xmlString); - } - return xmlMatch.Value; + return !xmlMatch.Success ? throw new XmlException("An error occurred while receiving xml: " + xmlString) : xmlMatch.Value; } /// @@ -721,7 +717,7 @@ public async Task ClickAsync(DeviceData device, Cords cords, CancellationToken c { throw JavaException.Parse(result); } - else if (result.IndexOf("ERROR", StringComparison.OrdinalIgnoreCase) != -1) // error or ERROR + else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR { throw new ElementNotFoundException("Coordinates of element is invalid"); } @@ -742,7 +738,7 @@ public async Task ClickAsync(DeviceData device, int x, int y, CancellationToken { throw JavaException.Parse(result); } - else if (result.IndexOf("ERROR", StringComparison.OrdinalIgnoreCase) != -1) // error or ERROR + else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR { throw new ElementNotFoundException("Coordinates of element is invalid"); } @@ -763,7 +759,7 @@ public async Task SwipeAsync(DeviceData device, Element first, Element second, l { throw JavaException.Parse(result); } - else if (result.IndexOf("ERROR", StringComparison.OrdinalIgnoreCase) != -1) // error or ERROR + else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR { throw new ElementNotFoundException("Coordinates of element is invalid"); } @@ -784,7 +780,7 @@ public async Task SwipeAsync(DeviceData device, int x1, int y1, int x2, int y2, { throw JavaException.Parse(result); } - else if (result.IndexOf("ERROR", StringComparison.OrdinalIgnoreCase) != -1) // error or ERROR + else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR { throw new ElementNotFoundException("Coordinates of element is invalid"); } @@ -958,7 +954,7 @@ public async Task SendKeyEventAsync(DeviceData device, string key, CancellationT { throw JavaException.Parse(result); } - else if (result.IndexOf("ERROR", StringComparison.OrdinalIgnoreCase) != -1) // error or ERROR + else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR { throw new InvalidKeyEventException("KeyEvent is invalid"); } @@ -979,7 +975,7 @@ public async Task SendTextAsync(DeviceData device, string text, CancellationToke { throw JavaException.Parse(result); } - else if (result.IndexOf("ERROR", StringComparison.OrdinalIgnoreCase) != -1) // error or ERROR + else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR { throw new InvalidTextException(); } diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index 60b18d81..b7d5bb53 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -3,18 +3,18 @@ // using AdvancedSharpAdbClient.Exceptions; +using AdvancedSharpAdbClient.Logs; using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; -using AdvancedSharpAdbClient.Logs; using System.IO; using System.Linq; using System.Net; using System.Text; +using System.Text.RegularExpressions; using System.Threading; using System.Xml; -using System.Text.RegularExpressions; namespace AdvancedSharpAdbClient { @@ -33,6 +33,8 @@ namespace AdvancedSharpAdbClient /// public partial class AdbClient : IAdbClient { + private static readonly char[] separator = ['\r', '\n']; + /// /// The default port to use when connecting to a device over TCP/IP. /// @@ -193,7 +195,7 @@ public IEnumerable GetDevices() socket.ReadAdbResponse(); string reply = socket.ReadString(); - string[] data = reply.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries); + string[] data = reply.Split(separator, StringSplitOptions.RemoveEmptyEntries); return data.Select(DeviceData.CreateFromAdbData); } @@ -291,7 +293,7 @@ public IEnumerable ListForward(DeviceData device) string data = socket.ReadString(); - string[] parts = data.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); + string[] parts = data.Split(separator, StringSplitOptions.RemoveEmptyEntries); return parts.Select(ForwardData.FromString); } @@ -309,7 +311,7 @@ public IEnumerable ListReverseForward(DeviceData device) string data = socket.ReadString(); - string[] parts = data.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); + string[] parts = data.Split(separator, StringSplitOptions.RemoveEmptyEntries); return parts.Select(ForwardData.FromString); } @@ -672,8 +674,8 @@ public string InstallCreate(DeviceData device, string packageName = null, params throw new AdbException(reader.ReadToEnd()); } - int arr = result.IndexOf("]") - 1 - result.IndexOf("["); - string session = result.Substring(result.IndexOf("[") + 1, arr); + int arr = result.IndexOf(']') - 1 - result.IndexOf('['); + string session = result.Substring(result.IndexOf('[') + 1, arr); return session; } @@ -755,7 +757,7 @@ public IEnumerable GetFeatureSet(DeviceData device) AdbResponse response = socket.ReadAdbResponse(); string features = socket.ReadString(); - IEnumerable featureList = features.Trim().Split(new char[] { '\n', ',' }); + IEnumerable featureList = features.Trim().Split('\n', ','); return featureList; } @@ -777,11 +779,7 @@ public string DumpScreenString(DeviceData device) return xmlString; } Match xmlMatch = GetXMLRegex().Match(xmlString); - if (!xmlMatch.Success) - { - throw new XmlException("An error occurred while receiving xml: " + xmlString); - } - return xmlMatch.Value; + return !xmlMatch.Success ? throw new XmlException("An error occurred while receiving xml: " + xmlString) : xmlMatch.Value; } /// @@ -827,7 +825,7 @@ public void Click(DeviceData device, Cords cords) { throw JavaException.Parse(result); } - else if (result.IndexOf("ERROR", StringComparison.OrdinalIgnoreCase) != -1) // error or ERROR + else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR { throw new ElementNotFoundException("Coordinates of element is invalid"); } @@ -848,7 +846,7 @@ public void Click(DeviceData device, int x, int y) { throw JavaException.Parse(result); } - else if (result.IndexOf("ERROR", StringComparison.OrdinalIgnoreCase) != -1) // error or ERROR + else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR { throw new ElementNotFoundException("Coordinates of element is invalid"); } @@ -869,7 +867,7 @@ public void Swipe(DeviceData device, Element first, Element second, long speed) { throw JavaException.Parse(result); } - else if (result.IndexOf("ERROR", StringComparison.OrdinalIgnoreCase) != -1) // error or ERROR + else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR { throw new ElementNotFoundException("Coordinates of element is invalid"); } @@ -890,7 +888,7 @@ public void Swipe(DeviceData device, int x1, int y1, int x2, int y2, long speed) { throw JavaException.Parse(result); } - else if (result.IndexOf("ERROR", StringComparison.OrdinalIgnoreCase) != -1) // error or ERROR + else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR { throw new ElementNotFoundException("Coordinates of element is invalid"); } @@ -1013,7 +1011,7 @@ public void SendKeyEvent(DeviceData device, string key) { throw JavaException.Parse(result); } - else if (result.IndexOf("ERROR", StringComparison.OrdinalIgnoreCase) != -1) // error or ERROR + else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR { throw new InvalidKeyEventException("KeyEvent is invalid"); } @@ -1034,7 +1032,7 @@ public void SendText(DeviceData device, string text) { throw JavaException.Parse(result); } - else if (result.IndexOf("ERROR", StringComparison.OrdinalIgnoreCase) != -1) // error or ERROR + else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR { throw new InvalidTextException(); } diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs index 8a58c5d2..609b68c3 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs @@ -22,7 +22,7 @@ public partial class AdbCommandLineClient public virtual async Task GetVersionAsync(CancellationToken cancellationToken = default) { // Run the adb.exe version command and capture the output. - List standardOutput = new(); + List standardOutput = []; await RunAdbProcessAsync("version", null, standardOutput, cancellationToken); diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.cs index 8cd435c8..5a8b8ed7 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.cs @@ -97,7 +97,7 @@ public AdbCommandLineClient(string adbPath, bool isForce = false public virtual Version GetVersion() { // Run the adb.exe version command and capture the output. - List standardOutput = new(); + List standardOutput = []; RunAdbProcess("version", null, standardOutput); diff --git a/AdvancedSharpAdbClient/AdbServer.cs b/AdvancedSharpAdbClient/AdbServer.cs index a08a9d72..db03ae1d 100644 --- a/AdvancedSharpAdbClient/AdbServer.cs +++ b/AdvancedSharpAdbClient/AdbServer.cs @@ -17,7 +17,9 @@ namespace AdvancedSharpAdbClient /// giant multiplexing loop whose purpose is to orchestrate the exchange of data /// between clients and devices. /// - public partial class AdbServer : IAdbServer + /// The current ADB client that manages the connection. + /// The to create . + public partial class AdbServer(IAdbClient adbClient, Func adbCommandLineClientFactory) : IAdbServer { /// /// The minimum version of adb.exe that is supported by this library. @@ -60,14 +62,14 @@ public partial class AdbServer : IAdbServer /// /// The current ADB client that manages the connection. /// - protected readonly IAdbClient adbClient; + protected readonly IAdbClient adbClient = adbClient ?? throw new ArgumentNullException(nameof(adbClient)); /// /// 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 = adbCommandLineClientFactory ?? throw new ArgumentNullException(nameof(adbCommandLineClientFactory)); /// /// Initializes a new instance of the class. @@ -92,17 +94,6 @@ public AdbServer(Func adbCommandLineClientFactory { } - /// - /// Initializes a new instance of the class. - /// - /// The current ADB client that manages the connection. - /// The to create . - public AdbServer(IAdbClient adbClient, Func adbCommandLineClientFactory) - { - this.adbCommandLineClientFactory = adbCommandLineClientFactory ?? throw new ArgumentNullException(nameof(adbCommandLineClientFactory)); - this.adbClient = adbClient ?? throw new ArgumentNullException(nameof(adbClient)); - } - /// /// Gets or sets the default instance of the interface. /// diff --git a/AdvancedSharpAdbClient/AdbSocket.cs b/AdvancedSharpAdbClient/AdbSocket.cs index e8430f9e..ccb296bd 100644 --- a/AdvancedSharpAdbClient/AdbSocket.cs +++ b/AdvancedSharpAdbClient/AdbSocket.cs @@ -82,7 +82,7 @@ public AdbSocket(string host, int port DnsEndPoint endPoint = values.Length <= 0 ? throw new ArgumentNullException(nameof(host)) : new DnsEndPoint(values[0], values.Length > 1 && int.TryParse(values[1], out int _port) ? _port : port); - + socket = new TcpSocket(); socket.Connect(endPoint); socket.ReceiveBufferSize = ReceiveBufferSize; diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs index 2f2a74bf..6db9ea95 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs @@ -69,7 +69,7 @@ public static async Task> List(this IAdbClient clien public static async Task PullAsync(this IAdbClient client, DeviceData device, string remotePath, Stream stream, EventHandler syncProgressEventHandler = null, - IProgress progress = null, CancellationToken cancellationToken = default ) + IProgress progress = null, CancellationToken cancellationToken = default) { using ISyncService service = Factories.SyncServiceFactory(client, device); if (syncProgressEventHandler != null) @@ -96,7 +96,7 @@ public static async Task PullAsync(this IAdbClient client, DeviceData device, public static async Task PushAsync(this IAdbClient client, DeviceData device, string remotePath, Stream stream, int permissions, DateTimeOffset timestamp, EventHandler syncProgressEventHandler = null, - IProgress progress = null, CancellationToken cancellationToken = default ) + IProgress progress = null, CancellationToken cancellationToken = default) { using ISyncService service = Factories.SyncServiceFactory(client, device); if (syncProgressEventHandler != null) @@ -200,7 +200,7 @@ public static async Task> ListProcessesAsync(this IA // The easiest way to do the directory listings would be to use the SyncService; unfortunately, // the sync service doesn't work very well with /proc/ so we're back to using ls and taking it // from there. - List processes = new(); + List processes = []; // List all processes by doing ls /proc/. // All subfolders which are completely numeric are PIDs @@ -224,7 +224,7 @@ await client.ExecuteShellCommandAsync(device, @"SDK=""$(/system/bin/getprop ro.b /system/bin/ls -1 /proc/ fi".Replace("\r\n", "\n"), receiver, cancellationToken); - Collection pids = new(); + Collection pids = []; string output = receiver.ToString(); using (StringReader reader = new(output)) diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs index e1d9f2e1..3f5090d3 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs @@ -213,7 +213,7 @@ public static IEnumerable ListProcesses(this IAdbClient client, // The easiest way to do the directory listings would be to use the SyncService; unfortunately, // the sync service doesn't work very well with /proc/ so we're back to using ls and taking it // from there. - List processes = new(); + List processes = []; // List all processes by doing ls /proc/. // All subfolders which are completely numeric are PIDs @@ -237,7 +237,7 @@ public static IEnumerable ListProcesses(this IAdbClient client, /system/bin/ls -1 /proc/ fi".Replace("\r\n", "\n"), receiver); - Collection pids = new(); + Collection pids = []; string output = receiver.ToString(); using (StringReader reader = new(output)) diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs index f110464c..75e06237 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs @@ -42,7 +42,7 @@ public virtual async Task InstallPackageAsync(string packageFilePath, bool reins ValidateDevice(); string remoteFilePath = await SyncPackageToDeviceAsync(packageFilePath, OnSyncProgressChanged, cancellationToken); - + await InstallRemotePackageAsync(remoteFilePath, reinstall, cancellationToken); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, 1, PackageInstallProgressState.PostInstall)); @@ -113,7 +113,7 @@ void OnSplitSyncProgressChanged(object sender, SyncProgressChangedEventArgs args double present = 0; foreach (KeyValuePair info in progress) { - present += (info.Value / splitPackageFilePaths.Count) / 2; + present += info.Value / splitPackageFilePaths.Count / 2; } InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitPackageFilePaths.Count + 1, present)); @@ -174,7 +174,7 @@ void OnSyncProgressChanged(object sender, SyncProgressChangedEventArgs args) double present = 0; foreach (KeyValuePair info in progress) { - present += (info.Value / splitPackageFilePaths.Count) / 2; + present += info.Value / splitPackageFilePaths.Count / 2; } InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitPackageFilePaths.Count, present)); @@ -372,7 +372,7 @@ protected virtual async Task SyncPackageToDeviceAsync(string localFilePa #if NETCOREAPP3_0_OR_GREATER await #endif - using Stream stream = File.OpenRead(localFilePath); + using Stream stream = File.OpenRead(localFilePath); #if HAS_LOGGER logger.LogDebug($"Uploading file onto device '{Device.Serial}'"); #endif diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs index 9f56ba00..d4879581 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs @@ -78,7 +78,7 @@ public PackageManager(IAdbClient client, DeviceData device, bool thirdPartyOnly ) { Device = device ?? throw new ArgumentNullException(nameof(device)); - Packages = new Dictionary(); + Packages = []; ThirdPartyOnly = thirdPartyOnly; this.client = client ?? throw new ArgumentNullException(nameof(client)); @@ -208,9 +208,9 @@ void OnSplitSyncProgressChanged(object sender, SyncProgressChangedEventArgs args } double present = 0; - foreach(KeyValuePair info in progress) + foreach (KeyValuePair info in progress) { - present += (info.Value / splitPackageFilePaths.Count) / 2; + present += info.Value / splitPackageFilePaths.Count / 2; } InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitPackageFilePaths.Count + 1, present)); diff --git a/AdvancedSharpAdbClient/DeviceMonitor.cs b/AdvancedSharpAdbClient/DeviceMonitor.cs index f7ae7ac3..cc03c2a5 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.cs @@ -81,7 +81,7 @@ public DeviceMonitor(IAdbSocket socket ) { Socket = socket ?? throw new ArgumentNullException(nameof(socket)); - devices = new List(); + devices = []; Devices = devices.AsReadOnly(); #if HAS_LOGGER this.logger = logger ?? NullLogger.Instance; diff --git a/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs b/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs index 8c9490eb..0fe5feb1 100644 --- a/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs +++ b/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs @@ -17,7 +17,7 @@ public static class SyncCommandConverter /// /// Maps the values to their string representations. /// - private static readonly Dictionary Values = new() + private static readonly Dictionary Values = new(9) { { SyncCommand.DATA, "DATA" }, { SyncCommand.DENT, "DENT" }, diff --git a/AdvancedSharpAdbClient/Extensions/Utilities.cs b/AdvancedSharpAdbClient/Extensions/Utilities.cs index 959dedec..951d7438 100644 --- a/AdvancedSharpAdbClient/Extensions/Utilities.cs +++ b/AdvancedSharpAdbClient/Extensions/Utilities.cs @@ -84,6 +84,21 @@ public static bool IsNullOrWhiteSpace(this string value) #endif } +#if !UAP10_0_15138_0 && !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER + /// + /// Returns a value indicating whether a specified string occurs within this string, using the specified comparison rules. + /// + /// A sequence in which to locate a value. + /// The string to seek. + /// One of the enumeration values that specifies the rules to use in the comparison. + /// if the parameter occurs within this string, + /// or if is the empty string (""); otherwise, . + public static bool Contains(this string text, string value, StringComparison comparisonType) + { + return text.IndexOf(value, comparisonType) != -1; + } +#endif + /// /// Concatenates the members of a constructed collection of type , /// using the specified separator between each member. diff --git a/AdvancedSharpAdbClient/Logs/AndroidLogEntry.cs b/AdvancedSharpAdbClient/Logs/AndroidLogEntry.cs index 9cf930ab..748fcbaf 100644 --- a/AdvancedSharpAdbClient/Logs/AndroidLogEntry.cs +++ b/AdvancedSharpAdbClient/Logs/AndroidLogEntry.cs @@ -51,6 +51,6 @@ public override string ToString() => /// The value to convert. /// A that represents in the system log. private static char FormatPriority(Priority value) => - PriorityFormatters?.TryGetValue(value, out var result) == true ? result : '?'; + PriorityFormatters?.TryGetValue(value, out char result) == true ? result : '?'; } } diff --git a/AdvancedSharpAdbClient/Logs/LogReader.Async.cs b/AdvancedSharpAdbClient/Logs/LogReader.Async.cs index 20ee8884..1c2947c9 100644 --- a/AdvancedSharpAdbClient/Logs/LogReader.Async.cs +++ b/AdvancedSharpAdbClient/Logs/LogReader.Async.cs @@ -153,7 +153,7 @@ or LogId.Radio #if NETCOREAPP3_0_OR_GREATER await #endif - using (MemoryStream dataStream = new(data)) + using (MemoryStream dataStream = new(data)) { using BinaryReader reader = new(dataStream); int priority = reader.ReadInt32(); diff --git a/AdvancedSharpAdbClient/Logs/LogReader.cs b/AdvancedSharpAdbClient/Logs/LogReader.cs index dec055bd..e637a025 100644 --- a/AdvancedSharpAdbClient/Logs/LogReader.cs +++ b/AdvancedSharpAdbClient/Logs/LogReader.cs @@ -213,7 +213,7 @@ protected void ReadLogEntry(BinaryReader reader, Collection parent) case EventLogType.List: byte listLength = reader.ReadByte(); - Collection list = new(); + Collection list = []; for (int i = 0; i < listLength; i++) { diff --git a/AdvancedSharpAdbClient/Models/ForwardData.cs b/AdvancedSharpAdbClient/Models/ForwardData.cs index 96b1d4f4..50f57f2c 100644 --- a/AdvancedSharpAdbClient/Models/ForwardData.cs +++ b/AdvancedSharpAdbClient/Models/ForwardData.cs @@ -48,7 +48,7 @@ public ForwardData() /// The that represents the local (PC) endpoint. /// The that represents the remote (device) endpoint. public ForwardData(string serialNumber, string local, string remote) - { + { SerialNumber = serialNumber; Local = local; Remote = remote; diff --git a/AdvancedSharpAdbClient/SyncService.Async.cs b/AdvancedSharpAdbClient/SyncService.Async.cs index d7fc5d85..9c092210 100644 --- a/AdvancedSharpAdbClient/SyncService.Async.cs +++ b/AdvancedSharpAdbClient/SyncService.Async.cs @@ -240,7 +240,7 @@ public virtual async Task StatAsync(string remotePath, Cancellat /// public virtual async Task> GetDirectoryListingAsync(string remotePath, CancellationToken cancellationToken = default) { - Collection value = new(); + Collection value = []; // create the stat request message. await Socket.SendSyncRequestAsync(SyncCommand.LIST, remotePath, cancellationToken); diff --git a/Directory.Build.props b/Directory.Build.props index d6573528..d02fd8da 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -44,11 +44,20 @@ True + + 10.0 + + + + $(DefineConstants);UAP10_0_15138_0 + 10.0.15138.0 + + False en-US - $(DefineConstants);NETCORE;NETCORE_5_0;NETFX_CORE;WINDOWS_UWP + $(DefineConstants);NETCORE;NETCORE_5_0;NETFX_CORE;WINDOWS_UWP;UAP10_0 $(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets UAP,Version=v10.0 .NETCore From 4f75be47a5e0e375aa8517912dbc3413b94db6fd Mon Sep 17 00:00:00 2001 From: wherewhere Date: Fri, 22 Sep 2023 21:37:59 +0800 Subject: [PATCH 03/66] Update constructors --- .../Models/AdbServerStatusTests.cs | 8 +- AdvancedSharpAdbClient/AdbClient.Async.cs | 6 +- AdvancedSharpAdbClient/AdbClient.cs | 6 +- AdvancedSharpAdbClient/AdbServer.Async.cs | 7 +- AdvancedSharpAdbClient/AdbServer.cs | 19 +- .../AdvancedSharpAdbClient.csproj | 2 +- .../DeviceCommands/LinuxPath.cs | 15 +- .../DeviceCommands/Models/AndroidProcess.cs | 557 +++++++++--------- .../Models/InstallProgressEventArgs.cs | 9 +- .../DeviceCommands/Models/VersionInfo.cs | 19 +- .../DeviceCommands/PackageManager.Async.cs | 4 +- .../DeviceCommands/PackageManager.cs | 4 +- .../Receivers/GetPropReceiver.cs | 5 + .../Receivers/InfoOutputReceiver.cs | 5 + .../Receivers/InstallOutputReceiver.cs | 5 + .../Receivers/PackageManagerReceiver.cs | 19 +- .../Receivers/ProcessOutputReceiver.cs | 7 +- AdvancedSharpAdbClient/DeviceMonitor.cs | 30 +- .../Exceptions/AdbException.cs | 3 +- .../Extensions/Utilities.cs | 24 +- .../Logs/AndroidLogEntry.cs | 7 +- AdvancedSharpAdbClient/Logs/EventLogEntry.cs | 5 + AdvancedSharpAdbClient/Logs/LogEntry.cs | 5 + AdvancedSharpAdbClient/Logs/LogReader.cs | 14 +- AdvancedSharpAdbClient/Logs/ShellStream.cs | 18 +- AdvancedSharpAdbClient/Models/AdbResponse.cs | 2 +- .../Models/AdbServerStatus.cs | 19 +- AdvancedSharpAdbClient/Models/DeviceData.cs | 52 +- .../Models/DeviceDataEventArgs.cs | 56 +- .../Models/FileStatistics.cs | 5 + AdvancedSharpAdbClient/Models/ForwardData.cs | 64 +- AdvancedSharpAdbClient/Models/ForwardSpec.cs | 76 +-- AdvancedSharpAdbClient/Models/Framebuffer.cs | 4 +- .../Models/FramebufferHeader.cs | 118 ++-- .../Models/SyncProgressChangedEventArgs.cs | 20 +- .../Receivers/ConsoleOutputReceiver.cs | 4 +- 36 files changed, 617 insertions(+), 606 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/Models/AdbServerStatusTests.cs b/AdvancedSharpAdbClient.Tests/Models/AdbServerStatusTests.cs index c6bca265..2c95bcf1 100644 --- a/AdvancedSharpAdbClient.Tests/Models/AdbServerStatusTests.cs +++ b/AdvancedSharpAdbClient.Tests/Models/AdbServerStatusTests.cs @@ -11,10 +11,10 @@ public class AdbServerStatusTests [Fact] public void ToStringTest() { - AdbServerStatus s = new(true, new Version(1, 0, 32)); - Assert.Equal("Version 1.0.32 of the adb daemon is running.", s.ToString()); - s.IsRunning = false; - Assert.Equal("The adb daemon is not running.", s.ToString()); + AdbServerStatus status = new(true, new Version(1, 0, 32)); + Assert.Equal("Version 1.0.32 of the adb daemon is running.", status.ToString()); + status.IsRunning = false; + Assert.Equal("The adb daemon is not running.", status.ToString()); } } } diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index 5ac7d873..7c0b8ca5 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -53,7 +53,7 @@ public async Task> GetDevicesAsync(CancellationToken can string[] data = reply.Split(separator, StringSplitOptions.RemoveEmptyEntries); - return data.Select(DeviceData.CreateFromAdbData); + return data.Select((x) => new DeviceData(x)); } /// @@ -151,7 +151,7 @@ public async Task> ListForwardAsync(DeviceData device, string[] parts = data.Split(separator, StringSplitOptions.RemoveEmptyEntries); - return parts.Select(ForwardData.FromString); + return parts.Select((x) => new ForwardData(x)); } /// @@ -169,7 +169,7 @@ public async Task> ListReverseForwardAsync(DeviceData d string[] parts = data.Split(separator, StringSplitOptions.RemoveEmptyEntries); - return parts.Select(ForwardData.FromString); + return parts.Select((x) => new ForwardData(x)); } /// diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index b7d5bb53..05fdbe16 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -197,7 +197,7 @@ public IEnumerable GetDevices() string[] data = reply.Split(separator, StringSplitOptions.RemoveEmptyEntries); - return data.Select(DeviceData.CreateFromAdbData); + return data.Select((x) => new DeviceData(x)); } /// @@ -295,7 +295,7 @@ public IEnumerable ListForward(DeviceData device) string[] parts = data.Split(separator, StringSplitOptions.RemoveEmptyEntries); - return parts.Select(ForwardData.FromString); + return parts.Select((x) => new ForwardData(x)); } /// @@ -313,7 +313,7 @@ public IEnumerable ListReverseForward(DeviceData device) string[] parts = data.Split(separator, StringSplitOptions.RemoveEmptyEntries); - return parts.Select(ForwardData.FromString); + return parts.Select((x) => new ForwardData(x)); } /// diff --git a/AdvancedSharpAdbClient/AdbServer.Async.cs b/AdvancedSharpAdbClient/AdbServer.Async.cs index 3dc11978..d5a43af5 100644 --- a/AdvancedSharpAdbClient/AdbServer.Async.cs +++ b/AdvancedSharpAdbClient/AdbServer.Async.cs @@ -22,7 +22,7 @@ public virtual async Task StartServerAsync(string adbPath, bo if (commandLineClient.IsValidAdbFile(adbPath)) { - cachedAdbPath = adbPath; + CachedAdbPath = adbPath; commandLineVersion = await commandLineClient.GetVersionAsync(cancellationToken); } @@ -43,9 +43,6 @@ public virtual async Task StartServerAsync(string adbPath, bo ExceptionExtensions.ThrowIfNull(adbPath); await adbClient.KillAdbAsync(cancellationToken); - serverStatus.IsRunning = false; - serverStatus.Version = null; - await commandLineClient.StartServerAsync(cancellationToken); return StartServerResult.RestartedOutdatedDaemon; } @@ -68,7 +65,7 @@ public virtual async Task StartServerAsync(string adbPath, bo /// public virtual async Task RestartServerAsync(string adbPath, CancellationToken cancellationToken = default) { - adbPath ??= cachedAdbPath; + adbPath ??= CachedAdbPath; if (!IsValidAdbFile(adbPath)) { diff --git a/AdvancedSharpAdbClient/AdbServer.cs b/AdvancedSharpAdbClient/AdbServer.cs index db03ae1d..a6bc7d77 100644 --- a/AdvancedSharpAdbClient/AdbServer.cs +++ b/AdvancedSharpAdbClient/AdbServer.cs @@ -53,12 +53,6 @@ public partial class AdbServer(IAdbClient adbClient, Func protected static readonly object RestartLock = new(); - /// - /// The path to the adb server. Cached from calls to . Used when restarting - /// the server to figure out where adb is located. - /// - protected static string cachedAdbPath; - /// /// The current ADB client that manages the connection. /// @@ -94,6 +88,12 @@ public AdbServer(Func adbCommandLineClientFactory { } + /// + /// The path to the adb server. Cached from calls to . Used when restarting + /// the server to figure out where adb is located. + /// + protected static string CachedAdbPath { get; set; } + /// /// Gets or sets the default instance of the interface. /// @@ -109,7 +109,7 @@ public virtual StartServerResult StartServer(string adbPath, bool restartServerI if (commandLineClient.IsValidAdbFile(adbPath)) { - cachedAdbPath = adbPath; + CachedAdbPath = adbPath; commandLineVersion = commandLineClient.GetVersion(); } @@ -130,9 +130,6 @@ public virtual StartServerResult StartServer(string adbPath, bool restartServerI ExceptionExtensions.ThrowIfNull(adbPath); adbClient.KillAdb(); - serverStatus.IsRunning = false; - serverStatus.Version = null; - commandLineClient.StartServer(); return StartServerResult.RestartedOutdatedDaemon; } @@ -152,7 +149,7 @@ public virtual StartServerResult StartServer(string adbPath, bool restartServerI /// public virtual StartServerResult RestartServer(string adbPath = null) { - adbPath ??= cachedAdbPath; + adbPath ??= CachedAdbPath; if (!IsValidAdbFile(adbPath)) { diff --git a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj index 162538f8..98083f69 100644 --- a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj +++ b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj @@ -12,7 +12,7 @@ $(NoWarn);NU1603;NU1605 - net6.0;netstandard1.3;netstandard2.0;netstandard2.1 + net6.0;net8.0;netstandard1.3;netstandard2.0;netstandard2.1 $(TargetFrameworks);net8.0;netcoreapp2.1;netcoreapp3.1 $(TargetFrameworks);net2.0-client;net3.5-client;net4.0-client;net4.5.2;net4.6.2;net4.7.2;net4.8.1;net6.0-windows10.0.17763.0;net8.0-windows10.0.17763.0;netcore50;uap10.0;uap10.0.15138.0 diff --git a/AdvancedSharpAdbClient/DeviceCommands/LinuxPath.cs b/AdvancedSharpAdbClient/DeviceCommands/LinuxPath.cs index 9f3cf83a..ea7239b3 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/LinuxPath.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/LinuxPath.cs @@ -26,10 +26,7 @@ public static partial class LinuxPath /// private const string EscapePattern = "([\\\\()*+?\"'#/\\s])"; - private static readonly char[] InvalidCharacters = new char[] - { - '|', '\\', '?', '*', '<', '\"', ':', '>' - }; + private static readonly char[] InvalidCharacters = ['|', '\\', '?', '*', '<', '\"', ':', '>']; /// /// Combine the specified paths to form one path. @@ -126,7 +123,7 @@ public static string GetDirectoryName(string path) #if NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER if (tpath.EndsWith(DirectorySeparatorChar)) #else - if (tpath.EndsWith(new string(new char[] { DirectorySeparatorChar }))) + if (tpath.EndsWith(new string([DirectorySeparatorChar]))) #endif { return tpath; @@ -140,7 +137,7 @@ public static string GetDirectoryName(string path) } else if (tpath.Length == 1) { - return new string(new char[] { DirectorySeparatorChar }); + return new string([DirectorySeparatorChar]); } } @@ -240,7 +237,7 @@ private static string FixupPath(string path) #if NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER if (sb != "." && !sb.StartsWith(DirectorySeparatorChar)) #else - if (sb != "." && !sb.StartsWith(new string(new char[] { DirectorySeparatorChar }))) + if (sb != "." && !sb.StartsWith(new string([DirectorySeparatorChar]))) #endif { sb = $".{DirectorySeparatorChar}{sb}"; @@ -249,13 +246,13 @@ private static string FixupPath(string path) #if NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER if (!sb.EndsWith(DirectorySeparatorChar)) #else - if (!sb.EndsWith(new string(new char[] { DirectorySeparatorChar }))) + if (!sb.EndsWith(new string([DirectorySeparatorChar]))) #endif { sb = $"{sb}{DirectorySeparatorChar}"; } - sb = sb.Replace("//", new string(new char[] { DirectorySeparatorChar })); + sb = sb.Replace("//", new string([DirectorySeparatorChar])); return sb; } diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs index c7dcc21d..7d9e4bfc 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs @@ -12,6 +12,288 @@ namespace AdvancedSharpAdbClient.DeviceCommands /// public class AndroidProcess { + /// + /// Initializes a new instance of the class. + /// + public AndroidProcess() { } + + /// + /// Initializes a new instance of the class from it representation. + /// + /// A which represents a . + /// A value indicating whether the output of /proc/{pid}/stat is prefixed with /proc/{pid}/cmdline or not. + /// Because stat does not contain the full process name, this can be useful. + public AndroidProcess(string line, bool cmdLinePrefix = false) + { + ExceptionExtensions.ThrowIfNull(line); + + // See http://man7.org/linux/man-pages/man5/proc.5.html, + // section /proc/[pid]/stat, for more information about the file format + + // Space delimited, so normally we would just do a string.split + // The process name may contain spaces but is wrapped within parentheses, all other values (we know of) are + // numeric. + // So we parse the pid & process name manually, to account for this, and do the string.split afterwards :-) + int processNameStart = line.IndexOf('('); + int processNameEnd = line.LastIndexOf(')'); + + int pid; + string comm; + + bool parsedCmdLinePrefix = false; + + if (cmdLinePrefix) + { +#if HAS_INDEXRANGE + string[] cmdLineParts = line[..processNameStart] +#else + + string[] cmdLineParts = line.Substring(0, processNameStart) +#endif + .Split('\0'); + + if (cmdLineParts.Length <= 1) + { + parsedCmdLinePrefix = false; + } + else + { +#if HAS_INDEXRANGE + pid = int.Parse(cmdLineParts[^1]); +#else + pid = int.Parse(cmdLineParts[cmdLineParts.Length - 1]); +#endif + ProcessId = pid; + + comm = cmdLineParts[0]; + Name = comm; + + // All the other parts are the command line arguments, skip them. + parsedCmdLinePrefix = true; + } + } + + if (!parsedCmdLinePrefix) + { +#if HAS_INDEXRANGE + pid = int.Parse(line[..processNameStart]); +#else + pid = int.Parse(line.Substring(0, processNameStart)); +#endif + ProcessId = pid; + + comm = line.Substring(processNameStart + 1, processNameEnd - processNameStart - 1); + Name = comm; + } + +#if HAS_INDEXRANGE + string[] parts = line[(processNameEnd + 1)..] +#else + string[] parts = line.Substring(processNameEnd + 1) +#endif + .Split(' ', StringSplitOptions.RemoveEmptyEntries); + + if (parts.Length < 35) + { + throw new ArgumentOutOfRangeException(nameof(line)); + } + + // Only fields in Linux 2.1.10 and earlier are listed here, + // additional fields exist in newer versions of Linux. + string state = parts[0]; + State = (AndroidProcessState)Enum.Parse(typeof(AndroidProcessState), state, true); + + int ppid = ParseInt(parts[1]); + ParentProcessId = ppid; + + int pgrp = ParseInt(parts[2]); + ProcessGroupId = pgrp; + + int session = ParseInt(parts[3]); + SessionID = session; + + int tty_nr = ParseInt(parts[4]); + TTYNumber = tty_nr; + + int tpgid = ParseInt(parts[5]); + TopProcessGroupId = tpgid; + + uint flags = ParseUInt(parts[6]); + Flags = (PerProcessFlags)flags; + + ulong minflt = ParseULong(parts[7]); + MinorFaults = minflt; + + ulong cminflt = ParseULong(parts[8]); + ChildMinorFaults = cminflt; + + ulong majflt = ParseULong(parts[9]); + MajorFaults = majflt; + + ulong cmajflt = ParseULong(parts[10]); + ChildMajorFaults = cmajflt; + + ulong utime = ParseULong(parts[11]); + UserScheduledTime = utime; + + ulong stime = ParseULong(parts[12]); + ScheduledTime = stime; + + long cutime = ParseLong(parts[13]); + ChildUserScheduledTime = cutime; + + long cstime = ParseLong(parts[14]); + ChildScheduledTime = cstime; + + long priority = ParseLong(parts[15]); + Priority = priority; + + long nice = ParseLong(parts[16]); + Nice = nice; + + long num_threads = ParseLong(parts[17]); + ThreadsNumber = num_threads; + + long itrealvalue = ParseLong(parts[18]); + Interval = itrealvalue; + + ulong starttime = ParseULong(parts[19]); + StartTime = starttime; + + ulong vsize = ParseULong(parts[20]); + VirtualSize = vsize; + + int rss = int.Parse(parts[21]); + ResidentSetSize = rss; + + ulong rsslim = ParseULong(parts[22]); + ResidentSetSizeLimit = rsslim; + + ulong startcode = ParseULong(parts[23]); + StartCode = startcode; + + ulong endcode = ParseULong(parts[24]); + EndCode = endcode; + + ulong startstack = ParseULong(parts[25]); + StartStack = startstack; + + ulong kstkesp = ParseULong(parts[26]); + ESP = kstkesp; + + ulong kstkeip = ParseULong(parts[27]); + EIP = kstkeip; + + ulong signal = ParseULong(parts[28]); + Signal = signal; + + ulong blocked = ParseULong(parts[29]); + Blocked = blocked; + + ulong sigignore = ParseULong(parts[30]); + IgnoredSignals = sigignore; + + ulong sigcatch = ParseULong(parts[31]); + CaughtSignals = sigcatch; + + ulong wchan = ParseULong(parts[32]); + WChan = wchan; + + ulong nswap = ParseULong(parts[33]); + SwappedPagesNumber = nswap; + + ulong cnswap = ParseULong(parts[34]); + CumulativeSwappedPagesNumber = cnswap; + + if (parts.Length < 36) + { + return; + } + + // Linux 2.1.22 + int exit_signal = ParseInt(parts[35]); + ExitSignal = exit_signal; + + if (parts.Length < 37) + { + return; + } + + // Linux 2.2.8 + int processor = ParseInt(parts[36]); + Processor = processor; + + if (parts.Length < 39) + { + return; + } + + // Linux 2.5.19 + uint rt_priority = ParseUInt(parts[37]); + RealTimePriority = rt_priority; + + uint policy = ParseUInt(parts[38]); + Policy = policy; + + if (parts.Length < 40) + { + return; + } + + // Linux 2.6.18 + ulong delayacct_blkio_ticks = ParseULong(parts[39]); + + IODelays = delayacct_blkio_ticks; + + if (parts.Length < 42) + { + return; + } + + // Linux 2.6.24 + ulong guest_time = ParseULong(parts[40]); + GuestTime = guest_time; + + long cguest_time = ParseLong(parts[41]); + ChildGuestTime = cguest_time; + + if (parts.Length < 45) + { + return; + } + + // Linux 3.3 + ulong start_data = ParseULong(parts[42]); + StartData = start_data; + + ulong end_data = ParseULong(parts[43]); + EndData = end_data; + + ulong start_brk = ParseULong(parts[44]); + StartBrk = start_brk; + + if (parts.Length < 50) + { + return; + } + + // Linux 3.5 + ulong arg_start = ParseULong(parts[45]); + ArgStart = arg_start; + + ulong arg_end = ParseULong(parts[46]); + ArgEnd = arg_end; + + ulong env_start = ParseULong(parts[47]); + EnvStart = env_start; + + ulong env_end = ParseULong(parts[48]); + EnvEnd = env_end; + + int exit_code = ParseInt(parts[49]); + ExitCode = exit_code; + } + /// /// Gets or sets the state of the process. /// @@ -320,280 +602,7 @@ public class AndroidProcess /// A value indicating whether the output of /proc/{pid}/stat is prefixed with /proc/{pid}/cmdline or not. /// Because stat does not contain the full process name, this can be useful. /// The equivalent . - public static AndroidProcess Parse(string line, bool cmdLinePrefix = false) - { - ExceptionExtensions.ThrowIfNull(line); - - AndroidProcess process = new(); - - // See http://man7.org/linux/man-pages/man5/proc.5.html, - // section /proc/[pid]/stat, for more information about the file format - - // Space delimited, so normally we would just do a string.split - // The process name may contain spaces but is wrapped within parentheses, all other values (we know of) are - // numeric. - // So we parse the pid & process name manually, to account for this, and do the string.split afterwards :-) - int processNameStart = line.IndexOf('('); - int processNameEnd = line.LastIndexOf(')'); - - int pid; - string comm; - - bool parsedCmdLinePrefix = false; - - if (cmdLinePrefix) - { -#if HAS_INDEXRANGE - string[] cmdLineParts = line[..processNameStart] -#else - - string[] cmdLineParts = line.Substring(0, processNameStart) -#endif - .Split(new char[] { '\0' }); - - if (cmdLineParts.Length <= 1) - { - parsedCmdLinePrefix = false; - } - else - { -#if HAS_INDEXRANGE - pid = int.Parse(cmdLineParts[^1]); -#else - pid = int.Parse(cmdLineParts[cmdLineParts.Length - 1]); -#endif - process.ProcessId = pid; - - comm = cmdLineParts[0]; - process.Name = comm; - - // All the other parts are the command line arguments, skip them. - parsedCmdLinePrefix = true; - } - } - - if (!parsedCmdLinePrefix) - { -#if HAS_INDEXRANGE - pid = int.Parse(line[..processNameStart]); -#else - pid = int.Parse(line.Substring(0, processNameStart)); -#endif - process.ProcessId = pid; - - comm = line.Substring(processNameStart + 1, processNameEnd - processNameStart - 1); - process.Name = comm; - } - -#if HAS_INDEXRANGE - string[] parts = line[(processNameEnd + 1)..] -#else - string[] parts = line.Substring(processNameEnd + 1) -#endif - .Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); - - if (parts.Length < 35) - { - throw new ArgumentOutOfRangeException(nameof(line)); - } - - // Only fields in Linux 2.1.10 and earlier are listed here, - // additional fields exist in newer versions of Linux. - string state = parts[0]; - process.State = (AndroidProcessState)Enum.Parse(typeof(AndroidProcessState), state, true); - - int ppid = ParseInt(parts[1]); - process.ParentProcessId = ppid; - - int pgrp = ParseInt(parts[2]); - process.ProcessGroupId = pgrp; - - int session = ParseInt(parts[3]); - process.SessionID = session; - - int tty_nr = ParseInt(parts[4]); - process.TTYNumber = tty_nr; - - int tpgid = ParseInt(parts[5]); - process.TopProcessGroupId = tpgid; - - uint flags = ParseUInt(parts[6]); - process.Flags = (PerProcessFlags)flags; - - ulong minflt = ParseULong(parts[7]); - process.MinorFaults = minflt; - - ulong cminflt = ParseULong(parts[8]); - process.ChildMinorFaults = cminflt; - - ulong majflt = ParseULong(parts[9]); - process.MajorFaults = majflt; - - ulong cmajflt = ParseULong(parts[10]); - process.ChildMajorFaults = cmajflt; - - ulong utime = ParseULong(parts[11]); - process.UserScheduledTime = utime; - - ulong stime = ParseULong(parts[12]); - process.ScheduledTime = stime; - - long cutime = ParseLong(parts[13]); - process.ChildUserScheduledTime = cutime; - - long cstime = ParseLong(parts[14]); - process.ChildScheduledTime = cstime; - - long priority = ParseLong(parts[15]); - process.Priority = priority; - - long nice = ParseLong(parts[16]); - process.Nice = nice; - - long num_threads = ParseLong(parts[17]); - process.ThreadsNumber = num_threads; - - long itrealvalue = ParseLong(parts[18]); - process.Interval = itrealvalue; - - ulong starttime = ParseULong(parts[19]); - process.StartTime = starttime; - - ulong vsize = ParseULong(parts[20]); - process.VirtualSize = vsize; - - int rss = int.Parse(parts[21]); - process.ResidentSetSize = rss; - - ulong rsslim = ParseULong(parts[22]); - process.ResidentSetSizeLimit = rsslim; - - ulong startcode = ParseULong(parts[23]); - process.StartCode = startcode; - - ulong endcode = ParseULong(parts[24]); - process.EndCode = endcode; - - ulong startstack = ParseULong(parts[25]); - process.StartStack = startstack; - - ulong kstkesp = ParseULong(parts[26]); - process.ESP = kstkesp; - - ulong kstkeip = ParseULong(parts[27]); - process.EIP = kstkeip; - - ulong signal = ParseULong(parts[28]); - process.Signal = signal; - - ulong blocked = ParseULong(parts[29]); - process.Blocked = blocked; - - ulong sigignore = ParseULong(parts[30]); - process.IgnoredSignals = sigignore; - - ulong sigcatch = ParseULong(parts[31]); - process.CaughtSignals = sigcatch; - - ulong wchan = ParseULong(parts[32]); - process.WChan = wchan; - - ulong nswap = ParseULong(parts[33]); - process.SwappedPagesNumber = nswap; - - ulong cnswap = ParseULong(parts[34]); - process.CumulativeSwappedPagesNumber = cnswap; - - if (parts.Length < 36) - { - return process; - } - - // Linux 2.1.22 - int exit_signal = ParseInt(parts[35]); - process.ExitSignal = exit_signal; - - if (parts.Length < 37) - { - return process; - } - - // Linux 2.2.8 - int processor = ParseInt(parts[36]); - process.Processor = processor; - - if (parts.Length < 39) - { - return process; - } - - // Linux 2.5.19 - uint rt_priority = ParseUInt(parts[37]); - process.RealTimePriority = rt_priority; - - uint policy = ParseUInt(parts[38]); - process.Policy = policy; - - if (parts.Length < 40) - { - return process; - } - - // Linux 2.6.18 - ulong delayacct_blkio_ticks = ParseULong(parts[39]); - - process.IODelays = delayacct_blkio_ticks; - - if (parts.Length < 42) - { - return process; - } - - // Linux 2.6.24 - ulong guest_time = ParseULong(parts[40]); - process.GuestTime = guest_time; - - long cguest_time = ParseLong(parts[41]); - process.ChildGuestTime = cguest_time; - - if (parts.Length < 45) - { - return process; - } - - // Linux 3.3 - ulong start_data = ParseULong(parts[42]); - process.StartData = start_data; - - ulong end_data = ParseULong(parts[43]); - process.EndData = end_data; - - ulong start_brk = ParseULong(parts[44]); - process.StartBrk = start_brk; - - if (parts.Length < 50) - { - return process; - } - - // Linux 3.5 - ulong arg_start = ParseULong(parts[45]); - process.ArgStart = arg_start; - - ulong arg_end = ParseULong(parts[46]); - process.ArgEnd = arg_end; - - ulong env_start = ParseULong(parts[47]); - process.EnvStart = env_start; - - ulong env_end = ParseULong(parts[48]); - process.EnvEnd = env_end; - - int exit_code = ParseInt(parts[49]); - process.ExitCode = exit_code; - - return process; - } + public static AndroidProcess Parse(string line, bool cmdLinePrefix = false) => new(line, cmdLinePrefix); /// /// Gets a that represents this , diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/InstallProgressEventArgs.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/InstallProgressEventArgs.cs index c23d17aa..7d39b6b1 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/InstallProgressEventArgs.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/InstallProgressEventArgs.cs @@ -9,12 +9,12 @@ namespace AdvancedSharpAdbClient.DeviceCommands /// /// Represents the state of the installation for . /// - public class InstallProgressEventArgs : EventArgs + public class InstallProgressEventArgs(PackageInstallProgressState state) : EventArgs { /// /// State of the installation. /// - public PackageInstallProgressState State { get; } + public PackageInstallProgressState State { get; } = state; /// /// Number of packages which is finished operation. @@ -38,11 +38,6 @@ public class InstallProgressEventArgs : EventArgs /// public double UploadProgress { get; } - /// - /// Initializes a new instance of the class. - /// - public InstallProgressEventArgs(PackageInstallProgressState state) => State = state; - /// /// Initializes a new instance of the class. /// Which is used for state. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/VersionInfo.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/VersionInfo.cs index c64b051a..600fd8fa 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/VersionInfo.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/VersionInfo.cs @@ -7,28 +7,19 @@ namespace AdvancedSharpAdbClient.DeviceCommands /// /// Represents a version of an Android application. /// - public class VersionInfo + /// The version code of the application. + /// The version name of the application. + public class VersionInfo(int versionCode, string versionName) { /// /// Gets or sets the version code of an Android application. /// - public int VersionCode { get; } + public int VersionCode { get; } = versionCode; /// /// Gets or sets the version name of an Android application. /// - public string VersionName { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The version code of the application. - /// The version name of the application. - public VersionInfo(int versionCode, string versionName) - { - VersionCode = versionCode; - VersionName = versionName; - } + public string VersionName { get; } = versionName; /// /// Deconstruct the class. diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs index 75e06237..8daf99c9 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs @@ -449,8 +449,8 @@ protected virtual async Task CreateInstallSessionAsync(bool reinstall, s } string result = receiver.SuccessMessage; - int arr = result.IndexOf("]") - 1 - result.IndexOf("["); - string session = result.Substring(result.IndexOf("[") + 1, arr); + int arr = result.IndexOf(']') - 1 - result.IndexOf('['); + string session = result.Substring(result.IndexOf('[') + 1, arr); return session; } diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs index d4879581..ff57eef4 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs @@ -527,8 +527,8 @@ protected virtual string CreateInstallSession(bool reinstall, string packageName } string result = receiver.SuccessMessage; - int arr = result.IndexOf("]") - 1 - result.IndexOf("["); - string session = result.Substring(result.IndexOf("[") + 1, arr); + int arr = result.IndexOf(']') - 1 - result.IndexOf('['); + string session = result.Substring(result.IndexOf('[') + 1, arr); return session; } diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs index 89435e1b..6d38e8f6 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs @@ -22,6 +22,11 @@ public sealed partial class GetPropReceiver : MultiLineReceiver /// private const string GetPropPattern = "^\\[([^]]+)\\]\\:\\s*\\[(.*)\\]$"; + /// + /// Initializes a new instance of the class. + /// + public GetPropReceiver() { } + /// /// Gets the list of properties which have been retrieved. /// diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InfoOutputReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InfoOutputReceiver.cs index f7c0e213..f1631bdc 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InfoOutputReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InfoOutputReceiver.cs @@ -12,6 +12,11 @@ namespace AdvancedSharpAdbClient.DeviceCommands /// public class InfoOutputReceiver : MultiLineReceiver { + /// + /// Initializes a new instance of the class. + /// + public InfoOutputReceiver() { } + /// /// Gets or sets a dictionary with the extracted properties and their corresponding values. /// diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs index 8e6a8a68..04c81832 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs @@ -42,6 +42,11 @@ public partial class InstallOutputReceiver : MultiLineReceiver /// private const string ErrorPattern = @"Error:\s+(.*)?"; + /// + /// Initializes a new instance of the class. + /// + public InstallOutputReceiver() { } + /// /// Gets the error message if the install was unsuccessful. /// diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/PackageManagerReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/PackageManagerReceiver.cs index 9cc921dc..2a7c8a75 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/PackageManagerReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/PackageManagerReceiver.cs @@ -9,28 +9,19 @@ namespace AdvancedSharpAdbClient.DeviceCommands /// /// Parses the output of the various pm commands. /// - public class PackageManagerReceiver : MultiLineReceiver + /// The device for which the package information is being received. + /// The parent package manager. + public class PackageManagerReceiver(DeviceData device, PackageManager packageManager) : MultiLineReceiver { - /// - /// Initializes a new instance of the class. - /// - /// The device for which the package information is being received. - /// The parent package manager. - public PackageManagerReceiver(DeviceData device, PackageManager packageManager) - { - Device = device; - PackageManager = packageManager; - } - /// /// Gets the device. /// - public DeviceData Device { get; private set; } + public DeviceData Device { get; private set; } = device; /// /// Gets the package manager. /// - public PackageManager PackageManager { get; private set; } + public PackageManager PackageManager { get; private set; } = packageManager; /// /// Processes the new lines. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs index 136adb4a..aff2b399 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs @@ -13,6 +13,11 @@ namespace AdvancedSharpAdbClient.DeviceCommands /// public class ProcessOutputReceiver : MultiLineReceiver { + /// + /// Initializes a new instance of the class. + /// + public ProcessOutputReceiver() { } + /// /// Gets a list of all processes that have been received. /// @@ -31,7 +36,7 @@ protected override void ProcessNewLines(IEnumerable lines) try { - Processes.Add(AndroidProcess.Parse(line, cmdLinePrefix: true)); + Processes.Add(new AndroidProcess(line, cmdLinePrefix: true)); } catch (Exception) { diff --git a/AdvancedSharpAdbClient/DeviceMonitor.cs b/AdvancedSharpAdbClient/DeviceMonitor.cs index cc03c2a5..f2f863fc 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.cs @@ -33,6 +33,8 @@ namespace AdvancedSharpAdbClient /// public partial class DeviceMonitor : IDeviceMonitor { + private static readonly string[] separator = ["\r\n", "\n"]; + private bool disposed = false; #if HAS_LOGGER @@ -66,6 +68,18 @@ public partial class DeviceMonitor : IDeviceMonitor protected Thread monitorThread; #endif + /// + public event EventHandler DeviceChanged; + + /// + public event EventHandler DeviceNotified; + + /// + public event EventHandler DeviceConnected; + + /// + public event EventHandler DeviceDisconnected; + #if !HAS_LOGGER #pragma warning disable CS1572 // XML 注释中有 param 标记,但是没有该名称的参数 #endif @@ -91,18 +105,6 @@ public DeviceMonitor(IAdbSocket socket #pragma warning restore CS1572 // XML 注释中有 param 标记,但是没有该名称的参数 #endif - /// - public event EventHandler DeviceChanged; - - /// - public event EventHandler DeviceNotified; - - /// - public event EventHandler DeviceConnected; - - /// - public event EventHandler DeviceDisconnected; - /// public ReadOnlyCollection Devices { get; private set; } @@ -285,9 +287,9 @@ private void InitializeSocket() /// private void ProcessIncomingDeviceData(string result) { - string[] deviceValues = result.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries); + string[] deviceValues = result.Split(separator, StringSplitOptions.RemoveEmptyEntries); - IEnumerable currentDevices = deviceValues.Select(DeviceData.CreateFromAdbData); + IEnumerable currentDevices = deviceValues.Select((x) => new DeviceData(x)); UpdateDevices(currentDevices); } diff --git a/AdvancedSharpAdbClient/Exceptions/AdbException.cs b/AdvancedSharpAdbClient/Exceptions/AdbException.cs index fd38c129..e67addee 100644 --- a/AdvancedSharpAdbClient/Exceptions/AdbException.cs +++ b/AdvancedSharpAdbClient/Exceptions/AdbException.cs @@ -84,6 +84,7 @@ public AdbException(string message, Exception innerException) : base(message, in /// that the connection was reset by the remote server. This happens when the adb server was killed. /// public bool ConnectionReset => - InnerException is SocketException socketException && socketException.SocketErrorCode == SocketError.ConnectionReset; + InnerException is SocketException socketException + && socketException.SocketErrorCode == SocketError.ConnectionReset; } } diff --git a/AdvancedSharpAdbClient/Extensions/Utilities.cs b/AdvancedSharpAdbClient/Extensions/Utilities.cs index 951d7438..2d8fd9e0 100644 --- a/AdvancedSharpAdbClient/Extensions/Utilities.cs +++ b/AdvancedSharpAdbClient/Extensions/Utilities.cs @@ -93,10 +93,26 @@ public static bool IsNullOrWhiteSpace(this string value) /// One of the enumeration values that specifies the rules to use in the comparison. /// if the parameter occurs within this string, /// or if is the empty string (""); otherwise, . - public static bool Contains(this string text, string value, StringComparison comparisonType) - { - return text.IndexOf(value, comparisonType) != -1; - } + public static bool Contains(this string text, string value, StringComparison comparisonType) => + text.IndexOf(value, comparisonType) != -1; + + /// + /// Splits a string into substrings based on a specified delimiting character and, optionally, options. + /// + /// The string to split. + /// A character that delimits the substrings in this string. + /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings. + /// An array whose elements contain the substrings from this instance that are delimited by . + public static string[] Split(this string text, char separator, StringSplitOptions options = StringSplitOptions.None) => + text.Split(new[] { separator }, options); + + /// + /// Determines whether this string instance starts with the specified character. + /// + /// A sequence in which to locate a value. + /// The character to compare. + /// if matches the beginning of this string; otherwise, . + public static bool StartsWith(this string text, char value) => text.StartsWith(new string([value])); #endif /// diff --git a/AdvancedSharpAdbClient/Logs/AndroidLogEntry.cs b/AdvancedSharpAdbClient/Logs/AndroidLogEntry.cs index 748fcbaf..8a5dfcf7 100644 --- a/AdvancedSharpAdbClient/Logs/AndroidLogEntry.cs +++ b/AdvancedSharpAdbClient/Logs/AndroidLogEntry.cs @@ -15,7 +15,7 @@ public class AndroidLogEntry : LogEntry /// /// Maps Android log priorities to chars used to represent them in the system log. /// - private static readonly Dictionary PriorityFormatters = new() + private static readonly Dictionary PriorityFormatters = new(6) { { Priority.Verbose, 'V' }, { Priority.Debug, 'D' }, @@ -25,6 +25,11 @@ public class AndroidLogEntry : LogEntry { Priority.Assert, 'A' } }; + /// + /// Initializes a new instance of the class. + /// + public AndroidLogEntry() { } + /// /// Gets or sets the priority of the log message. /// diff --git a/AdvancedSharpAdbClient/Logs/EventLogEntry.cs b/AdvancedSharpAdbClient/Logs/EventLogEntry.cs index 5e7d5c4c..61beb24c 100644 --- a/AdvancedSharpAdbClient/Logs/EventLogEntry.cs +++ b/AdvancedSharpAdbClient/Logs/EventLogEntry.cs @@ -12,6 +12,11 @@ namespace AdvancedSharpAdbClient.Logs /// public class EventLogEntry : LogEntry { + /// + /// Initializes a new instance of the class. + /// + public EventLogEntry() { } + /// /// Gets or sets the 4 bytes integer key from "/system/etc/event-log-tags" file. /// diff --git a/AdvancedSharpAdbClient/Logs/LogEntry.cs b/AdvancedSharpAdbClient/Logs/LogEntry.cs index 7076368e..d4aa810c 100644 --- a/AdvancedSharpAdbClient/Logs/LogEntry.cs +++ b/AdvancedSharpAdbClient/Logs/LogEntry.cs @@ -14,6 +14,11 @@ namespace AdvancedSharpAdbClient.Logs /// public class LogEntry { + /// + /// Initializes a new instance of the class. + /// + public LogEntry() { } + /// /// Gets or sets the process ID of the code that generated the log message. /// diff --git a/AdvancedSharpAdbClient/Logs/LogReader.cs b/AdvancedSharpAdbClient/Logs/LogReader.cs index e637a025..256a44fa 100644 --- a/AdvancedSharpAdbClient/Logs/LogReader.cs +++ b/AdvancedSharpAdbClient/Logs/LogReader.cs @@ -13,18 +13,13 @@ namespace AdvancedSharpAdbClient.Logs /// /// Processes Android log files in binary format. You usually get the binary format by running logcat -B. /// - public partial class LogReader + /// A that contains the logcat data. + public partial class LogReader(Stream stream) { /// /// The that contains the logcat data. /// - private readonly Stream stream; - - /// - /// Initializes a new instance of the class. - /// - /// A that contains the logcat data. - public LogReader(Stream stream) => this.stream = stream ?? throw new ArgumentNullException(nameof(stream)); + private readonly Stream stream = stream ?? throw new ArgumentNullException(nameof(stream)); /// /// Reads the next from the stream. @@ -59,7 +54,6 @@ public virtual LogEntry ReadEntry() // header size and payload length. // For both objects, the size should be 0x18 uint id = 0; - uint uid = 0; if (headerSize != 0) { @@ -84,7 +78,7 @@ public virtual LogEntry ReadEntry() return null; } - uid = uidValue.Value; + _ = uidValue.Value; } if (headerSize >= 0x20) diff --git a/AdvancedSharpAdbClient/Logs/ShellStream.cs b/AdvancedSharpAdbClient/Logs/ShellStream.cs index 740ba3f2..3b86044d 100644 --- a/AdvancedSharpAdbClient/Logs/ShellStream.cs +++ b/AdvancedSharpAdbClient/Logs/ShellStream.cs @@ -232,7 +232,7 @@ async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToke #endif } - byte[] minibuffer = new byte[1]; + byte[] miniBuffer = new byte[1]; // Loop over the data, and find a LF (0x0d) character. If it is // followed by a CR (0x0a) character, remove the LF character and @@ -264,11 +264,11 @@ async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToke int miniRead = #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - await Inner.ReadAsync(minibuffer.AsMemory(0, 1), cancellationToken).ConfigureAwait(false); + await Inner.ReadAsync(miniBuffer.AsMemory(0, 1), cancellationToken).ConfigureAwait(false); #elif !NET35 - await Inner.ReadAsync(minibuffer, 0, 1, cancellationToken).ConfigureAwait(false); + await Inner.ReadAsync(miniBuffer, 0, 1, cancellationToken).ConfigureAwait(false); #else - Inner.Read(minibuffer, 0, 1); + Inner.Read(miniBuffer, 0, 1); #endif if (miniRead == 0) @@ -280,7 +280,7 @@ async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToke else { // Append the byte to the buffer. - buffer[offset + read - 1] = minibuffer[0]; + buffer[offset + read - 1] = miniBuffer[0]; } } } @@ -291,13 +291,13 @@ async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToke { int miniRead = #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - await Inner.ReadAsync(minibuffer.AsMemory(0, 1), cancellationToken).ConfigureAwait(false); + await Inner.ReadAsync(miniBuffer.AsMemory(0, 1), cancellationToken).ConfigureAwait(false); #elif !NET35 - await Inner.ReadAsync(minibuffer, 0, 1, cancellationToken).ConfigureAwait(false); + await Inner.ReadAsync(miniBuffer, 0, 1, cancellationToken).ConfigureAwait(false); #else - Inner.Read(minibuffer, 0, 1); + Inner.Read(miniBuffer, 0, 1); #endif - int nextByte = minibuffer[0]; + int nextByte = miniBuffer[0]; if (nextByte == 0x0a) { diff --git a/AdvancedSharpAdbClient/Models/AdbResponse.cs b/AdvancedSharpAdbClient/Models/AdbResponse.cs index 5a89ddb0..1bfea2b8 100644 --- a/AdvancedSharpAdbClient/Models/AdbResponse.cs +++ b/AdvancedSharpAdbClient/Models/AdbResponse.cs @@ -19,7 +19,7 @@ public class AdbResponse /// /// Gets a that represents the OK response sent by ADB. /// - public static AdbResponse OK { get; } = new AdbResponse() + public static AdbResponse OK { get; } = new() { IOSuccess = true, Okay = true, diff --git a/AdvancedSharpAdbClient/Models/AdbServerStatus.cs b/AdvancedSharpAdbClient/Models/AdbServerStatus.cs index 1caa6bf7..49982160 100644 --- a/AdvancedSharpAdbClient/Models/AdbServerStatus.cs +++ b/AdvancedSharpAdbClient/Models/AdbServerStatus.cs @@ -9,28 +9,19 @@ namespace AdvancedSharpAdbClient /// /// Represents the status of the adb server. /// - public struct AdbServerStatus + /// The value indicating whether the server is currently running. + /// The version of the server when it is running. + public struct AdbServerStatus(bool isRunning, Version version) { /// /// Gets a value indicating whether the server is currently running. /// - public bool IsRunning { get; internal set; } + public bool IsRunning { get; set; } = isRunning; /// /// Gets the version of the server when it is running. /// - public Version Version { get; internal set; } - - /// - /// Initializes a new instance of the struct. - /// - /// The value indicating whether the server is currently running. - /// The version of the server when it is running. - public AdbServerStatus(bool isRunning, Version version) - { - IsRunning = isRunning; - Version = version; - } + public Version Version { get; set; } = version; /// /// Deconstruct the struct. diff --git a/AdvancedSharpAdbClient/Models/DeviceData.cs b/AdvancedSharpAdbClient/Models/DeviceData.cs index 08c1d54b..659c7ca1 100644 --- a/AdvancedSharpAdbClient/Models/DeviceData.cs +++ b/AdvancedSharpAdbClient/Models/DeviceData.cs @@ -15,13 +15,44 @@ public partial class DeviceData /// /// A regular expression that can be used to parse the device information that is returned by the Android Debut Bridge. /// - internal const string DeviceDataRegexString = @"^(?[a-zA-Z0-9_-]+(?:\s?[\.a-zA-Z0-9_-]+)?(?:\:\d{1,})?)\s+(?device|connecting|offline|unknown|bootloader|recovery|download|authorizing|unauthorized|host|no permissions)(?.*?)(\s+usb:(?[^:]+))?(?:\s+product:(?[^:]+))?(\s+model\:(?[\S]+))?(\s+device\:(?[\S]+))?(\s+features:(?[^:]+))?(\s+transport_id:(?[^:]+))?$"; + private const string DeviceDataRegexString = @"^(?[a-zA-Z0-9_-]+(?:\s?[\.a-zA-Z0-9_-]+)?(?:\:\d{1,})?)\s+(?device|connecting|offline|unknown|bootloader|recovery|download|authorizing|unauthorized|host|no permissions)(?.*?)(\s+usb:(?[^:]+))?(?:\s+product:(?[^:]+))?(\s+model\:(?[\S]+))?(\s+device\:(?[\S]+))?(\s+features:(?[^:]+))?(\s+transport_id:(?[^:]+))?$"; /// /// A regular expression that can be used to parse the device information that is returned by the Android Debut Bridge. /// private static readonly Regex Regex = DeviceDataRegex(); + /// + /// Initializes a new instance of the class. + /// + public DeviceData() { } + + /// + /// Initializes a new instance of the class based on + /// data retrieved from the Android Debug Bridge. + /// + /// The data retrieved from the Android Debug Bridge that represents a device. + public DeviceData(string data) + { + Match match = Regex.Match(data); + if (!match.Success) + { + throw new ArgumentException($"Invalid device list data '{data}'"); + } + else + { + Serial = match.Groups["serial"].Value; + State = GetStateFromString(match.Groups["state"].Value); + Model = match.Groups["model"].Value; + Product = match.Groups["product"].Value; + Name = match.Groups["device"].Value; + Features = match.Groups["features"].Value; + Usb = match.Groups["usb"].Value; + TransportId = match.Groups["transport_id"].Value; + Message = match.Groups["message"].Value; + } + } + /// /// Gets or sets the device serial number. /// @@ -73,24 +104,7 @@ public partial class DeviceData /// /// The data retrieved from the Android Debug Bridge that represents a device. /// A object that represents the device. - public static DeviceData CreateFromAdbData(string data) - { - Match m = Regex.Match(data); - return m.Success - ? new DeviceData() - { - Serial = m.Groups["serial"].Value, - State = GetStateFromString(m.Groups["state"].Value), - Model = m.Groups["model"].Value, - Product = m.Groups["product"].Value, - Name = m.Groups["device"].Value, - Features = m.Groups["features"].Value, - Usb = m.Groups["usb"].Value, - TransportId = m.Groups["transport_id"].Value, - Message = m.Groups["message"].Value - } - : throw new ArgumentException($"Invalid device list data '{data}'"); - } + public static DeviceData CreateFromAdbData(string data) => new(data); /// public override string ToString() => Serial; diff --git a/AdvancedSharpAdbClient/Models/DeviceDataEventArgs.cs b/AdvancedSharpAdbClient/Models/DeviceDataEventArgs.cs index d4d74ab9..7ca8681f 100644 --- a/AdvancedSharpAdbClient/Models/DeviceDataEventArgs.cs +++ b/AdvancedSharpAdbClient/Models/DeviceDataEventArgs.cs @@ -10,82 +10,58 @@ namespace AdvancedSharpAdbClient /// /// The event arguments that are passed when a device event occurs. /// - public class DeviceDataEventArgs : EventArgs + /// The device. + public class DeviceDataEventArgs(DeviceData device) : EventArgs { - /// - /// Initializes a new instance of the class. - /// - /// The device. - public DeviceDataEventArgs(DeviceData device) => Device = device; - /// /// Gets the device where the change occurred. /// /// The device where the change occurred. - public DeviceData Device { get; } + public DeviceData Device { get; } = device; } /// /// The event arguments that are passed when a device event occurs. /// - public class DeviceDataNotifyEventArgs : EventArgs + /// The list of device. + public class DeviceDataNotifyEventArgs(IEnumerable devices) : EventArgs { - /// - /// Initializes a new instance of the class. - /// - /// The list of device. - public DeviceDataNotifyEventArgs(IEnumerable devices) => Devices = devices; - /// /// Gets the list of device where the change occurred. /// /// The list of device where the change occurred. - public IEnumerable Devices { get; } + public IEnumerable Devices { get; } = devices; } /// /// The event arguments that are passed when a device event occurs. /// - public class DeviceDataConnectEventArgs : DeviceDataEventArgs + /// The device. + /// The device after the reported change. + public class DeviceDataConnectEventArgs(DeviceData device, bool isConnect) : DeviceDataEventArgs(device) { - /// - /// Initializes a new instance of the class. - /// - /// The device. - /// The device after the reported change. - public DeviceDataConnectEventArgs(DeviceData device, bool isConnect) : base(device) => IsConnect = isConnect; - /// /// Gets the connect state of the device after the reported change. /// - public bool IsConnect { get; } + public bool IsConnect { get; } = isConnect; } /// /// The event arguments that are passed when a device event occurs. /// - public class DeviceDataChangeEventArgs : DeviceDataEventArgs + /// The device. + /// The state of the device after the reported change. + /// The state of the device before the reported change. + public class DeviceDataChangeEventArgs(DeviceData device, DeviceState newState, DeviceState oldState) : DeviceDataEventArgs(device) { - /// - /// Initializes a new instance of the class. - /// - /// The device. - /// The state of the device after the reported change. - /// The state of the device before the reported change. - public DeviceDataChangeEventArgs(DeviceData device, DeviceState newState, DeviceState oldState) : base(device) - { - NewState = newState; - OldState = oldState; - } - /// /// Gets the state of the device after the reported change. /// - public DeviceState NewState { get; } + public DeviceState NewState { get; } = newState; /// /// Gets the state of the device before the reported change. /// - public DeviceState OldState { get; } + public DeviceState OldState { get; } = oldState; } } diff --git a/AdvancedSharpAdbClient/Models/FileStatistics.cs b/AdvancedSharpAdbClient/Models/FileStatistics.cs index 6f6af668..186d65e9 100644 --- a/AdvancedSharpAdbClient/Models/FileStatistics.cs +++ b/AdvancedSharpAdbClient/Models/FileStatistics.cs @@ -11,6 +11,11 @@ namespace AdvancedSharpAdbClient /// public class FileStatistics { + /// + /// Initializes a new instance of the class. + /// + public FileStatistics() { } + /// /// Gets or sets the path of the file. /// diff --git a/AdvancedSharpAdbClient/Models/ForwardData.cs b/AdvancedSharpAdbClient/Models/ForwardData.cs index 50f57f2c..782e070b 100644 --- a/AdvancedSharpAdbClient/Models/ForwardData.cs +++ b/AdvancedSharpAdbClient/Models/ForwardData.cs @@ -9,6 +9,36 @@ namespace AdvancedSharpAdbClient /// public class ForwardData { + /// + /// Initializes a new instance of the class. + /// + public ForwardData() { } + + /// + /// Initializes a new instance of the class. + /// + /// The serial number of the device for which the port forwarding is configured. + /// The that represents the local (PC) endpoint. + /// The that represents the remote (device) endpoint. + public ForwardData(string serialNumber, string local, string remote) + { + SerialNumber = serialNumber; + Local = local; + Remote = remote; + } + + /// + /// Initializes a new instance of the class by parsing a . + /// + /// The value to parse. + public ForwardData(string value) + { + string[] parts = value.Split(' '); + SerialNumber = parts[0]; + Local = parts[1]; + Remote = parts[2]; + } + /// /// Gets or sets the serial number of the device for which the port forwarding is configured. /// @@ -22,7 +52,7 @@ public class ForwardData /// /// Gets a that represents the local (PC) endpoint. /// - public ForwardSpec LocalSpec => ForwardSpec.Parse(Local); + public ForwardSpec LocalSpec => new(Local); /// /// Gets or sets a that represents the remote (device) endpoint. @@ -32,42 +62,14 @@ public class ForwardData /// /// Gets a that represents the remote (device) endpoint. /// - public ForwardSpec RemoteSpec => ForwardSpec.Parse(Remote); - - /// - /// Initializes a new instance of the struct. - /// - public ForwardData() - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The serial number of the device for which the port forwarding is configured. - /// The that represents the local (PC) endpoint. - /// The that represents the remote (device) endpoint. - public ForwardData(string serialNumber, string local, string remote) - { - SerialNumber = serialNumber; - Local = local; - Remote = remote; - } + public ForwardSpec RemoteSpec => new(Remote); /// /// Creates a new instance of the class by parsing a . /// /// The value to parse. /// A object that represents the port forwarding information contained in . - public static ForwardData FromString(string value) - { - if (value == null) - { - return null; - } - string[] parts = value.Split(' '); - return new ForwardData(parts[0], parts[1], parts[2]); - } + public static ForwardData FromString(string value) => value == null ? null : new ForwardData(value); /// public override string ToString() => $"{SerialNumber} {Local} {Remote}"; diff --git a/AdvancedSharpAdbClient/Models/ForwardSpec.cs b/AdvancedSharpAdbClient/Models/ForwardSpec.cs index 305a69a6..03a7c568 100644 --- a/AdvancedSharpAdbClient/Models/ForwardSpec.cs +++ b/AdvancedSharpAdbClient/Models/ForwardSpec.cs @@ -19,7 +19,7 @@ public class ForwardSpec /// value, used for the representation of the /// class. /// - private static readonly Dictionary Mappings = new(StringComparer.OrdinalIgnoreCase) + private static readonly Dictionary Mappings = new(6, StringComparer.OrdinalIgnoreCase) { { "tcp", ForwardProtocol.Tcp }, { "localabstract", ForwardProtocol.LocalAbstract }, @@ -30,35 +30,15 @@ public class ForwardSpec }; /// - /// Gets or sets the protocol which is being forwarded. + /// Initializes a new instance of the class. /// - public ForwardProtocol Protocol { get; set; } + public ForwardSpec() { } /// - /// Gets or sets, when the is , the port - /// number of the port being forwarded. - /// - public int Port { get; set; } - - /// - /// Gets or sets, when the is , - /// or , - /// the Unix domain socket name of the socket being forwarded. - /// - public string SocketName { get; set; } - - /// - /// Gets or sets, when the is , - /// the process id of the process to which the debugger is attached. - /// - public int ProcessId { get; set; } - - /// - /// Creates a from its representation. + /// Initializes a new instance of the class from its representation. /// /// A which represents a . - /// A which represents . - public static ForwardSpec Parse(string spec) + public ForwardSpec(string spec) { ExceptionExtensions.ThrowIfNull(spec); @@ -75,12 +55,7 @@ public static ForwardSpec Parse(string spec) } ForwardProtocol protocol = Mappings[parts[0]]; - - ForwardSpec value = new() - { - Protocol = protocol - }; - + Protocol = protocol; bool isInt = int.TryParse(parts[1], out int intValue); @@ -92,7 +67,7 @@ public static ForwardSpec Parse(string spec) throw new ArgumentOutOfRangeException(nameof(spec)); } - value.ProcessId = intValue; + ProcessId = intValue; break; case ForwardProtocol.Tcp: @@ -101,20 +76,49 @@ public static ForwardSpec Parse(string spec) throw new ArgumentOutOfRangeException(nameof(spec)); } - value.Port = intValue; + Port = intValue; break; case ForwardProtocol.LocalAbstract or ForwardProtocol.LocalFilesystem or ForwardProtocol.LocalReserved or ForwardProtocol.Device: - value.SocketName = parts[1]; + SocketName = parts[1]; break; } - - return value; } + /// + /// Gets or sets the protocol which is being forwarded. + /// + public ForwardProtocol Protocol { get; set; } + + /// + /// Gets or sets, when the is , the port + /// number of the port being forwarded. + /// + public int Port { get; set; } + + /// + /// Gets or sets, when the is , + /// or , + /// the Unix domain socket name of the socket being forwarded. + /// + public string SocketName { get; set; } + + /// + /// Gets or sets, when the is , + /// the process id of the process to which the debugger is attached. + /// + public int ProcessId { get; set; } + + /// + /// Creates a from its representation. + /// + /// A which represents a . + /// A which represents . + public static ForwardSpec Parse(string spec) => new(spec); + /// public override string ToString() { diff --git a/AdvancedSharpAdbClient/Models/Framebuffer.cs b/AdvancedSharpAdbClient/Models/Framebuffer.cs index 6093e46a..8657afb1 100644 --- a/AdvancedSharpAdbClient/Models/Framebuffer.cs +++ b/AdvancedSharpAdbClient/Models/Framebuffer.cs @@ -100,7 +100,7 @@ public virtual void Refresh(bool reset = false) if (reset || !headerInitialized) { - Header = FramebufferHeader.Read(headerData); + Header = new FramebufferHeader(headerData); headerInitialized = true; } @@ -148,7 +148,7 @@ public virtual async Task RefreshAsync(bool reset = false, CancellationToken can if (reset || !headerInitialized) { - Header = FramebufferHeader.Read(headerData); + Header = new FramebufferHeader(headerData); headerInitialized = true; } diff --git a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs index dabb4cbc..fc081f4e 100644 --- a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs +++ b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs @@ -18,6 +18,65 @@ namespace AdvancedSharpAdbClient /// public struct FramebufferHeader { + /// + /// Initializes a new instance of the struct based on a byte array which contains the data. + /// + /// The data that feeds the struct. + /// As defined in + public FramebufferHeader(byte[] data) + { + // Read the data from a MemoryStream so we can use the BinaryReader to process the data. + using MemoryStream stream = new(data); + using BinaryReader reader = new(stream, Encoding.ASCII +#if !NETFRAMEWORK || NET45_OR_GREATER + , leaveOpen: true +#endif + ); + Version = reader.ReadUInt32(); + + if (Version > 2) + { + // Technically, 0 is not a supported version either; we assume version 0 indicates + // an empty framebuffer. + throw new InvalidOperationException($"Framebuffer version {Version} is not supported"); + } + + Bpp = reader.ReadUInt32(); + + if (Version >= 2) + { + ColorSpace = reader.ReadUInt32(); + } + + Size = reader.ReadUInt32(); + Width = reader.ReadUInt32(); + Height = reader.ReadUInt32(); + + Red = new ColorData + { + Offset = reader.ReadUInt32(), + Length = reader.ReadUInt32() + }; + + Blue = new ColorData + { + Offset = reader.ReadUInt32(), + Length = reader.ReadUInt32() + }; + + Green = new ColorData + { + Offset = reader.ReadUInt32(), + Length = reader.ReadUInt32() + }; + + Alpha = new ColorData + { + Offset = reader.ReadUInt32(), + Length = reader.ReadUInt32() + }; + } + /// /// Gets or sets the version of the framebuffer struct. /// @@ -73,64 +132,7 @@ public struct FramebufferHeader /// /// The data that feeds the struct. /// A new object. - public static FramebufferHeader Read(byte[] data) - { - // as defined in https://android.googlesource.com/platform/system/core/+/master/adb/framebuffer_service.cpp - FramebufferHeader header = default; - - // Read the data from a MemoryStream so we can use the BinaryReader to process the data. - using MemoryStream stream = new(data); - using BinaryReader reader = new(stream, Encoding.ASCII -#if !NETFRAMEWORK || NET45_OR_GREATER - , leaveOpen: true -#endif - ); - header.Version = reader.ReadUInt32(); - - if (header.Version > 2) - { - // Technically, 0 is not a supported version either; we assume version 0 indicates - // an empty framebuffer. - throw new InvalidOperationException($"Framebuffer version {header.Version} is not supported"); - } - - header.Bpp = reader.ReadUInt32(); - - if (header.Version >= 2) - { - header.ColorSpace = reader.ReadUInt32(); - } - - header.Size = reader.ReadUInt32(); - header.Width = reader.ReadUInt32(); - header.Height = reader.ReadUInt32(); - - header.Red = new ColorData - { - Offset = reader.ReadUInt32(), - Length = reader.ReadUInt32() - }; - - header.Blue = new ColorData - { - Offset = reader.ReadUInt32(), - Length = reader.ReadUInt32() - }; - - header.Green = new ColorData - { - Offset = reader.ReadUInt32(), - Length = reader.ReadUInt32() - }; - - header.Alpha = new ColorData - { - Offset = reader.ReadUInt32(), - Length = reader.ReadUInt32() - }; - - return header; - } + public static FramebufferHeader Read(byte[] data) => new(data); #if HAS_DRAWING /// diff --git a/AdvancedSharpAdbClient/Models/SyncProgressChangedEventArgs.cs b/AdvancedSharpAdbClient/Models/SyncProgressChangedEventArgs.cs index 38cc3e7c..d054258b 100644 --- a/AdvancedSharpAdbClient/Models/SyncProgressChangedEventArgs.cs +++ b/AdvancedSharpAdbClient/Models/SyncProgressChangedEventArgs.cs @@ -9,29 +9,23 @@ namespace AdvancedSharpAdbClient /// /// Provides data for the event. /// - public class SyncProgressChangedEventArgs : EventArgs + public class SyncProgressChangedEventArgs(long received, long total) : EventArgs { - /// - /// Gets the number of progress percentage for the sync operation. - /// - public double ProgressPercentage => TotalBytesToReceive != 0L ? ReceivedBytesSize * 100.0 / TotalBytesToReceive : 0.0; - /// /// Gets the number of bytes sync to the local computer. /// /// An representing the number of sync bytes. - public long ReceivedBytesSize { get; } + public long ReceivedBytesSize { get; } = received; /// /// Gets the total number of bytes for the sync operation. /// /// An representing the total size of the download, in bytes. - public long TotalBytesToReceive { get; } + public long TotalBytesToReceive { get; } = total; - internal SyncProgressChangedEventArgs(long received, long total) - { - ReceivedBytesSize = received; - TotalBytesToReceive = total; - } + /// + /// Gets the number of progress percentage for the sync operation. + /// + public double ProgressPercentage => TotalBytesToReceive != 0L ? ReceivedBytesSize * 100.0 / TotalBytesToReceive : 0.0; } } diff --git a/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs b/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs index 5ad3702f..69265c31 100644 --- a/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs +++ b/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs @@ -132,13 +132,11 @@ protected override void ProcessNewLines(IEnumerable lines) { foreach (string line in lines) { - if (string.IsNullOrEmpty(line) || line.StartsWith("#") || line.StartsWith("$")) + if (string.IsNullOrEmpty(line) || line.StartsWith('#') || line.StartsWith('$')) { continue; } - output.AppendLine(line); - #if HAS_LOGGER logger.LogDebug(line); #endif From f039ff62b233489fb73bf8447e7dfa6ba48cbdf1 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Fri, 22 Sep 2023 22:11:22 +0800 Subject: [PATCH 04/66] Update build-and-test.yml --- .github/workflows/build-and-test.yml | 50 +++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index bfd7d215..38113b28 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -1,13 +1,20 @@ name: build and test -on: [push, pull_request] +on: + push: + branches: + - main + - 'version/**' + pull_request: + branches: + - main + workflow_dispatch: env: DOTNET_VERSION: '8.0.x' # The .NET SDK version to use jobs: build-and-test: - name: build-and-test-${{matrix.os}} runs-on: ${{matrix.os}} strategy: @@ -15,7 +22,11 @@ jobs: os: [windows-latest, ubuntu-latest, macos-latest] steps: - - uses: actions/checkout@v3 + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Setup .NET Core App uses: actions/setup-dotnet@v3 with: @@ -24,9 +35,38 @@ jobs: - name: Install dependencies run: dotnet restore -p:FullTargets=false - + - name: Build run: dotnet build --no-restore -p:FullTargets=false - + - name: Test run: dotnet test --no-restore -p:FullTargets=false + + pack-and-upload: + name: pack-and-upload + needs: build-and-test + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Setup .NET Core App + uses: actions/setup-dotnet@v3 + with: + dotnet-version: ${{env.DOTNET_VERSION}} + dotnet-quality: 'preview' + + - name: Install dependencies + run: dotnet restore -p:FullTargets=false + + - name: Pack + run: dotnet pack --no-restore -o "nugets" -p:FullTargets=false + + - name: Upload + uses: actions/upload-artifact@v3 + with: + name: Nuget Package + path: nugets/** From 564a6e2aba7de6e0c4f12b4b7ac19be9784444bc Mon Sep 17 00:00:00 2001 From: wherewhere Date: Tue, 26 Sep 2023 18:12:00 +0800 Subject: [PATCH 05/66] Update array initialize --- .../PackageManagerTests.Async.cs | 12 ++++----- .../DeviceCommands/PackageManagerTests.cs | 12 ++++----- .../DeviceMonitorTests.cs | 2 +- .../Extensions/SyncCommandConverterTests.cs | 4 +-- .../Models/FramebufferHeaderTests.cs | 2 +- .../SyncServiceTests.Async.cs | 25 ++++++++----------- .../SyncServiceTests.cs | 11 ++++---- .../Exceptions/JavaException.cs | 4 ++- .../Extensions/CrossPlatformFunc.cs | 10 +++++--- .../Extensions/Utilities.cs | 14 +++++++++++ AdvancedSharpAdbClient/Models/ForwardSpec.cs | 2 +- 11 files changed, 55 insertions(+), 43 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs index 381b856a..9aa6744a 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs @@ -85,7 +85,7 @@ public async void InstallMultipleRemotePackageAsyncTest() }; PackageManager manager = new(adbClient, device); - await manager.InstallMultipleRemotePackageAsync("/data/base.apk", new string[] { "/data/split-dpi.apk", "/data/split-abi.apk" }, false); + await manager.InstallMultipleRemotePackageAsync("/data/base.apk", ["/data/split-dpi.apk", "/data/split-abi.apk"], false); Assert.Equal(6, adbClient.ReceivedCommands.Count); Assert.Equal("pm install-create", adbClient.ReceivedCommands[1]); @@ -94,7 +94,7 @@ public async void InstallMultipleRemotePackageAsyncTest() Assert.Equal("pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[4]); Assert.Equal("pm install-commit 936013062", adbClient.ReceivedCommands[5]); - await manager.InstallMultipleRemotePackageAsync("/data/base.apk", new string[] { "/data/split-dpi.apk", "/data/split-abi.apk" }, true); + await manager.InstallMultipleRemotePackageAsync("/data/base.apk", ["/data/split-dpi.apk", "/data/split-abi.apk"], true); Assert.Equal(11, adbClient.ReceivedCommands.Count); Assert.Equal("pm install-create -r", adbClient.ReceivedCommands[6]); @@ -103,7 +103,7 @@ public async void InstallMultipleRemotePackageAsyncTest() Assert.Equal("pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[9]); Assert.Equal("pm install-commit 936013062", adbClient.ReceivedCommands[10]); - await manager.InstallMultipleRemotePackageAsync(new string[] { "/data/split-dpi.apk", "/data/split-abi.apk" }, "com.google.android.gms", false); + await manager.InstallMultipleRemotePackageAsync(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms", false); Assert.Equal(15, adbClient.ReceivedCommands.Count); Assert.Equal("pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[11]); @@ -111,7 +111,7 @@ public async void InstallMultipleRemotePackageAsyncTest() Assert.Equal("pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[13]); Assert.Equal("pm install-commit 936013062", adbClient.ReceivedCommands[14]); - await manager.InstallMultipleRemotePackageAsync(new string[] { "/data/split-dpi.apk", "/data/split-abi.apk" }, "com.google.android.gms", true); + await manager.InstallMultipleRemotePackageAsync(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms", true); Assert.Equal(19, adbClient.ReceivedCommands.Count); Assert.Equal("pm install-create -r -p com.google.android.gms", adbClient.ReceivedCommands[15]); @@ -148,7 +148,7 @@ public async void InstallMultiplePackageAsyncTest() }; PackageManager manager = new(adbClient, device); - await manager.InstallMultiplePackageAsync("Assets/test.txt", new string[] { "Assets/gapps.txt", "Assets/logcat.bin" }, false); + await manager.InstallMultiplePackageAsync("Assets/test.txt", ["Assets/gapps.txt", "Assets/logcat.bin"], false); Assert.Equal(9, adbClient.ReceivedCommands.Count); Assert.Equal("pm install-create", adbClient.ReceivedCommands[1]); Assert.Equal("pm install-write 936013062 base.apk \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[2]); @@ -165,7 +165,7 @@ public async void InstallMultiplePackageAsyncTest() Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/logcat.bin")); syncService.UploadedFiles.Clear(); - await manager.InstallMultiplePackageAsync(new string[] { "Assets/gapps.txt", "Assets/logcat.bin" }, "com.google.android.gms", false); + await manager.InstallMultiplePackageAsync(["Assets/gapps.txt", "Assets/logcat.bin"], "com.google.android.gms", false); Assert.Equal(15, adbClient.ReceivedCommands.Count); Assert.Equal("pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[9]); Assert.Equal("pm install-write 936013062 splitapp0.apk \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[10]); diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs index 3710528d..bbefd19d 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs @@ -114,7 +114,7 @@ public void InstallMultipleRemotePackageTest() }; PackageManager manager = new(adbClient, device); - manager.InstallMultipleRemotePackage("/data/base.apk", new string[] { "/data/split-dpi.apk", "/data/split-abi.apk" }, false); + manager.InstallMultipleRemotePackage("/data/base.apk", ["/data/split-dpi.apk", "/data/split-abi.apk"], false); Assert.Equal(6, adbClient.ReceivedCommands.Count); Assert.Equal("pm install-create", adbClient.ReceivedCommands[1]); @@ -123,7 +123,7 @@ public void InstallMultipleRemotePackageTest() Assert.Equal("pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[4]); Assert.Equal("pm install-commit 936013062", adbClient.ReceivedCommands[5]); - manager.InstallMultipleRemotePackage("/data/base.apk", new string[] { "/data/split-dpi.apk", "/data/split-abi.apk" }, true); + manager.InstallMultipleRemotePackage("/data/base.apk", ["/data/split-dpi.apk", "/data/split-abi.apk"], true); Assert.Equal(11, adbClient.ReceivedCommands.Count); Assert.Equal("pm install-create -r", adbClient.ReceivedCommands[6]); @@ -132,7 +132,7 @@ public void InstallMultipleRemotePackageTest() Assert.Equal("pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[9]); Assert.Equal("pm install-commit 936013062", adbClient.ReceivedCommands[10]); - manager.InstallMultipleRemotePackage(new string[] { "/data/split-dpi.apk", "/data/split-abi.apk" }, "com.google.android.gms", false); + manager.InstallMultipleRemotePackage(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms", false); Assert.Equal(15, adbClient.ReceivedCommands.Count); Assert.Equal("pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[11]); @@ -140,7 +140,7 @@ public void InstallMultipleRemotePackageTest() Assert.Equal("pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[13]); Assert.Equal("pm install-commit 936013062", adbClient.ReceivedCommands[14]); - manager.InstallMultipleRemotePackage(new string[] { "/data/split-dpi.apk", "/data/split-abi.apk" }, "com.google.android.gms", true); + manager.InstallMultipleRemotePackage(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms", true); Assert.Equal(19, adbClient.ReceivedCommands.Count); Assert.Equal("pm install-create -r -p com.google.android.gms", adbClient.ReceivedCommands[15]); @@ -177,7 +177,7 @@ public void InstallMultiplePackageTest() }; PackageManager manager = new(adbClient, device); - manager.InstallMultiplePackage("Assets/test.txt", new string[] { "Assets/gapps.txt", "Assets/logcat.bin" }, false); + manager.InstallMultiplePackage("Assets/test.txt", ["Assets/gapps.txt", "Assets/logcat.bin"], false); Assert.Equal(9, adbClient.ReceivedCommands.Count); Assert.Equal("pm install-create", adbClient.ReceivedCommands[1]); Assert.Equal("pm install-write 936013062 base.apk \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[2]); @@ -194,7 +194,7 @@ public void InstallMultiplePackageTest() Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/logcat.bin")); syncService.UploadedFiles.Clear(); - manager.InstallMultiplePackage(new string[] { "Assets/gapps.txt", "Assets/logcat.bin" }, "com.google.android.gms", false); + manager.InstallMultiplePackage(["Assets/gapps.txt", "Assets/logcat.bin"], "com.google.android.gms", false); Assert.Equal(15, adbClient.ReceivedCommands.Count); Assert.Equal("pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[9]); Assert.Equal("pm install-write 936013062 splitapp0.apk \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[10]); diff --git a/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs b/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs index 01c76b47..67576303 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs @@ -280,7 +280,7 @@ public void AdbKilledTest() using DeviceMonitor monitor = new(Socket); RunTest( - new AdbResponse[] { AdbResponse.OK, AdbResponse.OK }, + [AdbResponse.OK, AdbResponse.OK], ResponseMessages( DummyAdbSocket.ServerDisconnected, string.Empty), diff --git a/AdvancedSharpAdbClient.Tests/Extensions/SyncCommandConverterTests.cs b/AdvancedSharpAdbClient.Tests/Extensions/SyncCommandConverterTests.cs index e0e65c15..1840e85d 100644 --- a/AdvancedSharpAdbClient.Tests/Extensions/SyncCommandConverterTests.cs +++ b/AdvancedSharpAdbClient.Tests/Extensions/SyncCommandConverterTests.cs @@ -14,11 +14,11 @@ public void GetCommandNullTest() => [Fact] public void GetCommandInvalidNumberOfBytesTest() => - _ = Assert.Throws(() => SyncCommandConverter.GetCommand(new byte[] { })); + _ = Assert.Throws(() => SyncCommandConverter.GetCommand([])); [Fact] public void GetCommandInvalidCommandTest() => - _ = Assert.Throws(() => SyncCommandConverter.GetCommand(new byte[] { (byte)'Q', (byte)'M', (byte)'T', (byte)'V' })); + _ = Assert.Throws(() => SyncCommandConverter.GetCommand("QMTV"u8.ToArray())); [Fact] public void GetBytesInvalidCommandTest() => diff --git a/AdvancedSharpAdbClient.Tests/Models/FramebufferHeaderTests.cs b/AdvancedSharpAdbClient.Tests/Models/FramebufferHeaderTests.cs index b407cedb..2992625a 100644 --- a/AdvancedSharpAdbClient.Tests/Models/FramebufferHeaderTests.cs +++ b/AdvancedSharpAdbClient.Tests/Models/FramebufferHeaderTests.cs @@ -91,7 +91,7 @@ public void ToImageEmptyTest() byte[] data = File.ReadAllBytes("Assets/framebufferheader-empty.bin"); FramebufferHeader header = FramebufferHeader.Read(data); - byte[] framebuffer = Array.Empty(); + byte[] framebuffer = []; Bitmap image = header.ToImage(framebuffer); Assert.Null(image); diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs index 1337dc61..29533b22 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs @@ -25,8 +25,8 @@ await RunTestAsync( NoResponseMessages, Requests("host:transport:169.254.109.177:5555", "sync:"), SyncRequests(SyncCommand.STAT, "/fstab.donatello"), - new[] { SyncCommand.STAT }, - new byte[][] { [160, 129, 0, 0, 85, 2, 0, 0, 0, 0, 0, 0] }, + [SyncCommand.STAT], + [[160, 129, 0, 0, 85, 2, 0, 0, 0, 0, 0, 0]], null, async () => { @@ -56,14 +56,13 @@ await RunTestAsync( ResponseMessages(".", "..", "sdcard0", "emulated"), Requests("host:transport:169.254.109.177:5555", "sync:"), SyncRequests(SyncCommand.LIST, "/storage"), - new[] { SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DONE }, - new byte[][] - { + [SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DONE], + [ [233, 65, 0, 0, 0, 0, 0, 0, 152, 130, 56, 86], [237, 65, 0, 0, 0, 0, 0, 0, 152, 130, 56, 86], [255, 161, 0, 0, 24, 0, 0, 0, 152, 130, 56, 86], [109, 65, 0, 0, 0, 0, 0, 0, 152, 130, 56, 86] - }, + ], null, async () => { @@ -118,13 +117,12 @@ await RunTestAsync( ResponseMessages(), Requests("host:transport:169.254.109.177:5555", "sync:"), SyncRequests(SyncCommand.STAT, "/fstab.donatello").Union(SyncRequests(SyncCommand.RECV, "/fstab.donatello")), - new SyncCommand[] { SyncCommand.STAT, SyncCommand.DATA, SyncCommand.DONE }, - new byte[][] - { + [SyncCommand.STAT, SyncCommand.DATA, SyncCommand.DONE], + [ [160, 129, 0, 0, 85, 2, 0, 0, 0, 0, 0, 0], contentLength, content - }, + ], null, async () => { @@ -161,12 +159,9 @@ await RunTestAsync( SyncRequests( SyncCommand.SEND, "/sdcard/test,644", SyncCommand.DONE, "1446505200"), - new[] { SyncCommand.OKAY }, + [SyncCommand.OKAY], null, - new byte[][] - { - [.. contentMessage] - }, + [[.. contentMessage]], async () => { using SyncService service = new(Socket, device); diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs index 56582c2f..b2d0c376 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs @@ -36,8 +36,8 @@ public void StatTest() NoResponseMessages, Requests("host:transport:169.254.109.177:5555", "sync:"), SyncRequests(SyncCommand.STAT, "/fstab.donatello"), - new[] { SyncCommand.STAT }, - new byte[][] { [160, 129, 0, 0, 85, 2, 0, 0, 0, 0, 0, 0] }, + [SyncCommand.STAT], + [[160, 129, 0, 0, 85, 2, 0, 0, 0, 0, 0, 0]], null, () => { @@ -67,14 +67,13 @@ public void GetListingTest() ResponseMessages(".", "..", "sdcard0", "emulated"), Requests("host:transport:169.254.109.177:5555", "sync:"), SyncRequests(SyncCommand.LIST, "/storage"), - new[] { SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DONE }, - new byte[][] - { + [SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DONE], + [ [233, 65, 0, 0, 0, 0, 0, 0, 152, 130, 56, 86], [237, 65, 0, 0, 0, 0, 0, 0, 152, 130, 56, 86], [255, 161, 0, 0, 24, 0, 0, 0, 152, 130, 56, 86], [109, 65, 0, 0, 0, 0, 0, 0, 152, 130, 56, 86] - }, + ], null, () => { diff --git a/AdvancedSharpAdbClient/Exceptions/JavaException.cs b/AdvancedSharpAdbClient/Exceptions/JavaException.cs index aae34213..91ec24a8 100644 --- a/AdvancedSharpAdbClient/Exceptions/JavaException.cs +++ b/AdvancedSharpAdbClient/Exceptions/JavaException.cs @@ -15,6 +15,8 @@ public partial class JavaException : Exception private const string ExceptionOutput = "java.lang."; private const string ExceptionPattern = @"java.lang.(\w+Exception):\s+(.*)?"; + private static readonly char[] separator = ['\r', '\n']; + /// /// Gets the name of Java exception. /// @@ -80,7 +82,7 @@ public JavaException(string name, string message, string stackTrace, Exception i /// /// A which represents a . /// The equivalent . - public static JavaException Parse(string line) => Parse(line.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries)); + public static JavaException Parse(string line) => Parse(line.Split(separator, StringSplitOptions.RemoveEmptyEntries)); /// diff --git a/AdvancedSharpAdbClient/Extensions/CrossPlatformFunc.cs b/AdvancedSharpAdbClient/Extensions/CrossPlatformFunc.cs index af5c80a6..e21642dd 100644 --- a/AdvancedSharpAdbClient/Extensions/CrossPlatformFunc.cs +++ b/AdvancedSharpAdbClient/Extensions/CrossPlatformFunc.cs @@ -15,6 +15,8 @@ namespace AdvancedSharpAdbClient /// public static class CrossPlatformFunc { + private static readonly char[] separator = ['\r', '\n']; + /// /// Determines whether the specified file exists. /// @@ -40,9 +42,9 @@ public static class CrossPlatformFunc string standardErrorString = process.StandardError.ReadToEnd(); string standardOutputString = process.StandardOutput.ReadToEnd(); - errorOutput?.AddRange(standardErrorString.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)); + errorOutput?.AddRange(standardErrorString.Split(separator, StringSplitOptions.RemoveEmptyEntries)); - standardOutput?.AddRange(standardOutputString.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)); + standardOutput?.AddRange(standardOutputString.Split(separator, StringSplitOptions.RemoveEmptyEntries)); // get the return code from the process if (!process.WaitForExit(5000)) @@ -89,9 +91,9 @@ public static class CrossPlatformFunc string standardErrorString = await process.StandardError.ReadToEndAsync(cancellationToken).ConfigureAwait(false); string standardOutputString = await process.StandardOutput.ReadToEndAsync(cancellationToken).ConfigureAwait(false); - errorOutput?.AddRange(standardErrorString.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)); + errorOutput?.AddRange(standardErrorString.Split(separator, StringSplitOptions.RemoveEmptyEntries)); - standardOutput?.AddRange(standardOutputString.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)); + standardOutput?.AddRange(standardOutputString.Split(separator, StringSplitOptions.RemoveEmptyEntries)); #if NET5_0_OR_GREATER using (CancellationTokenSource completionSource = new(TimeSpan.FromMilliseconds(5000))) diff --git a/AdvancedSharpAdbClient/Extensions/Utilities.cs b/AdvancedSharpAdbClient/Extensions/Utilities.cs index 2d8fd9e0..9c878119 100644 --- a/AdvancedSharpAdbClient/Extensions/Utilities.cs +++ b/AdvancedSharpAdbClient/Extensions/Utilities.cs @@ -106,6 +106,20 @@ public static bool Contains(this string text, string value, StringComparison com public static string[] Split(this string text, char separator, StringSplitOptions options = StringSplitOptions.None) => text.Split(new[] { separator }, options); + /// + /// Splits a string into a maximum number of substrings based on a specified delimiting + /// character and, optionally, options. Splits a string into a maximum number of + /// substrings based on the provided character separator, optionally omitting empty + /// substrings from the result. + /// + /// The string to split. + /// A character that delimits the substrings in this string. + /// The maximum number of elements expected in the array. + /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings. + /// An array that contains at most count substrings from this instance that are delimited by . + public static string[] Split(this string text, char separator, int count, StringSplitOptions options = StringSplitOptions.None) => + text.Split(new[] { separator }, count, options); + /// /// Determines whether this string instance starts with the specified character. /// diff --git a/AdvancedSharpAdbClient/Models/ForwardSpec.cs b/AdvancedSharpAdbClient/Models/ForwardSpec.cs index 03a7c568..a1306adb 100644 --- a/AdvancedSharpAdbClient/Models/ForwardSpec.cs +++ b/AdvancedSharpAdbClient/Models/ForwardSpec.cs @@ -42,7 +42,7 @@ public ForwardSpec(string spec) { ExceptionExtensions.ThrowIfNull(spec); - string[] parts = spec.Split(new char[] { ':' }, 2, StringSplitOptions.RemoveEmptyEntries); + string[] parts = spec.Split(':', 2, StringSplitOptions.RemoveEmptyEntries); if (parts.Length != 2) { From f81b8555e3de00c0ffe4b260505b4179d45677f5 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Wed, 27 Sep 2023 16:00:09 +0800 Subject: [PATCH 06/66] Move ShellStream out of logs folder --- .../{Logs => Models}/ShellStreamTests.cs | 2 +- AdvancedSharpAdbClient/{Logs => Models}/ShellStream.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) rename AdvancedSharpAdbClient.Tests/{Logs => Models}/ShellStreamTests.cs (99%) rename AdvancedSharpAdbClient/{Logs => Models}/ShellStream.cs (98%) diff --git a/AdvancedSharpAdbClient.Tests/Logs/ShellStreamTests.cs b/AdvancedSharpAdbClient.Tests/Models/ShellStreamTests.cs similarity index 99% rename from AdvancedSharpAdbClient.Tests/Logs/ShellStreamTests.cs rename to AdvancedSharpAdbClient.Tests/Models/ShellStreamTests.cs index 13a2bff3..6556db21 100644 --- a/AdvancedSharpAdbClient.Tests/Logs/ShellStreamTests.cs +++ b/AdvancedSharpAdbClient.Tests/Models/ShellStreamTests.cs @@ -3,7 +3,7 @@ using System.Text; using Xunit; -namespace AdvancedSharpAdbClient.Logs.Tests +namespace AdvancedSharpAdbClient.Tests { public class ShellStreamTests { diff --git a/AdvancedSharpAdbClient/Logs/ShellStream.cs b/AdvancedSharpAdbClient/Models/ShellStream.cs similarity index 98% rename from AdvancedSharpAdbClient/Logs/ShellStream.cs rename to AdvancedSharpAdbClient/Models/ShellStream.cs index 3b86044d..664c837c 100644 --- a/AdvancedSharpAdbClient/Logs/ShellStream.cs +++ b/AdvancedSharpAdbClient/Models/ShellStream.cs @@ -8,7 +8,7 @@ using System.Runtime.InteropServices; using System.Threading; -namespace AdvancedSharpAdbClient.Logs +namespace AdvancedSharpAdbClient { /// Represents a that wraps around an inner that contains /// output from an Android shell command. In the shell output, the LF character is replaced by a @@ -114,8 +114,8 @@ public override int Read(byte[] buffer, int offset, int count) continue; } - byte[] minibuffer = new byte[1]; - int miniRead = Inner.Read(minibuffer, 0, 1); + byte[] miniBuffer = new byte[1]; + int miniRead = Inner.Read(miniBuffer, 0, 1); if (miniRead == 0) { @@ -126,7 +126,7 @@ public override int Read(byte[] buffer, int offset, int count) else { // Append the byte to the buffer. - buffer[offset + read - 1] = minibuffer[0]; + buffer[offset + read - 1] = miniBuffer[0]; } } } From 09d57c4585583b7bc74eaddaf047370cd531c029 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Wed, 27 Sep 2023 17:02:52 +0800 Subject: [PATCH 07/66] Replace Microsoft.Extensions.Logging with custom ILogger --- .../AdbCommandLineClient.Async.cs | 2 - .../AdbCommandLineClient.cs | 21 +-- AdvancedSharpAdbClient/AdbSocket.Async.cs | 16 -- AdvancedSharpAdbClient/AdbSocket.cs | 59 +------ .../AdvancedSharpAdbClient.csproj | 126 ++------------- .../DeviceCommands/PackageManager.Async.cs | 15 +- .../DeviceCommands/PackageManager.cs | 34 +--- AdvancedSharpAdbClient/DeviceMonitor.Async.cs | 17 -- AdvancedSharpAdbClient/DeviceMonitor.cs | 19 +-- .../Extensions/LoggerExtensions.cs | 149 +++++++++++------- AdvancedSharpAdbClient/Logs/Enums/LogLevel.cs | 52 ++++++ .../Logs/Interfaces/ILogger.cs | 35 ++++ .../Logs/Interfaces/ILoggerFactory.cs | 28 ++++ AdvancedSharpAdbClient/Logs/LoggerProvider.cs | 34 ++++ AdvancedSharpAdbClient/Logs/NullLogger.cs | 34 ++++ AdvancedSharpAdbClient/Logs/NullLoggerT.cs | 55 ------- .../Properties/GlobalUsings.cs | 5 - .../Receivers/ConsoleOutputReceiver.cs | 43 +---- 18 files changed, 309 insertions(+), 435 deletions(-) create mode 100644 AdvancedSharpAdbClient/Logs/Enums/LogLevel.cs create mode 100644 AdvancedSharpAdbClient/Logs/Interfaces/ILogger.cs create mode 100644 AdvancedSharpAdbClient/Logs/Interfaces/ILoggerFactory.cs create mode 100644 AdvancedSharpAdbClient/Logs/LoggerProvider.cs create mode 100644 AdvancedSharpAdbClient/Logs/NullLogger.cs delete mode 100644 AdvancedSharpAdbClient/Logs/NullLoggerT.cs diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs index 609b68c3..b6ed11cf 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs @@ -32,9 +32,7 @@ public virtual async Task GetVersionAsync(CancellationToken cancellatio if (version < AdbServer.RequiredAdbVersion) { AdbException ex = new($"Required minimum version of adb: {AdbServer.RequiredAdbVersion}. Current version is {version}"); -#if HAS_LOGGER logger.LogError(ex, ex.Message); -#endif throw ex; } diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.cs index 5a8b8ed7..4aea2e19 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.cs @@ -3,6 +3,7 @@ // using AdvancedSharpAdbClient.Exceptions; +using AdvancedSharpAdbClient.Logs; using System; using System.Collections.Generic; using System.ComponentModel; @@ -22,27 +23,18 @@ public partial class AdbCommandLineClient : IAdbCommandLineClient /// protected const string AdbVersionPattern = "^.*(\\d+)\\.(\\d+)\\.(\\d+)$"; -#if HAS_LOGGER /// /// The logger to use when logging messages. /// protected readonly ILogger logger; -#endif -#if !HAS_LOGGER -#pragma warning disable CS1572 // XML 注释中有 param 标记,但是没有该名称的参数 -#endif /// /// Initializes a new instance of the class. /// /// The path to the adb.exe executable. /// Don't check adb file name when . /// The logger to use when logging. - public AdbCommandLineClient(string adbPath, bool isForce = false -#if HAS_LOGGER - , ILogger logger = null -#endif - ) + public AdbCommandLineClient(string adbPath, bool isForce = false, ILogger logger = null) { if (adbPath.IsNullOrWhiteSpace()) { @@ -77,13 +69,8 @@ public AdbCommandLineClient(string adbPath, bool isForce = false this.EnsureIsValidAdbFile(adbPath); AdbPath = adbPath; -#if HAS_LOGGER - this.logger = logger ?? NullLogger.Instance; -#endif + this.logger = logger ?? LoggerProvider.CreateLogger(); } -#if !HAS_LOGGER -#pragma warning restore CS1572 // XML 注释中有 param 标记,但是没有该名称的参数 -#endif /// /// Gets the path to the adb.exe executable. @@ -107,9 +94,7 @@ public virtual Version GetVersion() if (version < AdbServer.RequiredAdbVersion) { AdbException ex = new($"Required minimum version of adb: {AdbServer.RequiredAdbVersion}. Current version is {version}"); -#if HAS_LOGGER logger.LogError(ex, ex.Message); -#endif throw ex; } diff --git a/AdvancedSharpAdbClient/AdbSocket.Async.cs b/AdvancedSharpAdbClient/AdbSocket.Async.cs index 34559e0b..8cc83f7e 100644 --- a/AdvancedSharpAdbClient/AdbSocket.Async.cs +++ b/AdvancedSharpAdbClient/AdbSocket.Async.cs @@ -28,14 +28,9 @@ public virtual async Task SendAsync(byte[] data, int offset, int length, Cancell throw new AdbException("channel EOF"); } } -#if HAS_LOGGER catch (SocketException ex) { logger.LogError(ex, ex.Message); -#else - catch (SocketException) - { -#endif throw; } } @@ -115,16 +110,12 @@ public virtual async Task ReadAsync(byte[] data, int length, CancellationTo if (count < 0) { -#if HAS_LOGGER logger.LogError("read: channel EOF"); -#endif throw new AdbException("EOF"); } else if (count == 0) { -#if HAS_LOGGER logger.LogInformation("DONE with Read"); -#endif } else { @@ -244,14 +235,9 @@ protected virtual async Task WriteAsync(byte[] data, CancellationToken can { await SendAsync(data, -1, cancellationToken); } -#if HAS_LOGGER catch (IOException e) { logger.LogError(e, e.Message); -#else - catch (IOException) - { -#endif return false; } @@ -278,9 +264,7 @@ protected virtual async Task ReadAdbResponseInnerAsync(Cancellation { string message = await ReadStringAsync(cancellationToken); rasps.Message = message; -#if HAS_LOGGER logger.LogError($"Got reply '{ReplyToString(reply)}', diag='{rasps.Message}'"); -#endif } return rasps; diff --git a/AdvancedSharpAdbClient/AdbSocket.cs b/AdvancedSharpAdbClient/AdbSocket.cs index ccb296bd..19e66e68 100644 --- a/AdvancedSharpAdbClient/AdbSocket.cs +++ b/AdvancedSharpAdbClient/AdbSocket.cs @@ -31,33 +31,22 @@ public partial class AdbSocket : IAdbSocket /// protected readonly ITcpSocket socket; -#if HAS_LOGGER /// /// The logger to use when logging messages. /// protected readonly ILogger logger; -#endif -#if !HAS_LOGGER -#pragma warning disable CS1572 // XML 注释中有 param 标记,但是没有该名称的参数 -#endif /// /// Initializes a new instance of the class. /// /// The at which the Android Debug Bridge is listening for clients. /// The logger to use when logging. - public AdbSocket(EndPoint endPoint -#if HAS_LOGGER - , ILogger logger = null -#endif - ) + public AdbSocket(EndPoint endPoint, ILogger logger = null) { socket = new TcpSocket(); socket.Connect(endPoint); socket.ReceiveBufferSize = ReceiveBufferSize; -#if HAS_LOGGER - this.logger = logger ?? NullLogger.Instance; -#endif + this.logger = logger ?? LoggerProvider.CreateLogger(); } /// @@ -66,11 +55,7 @@ public AdbSocket(EndPoint endPoint /// The host address at which the Android Debug Bridge is listening for clients. /// The port at which the Android Debug Bridge is listening for clients. /// The logger to use when logging. - public AdbSocket(string host, int port -#if HAS_LOGGER - , ILogger logger = null -#endif - ) + public AdbSocket(string host, int port, ILogger logger = null) { if (string.IsNullOrEmpty(host)) { @@ -86,24 +71,18 @@ public AdbSocket(string host, int port socket = new TcpSocket(); socket.Connect(endPoint); socket.ReceiveBufferSize = ReceiveBufferSize; -#if HAS_LOGGER - this.logger = logger ?? NullLogger.Instance; -#endif + this.logger = logger ?? LoggerProvider.CreateLogger(); } -#if !HAS_LOGGER -#pragma warning restore CS1572 // XML 注释中有 param 标记,但是没有该名称的参数 -#endif /// /// Initializes a new instance of the class. /// /// The at which the Android Debug Bridge is listening for clients. - public AdbSocket(ITcpSocket socket) + /// The logger to use when logging. + public AdbSocket(ITcpSocket socket, ILogger logger = null) { this.socket = socket; -#if HAS_LOGGER - logger ??= NullLogger.Instance; -#endif + this.logger = logger ?? LoggerProvider.CreateLogger(); } /// @@ -143,14 +122,9 @@ public virtual void Send(byte[] data, int offset, int length) throw new AdbException("channel EOF"); } } -#if HAS_LOGGER catch (SocketException sex) { logger.LogError(sex, sex.Message); -#else - catch (SocketException) - { -#endif throw; } } @@ -222,16 +196,12 @@ public virtual int Read(byte[] data, int length) count = socket.Receive(buffer, bufferLength, SocketFlags.None); if (count < 0) { -#if HAS_LOGGER logger.LogError("read: channel EOF"); -#endif throw new AdbException("EOF"); } else if (count == 0) { -#if HAS_LOGGER logger.LogInformation("DONE with Read"); -#endif } else { @@ -364,14 +334,9 @@ protected virtual bool Write(byte[] data) { Send(data, -1); } -#if HAS_LOGGER catch (IOException e) { logger.LogError(e, e.Message); -#else - catch (IOException) - { -#endif return false; } @@ -397,9 +362,7 @@ protected virtual AdbResponse ReadAdbResponseInner() { string message = ReadString(); rasps.Message = message; -#if HAS_LOGGER - logger.LogError($"Got reply '{ReplyToString(reply)}', diag='{rasps.Message}'"); -#endif + logger.LogError("Got reply '{0}', diag='{1}'", ReplyToString(reply), rasps.Message); } return rasps; @@ -417,17 +380,11 @@ protected virtual string ReplyToString(byte[] reply) { result = Encoding.ASCII.GetString(reply); } -#if HAS_LOGGER catch (DecoderFallbackException e) { logger.LogError(e, e.Message); -#else - catch (DecoderFallbackException) - { -#endif result = string.Empty; } - return result; } diff --git a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj index 98083f69..450e5d0c 100644 --- a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj +++ b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj @@ -4,8 +4,6 @@ False True True - True - False $(NoWarn);CS1685;CA2254 @@ -55,145 +53,39 @@ - + - - - - - - - - - + - + $(DefineConstants);HAS_TASK - + $(DefineConstants);HAS_BUFFERS - - $(DefineConstants);HAS_LOGGER - - - + $(DefineConstants);HAS_DRAWING - + $(DefineConstants);HAS_DRAWING - + $(DefineConstants);HAS_INDEXRANGE - + $(DefineConstants);HAS_RUNTIMEINFORMATION - - $(DefineConstants);HAS_LOGGER;HAS_OLDLOGGER - - - + $(DefineConstants);HAS_PROCESS;HAS_SERIALIZATION diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs index 8daf99c9..c5b58471 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs @@ -358,9 +358,7 @@ protected virtual async Task SyncPackageToDeviceAsync(string localFilePa // workitem: 19711 string remoteFilePath = LinuxPath.Combine(TempInstallationDirectory, packageFileName); -#if HAS_LOGGER logger.LogDebug(packageFileName, $"Uploading {packageFileName} onto device '{Device.Serial}'"); -#endif using (ISyncService sync = syncServiceFactory(client, Device)) { @@ -373,9 +371,8 @@ protected virtual async Task SyncPackageToDeviceAsync(string localFilePa await #endif using Stream stream = File.OpenRead(localFilePath); -#if HAS_LOGGER + logger.LogDebug($"Uploading file onto device '{Device.Serial}'"); -#endif // As C# can't use octal, the octal literal 666 (rw-Permission) is here converted to decimal (438) await sync.PushAsync(stream, remoteFilePath, 438, File.GetLastWriteTime(localFilePath), null, cancellationToken); @@ -383,14 +380,9 @@ protected virtual async Task SyncPackageToDeviceAsync(string localFilePa return remoteFilePath; } -#if HAS_LOGGER catch (IOException e) { logger.LogError(e, $"Unable to open sync connection! reason: {e.Message}"); -#else - catch (IOException) - { -#endif throw; } finally @@ -413,14 +405,9 @@ protected virtual async Task RemoveRemotePackageAsync(string remoteFilePath, Can { await client.ExecuteShellCommandAsync(Device, $"rm \"{remoteFilePath}\"", null, cancellationToken); } -#if HAS_LOGGER catch (IOException e) { logger.LogError(e, $"Failed to delete temporary package: {e.Message}"); -#else - catch (IOException) - { -#endif throw; } } diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs index ff57eef4..8439f9ef 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs @@ -3,6 +3,7 @@ // using AdvancedSharpAdbClient.Exceptions; +using AdvancedSharpAdbClient.Logs; using System; using System.Collections.Generic; using System.Diagnostics; @@ -31,12 +32,10 @@ public partial class PackageManager /// protected const string ListThirdPartyOnly = "pm list packages -f -3"; -#if HAS_LOGGER /// /// The logger to use when logging messages. /// protected readonly ILogger logger; -#endif /// /// The to use when communicating with the device. @@ -55,9 +54,6 @@ public partial class PackageManager /// public event EventHandler InstallProgressChanged; -#if !HAS_LOGGER -#pragma warning disable CS1572 // XML 注释中有 param 标记,但是没有该名称的参数 -#endif /// /// Initializes a new instance of the class. /// @@ -71,11 +67,7 @@ public partial class PackageManager /// A value indicating whether to skip the initial refresh of the package list or not. /// Used mainly by unit tests. /// The logger to use when logging. - public PackageManager(IAdbClient client, DeviceData device, bool thirdPartyOnly = false, Func syncServiceFactory = null, bool skipInit = false -#if HAS_LOGGER - , ILogger logger = null -#endif - ) + public PackageManager(IAdbClient client, DeviceData device, bool thirdPartyOnly = false, Func syncServiceFactory = null, bool skipInit = false , ILogger logger = null ) { Device = device ?? throw new ArgumentNullException(nameof(device)); Packages = []; @@ -89,13 +81,8 @@ public PackageManager(IAdbClient client, DeviceData device, bool thirdPartyOnly RefreshPackages(); } -#if HAS_LOGGER - this.logger = logger ?? NullLogger.Instance; -#endif + this.logger = logger ?? LoggerProvider.CreateLogger(); } -#if !HAS_LOGGER -#pragma warning restore CS1572 // XML 注释中有 param 标记,但是没有该名称的参数 -#endif /// /// Gets a value indicating whether this package manager only lists third party applications, @@ -438,9 +425,7 @@ protected virtual string SyncPackageToDevice(string localFilePath, Action using AdvancedSharpAdbClient.Exceptions; +using AdvancedSharpAdbClient.Logs; using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -37,12 +38,10 @@ public partial class DeviceMonitor : IDeviceMonitor private bool disposed = false; -#if HAS_LOGGER /// /// The logger to use when logging messages. /// protected readonly ILogger logger; -#endif /// /// The list of devices currently connected to the Android Debug Bridge. @@ -80,30 +79,18 @@ public partial class DeviceMonitor : IDeviceMonitor /// public event EventHandler DeviceDisconnected; -#if !HAS_LOGGER -#pragma warning disable CS1572 // XML 注释中有 param 标记,但是没有该名称的参数 -#endif /// /// Initializes a new instance of the class. /// /// The that manages the connection with the adb server. /// The logger to use when logging. - public DeviceMonitor(IAdbSocket socket -#if HAS_LOGGER - , ILogger logger = null -#endif - ) + public DeviceMonitor(IAdbSocket socket, ILogger logger = null) { Socket = socket ?? throw new ArgumentNullException(nameof(socket)); devices = []; Devices = devices.AsReadOnly(); -#if HAS_LOGGER - this.logger = logger ?? NullLogger.Instance; -#endif + this.logger = logger ?? LoggerProvider.CreateLogger(); } -#if !HAS_LOGGER -#pragma warning restore CS1572 // XML 注释中有 param 标记,但是没有该名称的参数 -#endif /// public ReadOnlyCollection Devices { get; private set; } diff --git a/AdvancedSharpAdbClient/Extensions/LoggerExtensions.cs b/AdvancedSharpAdbClient/Extensions/LoggerExtensions.cs index 89af0cbd..e54c84c7 100644 --- a/AdvancedSharpAdbClient/Extensions/LoggerExtensions.cs +++ b/AdvancedSharpAdbClient/Extensions/LoggerExtensions.cs @@ -1,19 +1,17 @@ -#if HAS_OLDLOGGER -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// +// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. +// -using Microsoft.Extensions.Logging.Internal; using System; +using AdvancedSharpAdbClient.Logs; -namespace Microsoft.Extensions.Logging +namespace AdvancedSharpAdbClient { /// /// ILogger extension methods for common scenarios. /// - public static class LoggerExtensionsEx + public static class LoggerExtensions { - private static readonly Func _messageFormatter = MessageFormatter; - //------------------------------------------DEBUG------------------------------------------// /// @@ -24,8 +22,22 @@ public static class LoggerExtensionsEx /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogDebug(exception, "Error while processing request from {Address}", address) - public static void LogDebug(this ILogger logger, Exception exception, string message, params object[] args) => + public static void LogDebug(this ILogger logger, Exception exception, string message, params object[] args) + { logger.Log(LogLevel.Debug, exception, message, args); + } + + /// + /// Formats and writes a debug log message. + /// + /// The to write to. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" + /// An object array that contains zero or more objects to format. + /// logger.LogDebug("Processing request from {Address}", address) + public static void LogDebug(this ILogger logger, string message, params object[] args) + { + logger.Log(LogLevel.Debug, message, args); + } //------------------------------------------TRACE------------------------------------------// @@ -37,8 +49,22 @@ public static void LogDebug(this ILogger logger, Exception exception, string mes /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogTrace(exception, "Error while processing request from {Address}", address) - public static void LogTrace(this ILogger logger, Exception exception, string message, params object[] args) => + public static void LogTrace(this ILogger logger, Exception exception, string message, params object[] args) + { logger.Log(LogLevel.Trace, exception, message, args); + } + + /// + /// Formats and writes a trace log message. + /// + /// The to write to. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" + /// An object array that contains zero or more objects to format. + /// logger.LogTrace("Processing request from {Address}", address) + public static void LogTrace(this ILogger logger, string message, params object[] args) + { + logger.Log(LogLevel.Trace, message, args); + } //------------------------------------------INFORMATION------------------------------------------// @@ -50,8 +76,22 @@ public static void LogTrace(this ILogger logger, Exception exception, string mes /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogInformation(exception, "Error while processing request from {Address}", address) - public static void LogInformation(this ILogger logger, Exception exception, string message, params object[] args) => + public static void LogInformation(this ILogger logger, Exception exception, string message, params object[] args) + { logger.Log(LogLevel.Information, exception, message, args); + } + + /// + /// Formats and writes an informational log message. + /// + /// The to write to. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" + /// An object array that contains zero or more objects to format. + /// logger.LogInformation("Processing request from {Address}", address) + public static void LogInformation(this ILogger logger, string message, params object[] args) + { + logger.Log(LogLevel.Information, message, args); + } //------------------------------------------WARNING------------------------------------------// @@ -63,86 +103,85 @@ public static void LogInformation(this ILogger logger, Exception exception, stri /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogWarning(exception, "Error while processing request from {Address}", address) - public static void LogWarning(this ILogger logger, Exception exception, string message, params object[] args) => + public static void LogWarning(this ILogger logger, Exception exception, string message, params object[] args) + { logger.Log(LogLevel.Warning, exception, message, args); - - //------------------------------------------ERROR------------------------------------------// + } /// - /// Formats and writes an error log message. + /// Formats and writes a warning log message. /// /// The to write to. - /// The exception to log. /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. - /// logger.LogError(exception, "Error while processing request from {Address}", address) - public static void LogError(this ILogger logger, Exception exception, string message, params object[] args) => - logger.Log(LogLevel.Error, exception, message, args); + /// logger.LogWarning("Processing request from {Address}", address) + public static void LogWarning(this ILogger logger, string message, params object[] args) + { + logger.Log(LogLevel.Warning, message, args); + } - //------------------------------------------CRITICAL------------------------------------------// + //------------------------------------------ERROR------------------------------------------// /// - /// Formats and writes a critical log message. + /// Formats and writes an error log message. /// /// The to write to. /// The exception to log. /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. - /// logger.LogCritical(exception, "Error while processing request from {Address}", address) - public static void LogCritical(this ILogger logger, Exception exception, string message, params object[] args) => - logger.Log(LogLevel.Critical, exception, message, args); + /// logger.LogError(exception, "Error while processing request from {Address}", address) + public static void LogError(this ILogger logger, Exception exception, string message, params object[] args) + { + logger.Log(LogLevel.Error, exception, message, args); + } /// - /// Formats and writes a log message at the specified log level. + /// Formats and writes an error log message. /// /// The to write to. - /// Entry will be written on this level. - /// Format string of the log message. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. - public static void Log(this ILogger logger, LogLevel logLevel, string message, params object[] args) => - logger.Log(logLevel, 0, null, message, args); + /// logger.LogError("Processing request from {Address}", address) + public static void LogError(this ILogger logger, string message, params object[] args) + { + logger.Log(LogLevel.Error, message, args); + } + + //------------------------------------------CRITICAL------------------------------------------// /// - /// Formats and writes a log message at the specified log level. + /// Formats and writes a critical log message. /// /// The to write to. - /// Entry will be written on this level. - /// The event id associated with the log. - /// Format string of the log message. + /// The exception to log. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. - public static void Log(this ILogger logger, LogLevel logLevel, EventId eventId, string message, params object[] args) => - logger.Log(logLevel, eventId, null, message, args); + /// logger.LogCritical(exception, "Error while processing request from {Address}", address) + public static void LogCritical(this ILogger logger, Exception exception, string message, params object[] args) + { + logger.Log(LogLevel.Critical, exception, message, args); + } /// - /// Formats and writes a log message at the specified log level. + /// Formats and writes a critical log message. /// /// The to write to. - /// Entry will be written on this level. - /// The exception to log. - /// Format string of the log message. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. - public static void Log(this ILogger logger, LogLevel logLevel, Exception exception, string message, params object[] args) => - logger.Log(logLevel, 0, exception, message, args); + /// logger.LogCritical("Processing request from {Address}", address) + public static void LogCritical(this ILogger logger, string message, params object[] args) + { + logger.Log(LogLevel.Critical, message, args); + } /// /// Formats and writes a log message at the specified log level. /// /// The to write to. /// Entry will be written on this level. - /// The event id associated with the log. - /// The exception to log. /// Format string of the log message. /// An object array that contains zero or more objects to format. - public static void Log(this ILogger logger, LogLevel logLevel, EventId eventId, Exception exception, string message, params object[] args) - { - if (logger == null) { throw new ArgumentNullException(nameof(logger)); } - - logger.Log(logLevel, eventId, new FormattedLogValues(message, args), exception, _messageFormatter); - } - - //------------------------------------------HELPERS------------------------------------------// - - private static string MessageFormatter(FormattedLogValues state, Exception error) => state.ToString(); + public static void Log(this ILogger logger, LogLevel logLevel, string message, params object[] args) => + logger.Log(logLevel, null, message, args); } -} -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Logs/Enums/LogLevel.cs b/AdvancedSharpAdbClient/Logs/Enums/LogLevel.cs new file mode 100644 index 00000000..0578c31c --- /dev/null +++ b/AdvancedSharpAdbClient/Logs/Enums/LogLevel.cs @@ -0,0 +1,52 @@ +// +// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. +// + +namespace AdvancedSharpAdbClient.Logs +{ + /// + /// Defines logging severity levels. + /// + public enum LogLevel : byte + { + /// + /// Logs that contain the most detailed messages. These messages may contain sensitive application data. + /// These messages are disabled by default and should never be enabled in a production environment. + /// + Trace = 0, + + /// + /// Logs that are used for interactive investigation during development. These logs should primarily contain + /// information useful for debugging and have no long-term value. + /// + Debug = 1, + + /// + /// Logs that track the general flow of the application. These logs should have long-term value. + /// + Information = 2, + + /// + /// Logs that highlight an abnormal or unexpected event in the application flow, but do not otherwise cause the + /// application execution to stop. + /// + Warning = 3, + + /// + /// Logs that highlight when the current flow of execution is stopped due to a failure. These should indicate a + /// failure in the current activity, not an application-wide failure. + /// + Error = 4, + + /// + /// Logs that describe an unrecoverable application or system crash, or a catastrophic failure that requires + /// immediate attention. + /// + Critical = 5, + + /// + /// Not used for writing log messages. Specifies that a logging category should not write any messages. + /// + None = 6, + } +} diff --git a/AdvancedSharpAdbClient/Logs/Interfaces/ILogger.cs b/AdvancedSharpAdbClient/Logs/Interfaces/ILogger.cs new file mode 100644 index 00000000..fa3460e8 --- /dev/null +++ b/AdvancedSharpAdbClient/Logs/Interfaces/ILogger.cs @@ -0,0 +1,35 @@ +// +// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. +// + +using System; + +namespace AdvancedSharpAdbClient.Logs +{ + /// + /// Represents a type used to perform logging. + /// + /// Aggregates most logging patterns to a single method. + public interface ILogger + { + /// + /// Formats and writes a log message at the specified log level. + /// + /// Entry will be written on this level. + /// The exception to log. + /// Format string of the log message. + /// An object array that contains zero or more objects to format. + void Log(LogLevel logLevel, Exception exception, string message, params object[] args); + } + + /// + /// A generic interface for logging where the category name is derived from the specified + /// type name. + /// Generally used to enable activation of a named from dependency injection. + /// + /// The type whose name is used for the logger category name. + public interface ILogger : ILogger + { + + } +} diff --git a/AdvancedSharpAdbClient/Logs/Interfaces/ILoggerFactory.cs b/AdvancedSharpAdbClient/Logs/Interfaces/ILoggerFactory.cs new file mode 100644 index 00000000..62f1096b --- /dev/null +++ b/AdvancedSharpAdbClient/Logs/Interfaces/ILoggerFactory.cs @@ -0,0 +1,28 @@ +// +// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. +// + +using System; + +namespace AdvancedSharpAdbClient.Logs +{ + /// + /// Represents a type used to configure the logging system and create instances of . + /// + public interface ILoggerFactory : IDisposable + { + /// + /// Creates a new instance. + /// + /// The category name for messages produced by the logger. + /// The . + ILogger CreateLogger(string categoryName); + + /// + /// Creates a new instance. + /// + /// The category name for messages produced by the logger. + /// The . + ILogger CreateLogger(); + } +} diff --git a/AdvancedSharpAdbClient/Logs/LoggerProvider.cs b/AdvancedSharpAdbClient/Logs/LoggerProvider.cs new file mode 100644 index 00000000..f2bc3026 --- /dev/null +++ b/AdvancedSharpAdbClient/Logs/LoggerProvider.cs @@ -0,0 +1,34 @@ +// +// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. +// + +namespace AdvancedSharpAdbClient.Logs +{ + /// + /// Provides a mechanism for creating instances of and classes. + /// + public static class LoggerProvider + { + private static ILoggerFactory _loggerFactory = null; + + /// + /// Sets the current log provider based on logger factory. + /// + /// The logger factory. + public static void SetLogProvider(ILoggerFactory loggerFactory) => _loggerFactory = loggerFactory; + + /// + /// Creates a new instance. + /// + /// The category name for messages produced by the logger. + /// A new instance. + public static ILogger CreateLogger(string category) => _loggerFactory == null ? NullLogger.Instance : _loggerFactory.CreateLogger(category); + + /// + /// Creates a new instance using the full name of the given type. + /// + /// The type. + /// The that was created + public static ILogger CreateLogger() => _loggerFactory == null ? NullLogger.Instance : _loggerFactory.CreateLogger(); + } +} diff --git a/AdvancedSharpAdbClient/Logs/NullLogger.cs b/AdvancedSharpAdbClient/Logs/NullLogger.cs new file mode 100644 index 00000000..229254cc --- /dev/null +++ b/AdvancedSharpAdbClient/Logs/NullLogger.cs @@ -0,0 +1,34 @@ +// +// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. +// + +using System; + +namespace AdvancedSharpAdbClient.Logs +{ + /// + /// Minimalistic logger that does nothing. + /// + public class NullLogger : ILogger + { + /// + /// Returns the shared instance of . + /// + public static NullLogger Instance { get; } = new(); + + /// + public void Log(LogLevel logLevel, Exception exception, string message, params object[] args) { } + } + + /// + /// Minimalistic logger that does nothing. + /// + public class NullLogger : NullLogger, ILogger + { + /// + /// Returns an instance of . + /// + /// An instance of . + public static new NullLogger Instance { get; } = new(); + } +} diff --git a/AdvancedSharpAdbClient/Logs/NullLoggerT.cs b/AdvancedSharpAdbClient/Logs/NullLoggerT.cs deleted file mode 100644 index 8d60373b..00000000 --- a/AdvancedSharpAdbClient/Logs/NullLoggerT.cs +++ /dev/null @@ -1,55 +0,0 @@ -#if HAS_OLDLOGGER -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Extensions.Logging; -using System; - -namespace Microsoft.Extensions.Logging.Abstractions -{ - /// - /// Minimalistic logger that does nothing. - /// - public class NullLogger : ILogger - { - /// - /// Returns an instance of . - /// - /// An instance of . - public static readonly NullLogger Instance = new(); - - /// - public IDisposable BeginScope(TState state) => NullScope.Instance; - - /// - /// - /// This method ignores the parameters and does nothing. - /// - public void Log( - LogLevel logLevel, - EventId eventId, - TState state, - Exception exception, - Func formatter) - { - } - - /// - public bool IsEnabled(LogLevel logLevel) => false; - } - - internal sealed class NullScope : IDisposable - { - public static NullScope Instance { get; } = new NullScope(); - - private NullScope() - { - } - - /// - public void Dispose() - { - } - } -} -#endif diff --git a/AdvancedSharpAdbClient/Properties/GlobalUsings.cs b/AdvancedSharpAdbClient/Properties/GlobalUsings.cs index 56cd0031..24e9282d 100644 --- a/AdvancedSharpAdbClient/Properties/GlobalUsings.cs +++ b/AdvancedSharpAdbClient/Properties/GlobalUsings.cs @@ -30,11 +30,6 @@ global using System.Threading.Tasks; #endif -#if HAS_LOGGER -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -#endif - #if HAS_BUFFERS global using System.Buffers; #endif diff --git a/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs b/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs index 69265c31..97082d73 100644 --- a/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs +++ b/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs @@ -3,6 +3,7 @@ // using AdvancedSharpAdbClient.Exceptions; +using AdvancedSharpAdbClient.Logs; using System.Collections.Generic; using System.IO; using System.Text; @@ -14,46 +15,24 @@ namespace AdvancedSharpAdbClient /// Receives console output, and makes the console output available as a . To /// fetch the console output that was received, used the method. /// - public partial class ConsoleOutputReceiver : MultiLineReceiver + /// The logger to use when logging. + public partial class ConsoleOutputReceiver(ILogger logger = null) : MultiLineReceiver { /// /// The default to use when parsing the output. /// protected const RegexOptions DefaultRegexOptions = RegexOptions.Singleline | RegexOptions.IgnoreCase; -#if HAS_LOGGER /// /// The logger to use when logging messages. /// - protected readonly ILogger logger; -#endif + protected readonly ILogger logger = logger ?? LoggerProvider.CreateLogger(); /// /// A which receives all output from the device. /// protected readonly StringBuilder output = new(); -#if !HAS_LOGGER -#pragma warning disable CS1572 // XML 注释中有 param 标记,但是没有该名称的参数 -#endif - /// - /// Initializes a new instance of the class. - /// - /// The logger to use when logging. - public ConsoleOutputReceiver( -#if HAS_LOGGER - ILogger logger = null -#endif - ) - { -#if HAS_LOGGER - this.logger = logger ?? NullLogger.Instance; -#endif - } -#if !HAS_LOGGER -#pragma warning restore CS1572 // XML 注释中有 param 标记,但是没有该名称的参数 -#endif - /// /// Gets a that represents the current . /// @@ -70,35 +49,27 @@ public virtual void ThrowOnError(string line) { if (line.EndsWith(": not found")) { -#if HAS_LOGGER logger.LogWarning($"The remote execution returned: '{line}'"); -#endif throw new FileNotFoundException($"The remote execution returned: '{line}'"); } if (line.EndsWith("No such file or directory")) { -#if HAS_LOGGER logger.LogWarning($"The remote execution returned: {line}"); -#endif throw new FileNotFoundException($"The remote execution returned: '{line}'"); } // for "unknown options" if (line.Contains("Unknown option")) { -#if HAS_LOGGER logger.LogWarning($"The remote execution returned: {line}"); -#endif throw new UnknownOptionException($"The remote execution returned: '{line}'"); } // for "aborting" commands if (AbortingRegex().IsMatch(line)) { -#if HAS_LOGGER logger.LogWarning($"The remote execution returned: {line}"); -#endif throw new CommandAbortingException($"The remote execution returned: '{line}'"); } @@ -106,9 +77,7 @@ public virtual void ThrowOnError(string line) // cmd: applet not found if (AppletRegex().IsMatch(line)) { -#if HAS_LOGGER logger.LogWarning($"The remote execution returned: '{line}'"); -#endif throw new FileNotFoundException($"The remote execution returned: '{line}'"); } @@ -116,9 +85,7 @@ public virtual void ThrowOnError(string line) // workitem: 16822 if (DeniedRegex().IsMatch(line)) { -#if HAS_LOGGER logger.LogWarning($"The remote execution returned: '{line}'"); -#endif throw new PermissionDeniedException($"The remote execution returned: '{line}'"); } } @@ -137,9 +104,7 @@ protected override void ProcessNewLines(IEnumerable lines) continue; } output.AppendLine(line); -#if HAS_LOGGER logger.LogDebug(line); -#endif } } From 243a7efea84b18de7b863c10f7516def900d31d4 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Wed, 27 Sep 2023 17:23:46 +0800 Subject: [PATCH 08/66] Remove CrossPlatformFunc Rename Utilities to Extensions --- .../AdbServerTests.cs | 1 - .../{UtilitiesTests.cs => ExtensionsTests.cs} | 16 +-- AdvancedSharpAdbClient/AdbClient.Async.cs | 4 +- AdvancedSharpAdbClient/AdbClient.cs | 4 +- .../AdbCommandLineClient.Async.cs | 50 ++++++- .../AdbCommandLineClient.cs | 46 ++++++- AdvancedSharpAdbClient/AdbServer.Async.cs | 5 +- AdvancedSharpAdbClient/AdbServer.cs | 15 ++- AdvancedSharpAdbClient/DeviceMonitor.Async.cs | 4 +- AdvancedSharpAdbClient/DeviceMonitor.cs | 2 +- .../Extensions/CrossPlatformFunc.cs | 123 ------------------ .../Extensions/Factories.cs | 7 + .../Extensions/Utilities.cs | 4 +- .../Logs/LogReader.Async.cs | 4 +- AdvancedSharpAdbClient/Logs/LogReader.cs | 2 +- AdvancedSharpAdbClient/Models/DeviceData.cs | 2 +- AdvancedSharpAdbClient/Models/ShellStream.cs | 2 +- AdvancedSharpAdbClient/SyncService.Async.cs | 4 +- AdvancedSharpAdbClient/SyncService.cs | 2 +- AdvancedSharpAdbClient/TcpSocket.Async.cs | 2 +- 20 files changed, 135 insertions(+), 164 deletions(-) rename AdvancedSharpAdbClient.Tests/Extensions/{UtilitiesTests.cs => ExtensionsTests.cs} (62%) delete mode 100644 AdvancedSharpAdbClient/Extensions/CrossPlatformFunc.cs diff --git a/AdvancedSharpAdbClient.Tests/AdbServerTests.cs b/AdvancedSharpAdbClient.Tests/AdbServerTests.cs index 8aaadb3d..54ec6738 100644 --- a/AdvancedSharpAdbClient.Tests/AdbServerTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbServerTests.cs @@ -27,7 +27,6 @@ public AdbServerTests() adbSocketFactory = (endPoint) => socket; commandLineClient = new DummyAdbCommandLineClient(); - AdbServer.IsValidAdbFile = commandLineClient.IsValidAdbFile; adbCommandLineClientFactory = (version) => commandLineClient; adbClient = new AdbClient(AdbClient.DefaultEndPoint, adbSocketFactory); diff --git a/AdvancedSharpAdbClient.Tests/Extensions/UtilitiesTests.cs b/AdvancedSharpAdbClient.Tests/Extensions/ExtensionsTests.cs similarity index 62% rename from AdvancedSharpAdbClient.Tests/Extensions/UtilitiesTests.cs rename to AdvancedSharpAdbClient.Tests/Extensions/ExtensionsTests.cs index 325a6753..793d7e4b 100644 --- a/AdvancedSharpAdbClient.Tests/Extensions/UtilitiesTests.cs +++ b/AdvancedSharpAdbClient.Tests/Extensions/ExtensionsTests.cs @@ -4,19 +4,19 @@ namespace AdvancedSharpAdbClient.Tests { /// - /// Tests the class. + /// Tests the class. /// - public class UtilitiesTests + public class ExtensionsTests { [Fact] public void TryParseTest() { - Assert.True(Utilities.TryParse("BootLoader", false, out DeviceState result)); + Assert.True(Extensions.TryParse("BootLoader", false, out DeviceState result)); Assert.Equal(DeviceState.BootLoader, result); - Assert.True(Utilities.TryParse("Bootloader", true, out result)); + Assert.True(Extensions.TryParse("Bootloader", true, out result)); Assert.Equal(DeviceState.BootLoader, result); - Assert.False(Utilities.TryParse("Bootloader", false, out _)); - Assert.False(Utilities.TryParse("Reset", true, out _)); + Assert.False(Extensions.TryParse("Bootloader", false, out _)); + Assert.False(Extensions.TryParse("Reset", true, out _)); } [Fact] @@ -28,13 +28,13 @@ public void IsNullOrWhiteSpaceTest() [Fact] public void JoinTest() => - Assert.Equal("Hello World!", Utilities.Join(" ", ["Hello", "World!"])); + Assert.Equal("Hello World!", Extensions.Join(" ", ["Hello", "World!"])); [Fact] public void FromUnixTimeSecondsTest() { DateTimeOffset time = new(new DateTime(2022, 6, 1, 12, 10, 34, DateTimeKind.Utc)); - Assert.Equal(time, Utilities.FromUnixTimeSeconds(1654085434)); + Assert.Equal(time, Extensions.FromUnixTimeSeconds(1654085434)); } [Fact] diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index 7c0b8ca5..70486bdc 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -380,7 +380,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. - Utilities.Delay(3000, cancellationToken).GetAwaiter().GetResult(); + Extensions.Delay(3000, cancellationToken).GetAwaiter().GetResult(); } } @@ -985,7 +985,7 @@ public async Task SendTextAsync(DeviceData device, string text, CancellationToke public async Task ClearInputAsync(DeviceData device, int charCount, CancellationToken cancellationToken = default) { await SendKeyEventAsync(device, "KEYCODE_MOVE_END", cancellationToken); - await ExecuteRemoteCommandAsync("input keyevent " + Utilities.Join(" ", Enumerable.Repeat("KEYCODE_DEL ", charCount)), device, null, cancellationToken); + await ExecuteRemoteCommandAsync("input keyevent " + Extensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL ", charCount)), device, null, cancellationToken); } /// diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index 05fdbe16..906b35d0 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -519,7 +519,7 @@ protected void Root(string request, DeviceData device) #if HAS_PROCESS && !WINDOWS_UWP Thread.Sleep(3000); #else - Utilities.Delay(3000).GetAwaiter().GetResult(); + Extensions.Delay(3000).GetAwaiter().GetResult(); #endif } } @@ -1042,7 +1042,7 @@ public void SendText(DeviceData device, string text) public void ClearInput(DeviceData device, int charCount) { SendKeyEvent(device, "KEYCODE_MOVE_END"); - ExecuteRemoteCommand("input keyevent " + Utilities.Join(" ", Enumerable.Repeat("KEYCODE_DEL ", charCount)), device, null); + ExecuteRemoteCommand("input keyevent " + Extensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL ", charCount)), device, null); } /// diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs index b6ed11cf..1f003f11 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs @@ -123,10 +123,58 @@ protected virtual async Task RunAdbProcessInnerAsync(string command, List + /// Runs process, invoking a specific command, and reads the standard output and standard error output. + /// + /// The return code of the process. + protected virtual async Task RunProcessAsync(string filename, string command, List errorOutput, List standardOutput, CancellationToken cancellationToken = default) + { +#if HAS_PROCESS + ProcessStartInfo psi = new(filename, command) + { + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden, + UseShellExecute = false, + RedirectStandardError = true, + RedirectStandardOutput = true + }; + + using Process process = Process.Start(psi); + string standardErrorString = await process.StandardError.ReadToEndAsync(cancellationToken).ConfigureAwait(false); + string standardOutputString = await process.StandardOutput.ReadToEndAsync(cancellationToken).ConfigureAwait(false); + + errorOutput?.AddRange(standardErrorString.Split(separator, StringSplitOptions.RemoveEmptyEntries)); + + standardOutput?.AddRange(standardOutputString.Split(separator, StringSplitOptions.RemoveEmptyEntries)); + +#if NET5_0_OR_GREATER + using (CancellationTokenSource completionSource = new(TimeSpan.FromMilliseconds(5000))) + { + await process.WaitForExitAsync(completionSource.Token); + if (!process.HasExited) + { + process.Kill(); + } + } +#else + // get the return code from the process + if (!process.WaitForExit(5000)) + { + process.Kill(); + } +#endif + return process.ExitCode; +#else + TaskCompletionSource source = new(); + source.SetException(new PlatformNotSupportedException()); + return await source.Task; +#endif + } } } #endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.cs index 4aea2e19..0d121032 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.cs @@ -23,6 +23,8 @@ public partial class AdbCommandLineClient : IAdbCommandLineClient /// protected const string AdbVersionPattern = "^.*(\\d+)\\.(\\d+)\\.(\\d+)$"; + private static readonly char[] separator = ['\r', '\n']; + /// /// The logger to use when logging messages. /// @@ -43,8 +45,8 @@ public AdbCommandLineClient(string adbPath, bool isForce = false, ILogger - public virtual bool IsValidAdbFile(string adbPath) => CrossPlatformFunc.CheckFileExists(adbPath); + public virtual bool IsValidAdbFile(string adbPath) => Factories.CheckFileExists(adbPath); /// /// Parses the output of the adb.exe version command and determines the adb version. @@ -213,11 +215,47 @@ protected virtual int RunAdbProcessInner(string command, List errorOutpu { ExceptionExtensions.ThrowIfNull(command); - int status = CrossPlatformFunc.RunProcess(AdbPath, command, errorOutput, standardOutput); + int status = RunProcess(AdbPath, command, errorOutput, standardOutput); return status; } + /// + /// Runs process, invoking a specific command, and reads the standard output and standard error output. + /// + /// The return code of the process. + protected virtual int RunProcess(string filename, string command, List errorOutput, List standardOutput) + { +#if HAS_PROCESS + ProcessStartInfo psi = new(filename, command) + { + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden, + UseShellExecute = false, + RedirectStandardError = true, + RedirectStandardOutput = true + }; + + using Process process = Process.Start(psi); + string standardErrorString = process.StandardError.ReadToEnd(); + string standardOutputString = process.StandardOutput.ReadToEnd(); + + errorOutput?.AddRange(standardErrorString.Split(separator, StringSplitOptions.RemoveEmptyEntries)); + + standardOutput?.AddRange(standardOutputString.Split(separator, StringSplitOptions.RemoveEmptyEntries)); + + // get the return code from the process + if (!process.WaitForExit(5000)) + { + process.Kill(); + } + + return process.ExitCode; +#else + throw new PlatformNotSupportedException(); +#endif + } + #if NET7_0_OR_GREATER [GeneratedRegex(AdbVersionPattern)] private static partial Regex AdbVersionRegex(); diff --git a/AdvancedSharpAdbClient/AdbServer.Async.cs b/AdvancedSharpAdbClient/AdbServer.Async.cs index d5a43af5..f69838d3 100644 --- a/AdvancedSharpAdbClient/AdbServer.Async.cs +++ b/AdvancedSharpAdbClient/AdbServer.Async.cs @@ -19,6 +19,7 @@ public virtual async Task StartServerAsync(string adbPath, bo Version commandLineVersion = null; IAdbCommandLineClient commandLineClient = adbCommandLineClientFactory(adbPath); + IsValidAdbFile = commandLineClient.IsValidAdbFile; if (commandLineClient.IsValidAdbFile(adbPath)) { @@ -73,7 +74,7 @@ public virtual async Task RestartServerAsync(string adbPath, } ManualResetEvent manualResetEvent = null; - await Utilities.Run(() => + await Extensions.Run(() => { lock (RestartLock) { @@ -81,7 +82,7 @@ await Utilities.Run(() => } }, cancellationToken); - _ = Utilities.Run(() => + _ = Extensions.Run(() => { lock (RestartLock) { diff --git a/AdvancedSharpAdbClient/AdbServer.cs b/AdvancedSharpAdbClient/AdbServer.cs index a6bc7d77..74813ad0 100644 --- a/AdvancedSharpAdbClient/AdbServer.cs +++ b/AdvancedSharpAdbClient/AdbServer.cs @@ -32,7 +32,7 @@ public partial class AdbServer(IAdbClient adbClient, FuncNo connection could be made because the target computer actively refused it.This usually /// results from trying to connect to a service that is inactive on the foreign host—that is, /// one with no server application running. - internal const int ConnectionRefused = 10061; + public const int ConnectionRefused = 10061; /// /// The error code that is returned by the when the connection was reset by the peer. @@ -41,12 +41,7 @@ public partial class AdbServer(IAdbClient adbClient, Func - internal const int ConnectionReset = 10054; - - /// - /// Throws an error if the path does not point to a valid instance of adb.exe. - /// - internal static Func IsValidAdbFile = CrossPlatformFunc.CheckFileExists; + public const int ConnectionReset = 10054; /// /// A lock used to ensure only one caller at a time can attempt to restart adb. @@ -99,6 +94,11 @@ public AdbServer(Func adbCommandLineClientFactory /// public static IAdbServer Instance { get; set; } = new AdbServer(); + /// + /// Throws an error if the path does not point to a valid instance of adb.exe. + /// + protected static Func IsValidAdbFile { get; set; } = Factories.CheckFileExists; + /// public virtual StartServerResult StartServer(string adbPath, bool restartServerIfNewer) { @@ -106,6 +106,7 @@ public virtual StartServerResult StartServer(string adbPath, bool restartServerI Version commandLineVersion = null; IAdbCommandLineClient commandLineClient = adbCommandLineClientFactory(adbPath); + IsValidAdbFile = commandLineClient.IsValidAdbFile; if (commandLineClient.IsValidAdbFile(adbPath)) { diff --git a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs index 38509d3f..0afff082 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs @@ -35,10 +35,10 @@ public virtual async Task StartAsync(CancellationToken cancellationToken = defau { _ = firstDeviceListParsed.Reset(); - monitorTask = Utilities.Run(() => DeviceMonitorLoopAsync(monitorTaskCancellationTokenSource.Token), cancellationToken); + monitorTask = Extensions.Run(() => DeviceMonitorLoopAsync(monitorTaskCancellationTokenSource.Token), cancellationToken); // Wait for the worker thread to have read the first list of devices. - _ = await Utilities.Run(firstDeviceListParsed.WaitOne, cancellationToken); + _ = await Extensions.Run(firstDeviceListParsed.WaitOne, cancellationToken); } } diff --git a/AdvancedSharpAdbClient/DeviceMonitor.cs b/AdvancedSharpAdbClient/DeviceMonitor.cs index 52cfb2f3..3af58648 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.cs @@ -115,7 +115,7 @@ public virtual void Start() { _ = firstDeviceListParsed.Reset(); - monitorTask = Utilities.Run(() => DeviceMonitorLoopAsync(monitorTaskCancellationTokenSource.Token)); + monitorTask = Extensions.Run(() => DeviceMonitorLoopAsync(monitorTaskCancellationTokenSource.Token)); // Wait for the worker thread to have read the first list of devices. _ = firstDeviceListParsed.WaitOne(); diff --git a/AdvancedSharpAdbClient/Extensions/CrossPlatformFunc.cs b/AdvancedSharpAdbClient/Extensions/CrossPlatformFunc.cs deleted file mode 100644 index e21642dd..00000000 --- a/AdvancedSharpAdbClient/Extensions/CrossPlatformFunc.cs +++ /dev/null @@ -1,123 +0,0 @@ -// -// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. -// - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Threading; - -namespace AdvancedSharpAdbClient -{ - /// - /// The functions which are used by the class, but which are platform-specific. - /// - public static class CrossPlatformFunc - { - private static readonly char[] separator = ['\r', '\n']; - - /// - /// Determines whether the specified file exists. - /// - public static Func CheckFileExists { get; set; } = File.Exists; - - /// - /// Runs process, invoking a specific command, and reads the standard output and standard error output. - /// - /// The return code of the process. - public static Func, List, int> RunProcess { get; set; } = (string filename, string command, List errorOutput, List standardOutput) => - { -#if HAS_PROCESS - ProcessStartInfo psi = new(filename, command) - { - CreateNoWindow = true, - WindowStyle = ProcessWindowStyle.Hidden, - UseShellExecute = false, - RedirectStandardError = true, - RedirectStandardOutput = true - }; - - using Process process = Process.Start(psi); - string standardErrorString = process.StandardError.ReadToEnd(); - string standardOutputString = process.StandardOutput.ReadToEnd(); - - errorOutput?.AddRange(standardErrorString.Split(separator, StringSplitOptions.RemoveEmptyEntries)); - - standardOutput?.AddRange(standardOutputString.Split(separator, StringSplitOptions.RemoveEmptyEntries)); - - // get the return code from the process - if (!process.WaitForExit(5000)) - { - process.Kill(); - } - - return process.ExitCode; -#else - throw new PlatformNotSupportedException(); -#endif - }; - -#if HAS_TASK -#if NETFRAMEWORK && !NET40_OR_GREATER - /// - /// Encapsulates a method that has five parameters and returns a value of the type specified by the parameter. - /// - /// The return value of the method that this delegate encapsulates. - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5); -#endif - - /// - /// Runs process, invoking a specific command, and reads the standard output and standard error output. - /// - /// The return code of the process. - public static Func, List, CancellationToken, Task> RunProcessAsync { get; set; } = -#if HAS_PROCESS - async -#endif - (string filename, string command, List errorOutput, List standardOutput, CancellationToken cancellationToken) => - { -#if HAS_PROCESS - ProcessStartInfo psi = new(filename, command) - { - CreateNoWindow = true, - WindowStyle = ProcessWindowStyle.Hidden, - UseShellExecute = false, - RedirectStandardError = true, - RedirectStandardOutput = true - }; - - using Process process = Process.Start(psi); - string standardErrorString = await process.StandardError.ReadToEndAsync(cancellationToken).ConfigureAwait(false); - string standardOutputString = await process.StandardOutput.ReadToEndAsync(cancellationToken).ConfigureAwait(false); - - errorOutput?.AddRange(standardErrorString.Split(separator, StringSplitOptions.RemoveEmptyEntries)); - - standardOutput?.AddRange(standardOutputString.Split(separator, StringSplitOptions.RemoveEmptyEntries)); - -#if NET5_0_OR_GREATER - using (CancellationTokenSource completionSource = new(TimeSpan.FromMilliseconds(5000))) - { - await process.WaitForExitAsync(completionSource.Token); - if (!process.HasExited) - { - process.Kill(); - } - } -#else - // get the return code from the process - if (!process.WaitForExit(5000)) - { - process.Kill(); - } -#endif - return process.ExitCode; -#else - TaskCompletionSource source = new(); - source.SetException(new PlatformNotSupportedException()); - return source.Task; -#endif - }; -#endif - } -} diff --git a/AdvancedSharpAdbClient/Extensions/Factories.cs b/AdvancedSharpAdbClient/Extensions/Factories.cs index 957f1f4e..036293c9 100644 --- a/AdvancedSharpAdbClient/Extensions/Factories.cs +++ b/AdvancedSharpAdbClient/Extensions/Factories.cs @@ -3,6 +3,7 @@ // using System; +using System.IO; using System.Net; namespace AdvancedSharpAdbClient @@ -14,6 +15,11 @@ public static class Factories { static Factories() => Reset(); + /// + /// Determines whether the specified file exists. + /// + public static Func CheckFileExists { get; set; } + /// /// Gets or sets a delegate which creates a new instance of the class. /// @@ -45,6 +51,7 @@ public static class Factories /// public static void Reset() { + CheckFileExists = File.Exists; AdbSocketFactory = (endPoint) => new AdbSocket(endPoint); AdbClientFactory = (endPoint) => new AdbClient(endPoint, AdbSocketFactory); AdbCommandLineClientFactory = (path) => new AdbCommandLineClient(path); diff --git a/AdvancedSharpAdbClient/Extensions/Utilities.cs b/AdvancedSharpAdbClient/Extensions/Utilities.cs index 9c878119..273a24b4 100644 --- a/AdvancedSharpAdbClient/Extensions/Utilities.cs +++ b/AdvancedSharpAdbClient/Extensions/Utilities.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // @@ -13,7 +13,7 @@ namespace AdvancedSharpAdbClient { - internal static class Utilities + internal static class Extensions { /// /// Converts the string representation of the name or numeric value of one or more diff --git a/AdvancedSharpAdbClient/Logs/LogReader.Async.cs b/AdvancedSharpAdbClient/Logs/LogReader.Async.cs index 1c2947c9..d224a321 100644 --- a/AdvancedSharpAdbClient/Logs/LogReader.Async.cs +++ b/AdvancedSharpAdbClient/Logs/LogReader.Async.cs @@ -94,7 +94,7 @@ public async Task ReadEntryAsync(CancellationToken cancellationToken = return null; } - DateTimeOffset timestamp = Utilities.FromUnixTimeSeconds(sec); + DateTimeOffset timestamp = Extensions.FromUnixTimeSeconds(sec); switch ((LogId)id) { @@ -213,7 +213,7 @@ await stream.ReadAsync(data.AsMemory(totalRead, count - totalRead), cancellation #elif !NET35 await stream.ReadAsync(data, totalRead, count - totalRead, cancellationToken).ConfigureAwait(false) #else - await Utilities.Run(() => stream.Read(data, totalRead, count - totalRead)).ConfigureAwait(false) + await Extensions.Run(() => stream.Read(data, totalRead, count - totalRead)).ConfigureAwait(false) #endif ) > 0) { diff --git a/AdvancedSharpAdbClient/Logs/LogReader.cs b/AdvancedSharpAdbClient/Logs/LogReader.cs index 256a44fa..1532b639 100644 --- a/AdvancedSharpAdbClient/Logs/LogReader.cs +++ b/AdvancedSharpAdbClient/Logs/LogReader.cs @@ -100,7 +100,7 @@ public virtual LogEntry ReadEntry() return null; } - DateTimeOffset timestamp = Utilities.FromUnixTimeSeconds(sec); + DateTimeOffset timestamp = Extensions.FromUnixTimeSeconds(sec); switch ((LogId)id) { diff --git a/AdvancedSharpAdbClient/Models/DeviceData.cs b/AdvancedSharpAdbClient/Models/DeviceData.cs index 659c7ca1..ceeb94e0 100644 --- a/AdvancedSharpAdbClient/Models/DeviceData.cs +++ b/AdvancedSharpAdbClient/Models/DeviceData.cs @@ -132,7 +132,7 @@ internal static DeviceState GetStateFromString(string state) else { // Else, we try to match a value of the DeviceState enumeration. - if (!Utilities.TryParse(state, true, out value)) + if (!Extensions.TryParse(state, true, out value)) { value = DeviceState.Unknown; } diff --git a/AdvancedSharpAdbClient/Models/ShellStream.cs b/AdvancedSharpAdbClient/Models/ShellStream.cs index 664c837c..06090f87 100644 --- a/AdvancedSharpAdbClient/Models/ShellStream.cs +++ b/AdvancedSharpAdbClient/Models/ShellStream.cs @@ -215,7 +215,7 @@ async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToke #elif !NET35 await Inner.ReadAsync(buffer, offset + 1, count - 1, cancellationToken).ConfigureAwait(false); #else - await Utilities.Run(() => Inner.Read(buffer, offset + 1, count - 1)).ConfigureAwait(false); + await Extensions.Run(() => Inner.Read(buffer, offset + 1, count - 1)).ConfigureAwait(false); #endif read++; pendingByte = null; diff --git a/AdvancedSharpAdbClient/SyncService.Async.cs b/AdvancedSharpAdbClient/SyncService.Async.cs index 9c092210..affaef26 100644 --- a/AdvancedSharpAdbClient/SyncService.Async.cs +++ b/AdvancedSharpAdbClient/SyncService.Async.cs @@ -200,7 +200,7 @@ public virtual async Task PullAsync(string remoteFilePath, Stream stream, IProgr #elif !NET35 await stream.WriteAsync(buffer, 0, size, cancellationToken); #else - await Utilities.Run(() => stream.Write(buffer, 0, size)); + await Extensions.Run(() => stream.Write(buffer, 0, size)); #endif totalBytesRead += size; @@ -311,7 +311,7 @@ private async Task ReadStatisticsAsync(FileStatistics value, CancellationToken c value.FileMode = (UnixFileMode)BitConverter.ToInt32(statResult, 0); value.Size = BitConverter.ToInt32(statResult, 4); - value.Time = Utilities.FromUnixTimeSeconds(BitConverter.ToInt32(statResult, 8)); + value.Time = Extensions.FromUnixTimeSeconds(BitConverter.ToInt32(statResult, 8)); } } } diff --git a/AdvancedSharpAdbClient/SyncService.cs b/AdvancedSharpAdbClient/SyncService.cs index d29577ab..afd58c67 100644 --- a/AdvancedSharpAdbClient/SyncService.cs +++ b/AdvancedSharpAdbClient/SyncService.cs @@ -376,7 +376,7 @@ private void ReadStatistics(FileStatistics value) value.FileMode = (UnixFileMode)BitConverter.ToInt32(statResult, 0); value.Size = BitConverter.ToInt32(statResult, 4); - value.Time = Utilities.FromUnixTimeSeconds(BitConverter.ToInt32(statResult, 8)); + value.Time = Extensions.FromUnixTimeSeconds(BitConverter.ToInt32(statResult, 8)); } } } diff --git a/AdvancedSharpAdbClient/TcpSocket.Async.cs b/AdvancedSharpAdbClient/TcpSocket.Async.cs index d4387163..5f503bb7 100644 --- a/AdvancedSharpAdbClient/TcpSocket.Async.cs +++ b/AdvancedSharpAdbClient/TcpSocket.Async.cs @@ -18,7 +18,7 @@ public virtual async Task SendAsync(byte[] buffer, int offset, int size, So #else /// public virtual async Task SendAsync(byte[] buffer, int offset, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) => - await Utilities.Run(() => Send(buffer, offset, size, socketFlags), cancellationToken); + await Extensions.Run(() => Send(buffer, offset, size, socketFlags), cancellationToken); #endif #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER From 1dd979eda01a32f95bbc5ab4399da681231e2c0c Mon Sep 17 00:00:00 2001 From: wherewhere Date: Wed, 27 Sep 2023 17:28:19 +0800 Subject: [PATCH 09/66] Use format when log --- AdvancedSharpAdbClient/AdbSocket.Async.cs | 2 +- .../DeviceCommands/PackageManager.Async.cs | 8 ++++---- AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/AdvancedSharpAdbClient/AdbSocket.Async.cs b/AdvancedSharpAdbClient/AdbSocket.Async.cs index 8cc83f7e..358927e9 100644 --- a/AdvancedSharpAdbClient/AdbSocket.Async.cs +++ b/AdvancedSharpAdbClient/AdbSocket.Async.cs @@ -264,7 +264,7 @@ protected virtual async Task ReadAdbResponseInnerAsync(Cancellation { string message = await ReadStringAsync(cancellationToken); rasps.Message = message; - logger.LogError($"Got reply '{ReplyToString(reply)}', diag='{rasps.Message}'"); + logger.LogError("Got reply '{0}', diag='{1}'", ReplyToString(reply), rasps.Message); } return rasps; diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs index c5b58471..5ec44a81 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs @@ -358,7 +358,7 @@ protected virtual async Task SyncPackageToDeviceAsync(string localFilePa // workitem: 19711 string remoteFilePath = LinuxPath.Combine(TempInstallationDirectory, packageFileName); - logger.LogDebug(packageFileName, $"Uploading {packageFileName} onto device '{Device.Serial}'"); + logger.LogDebug("Uploading {0} onto device '{1}'", packageFileName, Device.Serial); using (ISyncService sync = syncServiceFactory(client, Device)) { @@ -372,7 +372,7 @@ protected virtual async Task SyncPackageToDeviceAsync(string localFilePa #endif using Stream stream = File.OpenRead(localFilePath); - logger.LogDebug($"Uploading file onto device '{Device.Serial}'"); + logger.LogDebug("Uploading file onto device '{0}'", Device.Serial); // As C# can't use octal, the octal literal 666 (rw-Permission) is here converted to decimal (438) await sync.PushAsync(stream, remoteFilePath, 438, File.GetLastWriteTime(localFilePath), null, cancellationToken); @@ -382,7 +382,7 @@ protected virtual async Task SyncPackageToDeviceAsync(string localFilePa } catch (IOException e) { - logger.LogError(e, $"Unable to open sync connection! reason: {e.Message}"); + logger.LogError(e, "Unable to open sync connection! reason: {0}", e.Message); throw; } finally @@ -407,7 +407,7 @@ protected virtual async Task RemoveRemotePackageAsync(string remoteFilePath, Can } catch (IOException e) { - logger.LogError(e, $"Failed to delete temporary package: {e.Message}"); + logger.LogError(e, "Failed to delete temporary package: {0}", e.Message); throw; } } diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs index 8439f9ef..9e5694c4 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs @@ -425,7 +425,7 @@ protected virtual string SyncPackageToDevice(string localFilePath, Action Date: Fri, 29 Sep 2023 16:14:02 +0800 Subject: [PATCH 10/66] Remove nuget.config --- .github/workflows/build-and-test.yml | 8 ++- .../AdvancedSharpAdbClient.csproj | 70 +++++++++++++++---- nuget.config | 8 --- 3 files changed, 61 insertions(+), 25 deletions(-) delete mode 100644 nuget.config diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 9f2a6a22..319d1667 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -33,7 +33,7 @@ jobs: dotnet-version: ${{ env.DOTNET_VERSION }} dotnet-quality: 'preview' - - name: Install dependencies + - name: Install Dependencies run: dotnet restore -p:FullTargets=false - name: Build @@ -46,6 +46,8 @@ jobs: name: pack-and-publish needs: build-and-test runs-on: ubuntu-latest + env: + NUGET_SOURCE: https://nuget.pkg.github.com/yungd1plomat/index.json steps: - name: Checkout @@ -59,7 +61,7 @@ jobs: dotnet-version: ${{ env.DOTNET_VERSION }} dotnet-quality: 'preview' - - name: Install dependencies + - name: Install Dependencies run: dotnet restore -p:FullTargets=true - name: Pack @@ -67,7 +69,7 @@ jobs: - name: Publish if: ${{ github.event_name == 'push' && github.ref_name == 'main' }} - run: dotnet nuget push ./nugets/*nupkg --source "github" --skip-duplicate --api-key ${{ secrets.NUGET_KEY }} + run: dotnet nuget push ./nugets/*nupkg --source "$env:NUGET_SOURCE" --skip-duplicate --api-key ${{ secrets.NUGET_KEY }} - name: Upload uses: actions/upload-artifact@v3 diff --git a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj index 450e5d0c..ad12a25b 100644 --- a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj +++ b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj @@ -53,39 +53,81 @@ - + - + - + $(DefineConstants);HAS_TASK - - $(DefineConstants);HAS_BUFFERS - - - + $(DefineConstants);HAS_DRAWING - + $(DefineConstants);HAS_DRAWING - - $(DefineConstants);HAS_INDEXRANGE + + $(DefineConstants);HAS_RUNTIMEINFORMATION - - $(DefineConstants);HAS_RUNTIMEINFORMATION + + $(DefineConstants);HAS_BUFFERS;HAS_INDEXRANGE - + $(DefineConstants);HAS_PROCESS;HAS_SERIALIZATION diff --git a/nuget.config b/nuget.config deleted file mode 100644 index bad35838..00000000 --- a/nuget.config +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file From f0b939fc511114deac60bc48bba95fba8e078002 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Fri, 29 Sep 2023 16:42:37 +0800 Subject: [PATCH 11/66] Rename UnixFileMode to UnixFileType --- AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs | 10 +++++----- AdvancedSharpAdbClient.Tests/SyncServiceTests.cs | 10 +++++----- .../Models/Enums/{UnixFileMode.cs => UnixFileType.cs} | 6 +++--- AdvancedSharpAdbClient/Models/FileStatistics.cs | 4 ++-- AdvancedSharpAdbClient/SyncService.Async.cs | 2 +- AdvancedSharpAdbClient/SyncService.cs | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) rename AdvancedSharpAdbClient/Models/Enums/{UnixFileMode.cs => UnixFileType.cs} (90%) diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs index 29533b22..ea3e3f21 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs @@ -35,7 +35,7 @@ await RunTestAsync( }); Assert.NotNull(value); - Assert.Equal(UnixFileMode.Regular, value.FileMode & UnixFileMode.TypeMask); + Assert.Equal(UnixFileType.Regular, value.FileType & UnixFileType.TypeMask); Assert.Equal(597, value.Size); Assert.Equal(DateTimeHelper.Epoch.ToLocalTime(), value.Time); } @@ -76,25 +76,25 @@ await RunTestAsync( FileStatistics dir = value[0]; Assert.Equal(".", dir.Path); - Assert.Equal((UnixFileMode)16873, dir.FileMode); + Assert.Equal((UnixFileType)16873, dir.FileType); Assert.Equal(0, dir.Size); Assert.Equal(time, dir.Time); FileStatistics parentDir = value[1]; Assert.Equal("..", parentDir.Path); - Assert.Equal((UnixFileMode)16877, parentDir.FileMode); + Assert.Equal((UnixFileType)16877, parentDir.FileType); Assert.Equal(0, parentDir.Size); Assert.Equal(time, parentDir.Time); FileStatistics sdcard0 = value[2]; Assert.Equal("sdcard0", sdcard0.Path); - Assert.Equal((UnixFileMode)41471, sdcard0.FileMode); + Assert.Equal((UnixFileType)41471, sdcard0.FileType); Assert.Equal(24, sdcard0.Size); Assert.Equal(time, sdcard0.Time); FileStatistics emulated = value[3]; Assert.Equal("emulated", emulated.Path); - Assert.Equal((UnixFileMode)16749, emulated.FileMode); + Assert.Equal((UnixFileType)16749, emulated.FileType); Assert.Equal(0, emulated.Size); Assert.Equal(time, emulated.Time); } diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs index b2d0c376..8512631a 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs @@ -46,7 +46,7 @@ public void StatTest() }); Assert.NotNull(value); - Assert.Equal(UnixFileMode.Regular, value.FileMode & UnixFileMode.TypeMask); + Assert.Equal(UnixFileType.Regular, value.FileType & UnixFileType.TypeMask); Assert.Equal(597, value.Size); Assert.Equal(DateTimeHelper.Epoch.ToLocalTime(), value.Time); } @@ -87,25 +87,25 @@ public void GetListingTest() FileStatistics dir = value[0]; Assert.Equal(".", dir.Path); - Assert.Equal((UnixFileMode)16873, dir.FileMode); + Assert.Equal((UnixFileType)16873, dir.FileType); Assert.Equal(0, dir.Size); Assert.Equal(time, dir.Time); FileStatistics parentDir = value[1]; Assert.Equal("..", parentDir.Path); - Assert.Equal((UnixFileMode)16877, parentDir.FileMode); + Assert.Equal((UnixFileType)16877, parentDir.FileType); Assert.Equal(0, parentDir.Size); Assert.Equal(time, parentDir.Time); FileStatistics sdcard0 = value[2]; Assert.Equal("sdcard0", sdcard0.Path); - Assert.Equal((UnixFileMode)41471, sdcard0.FileMode); + Assert.Equal((UnixFileType)41471, sdcard0.FileType); Assert.Equal(24, sdcard0.Size); Assert.Equal(time, sdcard0.Time); FileStatistics emulated = value[3]; Assert.Equal("emulated", emulated.Path); - Assert.Equal((UnixFileMode)16749, emulated.FileMode); + Assert.Equal((UnixFileType)16749, emulated.FileType); Assert.Equal(0, emulated.Size); Assert.Equal(time, emulated.Time); } diff --git a/AdvancedSharpAdbClient/Models/Enums/UnixFileMode.cs b/AdvancedSharpAdbClient/Models/Enums/UnixFileType.cs similarity index 90% rename from AdvancedSharpAdbClient/Models/Enums/UnixFileMode.cs rename to AdvancedSharpAdbClient/Models/Enums/UnixFileType.cs index 0aa3cbde..ec47bc17 100644 --- a/AdvancedSharpAdbClient/Models/Enums/UnixFileMode.cs +++ b/AdvancedSharpAdbClient/Models/Enums/UnixFileType.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // @@ -10,10 +10,10 @@ namespace AdvancedSharpAdbClient /// Describes the properties of a file on an Android device. /// [Flags] - public enum UnixFileMode : ushort + public enum UnixFileType : ushort { /// - /// The mask that can be used to retrieve the file type from a . + /// The mask that can be used to retrieve the file type from a . /// TypeMask = 0x8000, diff --git a/AdvancedSharpAdbClient/Models/FileStatistics.cs b/AdvancedSharpAdbClient/Models/FileStatistics.cs index 186d65e9..88a38419 100644 --- a/AdvancedSharpAdbClient/Models/FileStatistics.cs +++ b/AdvancedSharpAdbClient/Models/FileStatistics.cs @@ -22,9 +22,9 @@ public FileStatistics() { } public string Path { get; set; } /// - /// Gets or sets the attributes of the file. + /// Gets or sets the attributes of the file. /// - public UnixFileMode FileMode { get; set; } + public UnixFileType FileType { get; set; } /// /// Gets or sets the total file size, in bytes. diff --git a/AdvancedSharpAdbClient/SyncService.Async.cs b/AdvancedSharpAdbClient/SyncService.Async.cs index affaef26..e2f1ea7d 100644 --- a/AdvancedSharpAdbClient/SyncService.Async.cs +++ b/AdvancedSharpAdbClient/SyncService.Async.cs @@ -309,7 +309,7 @@ private async Task ReadStatisticsAsync(FileStatistics value, CancellationToken c Array.Reverse(statResult, 8, 4); } - value.FileMode = (UnixFileMode)BitConverter.ToInt32(statResult, 0); + value.FileType = (UnixFileType)BitConverter.ToInt32(statResult, 0); value.Size = BitConverter.ToInt32(statResult, 4); value.Time = Extensions.FromUnixTimeSeconds(BitConverter.ToInt32(statResult, 8)); } diff --git a/AdvancedSharpAdbClient/SyncService.cs b/AdvancedSharpAdbClient/SyncService.cs index afd58c67..584cd6c1 100644 --- a/AdvancedSharpAdbClient/SyncService.cs +++ b/AdvancedSharpAdbClient/SyncService.cs @@ -374,7 +374,7 @@ private void ReadStatistics(FileStatistics value) Array.Reverse(statResult, 8, 4); } - value.FileMode = (UnixFileMode)BitConverter.ToInt32(statResult, 0); + value.FileType = (UnixFileType)BitConverter.ToInt32(statResult, 0); value.Size = BitConverter.ToInt32(statResult, 4); value.Time = Extensions.FromUnixTimeSeconds(BitConverter.ToInt32(statResult, 8)); } From 18ec39e6ea80089a6c3c5ca1980682a7bd2796d8 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Fri, 29 Sep 2023 22:04:45 +0800 Subject: [PATCH 12/66] Rename DateTimeHelper to DateTimeExtension --- .../AdbCommandLineClientExtensionsTests.cs | 6 + .../Extensions/DateTimeExtensionTests.cs | 53 ++++++++ .../Extensions/DateTimeHelperTests.cs | 25 ---- .../ExceptionExtensionsTests.cs | 2 +- .../Extensions/ExtensionsTests.cs | 25 ++-- .../SyncServiceTests.Async.cs | 2 +- .../SyncServiceTests.cs | 2 +- .../Extensions/DateTimeExtension.cs | 118 ++++++++++++++++++ .../Extensions/DateTimeHelper.cs | 38 ------ .../ExceptionExtensions.cs | 2 +- .../{Utilities.cs => Extensions.cs} | 39 ------ .../Extensions/SyncCommandConverter.cs | 4 +- .../Logs/LogReader.Async.cs | 2 +- AdvancedSharpAdbClient/Logs/LogReader.cs | 2 +- AdvancedSharpAdbClient/Models/ForwardSpec.cs | 4 +- AdvancedSharpAdbClient/SyncService.Async.cs | 2 +- AdvancedSharpAdbClient/SyncService.cs | 2 +- 17 files changed, 199 insertions(+), 129 deletions(-) create mode 100644 AdvancedSharpAdbClient.Tests/Extensions/DateTimeExtensionTests.cs delete mode 100644 AdvancedSharpAdbClient.Tests/Extensions/DateTimeHelperTests.cs rename AdvancedSharpAdbClient.Tests/{Exceptions => Extensions}/ExceptionExtensionsTests.cs (98%) create mode 100644 AdvancedSharpAdbClient/Extensions/DateTimeExtension.cs delete mode 100644 AdvancedSharpAdbClient/Extensions/DateTimeHelper.cs rename AdvancedSharpAdbClient/{Exceptions => Extensions}/ExceptionExtensions.cs (99%) rename AdvancedSharpAdbClient/Extensions/{Utilities.cs => Extensions.cs} (89%) diff --git a/AdvancedSharpAdbClient.Tests/Extensions/AdbCommandLineClientExtensionsTests.cs b/AdvancedSharpAdbClient.Tests/Extensions/AdbCommandLineClientExtensionsTests.cs index f7449eda..5b9e2e1e 100644 --- a/AdvancedSharpAdbClient.Tests/Extensions/AdbCommandLineClientExtensionsTests.cs +++ b/AdvancedSharpAdbClient.Tests/Extensions/AdbCommandLineClientExtensionsTests.cs @@ -10,10 +10,16 @@ namespace AdvancedSharpAdbClient.Tests /// public class AdbCommandLineClientExtensionsTests { + /// + /// Tests the method. + /// [Fact] public void EnsureIsValidAdbFileNullValueTest() => _ = Assert.Throws(() => AdbCommandLineClientExtensions.EnsureIsValidAdbFile(null, "adb.exe")); + /// + /// Tests the method. + /// [Fact] public void EnsureIsValidAdbFileInvalidFileTest() { diff --git a/AdvancedSharpAdbClient.Tests/Extensions/DateTimeExtensionTests.cs b/AdvancedSharpAdbClient.Tests/Extensions/DateTimeExtensionTests.cs new file mode 100644 index 00000000..fe83bf5c --- /dev/null +++ b/AdvancedSharpAdbClient.Tests/Extensions/DateTimeExtensionTests.cs @@ -0,0 +1,53 @@ +using System; +using Xunit; + +namespace AdvancedSharpAdbClient.Tests +{ + /// + /// Tests the class. + /// + public class DateTimeExtensionTests + { + /// + /// Tests the method. + /// + [Fact] + public void FromUnixTimeSecondsTest() + { + DateTimeOffset time = new(new DateTime(2022, 6, 1, 12, 10, 34, DateTimeKind.Utc)); + Assert.Equal(time, DateTimeExtension.FromUnixTimeSeconds(1654085434)); + } + +#if NETFRAMEWORK && !NET46_OR_GREATER + /// + /// Tests the method. + /// + [Fact] + public void ToUnixTimeSecondsTest() + { + DateTimeOffset time = new(new DateTime(2022, 6, 1, 12, 10, 34, DateTimeKind.Utc)); + Assert.Equal(1654085434, time.ToUnixTimeSeconds()); + } +#endif + + /// + /// Tests the method. + /// + [Fact] + public void FromUnixEpochTest() + { + DateTime time = new(2022, 6, 1, 12, 10, 34, DateTimeKind.Utc); + Assert.Equal(time, DateTimeExtension.FromUnixEpoch(1654085434)); + } + + /// + /// Tests the method. + /// + [Fact] + public void ToUnixEpochTest() + { + DateTime time = new(2022, 6, 1, 12, 10, 34, DateTimeKind.Utc); + Assert.Equal(1654085434, time.ToUnixEpoch()); + } + } +} diff --git a/AdvancedSharpAdbClient.Tests/Extensions/DateTimeHelperTests.cs b/AdvancedSharpAdbClient.Tests/Extensions/DateTimeHelperTests.cs deleted file mode 100644 index ab4c8843..00000000 --- a/AdvancedSharpAdbClient.Tests/Extensions/DateTimeHelperTests.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using Xunit; - -namespace AdvancedSharpAdbClient.Tests -{ - /// - /// Tests the class. - /// - public class DateTimeHelperTests - { - [Fact] - public void ToUnixEpochTest() - { - DateTime time = new(2022, 6, 1, 12, 10, 34, DateTimeKind.Utc); - Assert.Equal(1654085434, time.ToUnixEpoch()); - } - - [Fact] - public void ToDateTimeTest() - { - DateTime time = new(2022, 6, 1, 12, 10, 34, DateTimeKind.Utc); - Assert.Equal(time, ((long)1654085434).ToDateTime()); - } - } -} diff --git a/AdvancedSharpAdbClient.Tests/Exceptions/ExceptionExtensionsTests.cs b/AdvancedSharpAdbClient.Tests/Extensions/ExceptionExtensionsTests.cs similarity index 98% rename from AdvancedSharpAdbClient.Tests/Exceptions/ExceptionExtensionsTests.cs rename to AdvancedSharpAdbClient.Tests/Extensions/ExceptionExtensionsTests.cs index 288d02f4..61405f28 100644 --- a/AdvancedSharpAdbClient.Tests/Exceptions/ExceptionExtensionsTests.cs +++ b/AdvancedSharpAdbClient.Tests/Extensions/ExceptionExtensionsTests.cs @@ -1,7 +1,7 @@ using System; using Xunit; -namespace AdvancedSharpAdbClient.Exceptions.Tests +namespace AdvancedSharpAdbClient.Tests { /// /// Tests the class. diff --git a/AdvancedSharpAdbClient.Tests/Extensions/ExtensionsTests.cs b/AdvancedSharpAdbClient.Tests/Extensions/ExtensionsTests.cs index 793d7e4b..dc23e4b8 100644 --- a/AdvancedSharpAdbClient.Tests/Extensions/ExtensionsTests.cs +++ b/AdvancedSharpAdbClient.Tests/Extensions/ExtensionsTests.cs @@ -1,4 +1,4 @@ -using System; +using System.Collections.Generic; using Xunit; namespace AdvancedSharpAdbClient.Tests @@ -8,6 +8,9 @@ namespace AdvancedSharpAdbClient.Tests /// public class ExtensionsTests { + /// + /// Tests the method. + /// [Fact] public void TryParseTest() { @@ -19,6 +22,9 @@ public void TryParseTest() Assert.False(Extensions.TryParse("Reset", true, out _)); } + /// + /// Tests the method. + /// [Fact] public void IsNullOrWhiteSpaceTest() { @@ -26,22 +32,11 @@ public void IsNullOrWhiteSpaceTest() Assert.False(" test ".IsNullOrWhiteSpace()); } + /// + /// Tests the method. + /// [Fact] public void JoinTest() => Assert.Equal("Hello World!", Extensions.Join(" ", ["Hello", "World!"])); - - [Fact] - public void FromUnixTimeSecondsTest() - { - DateTimeOffset time = new(new DateTime(2022, 6, 1, 12, 10, 34, DateTimeKind.Utc)); - Assert.Equal(time, Extensions.FromUnixTimeSeconds(1654085434)); - } - - [Fact] - public void ToUnixTimeSecondsTest() - { - DateTimeOffset time = new(new DateTime(2022, 6, 1, 12, 10, 34, DateTimeKind.Utc)); - Assert.Equal(1654085434, time.ToUnixTimeSeconds()); - } } } diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs index ea3e3f21..acd05c1b 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs @@ -37,7 +37,7 @@ await RunTestAsync( Assert.NotNull(value); Assert.Equal(UnixFileType.Regular, value.FileType & UnixFileType.TypeMask); Assert.Equal(597, value.Size); - Assert.Equal(DateTimeHelper.Epoch.ToLocalTime(), value.Time); + Assert.Equal(DateTimeExtension.Epoch.ToLocalTime(), value.Time); } [Fact] diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs index 8512631a..79c872af 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs @@ -48,7 +48,7 @@ public void StatTest() Assert.NotNull(value); Assert.Equal(UnixFileType.Regular, value.FileType & UnixFileType.TypeMask); Assert.Equal(597, value.Size); - Assert.Equal(DateTimeHelper.Epoch.ToLocalTime(), value.Time); + Assert.Equal(DateTimeExtension.Epoch.ToLocalTime(), value.Time); } [Fact] diff --git a/AdvancedSharpAdbClient/Extensions/DateTimeExtension.cs b/AdvancedSharpAdbClient/Extensions/DateTimeExtension.cs new file mode 100644 index 00000000..ceb4d4c8 --- /dev/null +++ b/AdvancedSharpAdbClient/Extensions/DateTimeExtension.cs @@ -0,0 +1,118 @@ +// +// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. +// + +using System; + +namespace AdvancedSharpAdbClient +{ + /// + /// Provides helper methods for working with Unix-based date formats. + /// + public static class DateTimeExtension + { +#if NETFRAMEWORK && !NET46_OR_GREATER + // Number of 100ns ticks per time unit + internal const int MicrosecondsPerMillisecond = 1000; + private const long TicksPerMicrosecond = 10; + private const long TicksPerMillisecond = TicksPerMicrosecond * MicrosecondsPerMillisecond; + + private const int HoursPerDay = 24; + private const long TicksPerSecond = TicksPerMillisecond * 1000; + private const long TicksPerMinute = TicksPerSecond * 60; + private const long TicksPerHour = TicksPerMinute * 60; + private const long TicksPerDay = TicksPerHour * HoursPerDay; + + // Number of days in a non-leap year + private const int DaysPerYear = 365; + // Number of days in 4 years + private const int DaysPer4Years = DaysPerYear * 4 + 1; // 1461 + // Number of days in 100 years + private const int DaysPer100Years = DaysPer4Years * 25 - 1; // 36524 + // Number of days in 400 years + private const int DaysPer400Years = DaysPer100Years * 4 + 1; // 146097 + + // Number of days from 1/1/0001 to 12/31/1969 + internal const int DaysTo1970 = DaysPer400Years * 4 + DaysPer100Years * 3 + DaysPer4Years * 17 + DaysPerYear; // 719,162 + // Number of days from 1/1/0001 to 12/31/9999 + private const int DaysTo10000 = DaysPer400Years * 25 - 366; // 3652059 + + internal const long MinTicks = 0; + internal const long MaxTicks = DaysTo10000 * TicksPerDay - 1; + + internal const long UnixEpochTicks = DaysTo1970 * TicksPerDay; + + private const long UnixEpochSeconds = UnixEpochTicks / TimeSpan.TicksPerSecond; // 62,135,596,800 + + internal const long UnixMinSeconds = MinTicks / TimeSpan.TicksPerSecond - UnixEpochSeconds; + internal const long UnixMaxSeconds = MaxTicks / TimeSpan.TicksPerSecond - UnixEpochSeconds; +#endif + + /// + /// Gets EPOCH time. + /// + public static DateTime Epoch { get; } = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + +#if NET20 +#pragma warning disable CS1574 // XML 注释中有无法解析的 cref 特性 +#endif + /// + /// Converts a Unix time expressed as the number of seconds that have elapsed + /// since 1970-01-01T00:00:00Z to a value. + /// + /// A Unix time, expressed as the number of seconds that have elapsed + /// since 1970-01-01T00:00:00Z (January 1, 1970, at 12:00 AM UTC). For Unix times before this date, + /// its value is negative. + /// A date and time value that represents the same moment in time as the Unix time. + /// is less than -62,135,596,800. + /// -or- is greater than 253,402,300,799. + /// The Offset property value of the returned instance is + /// , which represents Coordinated Universal Time. You can convert it to the time in + /// a specific time zone by calling the method. + public static DateTimeOffset FromUnixTimeSeconds(long seconds) + { +#if NETFRAMEWORK && !NET46_OR_GREATER + if (seconds < UnixMinSeconds || seconds > UnixMaxSeconds) + { + throw new ArgumentOutOfRangeException(nameof(seconds), + string.Format("Valid values are between {0} and {1}, inclusive.", UnixMinSeconds, UnixMaxSeconds)); + } + + long ticks = seconds * TimeSpan.TicksPerSecond + UnixEpochTicks; + return new DateTimeOffset(ticks, TimeSpan.Zero); +#else + return DateTimeOffset.FromUnixTimeSeconds(seconds); +#endif + } +#if NET20 +#pragma warning restore CS1574 // XML 注释中有无法解析的 cref 特性 +#endif + +#if NETFRAMEWORK && !NET46_OR_GREATER + /// + /// Returns the number of seconds that have elapsed since 1970-01-01T00:00:00Z. + /// + /// The DateTimeOffset + /// The number of seconds that have elapsed since 1970-01-01T00:00:00Z. + public static long ToUnixTimeSeconds(this DateTimeOffset dateTimeOffset) + { + long seconds = dateTimeOffset.UtcDateTime.Ticks / TimeSpan.TicksPerSecond; + return seconds - UnixEpochSeconds; + } +#endif + + /// + /// Converts a Unix equivalent to the . + /// + /// The Unix equivalent to convert to the date. + /// A that represents the date. + public static DateTime FromUnixEpoch(long time) => Epoch.Add(new TimeSpan(time * 1000_0000)); + + /// + /// Converts a to the Unix equivalent. + /// + /// The date to convert to the Unix format. + /// A that represents the date, in Unix format. + public static long ToUnixEpoch(this DateTime date) => (long)Math.Round(date.ToUniversalTime().Subtract(Epoch).TotalSeconds); + } +} diff --git a/AdvancedSharpAdbClient/Extensions/DateTimeHelper.cs b/AdvancedSharpAdbClient/Extensions/DateTimeHelper.cs deleted file mode 100644 index ee2a24a6..00000000 --- a/AdvancedSharpAdbClient/Extensions/DateTimeHelper.cs +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. -// - -using System; - -namespace AdvancedSharpAdbClient -{ - /// - /// Provides helper methods for working with Unix-based date formats. - /// - public static class DateTimeHelper - { - /// - /// Gets EPOCH time. - /// - public static DateTime Epoch { get; } = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); - - /// - /// Converts a to the Unix equivalent. - /// - /// The date to convert to the Unix format. - /// A that represents the date, in Unix format. - public static long ToUnixEpoch(this DateTime date) - { - TimeSpan t = date.ToUniversalTime() - Epoch; - long epoch = (long)t.TotalSeconds; - return epoch; - } - - /// - /// Converts a Unix equivalent to the . - /// - /// The Unix equivalent to convert to the date. - /// A that represents the date. - public static DateTime ToDateTime(this long time) => Epoch.Add(new TimeSpan(time * 1000_0000)); - } -} diff --git a/AdvancedSharpAdbClient/Exceptions/ExceptionExtensions.cs b/AdvancedSharpAdbClient/Extensions/ExceptionExtensions.cs similarity index 99% rename from AdvancedSharpAdbClient/Exceptions/ExceptionExtensions.cs rename to AdvancedSharpAdbClient/Extensions/ExceptionExtensions.cs index 4722c3b6..4ab9d9f9 100644 --- a/AdvancedSharpAdbClient/Exceptions/ExceptionExtensions.cs +++ b/AdvancedSharpAdbClient/Extensions/ExceptionExtensions.cs @@ -3,7 +3,7 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; -namespace AdvancedSharpAdbClient.Exceptions +namespace AdvancedSharpAdbClient { internal static class ExceptionExtensions { diff --git a/AdvancedSharpAdbClient/Extensions/Utilities.cs b/AdvancedSharpAdbClient/Extensions/Extensions.cs similarity index 89% rename from AdvancedSharpAdbClient/Extensions/Utilities.cs rename to AdvancedSharpAdbClient/Extensions/Extensions.cs index 273a24b4..c8b43aad 100644 --- a/AdvancedSharpAdbClient/Extensions/Utilities.cs +++ b/AdvancedSharpAdbClient/Extensions/Extensions.cs @@ -2,7 +2,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; using System; using System.Collections.Generic; using System.IO; @@ -301,44 +300,6 @@ public static Task ReadToEndAsync(this TextReader reader, CancellationTo #endif #endif -#if NET20 -#pragma warning disable CS1574 // XML 注释中有无法解析的 cref 特性 -#endif - /// - /// Converts a Unix time expressed as the number of seconds that have elapsed - /// since 1970-01-01T00:00:00Z to a value. - /// - /// A Unix time, expressed as the number of seconds that have elapsed - /// since 1970-01-01T00:00:00Z (January 1, 1970, at 12:00 AM UTC). For Unix times before this date, - /// its value is negative. - /// A date and time value that represents the same moment in time as the Unix time. - /// is less than -62,135,596,800. - /// -or- is greater than 253,402,300,799. - /// The Offset property value of the returned instance is - /// , which represents Coordinated Universal Time. You can convert it to the time in - /// a specific time zone by calling the method. - public static DateTimeOffset FromUnixTimeSeconds(long seconds) => -#if NETFRAMEWORK && !NET46_OR_GREATER - new(seconds.ToDateTime()); -#else - DateTimeOffset.FromUnixTimeSeconds(seconds); -#endif -#if NET20 -#pragma warning restore CS1574 // XML 注释中有无法解析的 cref 特性 -#endif - - /// - /// Returns the number of seconds that have elapsed since 1970-01-01T00:00:00Z. - /// - /// The DateTimeOffset - /// The number of seconds that have elapsed since 1970-01-01T00:00:00Z. - public static long ToUnixTimeSeconds(this DateTimeOffset dateTimeOffset) => -#if NETFRAMEWORK && !NET46_OR_GREATER - dateTimeOffset.DateTime.ToUnixEpoch(); -#else - dateTimeOffset.ToUnixTimeSeconds(); -#endif - public static bool IsWindowsPlatform() => #if HAS_RUNTIMEINFORMATION RuntimeInformation.IsOSPlatform(OSPlatform.Windows); diff --git a/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs b/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs index 0fe5feb1..91d171d0 100644 --- a/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs +++ b/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs @@ -37,12 +37,12 @@ public static class SyncCommandConverter /// A byte array that represents the . public static byte[] GetBytes(SyncCommand command) { - if (!Values.ContainsKey(command)) + if (!Values.TryGetValue(command, out string value)) { throw new ArgumentOutOfRangeException(nameof(command), $"{command} is not a valid sync command"); } - string commandText = Values[command]; + string commandText = value; byte[] commandBytes = AdbClient.Encoding.GetBytes(commandText); return commandBytes; diff --git a/AdvancedSharpAdbClient/Logs/LogReader.Async.cs b/AdvancedSharpAdbClient/Logs/LogReader.Async.cs index d224a321..ae6d01ea 100644 --- a/AdvancedSharpAdbClient/Logs/LogReader.Async.cs +++ b/AdvancedSharpAdbClient/Logs/LogReader.Async.cs @@ -94,7 +94,7 @@ public async Task ReadEntryAsync(CancellationToken cancellationToken = return null; } - DateTimeOffset timestamp = Extensions.FromUnixTimeSeconds(sec); + DateTimeOffset timestamp = DateTimeExtension.FromUnixTimeSeconds(sec); switch ((LogId)id) { diff --git a/AdvancedSharpAdbClient/Logs/LogReader.cs b/AdvancedSharpAdbClient/Logs/LogReader.cs index 1532b639..32dad781 100644 --- a/AdvancedSharpAdbClient/Logs/LogReader.cs +++ b/AdvancedSharpAdbClient/Logs/LogReader.cs @@ -100,7 +100,7 @@ public virtual LogEntry ReadEntry() return null; } - DateTimeOffset timestamp = Extensions.FromUnixTimeSeconds(sec); + DateTimeOffset timestamp = DateTimeExtension.FromUnixTimeSeconds(sec); switch ((LogId)id) { diff --git a/AdvancedSharpAdbClient/Models/ForwardSpec.cs b/AdvancedSharpAdbClient/Models/ForwardSpec.cs index a1306adb..bcdeb66f 100644 --- a/AdvancedSharpAdbClient/Models/ForwardSpec.cs +++ b/AdvancedSharpAdbClient/Models/ForwardSpec.cs @@ -49,12 +49,12 @@ public ForwardSpec(string spec) throw new ArgumentOutOfRangeException(nameof(spec)); } - if (!Mappings.ContainsKey(parts[0])) + if (!Mappings.TryGetValue(parts[0], out ForwardProtocol value)) { throw new ArgumentOutOfRangeException(nameof(spec)); } - ForwardProtocol protocol = Mappings[parts[0]]; + ForwardProtocol protocol = value; Protocol = protocol; bool isInt = int.TryParse(parts[1], out int intValue); diff --git a/AdvancedSharpAdbClient/SyncService.Async.cs b/AdvancedSharpAdbClient/SyncService.Async.cs index e2f1ea7d..635ad6ef 100644 --- a/AdvancedSharpAdbClient/SyncService.Async.cs +++ b/AdvancedSharpAdbClient/SyncService.Async.cs @@ -311,7 +311,7 @@ private async Task ReadStatisticsAsync(FileStatistics value, CancellationToken c value.FileType = (UnixFileType)BitConverter.ToInt32(statResult, 0); value.Size = BitConverter.ToInt32(statResult, 4); - value.Time = Extensions.FromUnixTimeSeconds(BitConverter.ToInt32(statResult, 8)); + value.Time = DateTimeExtension.FromUnixTimeSeconds(BitConverter.ToInt32(statResult, 8)); } } } diff --git a/AdvancedSharpAdbClient/SyncService.cs b/AdvancedSharpAdbClient/SyncService.cs index 584cd6c1..9ec35053 100644 --- a/AdvancedSharpAdbClient/SyncService.cs +++ b/AdvancedSharpAdbClient/SyncService.cs @@ -376,7 +376,7 @@ private void ReadStatistics(FileStatistics value) value.FileType = (UnixFileType)BitConverter.ToInt32(statResult, 0); value.Size = BitConverter.ToInt32(statResult, 4); - value.Time = Extensions.FromUnixTimeSeconds(BitConverter.ToInt32(statResult, 8)); + value.Time = DateTimeExtension.FromUnixTimeSeconds(BitConverter.ToInt32(statResult, 8)); } } } From 31eae26c85ff968b9f2e9960fe801e33136c6d36 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Fri, 29 Sep 2023 22:14:58 +0800 Subject: [PATCH 13/66] Update test to .net8 --- AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs | 5 +---- AdvancedSharpAdbClient.Tests/AdbSocketTests.cs | 1 - .../AdvancedSharpAdbClient.Tests.csproj | 2 +- AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs | 8 ++++---- .../Exceptions/AdbExceptionTests.cs | 4 +++- .../Exceptions/CommandAbortingExceptionTests.cs | 4 +++- .../Exceptions/DeviceNotFoundException.cs | 4 +++- .../Exceptions/ExceptionTester.cs | 1 + .../Exceptions/JavaExceptionTests.cs | 1 + .../Exceptions/PermissionDeniedExceptionTests.cs | 4 +++- .../Exceptions/ShellCommandUnresponsiveExceptionTests.cs | 4 +++- .../Exceptions/UnknownOptionExceptionTests.cs | 4 +++- .../Models/FramebufferHeaderTests.cs | 3 +-- 13 files changed, 27 insertions(+), 18 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs index 6ba0b93e..9c27886b 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs @@ -1290,10 +1290,7 @@ private Task RunCreateReverseAsyncTest(Func test, string rever private Task RunCreateForwardAsyncTest(Func test, string forwardString) { - string[] requests = new string[] - { - $"host-serial:169.254.109.177:5555:forward:{forwardString}" - }; + string[] requests = [$"host-serial:169.254.109.177:5555:forward:{forwardString}"]; return RunTestAsync( [AdbResponse.OK, AdbResponse.OK], diff --git a/AdvancedSharpAdbClient.Tests/AdbSocketTests.cs b/AdvancedSharpAdbClient.Tests/AdbSocketTests.cs index c5f6170a..b821cee4 100644 --- a/AdvancedSharpAdbClient.Tests/AdbSocketTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbSocketTests.cs @@ -1,5 +1,4 @@ using AdvancedSharpAdbClient.Exceptions; -using AdvancedSharpAdbClient.Logs; using System; using System.IO; using System.Text; diff --git a/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj b/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj index 945a2ced..e31f3bb1 100644 --- a/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj +++ b/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj @@ -3,7 +3,7 @@ latest CS1591 - net6.0 + net8.0 diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs index 0feec6a8..a12814c6 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs @@ -26,11 +26,11 @@ public void ExecuteRemoteCommand(string command, DeviceData device, IShellOutput { ReceivedCommands.Add(command); - if (Commands.ContainsKey(command)) + if (Commands.TryGetValue(command, out string value)) { if (receiver != null) { - StringReader reader = new(Commands[command]); + StringReader reader = new(value); while (reader.Peek() != -1) { @@ -53,11 +53,11 @@ public Task ExecuteRemoteCommandAsync(string command, DeviceData device, IShellO { ReceivedCommands.Add(command); - if (Commands.ContainsKey(command)) + if (Commands.TryGetValue(command, out string value)) { if (receiver != null) { - StringReader reader = new(Commands[command]); + StringReader reader = new(value); while (reader.Peek() != -1) { diff --git a/AdvancedSharpAdbClient.Tests/Exceptions/AdbExceptionTests.cs b/AdvancedSharpAdbClient.Tests/Exceptions/AdbExceptionTests.cs index 8c17e5d8..60baff78 100644 --- a/AdvancedSharpAdbClient.Tests/Exceptions/AdbExceptionTests.cs +++ b/AdvancedSharpAdbClient.Tests/Exceptions/AdbExceptionTests.cs @@ -1,4 +1,5 @@ -using Xunit; +using System; +using Xunit; namespace AdvancedSharpAdbClient.Exceptions.Tests { @@ -17,6 +18,7 @@ public void MessageAndInnerConstructorTest() => ExceptionTester.MessageAndInnerConstructorTest((message, inner) => new AdbException(message, inner)); [Fact] + [Obsolete] public void SerializationConstructorTest() => ExceptionTester.SerializationConstructorTest((info, context) => new AdbException(info, context)); } diff --git a/AdvancedSharpAdbClient.Tests/Exceptions/CommandAbortingExceptionTests.cs b/AdvancedSharpAdbClient.Tests/Exceptions/CommandAbortingExceptionTests.cs index 2732fdbb..4465153b 100644 --- a/AdvancedSharpAdbClient.Tests/Exceptions/CommandAbortingExceptionTests.cs +++ b/AdvancedSharpAdbClient.Tests/Exceptions/CommandAbortingExceptionTests.cs @@ -1,4 +1,5 @@ -using Xunit; +using System; +using Xunit; namespace AdvancedSharpAdbClient.Exceptions.Tests { @@ -17,6 +18,7 @@ public void MessageAndInnerConstructorTest() => ExceptionTester.MessageAndInnerConstructorTest((message, inner) => new CommandAbortingException(message, inner)); [Fact] + [Obsolete] public void SerializationConstructorTest() => ExceptionTester.SerializationConstructorTest((info, context) => new CommandAbortingException(info, context)); } diff --git a/AdvancedSharpAdbClient.Tests/Exceptions/DeviceNotFoundException.cs b/AdvancedSharpAdbClient.Tests/Exceptions/DeviceNotFoundException.cs index 7a77ff93..e8ad0d58 100644 --- a/AdvancedSharpAdbClient.Tests/Exceptions/DeviceNotFoundException.cs +++ b/AdvancedSharpAdbClient.Tests/Exceptions/DeviceNotFoundException.cs @@ -1,4 +1,5 @@ -using Xunit; +using System; +using Xunit; namespace AdvancedSharpAdbClient.Exceptions.Tests { @@ -13,6 +14,7 @@ public void MessageAndInnerConstructorTest() => ExceptionTester.MessageAndInnerConstructorTest((message, inner) => new DeviceNotFoundException(message, inner)); [Fact] + [Obsolete] public void SerializationConstructorTest() => ExceptionTester.SerializationConstructorTest((info, context) => new DeviceNotFoundException(info, context)); } diff --git a/AdvancedSharpAdbClient.Tests/Exceptions/ExceptionTester.cs b/AdvancedSharpAdbClient.Tests/Exceptions/ExceptionTester.cs index 6d1895b3..9f4f8a25 100644 --- a/AdvancedSharpAdbClient.Tests/Exceptions/ExceptionTester.cs +++ b/AdvancedSharpAdbClient.Tests/Exceptions/ExceptionTester.cs @@ -30,6 +30,7 @@ public static void MessageAndInnerConstructorTest(Func con Assert.Equal(inner, ex.InnerException); } + [Obsolete] public static void SerializationConstructorTest(Func constructor) { SerializationInfo info = new(typeof(T), new FormatterConverter()); diff --git a/AdvancedSharpAdbClient.Tests/Exceptions/JavaExceptionTests.cs b/AdvancedSharpAdbClient.Tests/Exceptions/JavaExceptionTests.cs index a9bc4c23..04e0c17f 100644 --- a/AdvancedSharpAdbClient.Tests/Exceptions/JavaExceptionTests.cs +++ b/AdvancedSharpAdbClient.Tests/Exceptions/JavaExceptionTests.cs @@ -18,6 +18,7 @@ public void MessageAndInnerConstructorTest() => ExceptionTester.MessageAndInnerConstructorTest((message, inner) => new JavaException(string.Empty, message, string.Empty, inner)); [Fact] + [Obsolete] public void SerializationConstructorTest() => ExceptionTester.SerializationConstructorTest((info, context) => new JavaException(info, context)); diff --git a/AdvancedSharpAdbClient.Tests/Exceptions/PermissionDeniedExceptionTests.cs b/AdvancedSharpAdbClient.Tests/Exceptions/PermissionDeniedExceptionTests.cs index 371a86d6..8f8f78de 100644 --- a/AdvancedSharpAdbClient.Tests/Exceptions/PermissionDeniedExceptionTests.cs +++ b/AdvancedSharpAdbClient.Tests/Exceptions/PermissionDeniedExceptionTests.cs @@ -1,4 +1,5 @@ -using Xunit; +using System; +using Xunit; namespace AdvancedSharpAdbClient.Exceptions.Tests { @@ -17,6 +18,7 @@ public void MessageAndInnerConstructorTest() => ExceptionTester.MessageAndInnerConstructorTest((message, inner) => new PermissionDeniedException(message, inner)); [Fact] + [Obsolete] public void SerializationConstructorTest() => ExceptionTester.SerializationConstructorTest((info, context) => new PermissionDeniedException(info, context)); } diff --git a/AdvancedSharpAdbClient.Tests/Exceptions/ShellCommandUnresponsiveExceptionTests.cs b/AdvancedSharpAdbClient.Tests/Exceptions/ShellCommandUnresponsiveExceptionTests.cs index 7cd38a6c..36e8190b 100644 --- a/AdvancedSharpAdbClient.Tests/Exceptions/ShellCommandUnresponsiveExceptionTests.cs +++ b/AdvancedSharpAdbClient.Tests/Exceptions/ShellCommandUnresponsiveExceptionTests.cs @@ -1,4 +1,5 @@ -using Xunit; +using System; +using Xunit; namespace AdvancedSharpAdbClient.Exceptions.Tests { @@ -17,6 +18,7 @@ public void MessageAndInnerConstructorTest() => ExceptionTester.MessageAndInnerConstructorTest((message, inner) => new ShellCommandUnresponsiveException(message, inner)); [Fact] + [Obsolete] public void SerializationConstructorTest() => ExceptionTester.SerializationConstructorTest((info, context) => new ShellCommandUnresponsiveException(info, context)); } diff --git a/AdvancedSharpAdbClient.Tests/Exceptions/UnknownOptionExceptionTests.cs b/AdvancedSharpAdbClient.Tests/Exceptions/UnknownOptionExceptionTests.cs index 58239de8..33123c4f 100644 --- a/AdvancedSharpAdbClient.Tests/Exceptions/UnknownOptionExceptionTests.cs +++ b/AdvancedSharpAdbClient.Tests/Exceptions/UnknownOptionExceptionTests.cs @@ -1,4 +1,5 @@ -using Xunit; +using System; +using Xunit; namespace AdvancedSharpAdbClient.Exceptions.Tests { @@ -17,6 +18,7 @@ public void MessageAndInnerConstructorTest() => ExceptionTester.MessageAndInnerConstructorTest((message, inner) => new UnknownOptionException(message, inner)); [Fact] + [Obsolete] public void SerializationConstructorTest() => ExceptionTester.SerializationConstructorTest((info, context) => new UnknownOptionException(info, context)); } diff --git a/AdvancedSharpAdbClient.Tests/Models/FramebufferHeaderTests.cs b/AdvancedSharpAdbClient.Tests/Models/FramebufferHeaderTests.cs index 2992625a..9be1a8d1 100644 --- a/AdvancedSharpAdbClient.Tests/Models/FramebufferHeaderTests.cs +++ b/AdvancedSharpAdbClient.Tests/Models/FramebufferHeaderTests.cs @@ -1,5 +1,4 @@ -using System; -using System.Drawing; +using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Runtime.InteropServices; From f06cb9ccb828041747bd9470974c87e34190572d Mon Sep 17 00:00:00 2001 From: wherewhere Date: Sun, 1 Oct 2023 00:34:13 +0800 Subject: [PATCH 14/66] Change a lot about Socket Add more methods and add span and memory supports --- .../AdbSocketTests.Async.cs | 55 +++++-- .../AdbSocketTests.cs | 73 ++++++--- .../Dummys/DummyAdbSocket.cs | 75 +++++---- .../Dummys/DummyTcpSocket.cs | 77 +++++++-- .../Extensions/DateTimeExtensionTests.cs | 14 +- .../SyncServiceTests.Async.cs | 2 +- .../SyncServiceTests.cs | 2 +- .../TcpSocketTests.Async.cs | 44 +++++- .../TcpSocketTests.cs | 32 +++- AdvancedSharpAdbClient/AdbClient.Async.cs | 6 +- AdvancedSharpAdbClient/AdbClient.cs | 2 +- AdvancedSharpAdbClient/AdbSocket.Async.cs | 123 +++++++++++++-- AdvancedSharpAdbClient/AdbSocket.cs | 137 ++++++++++++++-- .../AdvancedSharpAdbClient.csproj | 10 ++ ...TimeExtension.cs => DateTimeExtensions.cs} | 4 +- .../Extensions/Extensions.cs | 30 +--- .../Extensions/SocketExtensions.cs | 146 +++++++++++++++--- .../Extensions/TaskToApm.cs | 123 --------------- .../Interfaces/IAdbSocket.Async.cs | 58 +++++-- .../Interfaces/IAdbSocket.cs | 54 ++++++- .../Interfaces/ITcpSocket.Async.cs | 91 ++++++++++- .../Interfaces/ITcpSocket.cs | 70 ++++++++- .../Logs/LogReader.Async.cs | 4 +- AdvancedSharpAdbClient/Logs/LogReader.cs | 2 +- AdvancedSharpAdbClient/Models/ShellStream.cs | 10 +- AdvancedSharpAdbClient/SyncService.Async.cs | 4 +- AdvancedSharpAdbClient/SyncService.cs | 2 +- AdvancedSharpAdbClient/TcpSocket.Async.cs | 80 ++++++++-- AdvancedSharpAdbClient/TcpSocket.cs | 45 +++++- 29 files changed, 1025 insertions(+), 350 deletions(-) rename AdvancedSharpAdbClient/Extensions/{DateTimeExtension.cs => DateTimeExtensions.cs} (96%) delete mode 100644 AdvancedSharpAdbClient/Extensions/TaskToApm.cs diff --git a/AdvancedSharpAdbClient.Tests/AdbSocketTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbSocketTests.Async.cs index cedda4d1..0db0287d 100644 --- a/AdvancedSharpAdbClient.Tests/AdbSocketTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbSocketTests.Async.cs @@ -35,8 +35,8 @@ public async void SendSyncNullRequestAsyncTest() => [Fact] public async void ReadSyncResponseAsync() { - DummyTcpSocket tcpSocket = new(); - AdbSocket socket = new(tcpSocket); + using DummyTcpSocket tcpSocket = new(); + using AdbSocket socket = new(tcpSocket); await using (StreamWriter writer = new(tcpSocket.InputStream, Encoding.ASCII, 4, true)) { @@ -51,8 +51,8 @@ public async void ReadSyncResponseAsync() [Fact] public async void ReadStringAsyncTest() { - DummyTcpSocket tcpSocket = new(); - AdbSocket socket = new(tcpSocket); + using DummyTcpSocket tcpSocket = new(); + using AdbSocket socket = new(tcpSocket); await using (BinaryWriter writer = new(tcpSocket.InputStream, Encoding.ASCII, true)) { @@ -69,8 +69,8 @@ public async void ReadStringAsyncTest() [Fact] public async void ReadAdbOkayResponseAsyncTest() { - DummyTcpSocket tcpSocket = new(); - AdbSocket socket = new(tcpSocket); + using DummyTcpSocket tcpSocket = new(); + using AdbSocket socket = new(tcpSocket); await using (StreamWriter writer = new(tcpSocket.InputStream, Encoding.ASCII, 4, true)) { @@ -89,8 +89,8 @@ public async void ReadAdbOkayResponseAsyncTest() [Fact] public async void ReadAdbFailResponseAsyncTest() { - DummyTcpSocket tcpSocket = new(); - AdbSocket socket = new(tcpSocket); + using DummyTcpSocket tcpSocket = new(); + using AdbSocket socket = new(tcpSocket); await using (StreamWriter writer = new(tcpSocket.InputStream, Encoding.ASCII, 4, true)) { @@ -107,8 +107,8 @@ public async void ReadAdbFailResponseAsyncTest() [Fact] public async void ReadAsyncTest() { - DummyTcpSocket tcpSocket = new(); - AdbSocket socket = new(tcpSocket); + using DummyTcpSocket tcpSocket = new(); + using AdbSocket socket = new(tcpSocket); // Read 100 bytes from a stream which has 101 bytes available byte[] data = new byte[101]; @@ -117,7 +117,7 @@ public async void ReadAsyncTest() data[i] = (byte)i; } - await tcpSocket.InputStream.WriteAsync(data.AsMemory(0, 101)); + await tcpSocket.InputStream.WriteAsync(data); tcpSocket.InputStream.Position = 0; // Buffer has a capacity of 101, but we'll only want to read 100 bytes @@ -133,6 +133,35 @@ public async void ReadAsyncTest() Assert.Equal(0, received[100]); } + [Fact] + public async void ReadAsyncMemoryTest() + { + using DummyTcpSocket tcpSocket = new(); + using AdbSocket socket = new(tcpSocket); + + // Read 100 bytes from a stream which has 101 bytes available + byte[] data = new byte[101]; + for (int i = 0; i < 101; i++) + { + data[i] = (byte)i; + } + + await tcpSocket.InputStream.WriteAsync(data); + tcpSocket.InputStream.Position = 0; + + // Buffer has a capacity of 101, but we'll only want to read 100 bytes + byte[] received = new byte[101]; + + await socket.ReadAsync(received.AsMemory(0, 100), CancellationToken.None); + + for (int i = 0; i < 100; i++) + { + Assert.Equal(received[i], (byte)i); + } + + Assert.Equal(0, received[100]); + } + [Fact] public async void SendAdbRequestAsyncTest() => await RunTestAsync( @@ -141,8 +170,8 @@ await RunTestAsync( private static async Task RunTestAsync(Func test, byte[] expectedDataSent) { - DummyTcpSocket tcpSocket = new(); - AdbSocket socket = new(tcpSocket); + using DummyTcpSocket tcpSocket = new(); + using AdbSocket socket = new(tcpSocket); // Run the test. await test(socket); diff --git a/AdvancedSharpAdbClient.Tests/AdbSocketTests.cs b/AdvancedSharpAdbClient.Tests/AdbSocketTests.cs index b821cee4..f954d5d7 100644 --- a/AdvancedSharpAdbClient.Tests/AdbSocketTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbSocketTests.cs @@ -14,20 +14,20 @@ public partial class AdbSocketTests [Fact] public void CloseTest() { - DummyTcpSocket tcpSocket = new(); - AdbSocket socket = new(tcpSocket); + using DummyTcpSocket tcpSocket = new(); + using AdbSocket socket = new(tcpSocket); Assert.True(socket.Connected); - socket.Dispose(); + socket.Close(); Assert.False(socket.Connected); } [Fact] public void DisposeTest() { - DummyTcpSocket tcpSocket = new(); - AdbSocket socket = new(tcpSocket); + using DummyTcpSocket tcpSocket = new(); + using AdbSocket socket = new(tcpSocket); Assert.True(socket.Connected); @@ -70,8 +70,8 @@ public void SendSyncNullRequestTest() => [Fact] public void ReadSyncResponse() { - DummyTcpSocket tcpSocket = new(); - AdbSocket socket = new(tcpSocket); + using DummyTcpSocket tcpSocket = new(); + using AdbSocket socket = new(tcpSocket); using (StreamWriter writer = new(tcpSocket.InputStream, Encoding.ASCII, 4, true)) { @@ -86,8 +86,8 @@ public void ReadSyncResponse() [Fact] public void ReadSyncString() { - DummyTcpSocket tcpSocket = new(); - AdbSocket socket = new(tcpSocket); + using DummyTcpSocket tcpSocket = new(); + using AdbSocket socket = new(tcpSocket); using (BinaryWriter writer = new(tcpSocket.InputStream, Encoding.ASCII, true)) { @@ -104,8 +104,8 @@ public void ReadSyncString() [Fact] public void ReadAdbOkayResponseTest() { - DummyTcpSocket tcpSocket = new(); - AdbSocket socket = new(tcpSocket); + using DummyTcpSocket tcpSocket = new(); + using AdbSocket socket = new(tcpSocket); using (StreamWriter writer = new(tcpSocket.InputStream, Encoding.ASCII, 4, true)) { @@ -124,8 +124,8 @@ public void ReadAdbOkayResponseTest() [Fact] public void ReadAdbFailResponseTest() { - DummyTcpSocket tcpSocket = new(); - AdbSocket socket = new(tcpSocket); + using DummyTcpSocket tcpSocket = new(); + using AdbSocket socket = new(tcpSocket); using (StreamWriter writer = new(tcpSocket.InputStream, Encoding.ASCII, 4, true)) { @@ -142,8 +142,8 @@ public void ReadAdbFailResponseTest() [Fact] public void ReadTest() { - DummyTcpSocket tcpSocket = new(); - AdbSocket socket = new(tcpSocket); + using DummyTcpSocket tcpSocket = new(); + using AdbSocket socket = new(tcpSocket); // Read 100 bytes from a stream which has 101 bytes available byte[] data = new byte[101]; @@ -152,7 +152,7 @@ public void ReadTest() data[i] = (byte)i; } - tcpSocket.InputStream.Write(data, 0, 101); + tcpSocket.InputStream.Write(data); tcpSocket.InputStream.Position = 0; // Buffer has a capacity of 101, but we'll only want to read 100 bytes @@ -168,6 +168,35 @@ public void ReadTest() Assert.Equal(0, received[100]); } + [Fact] + public void ReadSpanTest() + { + using DummyTcpSocket tcpSocket = new(); + using AdbSocket socket = new(tcpSocket); + + // Read 100 bytes from a stream which has 101 bytes available + byte[] data = new byte[101]; + for (int i = 0; i < 101; i++) + { + data[i] = (byte)i; + } + + tcpSocket.InputStream.Write(data); + tcpSocket.InputStream.Position = 0; + + // Buffer has a capacity of 101, but we'll only want to read 100 bytes + byte[] received = new byte[101]; + + socket.Read(received.AsSpan(0, 100)); + + for (int i = 0; i < 100; i++) + { + Assert.Equal(received[i], (byte)i); + } + + Assert.Equal(0, received[100]); + } + [Fact] public void SendAdbRequestTest() => RunTest( @@ -177,20 +206,20 @@ public void SendAdbRequestTest() => [Fact] public void GetShellStreamTest() { - DummyTcpSocket tcpSocket = new(); - AdbSocket socket = new(tcpSocket); + using DummyTcpSocket tcpSocket = new(); + using AdbSocket socket = new(tcpSocket); - Stream stream = socket.GetShellStream(); + using Stream stream = socket.GetShellStream(); Assert.IsType(stream); - ShellStream shellStream = (ShellStream)stream; + using ShellStream shellStream = (ShellStream)stream; Assert.Equal(tcpSocket.OutputStream, shellStream.Inner); } private static void RunTest(Action test, byte[] expectedDataSent) { - DummyTcpSocket tcpSocket = new(); - AdbSocket socket = new(tcpSocket); + using DummyTcpSocket tcpSocket = new(); + using AdbSocket socket = new(tcpSocket); // Run the test. test(socket); diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs index a2f8d6fc..0c39933a 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; @@ -46,20 +45,19 @@ internal class DummyAdbSocket : IAdbSocket, IDummyAdbSocket /// public bool DidReconnect { get; private set; } - public Socket Socket => throw new NotImplementedException(); - - public void Send(byte[] data, int length) => SyncDataSent.Enqueue(data.Take(length).ToArray()); + public void Send(byte[] data, int length) + { + SyncDataSent.Enqueue(data[..length]); + } public void Send(byte[] data, int offset, int length) { - if (offset == 0) - { - Send(data, length); - } - else - { - throw new NotImplementedException(); - } + SyncDataSent.Enqueue(data[offset..(offset + length)]); + } + + public void Send(ReadOnlySpan data) + { + SyncDataSent.Enqueue(data.ToArray()); } public void SendSyncRequest(string command, int value) => SyncRequests.Add((Enum.Parse(command), value.ToString())); @@ -72,26 +70,29 @@ public void Send(byte[] data, int offset, int length) public void SendAdbRequest(string request) => Requests.Add(request); - public int Read(byte[] data) + public int Read(byte[] data, int length) { byte[] actual = SyncDataReceived.Dequeue(); - - for (int i = 0; i < data.Length && i < actual.Length; i++) - { - data[i] = actual[i]; - } - - return actual.Length; + Assert.True(actual.Length >= length); + Buffer.BlockCopy(actual, 0, data, 0, length); + return Math.Min(actual.Length, length); } - public int Read(byte[] data, int length) + public int Read(byte[] data, int offset, int length) { byte[] actual = SyncDataReceived.Dequeue(); + Assert.True(actual.Length >= length); + Buffer.BlockCopy(actual, 0, data, offset, length); + return Math.Min(actual.Length, length); + } - Assert.Equal(actual.Length, length); - - Buffer.BlockCopy(actual, 0, data, 0, length); - + public int Read(Span data) + { + byte[] actual = SyncDataReceived.Dequeue(); + for (int i = 0; i < data.Length && i < actual.Length; i++) + { + data[i] = actual[i]; + } return actual.Length; } @@ -159,6 +160,12 @@ public Task SendAsync(byte[] data, int offset, int length, CancellationToken can return Task.CompletedTask; } + public ValueTask SendAsync(ReadOnlyMemory data, CancellationToken cancellationToken = default) + { + Send(data.Span); + return ValueTask.CompletedTask; + } + public Task SendSyncRequestAsync(SyncCommand command, string path, int permissions, CancellationToken cancellationToken = default) { SendSyncRequest(command, path, permissions); @@ -183,20 +190,18 @@ public Task SendAdbRequestAsync(string request, CancellationToken cancellationTo return Task.CompletedTask; } - public Task ReadAsync(byte[] data, CancellationToken cancellationToken = default) + public Task ReadAsync(byte[] data, int length, CancellationToken cancellationToken = default) { - int result = Read(data); + int result = Read(data, length); TaskCompletionSource tcs = new(); tcs.SetResult(result); return tcs.Task; } - public Task ReadAsync(byte[] data, int length, CancellationToken cancellationToken = default) + public ValueTask ReadAsync(Memory data, CancellationToken cancellationToken) { - int result = Read(data, length); - TaskCompletionSource tcs = new(); - tcs.SetResult(result); - return tcs.Task; + int result = Read(data.Span); + return new ValueTask(result); } public async Task ReadStringAsync(CancellationToken cancellationToken = default) @@ -258,5 +263,11 @@ public Task SetDeviceAsync(DeviceData device, CancellationToken cancellationToke public void Close() => IsConnected = false; public void Reconnect() => DidReconnect = true; + + public ValueTask ReconnectAsync(CancellationToken cancellationToken = default) + { + DidReconnect = true; + return ValueTask.CompletedTask; + } } } diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyTcpSocket.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyTcpSocket.cs index 3be32d3b..b156bdf5 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyTcpSocket.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyTcpSocket.cs @@ -1,4 +1,5 @@ using System; +using System.Drawing; using System.IO; using System.Net; using System.Net.Sockets; @@ -27,30 +28,50 @@ internal class DummyTcpSocket : ITcpSocket public void Connect(EndPoint endPoint) => Connected = true; - public Task ConnectAsync(EndPoint endPoint) + public ValueTask ConnectAsync(EndPoint endPoint, CancellationToken cancellationToken) { Connected = true; - return Task.CompletedTask; + return ValueTask.CompletedTask; + } + + public void Reconnect() => Connected = true; + + public ValueTask ReconnectAsync(CancellationToken cancellationToken) + { + Connected = true; + return ValueTask.CompletedTask; } public void Dispose() => Connected = false; public Stream GetStream() => OutputStream; - public Task SendAsync(byte[] buffer, int offset, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) - { - int result = Send(buffer, offset, size, socketFlags); - TaskCompletionSource tcs = new(); - tcs.SetResult(result); - return tcs.Task; - } + public int Receive(byte[] buffer, SocketFlags socketFlags) => InputStream.Read(buffer); public int Receive(byte[] buffer, int size, SocketFlags socketFlags) => InputStream.Read(buffer, 0, size); - public Task ReceiveAsync(byte[] buffer, int offset, int size, SocketFlags socketFlags, CancellationToken cancellationToken) + public int Receive(byte[] buffer, int offset, int size, SocketFlags socketFlags) => InputStream.Read(buffer, offset, size); + + public int Receive(Span buffer, SocketFlags socketFlags) => InputStream.Read(buffer); + + public Task ReceiveAsync(byte[] buffer, SocketFlags socketFlags, CancellationToken cancellationToken) => InputStream.ReadAsync(buffer, cancellationToken).AsTask(); + + public Task ReceiveAsync(byte[] buffer, int size, SocketFlags socketFlags, CancellationToken cancellationToken) => InputStream.ReadAsync(buffer, 0, size, cancellationToken); + + public Task ReceiveAsync(byte[] buffer, int offset, int size, SocketFlags socketFlags, CancellationToken cancellationToken) => InputStream.ReadAsync(buffer, offset, size, cancellationToken); + + public ValueTask ReceiveAsync(Memory buffer, SocketFlags socketFlags, CancellationToken cancellationToken = default) => InputStream.ReadAsync(buffer, cancellationToken); + + public int Send(byte[] buffer, SocketFlags socketFlags) { - int value = InputStream.Read(buffer, offset, size); - return Task.FromResult(value); + OutputStream.Write(buffer); + return buffer.Length; + } + + public int Send(byte[] buffer, int size, SocketFlags socketFlags) + { + OutputStream.Write(buffer, 0, size); + return size; } public int Send(byte[] buffer, int offset, int size, SocketFlags socketFlags) @@ -59,8 +80,36 @@ public int Send(byte[] buffer, int offset, int size, SocketFlags socketFlags) return size; } - public byte[] GetBytesSent() => OutputStream.ToArray(); + public int Send(ReadOnlySpan buffer, SocketFlags socketFlags) + { + OutputStream.Write(buffer); + return buffer.Length; + } + + public async Task SendAsync(byte[] buffer, SocketFlags socketFlags, CancellationToken cancellationToken) + { + await OutputStream.WriteAsync(buffer, cancellationToken); + return buffer.Length; + } + + public async Task SendAsync(byte[] buffer, int size, SocketFlags socketFlags, CancellationToken cancellationToken) + { + await OutputStream.WriteAsync(buffer.AsMemory(0, size), cancellationToken); + return size; + } - public void Reconnect() => throw new NotImplementedException(); + public async Task SendAsync(byte[] buffer, int offset, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) + { + await OutputStream.WriteAsync(buffer.AsMemory(offset, size), cancellationToken); + return size; + } + + public async ValueTask SendAsync(ReadOnlyMemory buffer, SocketFlags socketFlags, CancellationToken cancellationToken = default) + { + await OutputStream.WriteAsync(buffer, cancellationToken); + return buffer.Length; + } + + public byte[] GetBytesSent() => OutputStream.ToArray(); } } diff --git a/AdvancedSharpAdbClient.Tests/Extensions/DateTimeExtensionTests.cs b/AdvancedSharpAdbClient.Tests/Extensions/DateTimeExtensionTests.cs index fe83bf5c..9e7d5923 100644 --- a/AdvancedSharpAdbClient.Tests/Extensions/DateTimeExtensionTests.cs +++ b/AdvancedSharpAdbClient.Tests/Extensions/DateTimeExtensionTests.cs @@ -4,23 +4,23 @@ namespace AdvancedSharpAdbClient.Tests { /// - /// Tests the class. + /// Tests the class. /// public class DateTimeExtensionTests { /// - /// Tests the method. + /// Tests the method. /// [Fact] public void FromUnixTimeSecondsTest() { DateTimeOffset time = new(new DateTime(2022, 6, 1, 12, 10, 34, DateTimeKind.Utc)); - Assert.Equal(time, DateTimeExtension.FromUnixTimeSeconds(1654085434)); + Assert.Equal(time, DateTimeExtensions.FromUnixTimeSeconds(1654085434)); } #if NETFRAMEWORK && !NET46_OR_GREATER /// - /// Tests the method. + /// Tests the method. /// [Fact] public void ToUnixTimeSecondsTest() @@ -31,17 +31,17 @@ public void ToUnixTimeSecondsTest() #endif /// - /// Tests the method. + /// Tests the method. /// [Fact] public void FromUnixEpochTest() { DateTime time = new(2022, 6, 1, 12, 10, 34, DateTimeKind.Utc); - Assert.Equal(time, DateTimeExtension.FromUnixEpoch(1654085434)); + Assert.Equal(time, DateTimeExtensions.FromUnixEpoch(1654085434)); } /// - /// Tests the method. + /// Tests the method. /// [Fact] public void ToUnixEpochTest() diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs index acd05c1b..a647c413 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs @@ -37,7 +37,7 @@ await RunTestAsync( Assert.NotNull(value); Assert.Equal(UnixFileType.Regular, value.FileType & UnixFileType.TypeMask); Assert.Equal(597, value.Size); - Assert.Equal(DateTimeExtension.Epoch.ToLocalTime(), value.Time); + Assert.Equal(DateTimeExtensions.Epoch.ToLocalTime(), value.Time); } [Fact] diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs index 79c872af..89434f07 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs @@ -48,7 +48,7 @@ public void StatTest() Assert.NotNull(value); Assert.Equal(UnixFileType.Regular, value.FileType & UnixFileType.TypeMask); Assert.Equal(597, value.Size); - Assert.Equal(DateTimeExtension.Epoch.ToLocalTime(), value.Time); + Assert.Equal(DateTimeExtensions.Epoch.ToLocalTime(), value.Time); } [Fact] diff --git a/AdvancedSharpAdbClient.Tests/TcpSocketTests.Async.cs b/AdvancedSharpAdbClient.Tests/TcpSocketTests.Async.cs index e33f62cf..e8faeb58 100644 --- a/AdvancedSharpAdbClient.Tests/TcpSocketTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/TcpSocketTests.Async.cs @@ -1,4 +1,5 @@ -using System.Net; +using System; +using System.Net; using System.Net.Sockets; using System.Text; using Xunit; @@ -10,20 +11,53 @@ public partial class TcpSocketTests [Fact] public async void LifecycleAsyncTest() { - TcpSocket socket = new(); + using TcpSocket socket = new(); Assert.False(socket.Connected); - socket.Connect(new DnsEndPoint("www.bing.com", 80)); + await socket.ConnectAsync(new DnsEndPoint("www.bing.com", 80)); Assert.True(socket.Connected); byte[] data = Encoding.ASCII.GetBytes("GET / HTTP/1.1\n\n"); - await socket.SendAsync(data, 0, data.Length, SocketFlags.None); + await socket.SendAsync(data, data.Length, SocketFlags.None); byte[] responseData = new byte[128]; - await socket.ReceiveAsync(responseData, 0, responseData.Length, SocketFlags.None); + await socket.ReceiveAsync(responseData, responseData.Length, SocketFlags.None); _ = Encoding.ASCII.GetString(responseData); + } + + [Fact] + public async void LifecycleAsyncMemoryTest() + { + using TcpSocket socket = new(); + Assert.False(socket.Connected); + + await socket.ConnectAsync(new DnsEndPoint("www.bing.com", 80)); + Assert.True(socket.Connected); + + ReadOnlyMemory data = Encoding.ASCII.GetBytes("GET / HTTP/1.1\n\n"); + await socket.SendAsync(data, SocketFlags.None); + + byte[] responseData = new byte[128]; + await socket.ReceiveAsync(responseData.AsMemory(), SocketFlags.None); + + _ = Encoding.ASCII.GetString(responseData); + } + + [Fact] + public async void ReconnectAsyncTest() + { + using TcpSocket socket = new(); + Assert.False(socket.Connected); + + await socket.ConnectAsync(new DnsEndPoint("www.bing.com", 80)); + Assert.True(socket.Connected); + socket.Dispose(); + Assert.False(socket.Connected); + + await socket.ReconnectAsync(); + Assert.True(socket.Connected); } } } diff --git a/AdvancedSharpAdbClient.Tests/TcpSocketTests.cs b/AdvancedSharpAdbClient.Tests/TcpSocketTests.cs index 4dcd5b9f..8e540bac 100644 --- a/AdvancedSharpAdbClient.Tests/TcpSocketTests.cs +++ b/AdvancedSharpAdbClient.Tests/TcpSocketTests.cs @@ -15,26 +15,43 @@ public partial class TcpSocketTests [Fact] public void LifecycleTest() { - TcpSocket socket = new(); + using TcpSocket socket = new(); Assert.False(socket.Connected); socket.Connect(new DnsEndPoint("www.bing.com", 80)); Assert.True(socket.Connected); byte[] data = Encoding.ASCII.GetBytes("GET / HTTP/1.1\n\n"); - socket.Send(data, 0, data.Length, SocketFlags.None); + socket.Send(data, data.Length, SocketFlags.None); byte[] responseData = new byte[128]; - socket.Receive(responseData, 0, SocketFlags.None); + socket.Receive(responseData, responseData.Length, SocketFlags.None); + + _ = Encoding.ASCII.GetString(responseData); + } + + [Fact] + public void LifecycleSpanTest() + { + using TcpSocket socket = new(); + Assert.False(socket.Connected); + + socket.Connect(new DnsEndPoint("www.bing.com", 80)); + Assert.True(socket.Connected); + + ReadOnlySpan data = Encoding.ASCII.GetBytes("GET / HTTP/1.1\n\n"); + socket.Send(data, SocketFlags.None); + + byte[] responseData = new byte[128]; + socket.Receive([.. responseData], SocketFlags.None); _ = Encoding.ASCII.GetString(responseData); - socket.Dispose(); } [Fact] public void ReconnectTest() { - TcpSocket socket = new(); + using TcpSocket socket = new(); Assert.False(socket.Connected); socket.Connect(new DnsEndPoint("www.bing.com", 80)); @@ -50,19 +67,18 @@ public void ReconnectTest() [Fact] public void BufferSizeTest() { - TcpSocket socket = new() + using TcpSocket socket = new() { ReceiveBufferSize = 1024 }; // https://stackoverflow.com/questions/29356626/is-there-a-way-to-reduce-the-minimum-lower-limit-of-the-socket-send-buffer-size Assert.Equal(RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? 2304 : 1024, socket.ReceiveBufferSize); - socket.Dispose(); } [Fact] public void CreateUnsupportedSocketTest() { - TcpSocket socket = new(); + using TcpSocket socket = new(); _ = Assert.Throws(() => socket.Connect(new CustomEndPoint())); } } diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index 70486bdc..1ea09b8c 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -368,7 +368,7 @@ protected async Task RootAsync(string request, DeviceData device, CancellationTo // see https://android.googlesource.com/platform/packages/modules/adb/+/refs/heads/master/daemon/restart_service.cpp // for possible return strings -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if HAS_FULLSTRING if (!responseMessage.Contains("restarting", StringComparison.OrdinalIgnoreCase)) #else if (responseMessage.IndexOf("restarting", StringComparison.OrdinalIgnoreCase) == -1) @@ -423,7 +423,7 @@ public async Task InstallAsync(DeviceData device, Stream apk, CancellationToken byte[] buffer = new byte[32 * 1024]; int read = 0; -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if HAS_BUFFERS while ((read = await apk.ReadAsync(buffer, cancellationToken)) > 0) #elif !NET35 while ((read = await apk.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0) @@ -603,7 +603,7 @@ public async Task InstallWriteAsync(DeviceData device, Stream apk, string apkNam byte[] buffer = new byte[32 * 1024]; int read = 0; -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if HAS_BUFFERS while ((read = await apk.ReadAsync(buffer, cancellationToken)) > 0) #elif !NET35 while ((read = await apk.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0) diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index 906b35d0..21572d4b 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -504,7 +504,7 @@ protected void Root(string request, DeviceData device) // see https://android.googlesource.com/platform/packages/modules/adb/+/refs/heads/master/daemon/restart_service.cpp // for possible return strings -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if HAS_FULLSTRING if (!responseMessage.Contains("restarting", StringComparison.OrdinalIgnoreCase)) #else if (responseMessage.IndexOf("restarting", StringComparison.OrdinalIgnoreCase) == -1) diff --git a/AdvancedSharpAdbClient/AdbSocket.Async.cs b/AdvancedSharpAdbClient/AdbSocket.Async.cs index 358927e9..cebc72b8 100644 --- a/AdvancedSharpAdbClient/AdbSocket.Async.cs +++ b/AdvancedSharpAdbClient/AdbSocket.Async.cs @@ -14,15 +14,35 @@ namespace AdvancedSharpAdbClient { public partial class AdbSocket { +#if NET6_0_OR_GREATER /// - public Task SendAsync(byte[] data, int length, CancellationToken cancellationToken = default) => SendAsync(data, 0, length, cancellationToken); + public virtual ValueTask ReconnectAsync(CancellationToken cancellationToken = default) => socket.ReconnectAsync(cancellationToken); +#endif + + /// + public virtual async Task SendAsync(byte[] data, int length, CancellationToken cancellationToken = default) + { + try + { + int count = await socket.SendAsync(data, length != -1 ? length : data.Length, SocketFlags.None, cancellationToken); + if (count < 0) + { + throw new AdbException("channel EOF"); + } + } + catch (SocketException ex) + { + logger.LogError(ex, ex.Message); + throw; + } + } /// public virtual async Task SendAsync(byte[] data, int offset, int length, CancellationToken cancellationToken = default) { try { - int count = await socket.SendAsync(data, offset, length != -1 ? length : data.Length, SocketFlags.None, cancellationToken); + int count = await socket.SendAsync(data, offset, length != -1 ? length : data.Length - offset, SocketFlags.None, cancellationToken); if (count < 0) { throw new AdbException("channel EOF"); @@ -82,20 +102,20 @@ public virtual async Task SendAdbRequestAsync(string request, CancellationToken } /// - public Task ReadAsync(byte[] data, CancellationToken cancellationToken = default) => - ReadAsync(data, data.Length, cancellationToken); + public virtual Task ReadAsync(byte[] data, int length, CancellationToken cancellationToken = default) => + ReadAsync(data, 0, length, cancellationToken); /// - public virtual async Task ReadAsync(byte[] data, int length, CancellationToken cancellationToken = default) + public virtual async Task ReadAsync(byte[] data, int offset, int length, CancellationToken cancellationToken = default) { - ExceptionExtensions.ThrowIfNegative(length); - ExceptionExtensions.ThrowIfNull(data); + ExceptionExtensions.ThrowIfNegative(offset); + length = length != -1 ? length : data.Length; ExceptionExtensions.ThrowIfLessThan(data.Length, length, nameof(data)); int count = -1; - int totalRead = 0; + int totalRead = offset; while (count != 0 && totalRead < length) { @@ -222,6 +242,91 @@ public virtual async Task SetDeviceAsync(DeviceData device, CancellationToken ca } } +#if HAS_BUFFERS + /// + public virtual async ValueTask SendAsync(ReadOnlyMemory data, CancellationToken cancellationToken = default) + { + try + { + int count = await socket.SendAsync(data, SocketFlags.None, cancellationToken); + if (count < 0) + { + throw new AdbException("channel EOF"); + } + } + catch (SocketException ex) + { + logger.LogError(ex, ex.Message); + throw; + } + } + + /// + public virtual async ValueTask ReadAsync(Memory data, CancellationToken cancellationToken) + { + ExceptionExtensions.ThrowIfNull(data); + + int count = -1; + int totalRead = 0; + int length = data.Length; + + while (count != 0 && totalRead < length) + { + cancellationToken.ThrowIfCancellationRequested(); + + try + { + int left = length - totalRead; + int bufferLength = left < ReceiveBufferSize ? left : ReceiveBufferSize; + + count = await socket.ReceiveAsync(data.Slice(totalRead, bufferLength), SocketFlags.None, cancellationToken).ConfigureAwait(false); + + if (count < 0) + { + logger.LogError("read: channel EOF"); + throw new AdbException("EOF"); + } + else if (count == 0) + { + logger.LogInformation("DONE with Read"); + } + else + { + totalRead += count; + } + } + catch (SocketException ex) + { + throw new AdbException($"An error occurred while receiving data from the adb server: {ex.Message}.", ex); + } + } + + return totalRead; + } +#else + /// + public virtual async Task SendAsync(byte[] data, CancellationToken cancellationToken = default) + { + try + { + int count = await socket.SendAsync(data, SocketFlags.None, cancellationToken); + if (count < 0) + { + throw new AdbException("channel EOF"); + } + } + catch (SocketException ex) + { + logger.LogError(ex, ex.Message); + throw; + } + } + + /// + public virtual Task ReadAsync(byte[] data, CancellationToken cancellationToken = default) => + ReadAsync(data, 0, data.Length, cancellationToken); +#endif + /// /// Write until all data in "data" is written or the connection fails or times out. /// @@ -233,7 +338,7 @@ protected virtual async Task WriteAsync(byte[] data, CancellationToken can { try { - await SendAsync(data, -1, cancellationToken); + await SendAsync(data, cancellationToken); } catch (IOException e) { diff --git a/AdvancedSharpAdbClient/AdbSocket.cs b/AdvancedSharpAdbClient/AdbSocket.cs index 19e66e68..75f51b1f 100644 --- a/AdvancedSharpAdbClient/AdbSocket.cs +++ b/AdvancedSharpAdbClient/AdbSocket.cs @@ -13,7 +13,6 @@ namespace AdvancedSharpAdbClient { - /// /// Implements a client for the Android Debug Bridge client-server protocol. Using the client, you /// can send messages to and receive messages from the Android Debug Bridge. @@ -109,14 +108,29 @@ public AdbSocket(ITcpSocket socket, ILogger logger = null) public virtual void Reconnect() => socket.Reconnect(); /// - public void Send(byte[] data, int length) => Send(data, 0, length); + public virtual void Send(byte[] data, int length) + { + try + { + int count = socket.Send(data, length != -1 ? length : data.Length, SocketFlags.None); + if (count < 0) + { + throw new AdbException("channel EOF"); + } + } + catch (SocketException sex) + { + logger.LogError(sex, sex.Message); + throw; + } + } /// public virtual void Send(byte[] data, int offset, int length) { try { - int count = socket.Send(data, 0, length != -1 ? length : data.Length, SocketFlags.None); + int count = socket.Send(data, offset, length != -1 ? length : data.Length, SocketFlags.None); if (count < 0) { throw new AdbException("channel EOF"); @@ -176,24 +190,29 @@ public virtual void SendAdbRequest(string request) } /// - public int Read(byte[] data) => Read(data, data.Length); + public virtual int Read(byte[] data, int length) => Read(data, 0, length); /// - public virtual int Read(byte[] data, int length) + public virtual int Read(byte[] data, int offset, int length) { - int expLen = length != -1 ? length : data.Length; + ExceptionExtensions.ThrowIfNull(data); + ExceptionExtensions.ThrowIfNegative(offset); + + length = length != -1 ? length : data.Length; + ExceptionExtensions.ThrowIfLessThan(data.Length, length, nameof(data)); + int count = -1; - int totalRead = 0; + int totalRead = offset; - while (count != 0 && totalRead < expLen) + while (count != 0 && totalRead < length) { try { - int left = expLen - totalRead; + int left = length - totalRead; int bufferLength = left < ReceiveBufferSize ? left : ReceiveBufferSize; - byte[] buffer = new byte[bufferLength]; - count = socket.Receive(buffer, bufferLength, SocketFlags.None); + count = socket.Receive(data, totalRead, bufferLength, SocketFlags.None); + if (count < 0) { logger.LogError("read: channel EOF"); @@ -205,13 +224,12 @@ public virtual int Read(byte[] data, int length) } else { - Array.Copy(buffer, 0, data, totalRead, count); totalRead += count; } } - catch (SocketException sex) + catch (SocketException ex) { - throw new AdbException($"No Data to read: {sex.Message}"); + throw new AdbException($"An error occurred while receiving data from the adb server: {ex.Message}.", ex); } } @@ -288,8 +306,88 @@ public virtual AdbResponse ReadAdbResponse() return response; } +#if HAS_BUFFERS + /// + public virtual void Send(ReadOnlySpan data) + { + try + { + int count = socket.Send(data, SocketFlags.None); + if (count < 0) + { + throw new AdbException("channel EOF"); + } + } + catch (SocketException sex) + { + logger.LogError(sex, sex.Message); + throw; + } + } + + /// + public virtual int Read(Span data) + { + int count = -1; + int totalRead = 0; + int length = data.Length; + + while (count != 0 && totalRead < length) + { + try + { + int left = length - totalRead; + int bufferLength = left < ReceiveBufferSize ? left : ReceiveBufferSize; + + count = socket.Receive(data.Slice(totalRead, bufferLength), SocketFlags.None); + + if (count < 0) + { + logger.LogError("read: channel EOF"); + throw new AdbException("EOF"); + } + else if (count == 0) + { + logger.LogInformation("DONE with Read"); + } + else + { + totalRead += count; + } + } + catch (SocketException ex) + { + throw new AdbException($"An error occurred while receiving data from the adb server: {ex.Message}.", ex); + } + } + + return totalRead; + } +#else + /// + public virtual void Send(byte[] data) + { + try + { + int count = socket.Send(data, SocketFlags.None); + if (count < 0) + { + throw new AdbException("channel EOF"); + } + } + catch (SocketException sex) + { + logger.LogError(sex, sex.Message); + throw; + } + } + + /// + public virtual int Read(byte[] data) => Read(data, 0, data.Length); +#endif + /// - public Stream GetShellStream() + public virtual Stream GetShellStream() { Stream stream = socket.GetStream(); return new ShellStream(stream, closeStream: true); @@ -328,11 +426,15 @@ public void SetDevice(DeviceData device) /// The data to send. /// Returns if all data was written; otherwise, . /// This uses the default time out value. +#if HAS_BUFFERS + protected virtual bool Write(ReadOnlySpan data) +#else protected virtual bool Write(byte[] data) +#endif { try { - Send(data, -1); + Send(data); } catch (IOException e) { @@ -405,5 +507,8 @@ public void Dispose() Dispose(disposing: true); GC.SuppressFinalize(this); } + + /// + public virtual void Close() => socket.Dispose(); } } diff --git a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj index ad12a25b..6478b2ad 100644 --- a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj +++ b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj @@ -104,6 +104,16 @@ $(DefineConstants);HAS_DRAWING + + $(DefineConstants);HAS_FULLSTRING + + +// // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // @@ -9,7 +9,7 @@ namespace AdvancedSharpAdbClient /// /// Provides helper methods for working with Unix-based date formats. /// - public static class DateTimeExtension + public static class DateTimeExtensions { #if NETFRAMEWORK && !NET46_OR_GREATER // Number of 100ns ticks per time unit diff --git a/AdvancedSharpAdbClient/Extensions/Extensions.cs b/AdvancedSharpAdbClient/Extensions/Extensions.cs index c8b43aad..957f82a6 100644 --- a/AdvancedSharpAdbClient/Extensions/Extensions.cs +++ b/AdvancedSharpAdbClient/Extensions/Extensions.cs @@ -83,7 +83,7 @@ public static bool IsNullOrWhiteSpace(this string value) #endif } -#if !UAP10_0_15138_0 && !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER +#if !HAS_FULLSTRING /// /// Returns a value indicating whether a specified string occurs within this string, using the specified comparison rules. /// @@ -329,33 +329,5 @@ public static bool IsUnixPlatform() => is PlatformID.Unix or PlatformID.MacOSX; #endif - -#if !HAS_PROCESS - /// - /// Begins to asynchronously receive data from a connected . - /// - /// The Socket. - /// An array of type that is the storage location for the received data. - /// The zero-based position in the parameter at which to store the received data. - /// The number of bytes to receive. - /// A bitwise combination of the values. - /// An delegate that references the method to invoke when the operation is complete. - /// A user-defined object that contains information about the receive operation. This object is - /// passed to the delegate when the operation is complete. - /// An that references the asynchronous read. - public static IAsyncResult BeginReceive(this System.Net.Sockets.Socket socket, byte[] buffer, int offset, int size, System.Net.Sockets.SocketFlags socketFlags, AsyncCallback callback, object state) => - TaskToApm.Begin(socket.ReceiveAsync(buffer, offset, size, socketFlags, default), callback, state); -#endif - -#if !HAS_PROCESS - /// - /// Ends a pending asynchronous read. - /// - /// The Socket. - /// An that stores state information and any user defined data for this asynchronous operation. - /// The number of bytes received. - public static int EndReceive(this System.Net.Sockets.Socket _, IAsyncResult asyncResult) => - TaskToApm.End(asyncResult); -#endif } } diff --git a/AdvancedSharpAdbClient/Extensions/SocketExtensions.cs b/AdvancedSharpAdbClient/Extensions/SocketExtensions.cs index 4fa7fed1..f079eb6b 100644 --- a/AdvancedSharpAdbClient/Extensions/SocketExtensions.cs +++ b/AdvancedSharpAdbClient/Extensions/SocketExtensions.cs @@ -12,8 +12,35 @@ namespace AdvancedSharpAdbClient /// /// Provides extension methods for the class. /// - internal static class SocketExtensions + public static class SocketExtensions { +#if !HAS_BUFFERS + /// + /// Asynchronously receives data from a connected socket. + /// + /// The socket from which to read data. + /// An array of type that is the storage location for the received data. + /// A bitwise combination of the values. + /// A which can be used to cancel the asynchronous task. + /// Cancelling the task will also close the socket. + /// The number of bytes received. + public static Task ReceiveAsync(this Socket socket, byte[] buffer, SocketFlags socketFlags, CancellationToken cancellationToken = default) + => ReceiveAsync(socket, buffer, 0, buffer.Length, socketFlags, cancellationToken); +#endif + + /// + /// Asynchronously receives data from a connected socket. + /// + /// The socket from which to read data. + /// An array of type that is the storage location for the received data. + /// The number of bytes to receive. + /// A bitwise combination of the values. + /// A which can be used to cancel the asynchronous task. + /// Cancelling the task will also close the socket. + /// The number of bytes received. + public static Task ReceiveAsync(this Socket socket, byte[] buffer, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) + => ReceiveAsync(socket, buffer, 0, size, socketFlags, cancellationToken); + /// /// Asynchronously receives data from a connected socket. /// @@ -25,14 +52,93 @@ internal static class SocketExtensions /// A which can be used to cancel the asynchronous task. /// Cancelling the task will also close the socket. /// The number of bytes received. - public static Task ReceiveAsync( - this Socket socket, - byte[] buffer, - int offset, - int size, - SocketFlags socketFlags, - CancellationToken cancellationToken) + public static Task ReceiveAsync(this Socket socket, byte[] buffer, int offset, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) + { +#if HAS_BUFFERS + return socket.ReceiveAsync(buffer.AsMemory(offset, size), socketFlags, cancellationToken).AsTask(); +#elif HAS_PROCESS + + // Register a callback so that when a cancellation is requested, the socket is closed. + // 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; + + try + { + taskCompletionSource.TrySetResult(socket.EndReceive(iar)); + } + catch (ObjectDisposedException) when (cancellationToken.IsCancellationRequested) + { + taskCompletionSource.TrySetCanceled(); + } + catch (Exception ex) + { + taskCompletionSource.TrySetException(ex); + } + finally + { + cancellationTokenRegistration.Dispose(); + } + }, taskCompletionSource); + + return taskCompletionSource.Task; +#else + return Extensions.Run(() => socket.Receive(buffer, offset, size, socketFlags)); +#endif + } + +#if !HAS_BUFFERS + /// + /// Asynchronously sends data to a connected socket. + /// + /// The socket from which to read data. + /// An array of type that contains the data to be sent. + /// A bitwise combination of the values. + /// A which can be used to cancel the asynchronous task. + /// Cancelling the task will also close the socket. + /// The number of bytes received. + public static Task SendAsync(this Socket socket, byte[] buffer, SocketFlags socketFlags, CancellationToken cancellationToken = default) + => SendAsync(socket, buffer, 0, buffer.Length, socketFlags, cancellationToken); +#endif + + /// + /// Asynchronously sends data to a connected socket. + /// + /// The socket from which to read data. + /// An array of type that contains the data to be sent. + /// The number of bytes to send. + /// A bitwise combination of the values. + /// A which can be used to cancel the asynchronous task. + /// Cancelling the task will also close the socket. + /// The number of bytes received. + public static Task SendAsync(this Socket socket, byte[] buffer, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) + => SendAsync(socket, buffer, 0, size, socketFlags, cancellationToken); + + /// + /// Asynchronously sends data to a connected socket. + /// + /// The socket from which to read data. + /// An array of type that contains the data to be sent. + /// The position in the data buffer at which to begin sending data. + /// The number of bytes to send. + /// A bitwise combination of the values. + /// A which can be used to cancel the asynchronous task. + /// Cancelling the task will also close the socket. + /// The number of bytes received. + public static Task SendAsync(this Socket socket, byte[] buffer, int offset, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) { +#if HAS_BUFFERS + return socket.SendAsync(buffer.AsMemory(offset, size), socketFlags, cancellationToken).AsTask(); +#elif HAS_PROCESS // Register a callback so that when a cancellation is requested, the socket is closed. // 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. @@ -40,27 +146,24 @@ public static Task ReceiveAsync( TaskCompletionSource taskCompletionSource = new(socket); - socket.BeginReceive(buffer, offset, size, socketFlags, (iar) => + _ = socket.BeginSend(buffer, offset, size, socketFlags, (iar) => { // this is the callback - TaskCompletionSource taskCompletionSource2 = (TaskCompletionSource)iar.AsyncState; - Socket socket2 = (Socket)taskCompletionSource2.Task.AsyncState; + TaskCompletionSource taskCompletionSource = (TaskCompletionSource)iar.AsyncState; + Socket socket = (Socket)taskCompletionSource.Task.AsyncState; try { - taskCompletionSource2.TrySetResult(socket2.EndReceive(iar)); + taskCompletionSource.TrySetResult(socket.EndSend(iar)); + } + catch (ObjectDisposedException) when (cancellationToken.IsCancellationRequested) + { + taskCompletionSource.TrySetCanceled(); } catch (Exception ex) { - if (ex is ObjectDisposedException && cancellationToken.IsCancellationRequested) - { - taskCompletionSource2.TrySetCanceled(); - } - else - { - taskCompletionSource2.TrySetException(ex); - } + taskCompletionSource.TrySetException(ex); } finally { @@ -69,6 +172,9 @@ public static Task ReceiveAsync( }, taskCompletionSource); return taskCompletionSource.Task; +#else + return Extensions.Run(() => socket.Receive(buffer, offset, size, socketFlags), cancellationToken); +#endif } } } diff --git a/AdvancedSharpAdbClient/Extensions/TaskToApm.cs b/AdvancedSharpAdbClient/Extensions/TaskToApm.cs deleted file mode 100644 index a0b7a9d3..00000000 --- a/AdvancedSharpAdbClient/Extensions/TaskToApm.cs +++ /dev/null @@ -1,123 +0,0 @@ -#if !HAS_PROCESS -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -// Helper methods for using Tasks to implement the APM pattern. -// -// Example usage, wrapping a Task-returning FooAsync method with Begin/EndFoo methods: -// -// public IAsyncResult BeginFoo(..., AsyncCallback? callback, object? state) => -// TaskToApm.Begin(FooAsync(...), callback, state); -// -// public int EndFoo(IAsyncResult asyncResult) => -// TaskToApm.End(asyncResult); - -namespace System.Threading.Tasks -{ - /// - /// Provides support for efficiently using Tasks to implement the APM (Begin/End) pattern. - /// - internal static class TaskToApm - { - /// - /// Marshals the Task as an IAsyncResult, using the supplied callback and state - /// to implement the APM pattern. - /// - /// The Task to be marshaled. - /// The callback to be invoked upon completion. - /// The state to be stored in the IAsyncResult. - /// An IAsyncResult to represent the task's asynchronous operation. - public static IAsyncResult Begin(Task task, AsyncCallback callback, object state) => - new TaskAsyncResult(task, state, callback); - - /// Processes an IAsyncResult returned by Begin. - /// The IAsyncResult to unwrap. - public static void End(IAsyncResult asyncResult) - { - if (GetTask(asyncResult) is Task t) - { - t.GetAwaiter().GetResult(); - return; - } - - ThrowArgumentException(asyncResult); - } - - /// Processes an IAsyncResult returned by Begin. - /// The IAsyncResult to unwrap. - public static TResult End(IAsyncResult asyncResult) - { - if (GetTask(asyncResult) is Task task) - { - return task.GetAwaiter().GetResult(); - } - - ThrowArgumentException(asyncResult); - return default!; // unreachable - } - - /// Gets the task represented by the IAsyncResult. - public static Task GetTask(IAsyncResult asyncResult) => (asyncResult as TaskAsyncResult)?._task; - - /// Throws an argument exception for the invalid . - private static void ThrowArgumentException(IAsyncResult asyncResult) => - throw (asyncResult is null - ? new ArgumentNullException(nameof(asyncResult)) - : new ArgumentException(null, nameof(asyncResult))); - - /// Provides a simple IAsyncResult that wraps a Task. - /// - /// We could use the Task as the IAsyncResult if the Task's AsyncState is the same as the object state, - /// but that's very rare, in particular in a situation where someone cares about allocation, and always - /// using TaskAsyncResult simplifies things and enables additional optimizations. - /// - internal sealed class TaskAsyncResult : IAsyncResult - { - /// The wrapped Task. - internal readonly Task _task; - /// Callback to invoke when the wrapped task completes. - private readonly AsyncCallback _callback; - - /// Initializes the IAsyncResult with the Task to wrap and the associated object state. - /// The Task to wrap. - /// The new AsyncState value. - /// Callback to invoke when the wrapped task completes. - internal TaskAsyncResult(Task task, object state, AsyncCallback callback) - { - _task = task; - AsyncState = state; - - if (task.IsCompleted) - { - // Synchronous completion. Invoke the callback. No need to store it. - CompletedSynchronously = true; - callback?.Invoke(this); - } - else if (callback != null) - { - // Asynchronous completion, and we have a callback; schedule it. We use OnCompleted rather than ContinueWith in - // order to avoid running synchronously if the task has already completed by the time we get here but still run - // synchronously as part of the task's completion if the task completes after (the more common case). - _callback = callback; - _task.ConfigureAwait(continueOnCapturedContext: false) - .GetAwaiter() - .OnCompleted(InvokeCallback); // allocates a delegate, but avoids a closure - } - } - - /// Invokes the callback. - private void InvokeCallback() => _callback.Invoke(this); - - /// Gets a user-defined object that qualifies or contains information about an asynchronous operation. - public object AsyncState { get; } - /// Gets a value that indicates whether the asynchronous operation completed synchronously. - /// This is set lazily based on whether the has completed by the time this object is created. - public bool CompletedSynchronously { get; } - /// Gets a value that indicates whether the asynchronous operation has completed. - public bool IsCompleted => _task.IsCompleted; - /// Gets a that is used to wait for an asynchronous operation to complete. - public WaitHandle AsyncWaitHandle => ((IAsyncResult)_task).AsyncWaitHandle; - } - } -} -#endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbSocket.Async.cs b/AdvancedSharpAdbClient/Interfaces/IAdbSocket.Async.cs index b406758e..27481e6c 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbSocket.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbSocket.Async.cs @@ -3,12 +3,25 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // +using System; +using System.Net; +using System.Net.Sockets; using System.Threading; namespace AdvancedSharpAdbClient { public partial interface IAdbSocket { +#if NET6_0_OR_GREATER + /// + /// Reconnects the to the same endpoint it was initially connected to. + /// Use this when the socket was disconnected by adb and you have restarted adb. + /// + /// A which can be used to cancel the asynchronous task. + /// A that represents the asynchronous operation. + ValueTask ReconnectAsync(CancellationToken cancellationToken); +#endif + /// /// Sends the specified number of bytes of data to a , /// @@ -65,15 +78,6 @@ public partial interface IAdbSocket /// A that represents the asynchronous operation. Task SendAdbRequestAsync(string request, CancellationToken cancellationToken); - /// - /// Reads a from an instance when - /// the connection is in sync mode. - /// - /// The buffer to store the read data into. - /// A that can be used to cancel the task. - /// A that represents the asynchronous operation. - Task ReadAsync(byte[] data, CancellationToken cancellationToken); - /// /// Receives data from a into a receive buffer. /// @@ -114,6 +118,42 @@ public partial interface IAdbSocket /// A object that represents the response from the Android Debug Bridge. Task ReadAdbResponseAsync(CancellationToken cancellationToken); +#if HAS_BUFFERS + /// + /// Sends the specified number of bytes of data to a , + /// + /// A array that acts as a buffer, containing the data to send. + /// A that can be used to cancel the task. + /// A that represents the asynchronous operation. + public ValueTask SendAsync(ReadOnlyMemory data, CancellationToken cancellationToken = default); + + /// + /// Receives data from a into a receive buffer. + /// + /// An array of type Byte that is the storage location for the received data. + /// A that can be used to cancel the task. + /// Cancelling the task will also close the socket. + /// A that represents the asynchronous operation. The result value of the task contains the number of bytes received. + public ValueTask ReadAsync(Memory data, CancellationToken cancellationToken); +#else + /// + /// Sends the specified number of bytes of data to a , + /// + /// A array that acts as a buffer, containing the data to send. + /// A that can be used to cancel the task. + /// A that represents the asynchronous operation. + Task SendAsync(byte[] data, CancellationToken cancellationToken); + + /// + /// Reads a from an instance when + /// the connection is in sync mode. + /// + /// The buffer to store the read data into. + /// A that can be used to cancel the task. + /// A that represents the asynchronous operation. The result value of the task contains the number of bytes received. + Task ReadAsync(byte[] data, CancellationToken cancellationToken); +#endif + /// /// Ask to switch the connection to the device/emulator identified by /// . After this request, every client request will diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbSocket.cs b/AdvancedSharpAdbClient/Interfaces/IAdbSocket.cs index 72617412..c397b5f6 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbSocket.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbSocket.cs @@ -5,6 +5,7 @@ using AdvancedSharpAdbClient.Exceptions; using System; using System.IO; +using System.Net.Sockets; namespace AdvancedSharpAdbClient { @@ -71,13 +72,15 @@ public partial interface IAdbSocket : IDisposable void SendAdbRequest(string request); /// - /// Reads from the socket until the array is filled, or no more data is coming(because - /// the socket closed or the timeout expired). + /// Reads from the socket until the array is filled, the optional + /// is reached, or no more data is coming (because the socket closed or the + /// timeout expired). /// - /// The buffer to store the read data into. + /// The buffer to store the read data into. + /// The length to read or -1 to fill the data buffer completely /// The total number of bytes read. - /// This uses the default time out value. - int Read(byte[] data); + /// EOF or No Data to read: exception.Message + int Read(byte[] data, int length); /// /// Reads from the socket until the array is filled, the optional @@ -85,10 +88,11 @@ public partial interface IAdbSocket : IDisposable /// timeout expired). /// /// The buffer to store the read data into. + /// The position to store the received data. /// The length to read or -1 to fill the data buffer completely /// The total number of bytes read. /// EOF or No Data to read: exception.Message - int Read(byte[] data, int length); + int Read(byte[] data, int offset, int length); /// /// Reads a from an instance. @@ -116,6 +120,39 @@ public partial interface IAdbSocket : IDisposable /// A object that represents the response from the Android Debug Bridge. AdbResponse ReadAdbResponse(); +#if HAS_BUFFERS + /// + /// Sends the specified number of bytes of data to a , + /// + /// A span of bytes that acts as a buffer, containing the data to send. + void Send(ReadOnlySpan data); + + /// + /// Reads from the socket until the array is filled, or no more data is coming(because + /// the socket closed or the timeout expired). + /// + /// A span of bytes to store the read data into. + /// The total number of bytes read. + /// This uses the default time out value. + int Read(Span data); +#else + /// + /// Sends the specified number of bytes of data to a , + /// + /// A array that acts as a buffer, containing the data to send. + void Send(byte[] data); + + /// + /// Reads from the socket until the array is filled, or no more data is coming(because + /// the socket closed or the timeout expired). + /// + /// The buffer to store the read data into. + /// The total number of bytes read. + /// This uses the default time out value. + int Read(byte[] data); +#endif + + /// /// Gets a that can be used to send and receive shell output to and /// from the device. @@ -134,5 +171,10 @@ public partial interface IAdbSocket : IDisposable /// The device to which to connect. /// If is , this method does nothing. void SetDevice(DeviceData device); + + /// + /// Closes the connection and releases all associated resources. + /// + void Close(); } } diff --git a/AdvancedSharpAdbClient/Interfaces/ITcpSocket.Async.cs b/AdvancedSharpAdbClient/Interfaces/ITcpSocket.Async.cs index ef95917f..b948d5c9 100644 --- a/AdvancedSharpAdbClient/Interfaces/ITcpSocket.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/ITcpSocket.Async.cs @@ -3,6 +3,8 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // +using System; +using System.Net; using System.Net.Sockets; using System.Threading; @@ -10,6 +12,35 @@ namespace AdvancedSharpAdbClient { public partial interface ITcpSocket { +#if NET6_0_OR_GREATER + /// + /// Begins an asynchronous request for a connection to a remote host. + /// + /// An that represents the remote device. + /// A which can be used to cancel the asynchronous task. + /// A that represents the asynchronous operation. + ValueTask ConnectAsync(EndPoint endPoint, CancellationToken cancellationToken); + + /// + /// Re-establishes the connection to a remote host. Assumes you have resolved the reason that caused the + /// socket to disconnect. + /// + /// A which can be used to cancel the asynchronous task. + /// A that represents the asynchronous operation. + ValueTask ReconnectAsync(CancellationToken cancellationToken); +#endif + + /// + /// Asynchronously sends the specified number of bytes of data to a connected + /// using the specified . + /// + /// An array of type Byte that contains the data to be sent. + /// The number of bytes to send. + /// A bitwise combination of the SocketFlags values. + /// A which can be used to cancel the asynchronous task. + /// The number of bytes sent to the Socket. + Task SendAsync(byte[] buffer, int size, SocketFlags socketFlags, CancellationToken cancellationToken); + /// /// Asynchronously sends the specified number of bytes of data to a connected /// , starting at the specified , @@ -24,8 +55,20 @@ public partial interface ITcpSocket Task SendAsync(byte[] buffer, int offset, int size, SocketFlags socketFlags, CancellationToken cancellationToken); /// - /// Receives the specified number of bytes from a bound into the specified offset position of the - /// receive buffer, using the specified SocketFlags. + /// Receives the specified number of bytes from a bound + /// using the specified SocketFlags. + /// + /// An array of type Byte that is the storage location for received data. + /// The number of bytes to receive. + /// A bitwise combination of the SocketFlags values. + /// A which can be used to cancel the asynchronous task. + /// Cancelling the task will also close the socket. + /// The number of bytes received. + Task ReceiveAsync(byte[] buffer, int size, SocketFlags socketFlags, CancellationToken cancellationToken); + + /// + /// Receives the specified number of bytes from a bound + /// into the specified offset position of the receive buffer, using the specified SocketFlags. /// /// An array of type Byte that is the storage location for received data. /// The location in buffer to store the received data. @@ -35,6 +78,50 @@ public partial interface ITcpSocket /// Cancelling the task will also close the socket. /// The number of bytes received. Task ReceiveAsync(byte[] buffer, int offset, int size, SocketFlags socketFlags, CancellationToken cancellationToken); + +#if HAS_BUFFERS + /// + /// Asynchronously sends the specified number of bytes of data to a connected + /// using the specified . + /// + /// An array of type Byte that contains the data to be sent. + /// A bitwise combination of the SocketFlags values. + /// A which can be used to cancel the asynchronous task. + /// The number of bytes sent to the Socket. + public ValueTask SendAsync(ReadOnlyMemory buffer, SocketFlags socketFlags, CancellationToken cancellationToken); + + /// + /// Receives the specified number of bytes from a bound + /// using the specified SocketFlags. + /// + /// An array of type Byte that is the storage location for received data. + /// A bitwise combination of the SocketFlags values. + /// A which can be used to cancel the asynchronous task. + /// Cancelling the task will also close the socket. + /// The number of bytes received. + public ValueTask ReceiveAsync(Memory buffer, SocketFlags socketFlags, CancellationToken cancellationToken); +#else + /// + /// Asynchronously sends the specified number of bytes of data to a connected + /// using the specified . + /// + /// An array of type Byte that contains the data to be sent. + /// A bitwise combination of the SocketFlags values. + /// A which can be used to cancel the asynchronous task. + /// The number of bytes sent to the Socket. + Task SendAsync(byte[] buffer, SocketFlags socketFlags, CancellationToken cancellationToken); + + /// + /// Receives the specified number of bytes from a bound + /// using the specified SocketFlags. + /// + /// An array of type Byte that is the storage location for received data. + /// A bitwise combination of the SocketFlags values. + /// A which can be used to cancel the asynchronous task. + /// Cancelling the task will also close the socket. + /// The number of bytes received. + Task ReceiveAsync(byte[] buffer, SocketFlags socketFlags, CancellationToken cancellationToken); +#endif } } #endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Interfaces/ITcpSocket.cs b/AdvancedSharpAdbClient/Interfaces/ITcpSocket.cs index eac68481..af91ac64 100644 --- a/AdvancedSharpAdbClient/Interfaces/ITcpSocket.cs +++ b/AdvancedSharpAdbClient/Interfaces/ITcpSocket.cs @@ -18,7 +18,7 @@ public partial interface ITcpSocket : IDisposable { /// /// Gets a value indicating whether a is connected to a remote host as of the last - /// or operation. + /// or operation. /// bool Connected { get; } @@ -39,6 +39,16 @@ public partial interface ITcpSocket : IDisposable /// void Reconnect(); + /// + /// Sends the specified number of bytes of data to a connected + /// using the specified . + /// + /// An array of type Byte that contains the data to be sent. + /// The number of bytes to send. + /// A bitwise combination of the SocketFlags values. + /// The number of bytes sent to the Socket. + int Send(byte[] buffer, int size, SocketFlags socketFlags); + /// /// Sends the specified number of bytes of data to a connected /// , starting at the specified , @@ -52,8 +62,8 @@ public partial interface ITcpSocket : IDisposable int Send(byte[] buffer, int offset, int size, SocketFlags socketFlags); /// - /// Receives the specified number of bytes from a bound into the specified offset position of the - /// receive buffer, using the specified SocketFlags. + /// Receives the specified number of bytes from a bound + /// using the specified SocketFlags. /// /// An array of type Byte that is the storage location for received data. /// The number of bytes to receive. @@ -61,10 +71,64 @@ public partial interface ITcpSocket : IDisposable /// The number of bytes received. int Receive(byte[] buffer, int size, SocketFlags socketFlags); + /// + /// Receives the specified number of bytes from a bound + /// into the specified offset position of the receive buffer, using the specified SocketFlags. + /// + /// An array of type Byte that is the storage location for received data. + /// The position in the parameter to store the received data. + /// The number of bytes to receive. + /// A bitwise combination of the SocketFlags values. + /// The number of bytes received. + int Receive(byte[] buffer, int offset, int size, SocketFlags socketFlags); + +#if HAS_BUFFERS + /// + /// Sends the specified number of bytes of data to a connected + /// using the specified . + /// + /// A span of bytes that contains the data to be sent. + /// A bitwise combination of the SocketFlags values. + /// The number of bytes sent to the Socket. + int Send(ReadOnlySpan buffer, SocketFlags socketFlags); + + /// + /// Receives the specified number of bytes from a bound + /// using the specified SocketFlags. + /// + /// A span of bytes that is the storage location for the received data. + /// A bitwise combination of the SocketFlags values. + /// The number of bytes received. + int Receive(Span buffer, SocketFlags socketFlags); +#else + /// + /// Sends the specified number of bytes of data to a connected + /// using the specified . + /// + /// An array of type Byte that contains the data to be sent. + /// A bitwise combination of the SocketFlags values. + /// The number of bytes sent to the Socket. + int Send(byte[] buffer, SocketFlags socketFlags); + + /// + /// Receives the specified number of bytes from a bound + /// using the specified SocketFlags. + /// + /// An array of type Byte that is the storage location for received data. + /// A bitwise combination of the SocketFlags values. + /// The number of bytes received. + int Receive(byte[] buffer, SocketFlags socketFlags); +#endif + /// /// Gets the underlying . /// /// The underlying stream. Stream GetStream(); + + /// + /// Closes the connection and releases all associated resources. + /// + void Close(); } } diff --git a/AdvancedSharpAdbClient/Logs/LogReader.Async.cs b/AdvancedSharpAdbClient/Logs/LogReader.Async.cs index ae6d01ea..1867196c 100644 --- a/AdvancedSharpAdbClient/Logs/LogReader.Async.cs +++ b/AdvancedSharpAdbClient/Logs/LogReader.Async.cs @@ -94,7 +94,7 @@ public async Task ReadEntryAsync(CancellationToken cancellationToken = return null; } - DateTimeOffset timestamp = DateTimeExtension.FromUnixTimeSeconds(sec); + DateTimeOffset timestamp = DateTimeExtensions.FromUnixTimeSeconds(sec); switch ((LogId)id) { @@ -208,7 +208,7 @@ private async Task ReadBytesSafeAsync(int count, CancellationToken cance byte[] data = new byte[count]; while ((read = -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if HAS_BUFFERS await stream.ReadAsync(data.AsMemory(totalRead, count - totalRead), cancellationToken).ConfigureAwait(false) #elif !NET35 await stream.ReadAsync(data, totalRead, count - totalRead, cancellationToken).ConfigureAwait(false) diff --git a/AdvancedSharpAdbClient/Logs/LogReader.cs b/AdvancedSharpAdbClient/Logs/LogReader.cs index 32dad781..85143f5f 100644 --- a/AdvancedSharpAdbClient/Logs/LogReader.cs +++ b/AdvancedSharpAdbClient/Logs/LogReader.cs @@ -100,7 +100,7 @@ public virtual LogEntry ReadEntry() return null; } - DateTimeOffset timestamp = DateTimeExtension.FromUnixTimeSeconds(sec); + DateTimeOffset timestamp = DateTimeExtensions.FromUnixTimeSeconds(sec); switch ((LogId)id) { diff --git a/AdvancedSharpAdbClient/Models/ShellStream.cs b/AdvancedSharpAdbClient/Models/ShellStream.cs index 06090f87..bf366aca 100644 --- a/AdvancedSharpAdbClient/Models/ShellStream.cs +++ b/AdvancedSharpAdbClient/Models/ShellStream.cs @@ -155,7 +155,7 @@ public override int Read(byte[] buffer, int offset, int count) return read; } -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if HAS_BUFFERS /// public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) { @@ -210,7 +210,7 @@ async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToke { buffer[offset] = pendingByte.Value; read = -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if HAS_BUFFERS await Inner.ReadAsync(buffer.AsMemory(offset + 1, count - 1), cancellationToken).ConfigureAwait(false); #elif !NET35 await Inner.ReadAsync(buffer, offset + 1, count - 1, cancellationToken).ConfigureAwait(false); @@ -223,7 +223,7 @@ async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToke else { read = -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if HAS_BUFFERS await Inner.ReadAsync(buffer.AsMemory(offset, count), cancellationToken).ConfigureAwait(false); #elif !NET35 await Inner.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); @@ -263,7 +263,7 @@ async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToke } int miniRead = -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if HAS_BUFFERS await Inner.ReadAsync(miniBuffer.AsMemory(0, 1), cancellationToken).ConfigureAwait(false); #elif !NET35 await Inner.ReadAsync(miniBuffer, 0, 1, cancellationToken).ConfigureAwait(false); @@ -290,7 +290,7 @@ async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToke if (read > 0 && buffer[offset + read - 1] == 0x0d) { int miniRead = -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if HAS_BUFFERS await Inner.ReadAsync(miniBuffer.AsMemory(0, 1), cancellationToken).ConfigureAwait(false); #elif !NET35 await Inner.ReadAsync(miniBuffer, 0, 1, cancellationToken).ConfigureAwait(false); diff --git a/AdvancedSharpAdbClient/SyncService.Async.cs b/AdvancedSharpAdbClient/SyncService.Async.cs index 635ad6ef..c4598480 100644 --- a/AdvancedSharpAdbClient/SyncService.Async.cs +++ b/AdvancedSharpAdbClient/SyncService.Async.cs @@ -195,7 +195,7 @@ public virtual async Task PullAsync(string remoteFilePath, Stream stream, IProgr // now read the length we received await Socket.ReadAsync(buffer, size, cancellationToken); -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if HAS_BUFFERS await stream.WriteAsync(buffer.AsMemory(0, size), cancellationToken); #elif !NET35 await stream.WriteAsync(buffer, 0, size, cancellationToken); @@ -311,7 +311,7 @@ private async Task ReadStatisticsAsync(FileStatistics value, CancellationToken c value.FileType = (UnixFileType)BitConverter.ToInt32(statResult, 0); value.Size = BitConverter.ToInt32(statResult, 4); - value.Time = DateTimeExtension.FromUnixTimeSeconds(BitConverter.ToInt32(statResult, 8)); + value.Time = DateTimeExtensions.FromUnixTimeSeconds(BitConverter.ToInt32(statResult, 8)); } } } diff --git a/AdvancedSharpAdbClient/SyncService.cs b/AdvancedSharpAdbClient/SyncService.cs index 9ec35053..8a2fb238 100644 --- a/AdvancedSharpAdbClient/SyncService.cs +++ b/AdvancedSharpAdbClient/SyncService.cs @@ -376,7 +376,7 @@ private void ReadStatistics(FileStatistics value) value.FileType = (UnixFileType)BitConverter.ToInt32(statResult, 0); value.Size = BitConverter.ToInt32(statResult, 4); - value.Time = DateTimeExtension.FromUnixTimeSeconds(BitConverter.ToInt32(statResult, 8)); + value.Time = DateTimeExtensions.FromUnixTimeSeconds(BitConverter.ToInt32(statResult, 8)); } } } diff --git a/AdvancedSharpAdbClient/TcpSocket.Async.cs b/AdvancedSharpAdbClient/TcpSocket.Async.cs index 5f503bb7..b04a8aa2 100644 --- a/AdvancedSharpAdbClient/TcpSocket.Async.cs +++ b/AdvancedSharpAdbClient/TcpSocket.Async.cs @@ -4,6 +4,7 @@ // using System; +using System.Net; using System.Net.Sockets; using System.Threading; @@ -11,21 +12,82 @@ namespace AdvancedSharpAdbClient { public partial class TcpSocket { -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if NET6_0_OR_GREATER /// - public virtual async Task SendAsync(byte[] buffer, int offset, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) => - await socket.SendAsync(buffer.AsMemory().Slice(offset, size), socketFlags, cancellationToken); -#else + public virtual async ValueTask ConnectAsync(EndPoint endPoint, CancellationToken cancellationToken = default) + { + if (endPoint is not (IPEndPoint or DnsEndPoint)) + { + throw new NotSupportedException("Only TCP endpoints are supported"); + } + + await socket.ConnectAsync(endPoint, cancellationToken); + socket.Blocking = true; + this.endPoint = endPoint; + } + /// - public virtual async Task SendAsync(byte[] buffer, int offset, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) => - await Extensions.Run(() => Send(buffer, offset, size, socketFlags), cancellationToken); + public virtual ValueTask ReconnectAsync(CancellationToken cancellationToken = default) + { + if (socket.Connected) + { + // Already connected - nothing to do. + return ValueTask.CompletedTask; + } + else + { + socket.Dispose(); + socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + return ConnectAsync(endPoint, cancellationToken); + } + } #endif -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if HAS_BUFFERS + /// + public virtual Task SendAsync(byte[] buffer, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) => + socket.SendAsync(buffer.AsMemory(0, size), socketFlags, cancellationToken).AsTask(); + + /// + public virtual Task SendAsync(byte[] buffer, int offset, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) => + socket.SendAsync(buffer.AsMemory(offset, size), socketFlags, cancellationToken).AsTask(); + + /// + public ValueTask SendAsync(ReadOnlyMemory buffer, SocketFlags socketFlags, CancellationToken cancellationToken = default)=> + socket.SendAsync(buffer, socketFlags, cancellationToken); + + /// + public virtual Task ReceiveAsync(byte[] buffer, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) => + socket.ReceiveAsync(buffer.AsMemory(0, size), socketFlags, cancellationToken).AsTask(); + + /// + public virtual Task ReceiveAsync(byte[] buffer, int offset, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) => + socket.ReceiveAsync(buffer.AsMemory(offset, size), socketFlags, cancellationToken).AsTask(); + /// - public virtual async Task ReceiveAsync(byte[] buffer, int offset, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) => - await socket.ReceiveAsync(buffer.AsMemory().Slice(offset, size), socketFlags, cancellationToken); + public ValueTask ReceiveAsync(Memory buffer, SocketFlags socketFlags, CancellationToken cancellationToken = default) => + socket.ReceiveAsync(buffer, socketFlags, cancellationToken); #else + /// + public virtual Task SendAsync(byte[] buffer, SocketFlags socketFlags, CancellationToken cancellationToken = default) => + socket.SendAsync(buffer, socketFlags, cancellationToken); + + /// + public virtual Task SendAsync(byte[] buffer, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) => + socket.SendAsync(buffer, size, socketFlags, cancellationToken); + + /// + public virtual Task SendAsync(byte[] buffer, int offset, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) => + socket.SendAsync(buffer, offset, size, socketFlags, cancellationToken); + + /// + public virtual Task ReceiveAsync(byte[] buffer, SocketFlags socketFlags, CancellationToken cancellationToken = default) => + socket.ReceiveAsync(buffer, socketFlags, cancellationToken); + + /// + public virtual Task ReceiveAsync(byte[] buffer, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) => + socket.ReceiveAsync(buffer, size, socketFlags, cancellationToken); + /// public virtual Task ReceiveAsync(byte[] buffer, int offset, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) => socket.ReceiveAsync(buffer, offset, size, socketFlags, cancellationToken); diff --git a/AdvancedSharpAdbClient/TcpSocket.cs b/AdvancedSharpAdbClient/TcpSocket.cs index 129a74ca..3f3aba94 100644 --- a/AdvancedSharpAdbClient/TcpSocket.cs +++ b/AdvancedSharpAdbClient/TcpSocket.cs @@ -60,9 +60,12 @@ public virtual void Reconnect() // Already connected - nothing to do. return; } - - socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - Connect(endPoint); + else + { + socket.Dispose(); + socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + Connect(endPoint); + } } /// @@ -81,6 +84,18 @@ public void Dispose() GC.SuppressFinalize(this); } + /// + public virtual void Close() => +#if HAS_PROCESS + socket.Close(); +#else + socket.Dispose(); +#endif + + /// + public virtual int Send(byte[] buffer, int size, SocketFlags socketFlags) => + socket.Send(buffer, size, socketFlags); + /// public virtual int Send(byte[] buffer, int offset, int size, SocketFlags socketFlags) => socket.Send(buffer, offset, size, socketFlags); @@ -90,6 +105,28 @@ public virtual int Receive(byte[] buffer, int size, SocketFlags socketFlags) => socket.Receive(buffer, size, socketFlags); /// - public Stream GetStream() => new NetworkStream(socket); + public virtual int Receive(byte[] buffer, int offset, int size, SocketFlags socketFlags) => + socket.Receive(buffer, offset, size, socketFlags); + +#if HAS_BUFFERS + /// + public virtual int Send(ReadOnlySpan buffer, SocketFlags socketFlags) => + socket.Send(buffer, socketFlags); + + /// + public virtual int Receive(Span buffer, SocketFlags socketFlags) => + socket.Receive(buffer, socketFlags); +#else + /// + public virtual int Send(byte[] buffer, SocketFlags socketFlags) => + socket.Send(buffer, socketFlags); + + /// + public virtual int Receive(byte[] buffer, SocketFlags socketFlags) => + socket.Receive(buffer, socketFlags); +#endif + + /// + public virtual Stream GetStream() => new NetworkStream(socket); } } From 15f27fdf28a68b9cde52bd6d9de47bb54b3ed185 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Sun, 1 Oct 2023 01:02:11 +0800 Subject: [PATCH 15/66] Replase CancellationToken to in bool of SyncService --- .../AdbClientTests.Async.cs | 73 +++++++++++++++---- .../Dummys/DummySyncService.cs | 18 ++--- .../SyncServiceTests.Async.cs | 63 ++++++++++++++++ .../SyncServiceTests.cs | 4 +- .../AdvancedSharpAdbClient.csproj | 6 +- .../DeviceCommands/DeviceExtensions.Async.cs | 2 - .../DeviceCommands/DeviceExtensions.cs | 40 ++-------- .../DeviceCommands/PackageManager.cs | 6 +- .../Interfaces/ISyncService.cs | 24 +----- .../Interfaces/ITcpSocket.cs | 2 +- AdvancedSharpAdbClient/SyncService.Async.cs | 4 +- AdvancedSharpAdbClient/SyncService.cs | 25 ++----- 12 files changed, 156 insertions(+), 111 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs index 9c27886b..3509d164 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs @@ -1195,19 +1195,18 @@ public async void FindElementAsyncTest() byte[] streamData = Encoding.UTF8.GetBytes(dump); await using MemoryStream shellStream = new(streamData); + Element element = null; await RunTestAsync( [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, shellStream, - async () => - { - Element element = await TestClient.FindElementAsync(device); - Assert.Equal(144, element.GetChildCount()); - element = element[0][0][0][0][0][0][0][0][2][1][0][0]; - Assert.Equal("where-where", element.Attributes["text"]); - Assert.Equal(Area.FromLTRB(45, 889, 427, 973), element.Area); - }); + async () => element = await TestClient.FindElementAsync(device)); + + Assert.Equal(144, element.GetChildCount()); + element = element[0][0][0][0][0][0][0][0][2][1][0][0]; + Assert.Equal("where-where", element.Attributes["text"]); + Assert.Equal(Area.FromLTRB(45, 889, 427, 973), element.Area); } /// @@ -1232,6 +1231,45 @@ public async void FindElementsAsyncTest() byte[] streamData = Encoding.UTF8.GetBytes(dump); await using MemoryStream shellStream = new(streamData); + List elements = null; + await RunTestAsync( + [AdbResponse.OK, AdbResponse.OK], + NoResponseMessages, + requests, + shellStream, + async () => elements = await TestClient.FindElementsAsync(device)); + + int childCount = elements.Count; + elements.ForEach(x => childCount += x.GetChildCount()); + Assert.Equal(145, childCount); + Element element = elements[0][0][0][0][0][0][0][0][0][2][1][0][0]; + Assert.Equal("where-where", element.Attributes["text"]); + Assert.Equal(Area.FromLTRB(45, 889, 427, 973), element.Area); + } + + /// + /// Tests the method. + /// + [Fact] + public async void FindAsyncElementsTest() + { + DeviceData device = new() + { + Serial = "009d1cd696d5194a", + State = DeviceState.Online + }; + + string[] requests = + [ + "host:transport:009d1cd696d5194a", + "shell:uiautomator dump /dev/tty" + ]; + + string dump = File.ReadAllText(@"Assets/dumpscreen.txt"); + byte[] streamData = Encoding.UTF8.GetBytes(dump); + await using MemoryStream shellStream = new(streamData); + + List elements = null; await RunTestAsync( [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, @@ -1239,14 +1277,19 @@ await RunTestAsync( shellStream, async () => { - List elements = await TestClient.FindElementsAsync(device); - int childCount = elements.Count; - elements.ForEach(x => childCount += x.GetChildCount()); - Assert.Equal(145, childCount); - Element element = elements[0][0][0][0][0][0][0][0][0][2][1][0][0]; - Assert.Equal("where-where", element.Attributes["text"]); - Assert.Equal(Area.FromLTRB(45, 889, 427, 973), element.Area); + elements = []; + await foreach(Element element in TestClient.FindAsyncElements(device)) + { + elements.Add(element); + } }); + + int childCount = elements.Count; + elements.ForEach(x => childCount += x.GetChildCount()); + Assert.Equal(145, childCount); + Element element = elements[0][0][0][0][0][0][0][0][0][2][1][0][0]; + Assert.Equal("where-where", element.Attributes["text"]); + Assert.Equal(Area.FromLTRB(45, 889, 427, 973), element.Area); } private Task RunConnectAsyncTest(Func test, string connectString) diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs index fcbffcb4..d684eaea 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs @@ -10,13 +10,11 @@ internal class DummySyncService : ISyncService { public Dictionary UploadedFiles { get; private set; } = new Dictionary(); - public bool IsOpen => true; + public bool IsOpen { get; private set; }= true; public event EventHandler SyncProgressChanged; - public void Dispose() - { - } + public void Dispose() => IsOpen = false; public IAsyncEnumerable GetDirectoryAsyncListing(string remotePath, CancellationToken cancellationToken) => throw new NotImplementedException(); @@ -24,13 +22,15 @@ public void Dispose() public Task> GetDirectoryListingAsync(string remotePath, CancellationToken cancellationToken) => throw new NotImplementedException(); - public void Open() + public void Open() => IsOpen = true; + + public Task OpenAsync(CancellationToken cancellationToken) { + IsOpen = true; + return Task.CompletedTask; } - public Task OpenAsync(CancellationToken cancellationToken) => Task.CompletedTask; - - public void Pull(string remotePath, Stream stream, IProgress progress, CancellationToken cancellationToken = default) => + public void Pull(string remotePath, Stream stream, IProgress progress = null, in bool isCancelled = false) => SyncProgressChanged?.Invoke(this, new SyncProgressChangedEventArgs(100, 100)); public Task PullAsync(string remotePath, Stream stream, IProgress progress, CancellationToken cancellationToken = default) @@ -39,7 +39,7 @@ public Task PullAsync(string remotePath, Stream stream, IProgress progress, return Task.CompletedTask; } - public void Push(Stream stream, string remotePath, int permissions, DateTimeOffset timestamp, IProgress progress, CancellationToken cancellationToken = default) + public void Push(Stream stream, string remotePath, int permissions, DateTimeOffset timestamp, IProgress progress = null, in bool isCancelled = false) { SyncProgressChanged?.Invoke(this, new SyncProgressChangedEventArgs(0, 100)); UploadedFiles[remotePath] = stream; diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs index a647c413..8e0ea66c 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs @@ -99,6 +99,69 @@ await RunTestAsync( Assert.Equal(time, emulated.Time); } + [Fact] + public async void GetAsyncListingTest() + { + DeviceData device = new() + { + Serial = "169.254.109.177:5555", + State = DeviceState.Online + }; + + List value = null; + + await RunTestAsync( + OkResponses(2), + ResponseMessages(".", "..", "sdcard0", "emulated"), + Requests("host:transport:169.254.109.177:5555", "sync:"), + SyncRequests(SyncCommand.LIST, "/storage"), + [SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DONE], + [ + [233, 65, 0, 0, 0, 0, 0, 0, 152, 130, 56, 86], + [237, 65, 0, 0, 0, 0, 0, 0, 152, 130, 56, 86], + [255, 161, 0, 0, 24, 0, 0, 0, 152, 130, 56, 86], + [109, 65, 0, 0, 0, 0, 0, 0, 152, 130, 56, 86] + ], + null, + async () => + { + using SyncService service = new(Socket, device); + value = []; + await foreach (FileStatistics statistics in service.GetDirectoryAsyncListing("/storage")) + { + value.Add(statistics); + } + }); + + Assert.Equal(4, value.Count); + + DateTime time = new DateTime(2015, 11, 3, 9, 47, 4, DateTimeKind.Utc).ToLocalTime(); + + FileStatistics dir = value[0]; + Assert.Equal(".", dir.Path); + Assert.Equal((UnixFileType)16873, dir.FileType); + Assert.Equal(0, dir.Size); + Assert.Equal(time, dir.Time); + + FileStatistics parentDir = value[1]; + Assert.Equal("..", parentDir.Path); + Assert.Equal((UnixFileType)16877, parentDir.FileType); + Assert.Equal(0, parentDir.Size); + Assert.Equal(time, parentDir.Time); + + FileStatistics sdcard0 = value[2]; + Assert.Equal("sdcard0", sdcard0.Path); + Assert.Equal((UnixFileType)41471, sdcard0.FileType); + Assert.Equal(24, sdcard0.Size); + Assert.Equal(time, sdcard0.Time); + + FileStatistics emulated = value[3]; + Assert.Equal("emulated", emulated.Path); + Assert.Equal((UnixFileType)16749, emulated.FileType); + Assert.Equal(0, emulated.Size); + Assert.Equal(time, emulated.Time); + } + [Fact] public async void PullAsyncTest() { diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs index 89434f07..21734e42 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs @@ -138,7 +138,7 @@ public void PullTest() () => { using SyncService service = new(Socket, device); - service.Pull("/fstab.donatello", stream, null, CancellationToken.None); + service.Pull("/fstab.donatello", stream); }); // Make sure the data that has been sent to the stream is the expected data @@ -176,7 +176,7 @@ .. BitConverter.GetBytes(content.Length), () => { using SyncService service = new(Socket, device); - service.Push(stream, "/sdcard/test", 0644, new DateTime(2015, 11, 2, 23, 0, 0, DateTimeKind.Utc), null, CancellationToken.None); + service.Push(stream, "/sdcard/test", 0644, new DateTime(2015, 11, 2, 23, 0, 0, DateTimeKind.Utc)); }); } } diff --git a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj index 6478b2ad..879b022e 100644 --- a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj +++ b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj @@ -11,13 +11,13 @@ $(NoWarn);NU1603;NU1605 net6.0;net8.0;netstandard1.3;netstandard2.0;netstandard2.1 - $(TargetFrameworks);net8.0;netcoreapp2.1;netcoreapp3.1 + $(TargetFrameworks);netcoreapp2.1;netcoreapp3.1 $(TargetFrameworks);net2.0-client;net3.5-client;net4.0-client;net4.5.2;net4.6.2;net4.7.2;net4.8.1;net6.0-windows10.0.17763.0;net8.0-windows10.0.17763.0;netcore50;uap10.0;uap10.0.15138.0 - netstandard1.3;netstandard2.0;netstandard2.1 - $(TargetFrameworks);net6.0;net8.0;netcoreapp3.1 + net6.0;net8.0;netstandard1.3;netstandard2.0;netstandard2.1 + $(TargetFrameworks);netcoreapp3.1 $(TargetFrameworks);net2.0-client;net3.5-client;net4.5.2;net4.8.1 diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs index 6db9ea95..e2a67362 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs @@ -76,7 +76,6 @@ public static async Task PullAsync(this IAdbClient client, DeviceData device, { service.SyncProgressChanged += syncProgressEventHandler; } - await service.PullAsync(remotePath, stream, progress, cancellationToken); } @@ -103,7 +102,6 @@ public static async Task PushAsync(this IAdbClient client, DeviceData device, { service.SyncProgressChanged += syncProgressEventHandler; } - await service.PushAsync(stream, remotePath, permissions, timestamp, progress, cancellationToken); } diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs index 3f5090d3..7ce21581 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs @@ -54,10 +54,6 @@ public static IEnumerable List(this IAdbClient client, DeviceDat return service.GetDirectoryListing(remotePath); } -#if !HAS_TASK -#pragma warning disable CS1572 // XML 注释中有 param 标记,但是没有该名称的参数 -#pragma warning disable CS1574 // XML 注释中有未能解析的 cref 特性 -#endif /// /// Pulls (downloads) a file from the remote device. /// @@ -67,27 +63,19 @@ public static IEnumerable List(this IAdbClient client, DeviceDat /// A that will receive the contents of the file. /// An optional handler for the event. /// An optional parameter which, when specified, returns progress notifications. The progress is reported as a value between 0 and 100, representing the percentage of the file which has been transferred. - /// A that can be used to cancel the task. + /// A that can be used to cancel the task. public static void Pull(this IAdbClient client, DeviceData device, string remotePath, Stream stream, EventHandler syncProgressEventHandler = null, - IProgress progress = null -#if HAS_TASK - , CancellationToken cancellationToken = default -#endif - ) + IProgress progress = null, + in bool isCancelled = false) { using ISyncService service = Factories.SyncServiceFactory(client, device); if (syncProgressEventHandler != null) { service.SyncProgressChanged += syncProgressEventHandler; } - - service.Pull(remotePath, stream, progress -#if HAS_TASK - , cancellationToken -#endif - ); + service.Pull(remotePath, stream, progress, in isCancelled); } /// @@ -101,32 +89,20 @@ public static void Pull(this IAdbClient client, DeviceData device, /// The time at which the file was last modified. /// An optional handler for the event. /// An optional parameter which, when specified, returns progress notifications. The progress is reported as a value between 0 and 100, representing the percentage of the file which has been transferred. - /// A that can be used to cancel the task. + /// A that can be used to cancel the task. public static void Push(this IAdbClient client, DeviceData device, string remotePath, Stream stream, int permissions, DateTimeOffset timestamp, EventHandler syncProgressEventHandler = null, - IProgress progress = null -#if HAS_TASK - , CancellationToken cancellationToken = default -#endif - ) + IProgress progress = null, + in bool isCancelled = false) { using ISyncService service = Factories.SyncServiceFactory(client, device); if (syncProgressEventHandler != null) { service.SyncProgressChanged += syncProgressEventHandler; } - - service.Push(stream, remotePath, permissions, timestamp, progress -#if HAS_TASK - , cancellationToken -#endif - ); + service.Push(stream, remotePath, permissions, timestamp, progress, in isCancelled); } -#if !HAS_TASK -#pragma warning restore CS1574 // XML 注释中有未能解析的 cref 特性 -#pragma warning restore CS1572 // XML 注释中有 param 标记,但是没有该名称的参数 -#endif /// /// Gets the property of a device. diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs index 9e5694c4..ddd76390 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs @@ -439,11 +439,7 @@ protected virtual string SyncPackageToDevice(string localFilePath, Action bool IsOpen { get; } -#if !HAS_TASK -#pragma warning disable CS1572 // XML 注释中有 param 标记,但是没有该名称的参数 -#pragma warning disable CS1574 // XML 注释中有未能解析的 cref 特性 -#endif /// /// Pushes (uploads) a file to the remote device. /// @@ -34,12 +30,8 @@ public partial interface ISyncService : IDisposable /// The permission octet that contains the permissions of the newly created file on the device. /// The time at which the file was last modified. /// An optional parameter which, when specified, returns progress notifications. The progress is reported as a value between 0 and 100, representing the percentage of the file which has been transferred. - /// A that can be used to cancel the task. - void Push(Stream stream, string remotePath, int permissions, DateTimeOffset timestamp, IProgress progress -#if HAS_TASK - , CancellationToken cancellationToken -#endif - ); + /// A that can be used to cancel the task. + void Push(Stream stream, string remotePath, int permissions, DateTimeOffset timestamp, IProgress progress, in bool isCancelled); /// /// Pulls (downloads) a file from the remote device. @@ -47,16 +39,8 @@ void Push(Stream stream, string remotePath, int permissions, DateTimeOffset time /// The path, on the device, of the file to pull. /// A that will receive the contents of the file. /// An optional parameter which, when specified, returns progress notifications. The progress is reported as a value between 0 and 100, representing the percentage of the file which has been transferred. - /// A that can be used to cancel the task. - void Pull(string remotePath, Stream stream, IProgress progress -#if HAS_TASK - , CancellationToken cancellationToken -#endif - ); -#if !HAS_TASK -#pragma warning restore CS1574 // XML 注释中有未能解析的 cref 特性 -#pragma warning restore CS1572 // XML 注释中有 param 标记,但是没有该名称的参数 -#endif + /// A that can be used to cancel the task. + void Pull(string remotePath, Stream stream, IProgress progress, in bool isCancelled); /// /// Returns information about a file on the device. diff --git a/AdvancedSharpAdbClient/Interfaces/ITcpSocket.cs b/AdvancedSharpAdbClient/Interfaces/ITcpSocket.cs index af91ac64..6e0567e3 100644 --- a/AdvancedSharpAdbClient/Interfaces/ITcpSocket.cs +++ b/AdvancedSharpAdbClient/Interfaces/ITcpSocket.cs @@ -18,7 +18,7 @@ public partial interface ITcpSocket : IDisposable { /// /// Gets a value indicating whether a is connected to a remote host as of the last - /// or operation. + /// or operation. /// bool Connected { get; } diff --git a/AdvancedSharpAdbClient/SyncService.Async.cs b/AdvancedSharpAdbClient/SyncService.Async.cs index c4598480..f934d173 100644 --- a/AdvancedSharpAdbClient/SyncService.Async.cs +++ b/AdvancedSharpAdbClient/SyncService.Async.cs @@ -51,7 +51,7 @@ public virtual Task ReopenAsync(IAdbSocket socket, CancellationToken cancellatio public Task ReopenAsync(IAdbClient client, CancellationToken cancellationToken = default) => ReopenAsync(Factories.AdbSocketFactory(client.EndPoint), cancellationToken); /// - public virtual async Task PushAsync(Stream stream, string remotePath, int permissions, DateTimeOffset timestamp, IProgress progress, CancellationToken cancellationToken = default) + public virtual async Task PushAsync(Stream stream, string remotePath, int permissions, DateTimeOffset timestamp, IProgress progress = null, CancellationToken cancellationToken = default) { ExceptionExtensions.ThrowIfNull(stream); @@ -143,7 +143,7 @@ public virtual async Task PushAsync(Stream stream, string remotePath, int permis } /// - public virtual async Task PullAsync(string remoteFilePath, Stream stream, IProgress progress, CancellationToken cancellationToken = default) + public virtual async Task PullAsync(string remoteFilePath, Stream stream, IProgress progress = null, CancellationToken cancellationToken = default) { ExceptionExtensions.ThrowIfNull(remoteFilePath); diff --git a/AdvancedSharpAdbClient/SyncService.cs b/AdvancedSharpAdbClient/SyncService.cs index 8a2fb238..53210439 100644 --- a/AdvancedSharpAdbClient/SyncService.cs +++ b/AdvancedSharpAdbClient/SyncService.cs @@ -122,11 +122,7 @@ public virtual void Reopen(IAdbSocket socket) public void Reopen(IAdbClient client) => Reopen(Factories.AdbSocketFactory(client.EndPoint)); /// - public virtual void Push(Stream stream, string remotePath, int permissions, DateTimeOffset timestamp, IProgress progress -#if HAS_TASK - , CancellationToken cancellationToken = default -#endif - ) + public virtual void Push(Stream stream, string remotePath, int permissions, DateTimeOffset timestamp, IProgress progress = null, in bool isCancelled = false) { ExceptionExtensions.ThrowIfNull(stream); @@ -159,13 +155,8 @@ public virtual void Push(Stream stream, string remotePath, int permissions, Date long totalBytesRead = 0; // look while there is something to read - while (true) + while (!isCancelled) { -#if HAS_TASK - // check if we're canceled - cancellationToken.ThrowIfCancellationRequested(); -#endif - // read up to SYNC_DATA_MAX int read = stream.Read(buffer, headerSize, maxDataSize); totalBytesRead += read; @@ -220,11 +211,7 @@ public virtual void Push(Stream stream, string remotePath, int permissions, Date } /// - public virtual void Pull(string remoteFilePath, Stream stream, IProgress progress -#if HAS_TASK - , CancellationToken cancellationToken = default -#endif - ) + public virtual void Pull(string remoteFilePath, Stream stream, IProgress progress = null, in bool isCancelled = false) { ExceptionExtensions.ThrowIfNull(remoteFilePath); @@ -239,12 +226,10 @@ public virtual void Pull(string remoteFilePath, Stream stream, IProgress pr Socket.SendSyncRequest(SyncCommand.RECV, remoteFilePath); - while (true) + while (!isCancelled) { SyncCommand response = Socket.ReadSyncResponse(); -#if HAS_TASK - cancellationToken.ThrowIfCancellationRequested(); -#endif + if (response == SyncCommand.DONE) { break; From ff36a66553ef4c32f8507bb121fc9c9e829f897f Mon Sep 17 00:00:00 2001 From: wherewhere Date: Sun, 1 Oct 2023 01:17:08 +0800 Subject: [PATCH 16/66] Use span and memory in SyncService --- AdvancedSharpAdbClient/SyncService.Async.cs | 20 +++++++++++++++++--- AdvancedSharpAdbClient/SyncService.cs | 13 +++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/AdvancedSharpAdbClient/SyncService.Async.cs b/AdvancedSharpAdbClient/SyncService.Async.cs index f934d173..ff2ce9e9 100644 --- a/AdvancedSharpAdbClient/SyncService.Async.cs +++ b/AdvancedSharpAdbClient/SyncService.Async.cs @@ -90,7 +90,14 @@ public virtual async Task PushAsync(Stream stream, string remotePath, int permis cancellationToken.ThrowIfCancellationRequested(); // read up to SYNC_DATA_MAX - int read = stream.Read(buffer, headerSize, maxDataSize); + int read = +#if HAS_BUFFERS + await stream.ReadAsync(buffer.AsMemory(headerSize, maxDataSize), cancellationToken); +#elif !NET35 + await stream.ReadAsync(buffer, headerSize, maxDataSize, cancellationToken); +#else + await Extensions.Run(() => stream.Read(buffer, headerSize, maxDataSize)); +#endif totalBytesRead += read; if (read == 0) @@ -111,7 +118,11 @@ public virtual async Task PushAsync(Stream stream, string remotePath, int permis Buffer.BlockCopy(lengthBytes, 0, buffer, startPosition + dataBytes.Length, lengthBytes.Length); // now send the data to the device +#if HAS_BUFFERS + await Socket.SendAsync(buffer.AsMemory(startPosition, read + dataBytes.Length + lengthBytes.Length), cancellationToken); +#else await Socket.SendAsync(buffer, startPosition, read + dataBytes.Length + lengthBytes.Length, cancellationToken); +#endif SyncProgressChanged?.Invoke(this, new SyncProgressChangedEventArgs(totalBytesRead, totalBytesToProcess)); @@ -194,13 +205,16 @@ public virtual async Task PullAsync(string remoteFilePath, Stream stream, IProgr } // now read the length we received - await Socket.ReadAsync(buffer, size, cancellationToken); #if HAS_BUFFERS + await Socket.ReadAsync(buffer.AsMemory(0, size), cancellationToken); await stream.WriteAsync(buffer.AsMemory(0, size), cancellationToken); -#elif !NET35 +#else + await Socket.ReadAsync(buffer, size, cancellationToken); +#if !NET35 await stream.WriteAsync(buffer, 0, size, cancellationToken); #else await Extensions.Run(() => stream.Write(buffer, 0, size)); +#endif #endif totalBytesRead += size; diff --git a/AdvancedSharpAdbClient/SyncService.cs b/AdvancedSharpAdbClient/SyncService.cs index 53210439..d40096a1 100644 --- a/AdvancedSharpAdbClient/SyncService.cs +++ b/AdvancedSharpAdbClient/SyncService.cs @@ -158,7 +158,11 @@ public virtual void Push(Stream stream, string remotePath, int permissions, Date while (!isCancelled) { // read up to SYNC_DATA_MAX +#if HAS_BUFFERS + int read = stream.Read(buffer.AsSpan(headerSize, maxDataSize)); +#else int read = stream.Read(buffer, headerSize, maxDataSize); +#endif totalBytesRead += read; if (read == 0) @@ -179,7 +183,11 @@ public virtual void Push(Stream stream, string remotePath, int permissions, Date Buffer.BlockCopy(lengthBytes, 0, buffer, startPosition + dataBytes.Length, lengthBytes.Length); // now send the data to the device +#if HAS_BUFFERS + Socket.Send(buffer.AsSpan(startPosition, read + dataBytes.Length + lengthBytes.Length)); +#else Socket.Send(buffer, startPosition, read + dataBytes.Length + lengthBytes.Length); +#endif SyncProgressChanged?.Invoke(this, new SyncProgressChangedEventArgs(totalBytesRead, totalBytesToProcess)); @@ -261,8 +269,13 @@ public virtual void Pull(string remoteFilePath, Stream stream, IProgress pr } // now read the length we received +#if HAS_BUFFERS + _ = Socket.Read(buffer.AsSpan(0, size)); + stream.Write(buffer.AsSpan(0, size)); +#else _ = Socket.Read(buffer, size); stream.Write(buffer, 0, size); +#endif totalBytesRead += size; SyncProgressChanged?.Invoke(this, new SyncProgressChangedEventArgs(totalBytesRead, totalBytesToProcess)); From bbae4ff96799f1496b2bc2c8377d9eb6bba45470 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Sun, 1 Oct 2023 17:36:31 +0800 Subject: [PATCH 17/66] Using span in Steam --- .../AdbClientTests.cs | 8 +- .../AdbSocketTests.cs | 2 +- .../Dummys/DummyTcpSocket.cs | 16 -- .../Models/ShellStreamTests.cs | 12 +- AdvancedSharpAdbClient/AdbClient.Async.cs | 53 ++-- AdvancedSharpAdbClient/AdbClient.cs | 47 ++-- .../AdbCommandLineClient.cs | 2 +- AdvancedSharpAdbClient/AdbServer.Async.cs | 15 +- AdvancedSharpAdbClient/DeviceMonitor.cs | 2 +- .../Exceptions/JavaException.cs | 2 +- .../Extensions/Extensions.cs | 2 + .../Extensions/SocketExtensions.cs | 10 +- .../Extensions/SteamExtensions.cs | 216 +++++++++++++++++ .../Logs/LogReader.Async.cs | 8 +- AdvancedSharpAdbClient/Logs/LogReader.cs | 4 + AdvancedSharpAdbClient/Models/Framebuffer.cs | 18 +- AdvancedSharpAdbClient/Models/ShellStream.cs | 226 +++++++++++++++--- AdvancedSharpAdbClient/SyncService.Async.cs | 10 +- AdvancedSharpAdbClient/SyncService.cs | 2 +- 19 files changed, 523 insertions(+), 132 deletions(-) create mode 100644 AdvancedSharpAdbClient/Extensions/SteamExtensions.cs diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs index 4710da56..f3ce1367 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs @@ -834,12 +834,12 @@ public void InstallTest() // The app data is sent in chunks of 32 kb Collection applicationDataChunks = []; - using (Stream stream = File.OpenRead("Assets/testapp.apk")) + using (FileStream stream = File.OpenRead("Assets/testapp.apk")) { while (true) { byte[] buffer = new byte[32 * 1024]; - int read = stream.Read(buffer, 0, buffer.Length); + int read = stream.Read(buffer.AsSpan(0, buffer.Length)); if (read == 0) { @@ -922,12 +922,12 @@ public void InstallWriteTest() // The app data is sent in chunks of 32 kb Collection applicationDataChunks = []; - using (Stream stream = File.OpenRead("Assets/testapp.apk")) + using (FileStream stream = File.OpenRead("Assets/testapp.apk")) { while (true) { byte[] buffer = new byte[32 * 1024]; - int read = stream.Read(buffer, 0, buffer.Length); + int read = stream.Read(buffer.AsSpan(0, buffer.Length)); if (read == 0) { diff --git a/AdvancedSharpAdbClient.Tests/AdbSocketTests.cs b/AdvancedSharpAdbClient.Tests/AdbSocketTests.cs index f954d5d7..db376c07 100644 --- a/AdvancedSharpAdbClient.Tests/AdbSocketTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbSocketTests.cs @@ -187,7 +187,7 @@ public void ReadSpanTest() // Buffer has a capacity of 101, but we'll only want to read 100 bytes byte[] received = new byte[101]; - socket.Read(received.AsSpan(0, 100)); + _ = socket.Read(received.AsSpan(0, 100)); for (int i = 0; i < 100; i++) { diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyTcpSocket.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyTcpSocket.cs index b156bdf5..ead0a901 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyTcpSocket.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyTcpSocket.cs @@ -46,28 +46,18 @@ public ValueTask ReconnectAsync(CancellationToken cancellationToken) public Stream GetStream() => OutputStream; - public int Receive(byte[] buffer, SocketFlags socketFlags) => InputStream.Read(buffer); - public int Receive(byte[] buffer, int size, SocketFlags socketFlags) => InputStream.Read(buffer, 0, size); public int Receive(byte[] buffer, int offset, int size, SocketFlags socketFlags) => InputStream.Read(buffer, offset, size); public int Receive(Span buffer, SocketFlags socketFlags) => InputStream.Read(buffer); - public Task ReceiveAsync(byte[] buffer, SocketFlags socketFlags, CancellationToken cancellationToken) => InputStream.ReadAsync(buffer, cancellationToken).AsTask(); - public Task ReceiveAsync(byte[] buffer, int size, SocketFlags socketFlags, CancellationToken cancellationToken) => InputStream.ReadAsync(buffer, 0, size, cancellationToken); public Task ReceiveAsync(byte[] buffer, int offset, int size, SocketFlags socketFlags, CancellationToken cancellationToken) => InputStream.ReadAsync(buffer, offset, size, cancellationToken); public ValueTask ReceiveAsync(Memory buffer, SocketFlags socketFlags, CancellationToken cancellationToken = default) => InputStream.ReadAsync(buffer, cancellationToken); - public int Send(byte[] buffer, SocketFlags socketFlags) - { - OutputStream.Write(buffer); - return buffer.Length; - } - public int Send(byte[] buffer, int size, SocketFlags socketFlags) { OutputStream.Write(buffer, 0, size); @@ -86,12 +76,6 @@ public int Send(ReadOnlySpan buffer, SocketFlags socketFlags) return buffer.Length; } - public async Task SendAsync(byte[] buffer, SocketFlags socketFlags, CancellationToken cancellationToken) - { - await OutputStream.WriteAsync(buffer, cancellationToken); - return buffer.Length; - } - public async Task SendAsync(byte[] buffer, int size, SocketFlags socketFlags, CancellationToken cancellationToken) { await OutputStream.WriteAsync(buffer.AsMemory(0, size), cancellationToken); diff --git a/AdvancedSharpAdbClient.Tests/Models/ShellStreamTests.cs b/AdvancedSharpAdbClient.Tests/Models/ShellStreamTests.cs index 6556db21..0afc4938 100644 --- a/AdvancedSharpAdbClient.Tests/Models/ShellStreamTests.cs +++ b/AdvancedSharpAdbClient.Tests/Models/ShellStreamTests.cs @@ -48,7 +48,7 @@ public void CRLFAtStartTest() stream.Position = 0; byte[] buffer = new byte[2]; - int read = shellStream.Read(buffer, 0, 2); + int read = shellStream.Read(buffer.AsSpan(0, 2)); Assert.Equal(2, read); Assert.Equal((byte)'\n', buffer[0]); Assert.Equal((byte)'H', buffer[1]); @@ -67,7 +67,7 @@ public void MultipleCRLFInStringTest() stream.Position = 0; byte[] buffer = new byte[100]; - int read = shellStream.Read(buffer, 0, 100); + int read = shellStream.Read(buffer.AsSpan(0, 100)); string actual = Encoding.ASCII.GetString(buffer, 0, read); Assert.Equal("\n1\n2\n3\n4\n5", actual); @@ -85,19 +85,19 @@ public void PendingByteTest() using MemoryStream stream = GetStream("\r\nH\ra"); using ShellStream shellStream = new(stream, false); byte[] buffer = new byte[1]; - int read = shellStream.Read(buffer, 0, 1); + int read = shellStream.Read(buffer.AsSpan(0, 1)); Assert.Equal(1, read); Assert.Equal((byte)'\n', buffer[0]); - read = shellStream.Read(buffer, 0, 1); + read = shellStream.Read(buffer.AsSpan(0, 1)); Assert.Equal(1, read); Assert.Equal((byte)'H', buffer[0]); - read = shellStream.Read(buffer, 0, 1); + read = shellStream.Read(buffer.AsSpan(0, 1)); Assert.Equal(1, read); Assert.Equal((byte)'\r', buffer[0]); - read = shellStream.Read(buffer, 0, 1); + read = shellStream.Read(buffer.AsSpan(0, 1)); Assert.Equal(1, read); Assert.Equal((byte)'a', buffer[0]); } diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index 1ea09b8c..9ea0f224 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -248,12 +248,11 @@ public async Task RunLogServiceAsync(DeviceData device, Action message using IAdbSocket socket = adbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken); - StringBuilder request = new(); - request.Append("shell:logcat -B"); + StringBuilder request = new StringBuilder().Append("shell:logcat -B"); foreach (LogId logName in logNames) { - request.Append($" -b {logName.ToString().ToLowerInvariant()}"); + _ = request.Append($" -b {logName.ToString().ToLowerInvariant()}"); } await socket.SendAdbRequestAsync(request.ToString(), cancellationToken); @@ -364,15 +363,16 @@ protected async Task RootAsync(string request, DeviceData device, CancellationTo byte[] buffer = new byte[1024]; int read = socket.Read(buffer); - string responseMessage = Encoding.UTF8.GetString(buffer, 0, read); + string responseMessage = +#if HAS_BUFFERS + Encoding.UTF8.GetString(buffer.AsSpan(0, read)); +#else + Encoding.UTF8.GetString(buffer, 0, read); +#endif // see https://android.googlesource.com/platform/packages/modules/adb/+/refs/heads/master/daemon/restart_service.cpp // for possible return strings -#if HAS_FULLSTRING if (!responseMessage.Contains("restarting", StringComparison.OrdinalIgnoreCase)) -#else - if (responseMessage.IndexOf("restarting", StringComparison.OrdinalIgnoreCase) == -1) -#endif { throw new AdbException(responseMessage); } @@ -399,8 +399,7 @@ public async Task InstallAsync(DeviceData device, Stream apk, CancellationToken throw new ArgumentOutOfRangeException(nameof(apk), "The apk stream must be a readable and seekable stream"); } - StringBuilder requestBuilder = new(); - _ = requestBuilder.Append("exec:cmd package 'install'"); + StringBuilder requestBuilder = new StringBuilder().Append("exec:cmd package 'install'"); if (arguments != null) { @@ -423,19 +422,22 @@ public async Task InstallAsync(DeviceData device, Stream apk, CancellationToken byte[] buffer = new byte[32 * 1024]; int read = 0; -#if HAS_BUFFERS while ((read = await apk.ReadAsync(buffer, cancellationToken)) > 0) -#elif !NET35 - while ((read = await apk.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0) -#else - while ((read = apk.Read(buffer, 0, buffer.Length)) > 0) -#endif { +#if HAS_BUFFERS + await socket.SendAsync(buffer.AsMemory(0, read), cancellationToken); +#else await socket.SendAsync(buffer, read, cancellationToken); +#endif } read = await socket.ReadAsync(buffer, cancellationToken); - string value = Encoding.UTF8.GetString(buffer, 0, read); + string value = +#if HAS_BUFFERS + Encoding.UTF8.GetString(buffer.AsSpan(0, read)); +#else + Encoding.UTF8.GetString(buffer, 0, read); +#endif if (!value.Contains("Success")) { @@ -603,19 +605,22 @@ public async Task InstallWriteAsync(DeviceData device, Stream apk, string apkNam byte[] buffer = new byte[32 * 1024]; int read = 0; -#if HAS_BUFFERS while ((read = await apk.ReadAsync(buffer, cancellationToken)) > 0) -#elif !NET35 - while ((read = await apk.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0) -#else - while ((read = apk.Read(buffer, 0, buffer.Length)) > 0) -#endif { +#if HAS_BUFFERS + await socket.SendAsync(buffer.AsMemory(0, read), cancellationToken); +#else await socket.SendAsync(buffer, read, cancellationToken); +#endif } read = await socket.ReadAsync(buffer, cancellationToken); - string value = Encoding.UTF8.GetString(buffer, 0, read); + string value = +#if HAS_BUFFERS + Encoding.UTF8.GetString(buffer.AsSpan(0, read)); +#else + Encoding.UTF8.GetString(buffer, 0, read); +#endif if (!value.Contains("Success")) { diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index 21572d4b..dc2e8261 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -33,7 +33,7 @@ namespace AdvancedSharpAdbClient /// public partial class AdbClient : IAdbClient { - private static readonly char[] separator = ['\r', '\n']; + private static readonly char[] separator = Extensions.NewLineSeparator; /// /// The default port to use when connecting to a device over TCP/IP. @@ -389,12 +389,11 @@ public void RunLogService(DeviceData device, Action messageSink, param using IAdbSocket socket = adbSocketFactory(EndPoint); socket.SetDevice(device); - StringBuilder request = new(); - request.Append("shell:logcat -B"); + StringBuilder request = new StringBuilder().Append("shell:logcat -B"); foreach (LogId logName in logNames) { - request.Append($" -b {logName.ToString().ToLowerInvariant()}"); + _ = request.Append($" -b {logName.ToString().ToLowerInvariant()}"); } socket.SendAdbRequest(request.ToString()); @@ -500,15 +499,16 @@ protected void Root(string request, DeviceData device) byte[] buffer = new byte[1024]; int read = socket.Read(buffer); - string responseMessage = Encoding.UTF8.GetString(buffer, 0, read); + string responseMessage = +#if HAS_BUFFERS + Encoding.UTF8.GetString(buffer.AsSpan(0, read)); +#else + Encoding.UTF8.GetString(buffer, 0, read); +#endif // see https://android.googlesource.com/platform/packages/modules/adb/+/refs/heads/master/daemon/restart_service.cpp // for possible return strings -#if HAS_FULLSTRING if (!responseMessage.Contains("restarting", StringComparison.OrdinalIgnoreCase)) -#else - if (responseMessage.IndexOf("restarting", StringComparison.OrdinalIgnoreCase) == -1) -#endif { throw new AdbException(responseMessage); } @@ -536,8 +536,7 @@ public void Install(DeviceData device, Stream apk, params string[] arguments) throw new ArgumentOutOfRangeException(nameof(apk), "The apk stream must be a readable and seekable stream"); } - StringBuilder requestBuilder = new(); - _ = requestBuilder.Append("exec:cmd package 'install'"); + StringBuilder requestBuilder = new StringBuilder().Append("exec:cmd package 'install'"); if (arguments != null) { @@ -560,13 +559,22 @@ public void Install(DeviceData device, Stream apk, params string[] arguments) byte[] buffer = new byte[32 * 1024]; int read = 0; - while ((read = apk.Read(buffer, 0, buffer.Length)) > 0) + while ((read = apk.Read(buffer)) > 0) { +#if HAS_BUFFERS + socket.Send(buffer.AsSpan(0, read)); +#else socket.Send(buffer, read); +#endif } read = socket.Read(buffer); - string value = Encoding.UTF8.GetString(buffer, 0, read); + string value = +#if HAS_BUFFERS + Encoding.UTF8.GetString(buffer.AsSpan(0, read)); +#else + Encoding.UTF8.GetString(buffer, 0, read); +#endif if (!value.Contains("Success")) { @@ -713,13 +721,22 @@ public void InstallWrite(DeviceData device, Stream apk, string apkName, string s byte[] buffer = new byte[32 * 1024]; int read = 0; - while ((read = apk.Read(buffer, 0, buffer.Length)) > 0) + while ((read = apk.Read(buffer)) > 0) { +#if HAS_BUFFERS + socket.Send(buffer.AsSpan(0, read)); +#else socket.Send(buffer, read); +#endif } read = socket.Read(buffer); - string value = Encoding.UTF8.GetString(buffer, 0, read); + string value = +#if HAS_BUFFERS + Encoding.UTF8.GetString(buffer.AsSpan(0, read)); +#else + Encoding.UTF8.GetString(buffer, 0, read); +#endif if (!value.Contains("Success")) { diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.cs index 0d121032..869b76f6 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.cs @@ -23,7 +23,7 @@ public partial class AdbCommandLineClient : IAdbCommandLineClient /// protected const string AdbVersionPattern = "^.*(\\d+)\\.(\\d+)\\.(\\d+)$"; - private static readonly char[] separator = ['\r', '\n']; + private static readonly char[] separator = Extensions.NewLineSeparator; /// /// The logger to use when logging messages. diff --git a/AdvancedSharpAdbClient/AdbServer.Async.cs b/AdvancedSharpAdbClient/AdbServer.Async.cs index f69838d3..30ef97f3 100644 --- a/AdvancedSharpAdbClient/AdbServer.Async.cs +++ b/AdvancedSharpAdbClient/AdbServer.Async.cs @@ -73,14 +73,14 @@ public virtual async Task RestartServerAsync(string adbPath, throw new InvalidOperationException($"The adb server was not started via {nameof(AdbServer)}.{nameof(this.StartServer)} or no path to adb was specified. The adb server cannot be restarted."); } - ManualResetEvent manualResetEvent = null; - await Extensions.Run(() => - { - lock (RestartLock) + using ManualResetEvent manualResetEvent = + await Extensions.Run(() => { - manualResetEvent = new(false); - } - }, cancellationToken); + lock (RestartLock) + { + return new ManualResetEvent(false); + } + }, cancellationToken); _ = Extensions.Run(() => { @@ -92,7 +92,6 @@ await Extensions.Run(() => StartServerResult result = await StartServerAsync(adbPath, false, cancellationToken); manualResetEvent.Set(); - manualResetEvent.Dispose(); return result; } diff --git a/AdvancedSharpAdbClient/DeviceMonitor.cs b/AdvancedSharpAdbClient/DeviceMonitor.cs index 3af58648..b5f8a470 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.cs @@ -34,7 +34,7 @@ namespace AdvancedSharpAdbClient /// public partial class DeviceMonitor : IDeviceMonitor { - private static readonly string[] separator = ["\r\n", "\n"]; + private static readonly char[] separator = Extensions.NewLineSeparator; private bool disposed = false; diff --git a/AdvancedSharpAdbClient/Exceptions/JavaException.cs b/AdvancedSharpAdbClient/Exceptions/JavaException.cs index 91ec24a8..ac56e717 100644 --- a/AdvancedSharpAdbClient/Exceptions/JavaException.cs +++ b/AdvancedSharpAdbClient/Exceptions/JavaException.cs @@ -15,7 +15,7 @@ public partial class JavaException : Exception private const string ExceptionOutput = "java.lang."; private const string ExceptionPattern = @"java.lang.(\w+Exception):\s+(.*)?"; - private static readonly char[] separator = ['\r', '\n']; + private static readonly char[] separator = Extensions.NewLineSeparator; /// /// Gets the name of Java exception. diff --git a/AdvancedSharpAdbClient/Extensions/Extensions.cs b/AdvancedSharpAdbClient/Extensions/Extensions.cs index 957f82a6..371d29d9 100644 --- a/AdvancedSharpAdbClient/Extensions/Extensions.cs +++ b/AdvancedSharpAdbClient/Extensions/Extensions.cs @@ -14,6 +14,8 @@ namespace AdvancedSharpAdbClient { internal static class Extensions { + public static char[] NewLineSeparator { get; } = ['\r', '\n']; + /// /// Converts the string representation of the name or numeric value of one or more /// enumerated constants to an equivalent enumerated object. A parameter specifies diff --git a/AdvancedSharpAdbClient/Extensions/SocketExtensions.cs b/AdvancedSharpAdbClient/Extensions/SocketExtensions.cs index f079eb6b..71ac861b 100644 --- a/AdvancedSharpAdbClient/Extensions/SocketExtensions.cs +++ b/AdvancedSharpAdbClient/Extensions/SocketExtensions.cs @@ -25,7 +25,7 @@ public static class SocketExtensions /// Cancelling the task will also close the socket. /// The number of bytes received. public static Task ReceiveAsync(this Socket socket, byte[] buffer, SocketFlags socketFlags, CancellationToken cancellationToken = default) - => ReceiveAsync(socket, buffer, 0, buffer.Length, socketFlags, cancellationToken); + => socket.ReceiveAsync(buffer, 0, buffer.Length, socketFlags, cancellationToken); #endif /// @@ -39,7 +39,7 @@ public static Task ReceiveAsync(this Socket socket, byte[] buffer, SocketFl /// Cancelling the task will also close the socket. /// The number of bytes received. public static Task ReceiveAsync(this Socket socket, byte[] buffer, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) - => ReceiveAsync(socket, buffer, 0, size, socketFlags, cancellationToken); + => socket.ReceiveAsync(buffer, 0, size, socketFlags, cancellationToken); /// /// Asynchronously receives data from a connected socket. @@ -100,7 +100,7 @@ public static Task ReceiveAsync(this Socket socket, byte[] buffer, int offs /// /// Asynchronously sends data to a connected socket. /// - /// The socket from which to read data. + /// The socket from which to send data. /// An array of type that contains the data to be sent. /// A bitwise combination of the values. /// A which can be used to cancel the asynchronous task. @@ -113,7 +113,7 @@ public static Task SendAsync(this Socket socket, byte[] buffer, SocketFlags /// /// Asynchronously sends data to a connected socket. /// - /// The socket from which to read data. + /// The socket from which to send data. /// An array of type that contains the data to be sent. /// The number of bytes to send. /// A bitwise combination of the values. @@ -126,7 +126,7 @@ public static Task SendAsync(this Socket socket, byte[] buffer, int size, S /// /// Asynchronously sends data to a connected socket. /// - /// The socket from which to read data. + /// The socket from which to send data. /// An array of type that contains the data to be sent. /// The position in the data buffer at which to begin sending data. /// The number of bytes to send. diff --git a/AdvancedSharpAdbClient/Extensions/SteamExtensions.cs b/AdvancedSharpAdbClient/Extensions/SteamExtensions.cs new file mode 100644 index 00000000..3bbb9e4a --- /dev/null +++ b/AdvancedSharpAdbClient/Extensions/SteamExtensions.cs @@ -0,0 +1,216 @@ +// +// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. +// + +using System; +using System.IO; +using System.Threading; + +namespace AdvancedSharpAdbClient +{ + /// + /// Provides extension methods for the class. + /// + public static class SteamExtensions + { +#if !HAS_BUFFERS + /// + /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. + /// + /// The stream from which to read data. + /// An array of bytes. When this method returns, the contents of this region are replaced by the bytes read from the current source. + /// The total number of bytes read into the buffer. This can be less than the size of the buffer if that many bytes are not currently available, + /// or zero (0) if the buffer's length is zero or the end of the stream has been reached. + public static int Read(this Stream stream, byte[] buffer) => + stream.Read(buffer, 0, buffer.Length); + + /// + /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. + /// + /// The stream from which to read data. + /// An array of bytes. This method copies the contents of this region to the current stream. + public static void Write(this Stream stream, byte[] buffer) => + stream.Write(buffer, 0, buffer.Length); + +#if HAS_TASK + /// + /// Asynchronously reads a sequence of bytes from the current stream, within monitors cancellation requests. + /// + /// The stream from which to read data. + /// The buffer to write the data into. + /// The token to monitor for cancellation requests. The default value is . + /// A task that represents the asynchronous read operation. The value of the TResult parameter + /// contains the total number of bytes read into the buffer. The result value can be less than the number + /// of bytes requested if the number of bytes currently available is less than the requested number, + /// or it can be 0 (zero) if length of the buffer is 0 or if the end of the stream has been reached. + /// Cancelling the task will also close the stream. + public static Task ReadAsync(this Stream stream, byte[] buffer, CancellationToken cancellationToken = default) => + stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken); + + /// + /// Asynchronously writes a sequence of bytes to the current stream, within this stream by the number of bytes written. + /// + /// The stream from which to write data. + /// The buffer to write data from. + /// The token to monitor for cancellation requests. The default value is . + /// A task that represents the asynchronous write operation. + /// Cancelling the task will also close the stream. + public static Task WriteAsync(this Stream stream, byte[] buffer, CancellationToken cancellationToken = default) => + stream.WriteAsync(buffer, 0, buffer.Length, cancellationToken); +#endif +#endif + + /// + /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. + /// + /// The stream from which to read data. + /// An array of bytes. When this method returns, the contents of this region are replaced by the bytes read from the current source. + /// The maximum number of bytes to read. + /// The total number of bytes read into the buffer. This can be less than the size of the buffer if that many bytes are not currently available, + /// or zero (0) if the buffer's length is zero or the end of the stream has been reached. + public static int Read(this Stream stream, byte[] buffer, int count) => + stream.Read(buffer, 0, count); + + /// + /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. + /// + /// The stream from which to read data. + /// An array of bytes. This method copies bytes from buffer to the current stream. + /// The number of bytes to be written to the current stream. + public static void Write(this Stream stream, byte[] buffer, int count) => + stream.Write(buffer, 0, count); + +#if HAS_TASK + /// + /// Asynchronously reads a sequence of bytes from the current stream, advances the position + /// within the stream by the number of bytes read, and monitors cancellation requests. + /// + /// The stream from which to read data. + /// The buffer to write the data into. + /// The maximum number of bytes to read. + /// The token to monitor for cancellation requests. The default value is . + /// A task that represents the asynchronous read operation. The value of the TResult parameter + /// contains the total number of bytes read into the buffer. The result value can be less than the number + /// of bytes requested if the number of bytes currently available is less than the requested number, + /// or it can be 0 (zero) if is 0 or if the end of the stream has been reached. + /// Cancelling the task will also close the stream. + public static Task ReadAsync(this Stream stream, byte[] buffer, int count, CancellationToken cancellationToken = default) => + stream.ReadAsync(buffer, 0, count, cancellationToken); + + /// + /// Asynchronously writes a sequence of bytes to the current stream, advances the current position + /// within this stream by the number of bytes written, and monitors cancellation requests. + /// + /// The stream from which to write data. + /// The buffer to write data from. + /// The maximum number of bytes to write. + /// The token to monitor for cancellation requests. The default value is . + /// A task that represents the asynchronous write operation. + /// Cancelling the task will also close the stream. + public static Task WriteAsync(this Stream stream, byte[] buffer, int count, CancellationToken cancellationToken = default) => + stream.WriteAsync(buffer, 0, count, cancellationToken); + +#if NET35 + /// + /// Asynchronously reads a sequence of bytes from the current stream, advances the position + /// within the stream by the number of bytes read, and monitors cancellation requests. + /// + /// The stream from which to read data. + /// The buffer to write the data into. + /// The byte offset in at which to begin writing data from the stream. + /// The maximum number of bytes to read. + /// The token to monitor for cancellation requests. The default value is . + /// A task that represents the asynchronous read operation. The value of the TResult parameter + /// contains the total number of bytes read into the buffer. The result value can be less than the number + /// of bytes requested if the number of bytes currently available is less than the requested number, + /// or it can be 0 (zero) if is 0 or if the end of the stream has been reached. + /// Cancelling the task will also close the stream. + public static Task ReadAsync(this Stream stream, byte[] buffer, int offset, int count, CancellationToken cancellationToken = default) + { + // Register a callback so that when a cancellation is requested, the socket is closed. + // 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) => + { + // this is the callback + + TaskCompletionSource taskCompletionSource = (TaskCompletionSource)iar.AsyncState; + Stream stream = (Stream)taskCompletionSource.Task.AsyncState; + + try + { + taskCompletionSource.TrySetResult(stream.EndRead(iar)); + } + catch (ObjectDisposedException) when (cancellationToken.IsCancellationRequested) + { + taskCompletionSource.TrySetCanceled(); + } + catch (Exception ex) + { + taskCompletionSource.TrySetException(ex); + } + finally + { + cancellationTokenRegistration.Dispose(); + } + }, taskCompletionSource); + + return taskCompletionSource.Task; + } + + /// + /// Asynchronously writes a sequence of bytes to the current stream, advances the current position + /// within this stream by the number of bytes written, and monitors cancellation requests. + /// + /// The stream from which to write data. + /// The buffer to write data from. + /// The zero-based byte offset in from which to begin copying bytes to the stream. + /// The maximum number of bytes to write. + /// The token to monitor for cancellation requests. The default value is . + /// A task that represents the asynchronous write operation. + /// Cancelling the task will also close the stream. + public static Task WriteAsync(this Stream stream, byte[] buffer, int offset, int count, CancellationToken cancellationToken = default) + { + // Register a callback so that when a cancellation is requested, the socket is closed. + // 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.BeginWrite(buffer, offset, count, (iar) => + { + // this is the callback + + TaskCompletionSource taskCompletionSource = (TaskCompletionSource)iar.AsyncState; + Stream stream = (Stream)taskCompletionSource.Task.AsyncState; + + try + { + stream.EndWrite(iar); + taskCompletionSource.TrySetResult(true); + } + catch (ObjectDisposedException) when (cancellationToken.IsCancellationRequested) + { + taskCompletionSource.TrySetCanceled(); + } + catch (Exception ex) + { + taskCompletionSource.TrySetException(ex); + } + finally + { + cancellationTokenRegistration.Dispose(); + } + }, taskCompletionSource); + + return taskCompletionSource.Task; + } +#endif +#endif + } +} diff --git a/AdvancedSharpAdbClient/Logs/LogReader.Async.cs b/AdvancedSharpAdbClient/Logs/LogReader.Async.cs index 1867196c..f03f732b 100644 --- a/AdvancedSharpAdbClient/Logs/LogReader.Async.cs +++ b/AdvancedSharpAdbClient/Logs/LogReader.Async.cs @@ -207,15 +207,11 @@ private async Task ReadBytesSafeAsync(int count, CancellationToken cance byte[] data = new byte[count]; - while ((read = #if HAS_BUFFERS - await stream.ReadAsync(data.AsMemory(totalRead, count - totalRead), cancellationToken).ConfigureAwait(false) -#elif !NET35 - await stream.ReadAsync(data, totalRead, count - totalRead, cancellationToken).ConfigureAwait(false) + while ((read = await stream.ReadAsync(data.AsMemory(totalRead, count - totalRead), cancellationToken).ConfigureAwait(false)) > 0) #else - await Extensions.Run(() => stream.Read(data, totalRead, count - totalRead)).ConfigureAwait(false) + while ((read = await stream.ReadAsync(data, totalRead, count - totalRead, cancellationToken).ConfigureAwait(false)) > 0) #endif - ) > 0) { totalRead += read; } diff --git a/AdvancedSharpAdbClient/Logs/LogReader.cs b/AdvancedSharpAdbClient/Logs/LogReader.cs index 85143f5f..51901bb1 100644 --- a/AdvancedSharpAdbClient/Logs/LogReader.cs +++ b/AdvancedSharpAdbClient/Logs/LogReader.cs @@ -266,7 +266,11 @@ protected byte[] ReadBytesSafe(int count) byte[] data = new byte[count]; int read; +#if HAS_BUFFERS + while ((read = stream.Read(data.AsSpan(totalRead, count - totalRead))) > 0) +#else while ((read = stream.Read(data, totalRead, count - totalRead)) > 0) +#endif { totalRead += read; } diff --git a/AdvancedSharpAdbClient/Models/Framebuffer.cs b/AdvancedSharpAdbClient/Models/Framebuffer.cs index 8657afb1..21383e52 100644 --- a/AdvancedSharpAdbClient/Models/Framebuffer.cs +++ b/AdvancedSharpAdbClient/Models/Framebuffer.cs @@ -120,7 +120,11 @@ public virtual void Refresh(bool reset = false) } // followed by the actual framebuffer content +#if HAS_BUFFERS + _ = socket.Read(Data.AsSpan(0, (int)Header.Size)); +#else _ = socket.Read(Data, (int)Header.Size); +#endif } #if HAS_TASK @@ -168,17 +172,21 @@ public virtual async Task RefreshAsync(bool reset = false, CancellationToken can } // followed by the actual framebuffer content +#if HAS_BUFFERS + _ = await socket.ReadAsync(Data.AsMemory(0, (int)Header.Size), cancellationToken).ConfigureAwait(false); +#else _ = await socket.ReadAsync(Data, (int)Header.Size, cancellationToken).ConfigureAwait(false); +#endif } #endif #if HAS_DRAWING - /// - /// Converts the framebuffer data to a . - /// - /// An which represents the framebuffer data. + /// + /// Converts the framebuffer data to a . + /// + /// An which represents the framebuffer data. #if NET - [SupportedOSPlatform("windows")] + [SupportedOSPlatform("windows")] #endif public virtual Bitmap ToImage() { diff --git a/AdvancedSharpAdbClient/Models/ShellStream.cs b/AdvancedSharpAdbClient/Models/ShellStream.cs index bf366aca..2ae4bc74 100644 --- a/AdvancedSharpAdbClient/Models/ShellStream.cs +++ b/AdvancedSharpAdbClient/Models/ShellStream.cs @@ -2,10 +2,8 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; using System; using System.IO; -using System.Runtime.InteropServices; using System.Threading; namespace AdvancedSharpAdbClient @@ -61,6 +59,102 @@ public override long Position set => throw new NotImplementedException(); } +#if HAS_BUFFERS + /// + public override int Read(Span buffer) + { + if (buffer.Length == 0) + { + return 0; + } + + // Read the raw data from the base stream. There may be a + // 'pending byte' from a previous operation; if that's the case, + // consume it. + int read; + + if (pendingByte != null) + { + buffer[0] = pendingByte.Value; + read = Inner.Read(buffer[1..]); + read++; + pendingByte = null; + } + else + { + read = Inner.Read(buffer); + } + + // Loop over the data, and find a LF (0x0d) character. If it is + // followed by a CR (0x0a) character, remove the LF chracter and + // keep only the LF character intact. + for (int i = 0; i < read - 1; i++) + { + if (buffer[i] == 0x0d && buffer[i + 1] == 0x0a) + { + buffer[i] = 0x0a; + + for (int j = i + 1; j < read - 1; j++) + { + buffer[j] = buffer[j + 1]; + } + + // Reset unused data to \0 + buffer[read - 1] = 0; + + // We have removed one byte from the array of bytes which has + // been read; but the caller asked for a fixed number of bytes. + // So we need to get the next byte from the base stream. + // If less bytes were received than asked, we know no more data is + // available so we can skip this step + if (read < buffer.Length) + { + read--; + continue; + } + + byte[] miniBuffer = new byte[1]; + int miniRead = Inner.Read(miniBuffer.AsSpan(0, 1)); + + if (miniRead == 0) + { + // If no byte was read, no more data is (currently) available, and reduce the + // number of bytes by 1. + read--; + } + else + { + // Append the byte to the buffer. + buffer[read - 1] = miniBuffer[0]; + } + } + } + + // The last byte is a special case, to find out if the next byte is 0x0a + // we need to read one more byte from the inner stream. + if (read > 0 && buffer[read - 1] == 0x0d) + { + int nextByte = Inner.ReadByte(); + + if (nextByte == 0x0a) + { + // If the next byte is 0x0a, set the last byte to 0x0a. The underlying + // stream has already advanced because of the ReadByte call, so all is good. + buffer[read - 1] = 0x0a; + } + else + { + // If the next byte was not 0x0a, store it as the 'pending byte' -- + // the next read operation will fetch this byte. We can't do a Seek here, + // because e.g. the network stream doesn't support seeking. + pendingByte = (byte)nextByte; + } + } + + return read; + } +#endif + /// public override int Read(byte[] buffer, int offset, int count) { @@ -77,13 +171,23 @@ public override int Read(byte[] buffer, int offset, int count) if (pendingByte != null) { buffer[offset] = pendingByte.Value; - read = Inner.Read(buffer, offset + 1, count - 1); + read = +#if HAS_BUFFERS + Inner.Read(buffer.AsSpan(offset + 1, count - 1)); +#else + Inner.Read(buffer, offset + 1, count - 1); +#endif read++; pendingByte = null; } else { - read = Inner.Read(buffer, offset, count); + read = +#if HAS_BUFFERS + Inner.Read(buffer.AsSpan(offset, count)); +#else + Inner.Read(buffer, offset, count); +#endif } // Loop over the data, and find a LF (0x0d) character. If it is @@ -115,7 +219,12 @@ public override int Read(byte[] buffer, int offset, int count) } byte[] miniBuffer = new byte[1]; - int miniRead = Inner.Read(miniBuffer, 0, 1); + int miniRead = +#if HAS_BUFFERS + Inner.Read(miniBuffer.AsSpan(0, 1)); +#else + Inner.Read(miniBuffer, 1); +#endif if (miniRead == 0) { @@ -157,34 +266,99 @@ public override int Read(byte[] buffer, int offset, int count) #if HAS_BUFFERS /// - public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + public override async ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) { if (buffer.Length == 0) { - return new ValueTask(0); + return 0; } - if (MemoryMarshal.TryGetArray(buffer, out ArraySegment array)) + // Read the raw data from the base stream. There may be a + // 'pending byte' from a previous operation; if that's the case, + // consume it. + int read; + + if (pendingByte != null) + { + buffer.Span[0] = pendingByte.Value; + read = await Inner.ReadAsync(buffer[1..], cancellationToken).ConfigureAwait(false); + read++; + pendingByte = null; + } + else { - return new ValueTask(ReadAsync(array.Array!, array.Offset, array.Count, cancellationToken)); + read = await Inner.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); } - byte[] sharedBuffer = ArrayPool.Shared.Rent(buffer.Length); - return FinishReadAsync(ReadAsync(sharedBuffer, 0, buffer.Length, cancellationToken), sharedBuffer, buffer); + byte[] miniBuffer = new byte[1]; + + // Loop over the data, and find a LF (0x0d) character. If it is + // followed by a CR (0x0a) character, remove the LF character and + // keep only the LF character intact. + for (int i = 0; i < read - 1; i++) + { + if (buffer.Span[i] == 0x0d && buffer.Span[i + 1] == 0x0a) + { + buffer.Span[i] = 0x0a; + + for (int j = i + 1; j < read - 1; j++) + { + buffer.Span[j] = buffer.Span[j + 1]; + } + + // Reset unused data to \0 + buffer.Span[read - 1] = 0; + + // We have removed one byte from the array of bytes which has + // been read; but the caller asked for a fixed number of bytes. + // So we need to get the next byte from the base stream. + // If less bytes were received than asked, we know no more data is + // available so we can skip this step + if (read < buffer.Length) + { + read--; + continue; + } + + int miniRead = await Inner.ReadAsync(miniBuffer.AsMemory(0, 1), cancellationToken).ConfigureAwait(false); - static async ValueTask FinishReadAsync(Task readTask, byte[] localBuffer, Memory localDestination) + if (miniRead == 0) + { + // If no byte was read, no more data is (currently) available, and reduce the + // number of bytes by 1. + read--; + } + else + { + // Append the byte to the buffer. + buffer.Span[read - 1] = miniBuffer[0]; + } + } + } + + // The last byte is a special case, to find out if the next byte is 0x0a + // we need to read one more byte from the inner stream. + if (read > 0 && buffer.Span[read - 1] == 0x0d) { - try + _ = await Inner.ReadAsync(miniBuffer.AsMemory(0, 1), cancellationToken).ConfigureAwait(false); + int nextByte = miniBuffer[0]; + + if (nextByte == 0x0a) { - int result = await readTask.ConfigureAwait(false); - new ReadOnlySpan(localBuffer, 0, result).CopyTo(localDestination.Span); - return result; + // If the next byte is 0x0a, set the last byte to 0x0a. The underlying + // stream has already advanced because of the ReadByte call, so all is good. + buffer.Span[read - 1] = 0x0a; } - finally + else { - ArrayPool.Shared.Return(localBuffer); + // If the next byte was not 0x0a, store it as the 'pending byte' -- + // the next read operation will fetch this byte. We can't do a Seek here, + // because e.g. the network stream doesn't support seeking. + pendingByte = (byte)nextByte; } } + + return read; } #endif @@ -212,10 +386,8 @@ async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToke read = #if HAS_BUFFERS await Inner.ReadAsync(buffer.AsMemory(offset + 1, count - 1), cancellationToken).ConfigureAwait(false); -#elif !NET35 - await Inner.ReadAsync(buffer, offset + 1, count - 1, cancellationToken).ConfigureAwait(false); #else - await Extensions.Run(() => Inner.Read(buffer, offset + 1, count - 1)).ConfigureAwait(false); + await Inner.ReadAsync(buffer, offset + 1, count - 1, cancellationToken).ConfigureAwait(false); #endif read++; pendingByte = null; @@ -225,10 +397,8 @@ async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToke read = #if HAS_BUFFERS await Inner.ReadAsync(buffer.AsMemory(offset, count), cancellationToken).ConfigureAwait(false); -#elif !NET35 - await Inner.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); #else - Inner.Read(buffer, offset, count); + await Inner.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); #endif } @@ -265,10 +435,8 @@ async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToke int miniRead = #if HAS_BUFFERS await Inner.ReadAsync(miniBuffer.AsMemory(0, 1), cancellationToken).ConfigureAwait(false); -#elif !NET35 - await Inner.ReadAsync(miniBuffer, 0, 1, cancellationToken).ConfigureAwait(false); #else - Inner.Read(miniBuffer, 0, 1); + await Inner.ReadAsync(miniBuffer, 1, cancellationToken).ConfigureAwait(false); #endif if (miniRead == 0) @@ -292,10 +460,8 @@ async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToke int miniRead = #if HAS_BUFFERS await Inner.ReadAsync(miniBuffer.AsMemory(0, 1), cancellationToken).ConfigureAwait(false); -#elif !NET35 - await Inner.ReadAsync(miniBuffer, 0, 1, cancellationToken).ConfigureAwait(false); #else - Inner.Read(miniBuffer, 0, 1); + await Inner.ReadAsync(miniBuffer, 1, cancellationToken).ConfigureAwait(false); #endif int nextByte = miniBuffer[0]; diff --git a/AdvancedSharpAdbClient/SyncService.Async.cs b/AdvancedSharpAdbClient/SyncService.Async.cs index ff2ce9e9..2b957203 100644 --- a/AdvancedSharpAdbClient/SyncService.Async.cs +++ b/AdvancedSharpAdbClient/SyncService.Async.cs @@ -93,10 +93,8 @@ public virtual async Task PushAsync(Stream stream, string remotePath, int permis int read = #if HAS_BUFFERS await stream.ReadAsync(buffer.AsMemory(headerSize, maxDataSize), cancellationToken); -#elif !NET35 - await stream.ReadAsync(buffer, headerSize, maxDataSize, cancellationToken); #else - await Extensions.Run(() => stream.Read(buffer, headerSize, maxDataSize)); + await stream.ReadAsync(buffer, headerSize, maxDataSize, cancellationToken); #endif totalBytesRead += read; @@ -210,11 +208,7 @@ public virtual async Task PullAsync(string remoteFilePath, Stream stream, IProgr await stream.WriteAsync(buffer.AsMemory(0, size), cancellationToken); #else await Socket.ReadAsync(buffer, size, cancellationToken); -#if !NET35 - await stream.WriteAsync(buffer, 0, size, cancellationToken); -#else - await Extensions.Run(() => stream.Write(buffer, 0, size)); -#endif + await stream.WriteAsync(buffer, size, cancellationToken); #endif totalBytesRead += size; diff --git a/AdvancedSharpAdbClient/SyncService.cs b/AdvancedSharpAdbClient/SyncService.cs index d40096a1..b9461236 100644 --- a/AdvancedSharpAdbClient/SyncService.cs +++ b/AdvancedSharpAdbClient/SyncService.cs @@ -274,7 +274,7 @@ public virtual void Pull(string remoteFilePath, Stream stream, IProgress pr stream.Write(buffer.AsSpan(0, size)); #else _ = Socket.Read(buffer, size); - stream.Write(buffer, 0, size); + stream.Write(buffer, size); #endif totalBytesRead += size; From 9cfcdb975ca8ced493233a6c2c16329a9eafb9a1 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Sun, 1 Oct 2023 21:20:24 +0800 Subject: [PATCH 18/66] Add ConfigureAwait(false) for await --- .../AdbClientTests.Async.cs | 20 +- .../AdbClientTests.cs | 12 +- .../Dummys/DeviceMonitorSink.cs | 10 +- .../Dummys/DummyAdbClient.cs | 6 +- .../Logs/LoggerTests.cs | 17 +- .../SyncServiceTests.Async.cs | 2 +- .../SyncServiceTests.cs | 2 +- AdvancedSharpAdbClient/AdbClient.Async.cs | 297 +++++++++--------- .../AdbCommandLineClient.Async.cs | 12 +- AdvancedSharpAdbClient/AdbServer.Async.cs | 16 +- AdvancedSharpAdbClient/AdbSocket.Async.cs | 36 +-- .../DeviceCommands/DeviceExtensions.Async.cs | 24 +- .../DeviceCommands/DeviceExtensions.cs | 4 +- .../DeviceCommands/PackageManager.Async.cs | 112 +++---- .../DeviceCommands/PackageManager.cs | 2 +- .../Receivers/ProcessOutputReceiver.cs | 3 +- AdvancedSharpAdbClient/DeviceMonitor.Async.cs | 14 +- .../Extensions/Extensions.cs | 13 + .../Interfaces/IAdbClient.Async.cs | 6 +- AdvancedSharpAdbClient/Logs/EventLogEntry.cs | 4 +- AdvancedSharpAdbClient/Logs/LogReader.cs | 6 +- AdvancedSharpAdbClient/Models/Element.cs | 14 +- AdvancedSharpAdbClient/Models/Framebuffer.cs | 6 +- AdvancedSharpAdbClient/SyncService.Async.cs | 67 ++-- AdvancedSharpAdbClient/TcpSocket.Async.cs | 2 +- 25 files changed, 336 insertions(+), 371 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs index 3509d164..c2d02d2b 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs @@ -450,9 +450,9 @@ public async void RunLogServiceAsyncTest() ConsoleOutputReceiver receiver = new(); - await using Stream stream = File.OpenRead("Assets/logcat.bin"); + await using FileStream stream = File.OpenRead("Assets/logcat.bin"); await using ShellStream shellStream = new(stream, false); - Collection logs = []; + List logs = []; Action sink = logs.Add; await RunTestAsync( @@ -719,9 +719,9 @@ public async void InstallAsyncTest() ]; // The app data is sent in chunks of 32 kb - Collection applicationDataChunks = []; + List applicationDataChunks = []; - await using (Stream stream = File.OpenRead("Assets/testapp.apk")) + await using (FileStream stream = File.OpenRead("Assets/testapp.apk")) { while (true) { @@ -742,7 +742,7 @@ public async void InstallAsyncTest() byte[] response = Encoding.UTF8.GetBytes("Success\n"); - await using (Stream stream = File.OpenRead("Assets/testapp.apk")) + await using (FileStream stream = File.OpenRead("Assets/testapp.apk")) { await RunTestAsync( [AdbResponse.OK, AdbResponse.OK], @@ -807,9 +807,9 @@ public async void InstallWriteAsyncTest() ]; // The app data is sent in chunks of 32 kb - Collection applicationDataChuncks = []; + List applicationDataChunks = []; - await using (Stream stream = File.OpenRead("Assets/testapp.apk")) + await using (FileStream stream = File.OpenRead("Assets/testapp.apk")) { while (true) { @@ -823,14 +823,14 @@ public async void InstallWriteAsyncTest() else { buffer = buffer.Take(read).ToArray(); - applicationDataChuncks.Add(buffer); + applicationDataChunks.Add(buffer); } } } byte[] response = Encoding.UTF8.GetBytes("Success: streamed 205774 bytes\n"); - await using (Stream stream = File.OpenRead("Assets/testapp.apk")) + await using (FileStream stream = File.OpenRead("Assets/testapp.apk")) { await RunTestAsync( [AdbResponse.OK, AdbResponse.OK], @@ -839,7 +839,7 @@ await RunTestAsync( Array.Empty<(SyncCommand, string)>(), Array.Empty(), [response], - applicationDataChuncks.ToArray(), + applicationDataChunks.ToArray(), () => TestClient.InstallWriteAsync(device, stream, "base", "936013062")); } } diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs index f3ce1367..41df0010 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs @@ -563,9 +563,9 @@ public void RunLogServiceTest() ConsoleOutputReceiver receiver = new(); - using Stream stream = File.OpenRead("Assets/logcat.bin"); + using FileStream stream = File.OpenRead("Assets/logcat.bin"); using ShellStream shellStream = new(stream, false); - Collection logs = []; + List logs = []; Action sink = logs.Add; RunTest( @@ -832,7 +832,7 @@ public void InstallTest() ]; // The app data is sent in chunks of 32 kb - Collection applicationDataChunks = []; + List applicationDataChunks = []; using (FileStream stream = File.OpenRead("Assets/testapp.apk")) { @@ -855,7 +855,7 @@ public void InstallTest() byte[] response = Encoding.UTF8.GetBytes("Success\n"); - using (Stream stream = File.OpenRead("Assets/testapp.apk")) + using (FileStream stream = File.OpenRead("Assets/testapp.apk")) { RunTest( [AdbResponse.OK, AdbResponse.OK], @@ -920,7 +920,7 @@ public void InstallWriteTest() ]; // The app data is sent in chunks of 32 kb - Collection applicationDataChunks = []; + List applicationDataChunks = []; using (FileStream stream = File.OpenRead("Assets/testapp.apk")) { @@ -943,7 +943,7 @@ public void InstallWriteTest() byte[] response = Encoding.UTF8.GetBytes("Success: streamed 205774 bytes\n"); - using (Stream stream = File.OpenRead("Assets/testapp.apk")) + using (FileStream stream = File.OpenRead("Assets/testapp.apk")) { RunTest( [AdbResponse.OK, AdbResponse.OK], diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DeviceMonitorSink.cs b/AdvancedSharpAdbClient.Tests/Dummys/DeviceMonitorSink.cs index 57bbe1db..b54bd6b9 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DeviceMonitorSink.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DeviceMonitorSink.cs @@ -1,5 +1,5 @@ using System; -using System.Collections.ObjectModel; +using System.Collections.Generic; using System.Threading; namespace AdvancedSharpAdbClient.Tests @@ -28,13 +28,13 @@ public void ResetSignals() DisconnectedEvents.Clear(); } - public Collection DisconnectedEvents { get; private set; } + public List DisconnectedEvents { get; private set; } - public Collection ConnectedEvents { get; private set; } + public List ConnectedEvents { get; private set; } - public Collection NotifiedEvents { get; private set; } + public List NotifiedEvents { get; private set; } - public Collection ChangedEvents { get; private set; } + public List ChangedEvents { get; private set; } public DeviceMonitor Monitor { get; private set; } diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs index a12814c6..28563176 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs @@ -15,7 +15,7 @@ internal class DummyAdbClient : IAdbClient { public Dictionary Commands { get; private set; } = new Dictionary(); - public Collection ReceivedCommands { get; private set; } = new Collection(); + public List ReceivedCommands { get; private set; } = new List(); public EndPoint EndPoint { get; private set; } @@ -79,7 +79,7 @@ public Task ExecuteRemoteCommandAsync(string command, DeviceData device, IShellO public void BackBtn(DeviceData device) => throw new NotImplementedException(); - public Task BackBtnAsync(DeviceData device) => throw new NotImplementedException(); + public Task BackBtnAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); public void ClearInput(DeviceData device, int charCount) => throw new NotImplementedException(); @@ -155,7 +155,7 @@ public Task ExecuteRemoteCommandAsync(string command, DeviceData device, IShellO public void HomeBtn(DeviceData device) => throw new NotImplementedException(); - public Task HomeBtnAsync(DeviceData device) => throw new NotImplementedException(); + public Task HomeBtnAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); public void Install(DeviceData device, Stream apk, params string[] arguments) => throw new NotImplementedException(); diff --git a/AdvancedSharpAdbClient.Tests/Logs/LoggerTests.cs b/AdvancedSharpAdbClient.Tests/Logs/LoggerTests.cs index d4231b38..89598486 100644 --- a/AdvancedSharpAdbClient.Tests/Logs/LoggerTests.cs +++ b/AdvancedSharpAdbClient.Tests/Logs/LoggerTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Threading; @@ -11,7 +12,7 @@ public class LoggerTests [Fact] public void ReadLogTest() { - using Stream stream = File.OpenRead(@"Assets/logcat.bin"); + using FileStream stream = File.OpenRead(@"Assets/logcat.bin"); using ShellStream shellStream = new(stream, false); LogReader reader = new(shellStream); @@ -40,7 +41,7 @@ public void ReadLogTest() [Fact] public async void ReadLogAsyncTest() { - await using Stream stream = File.OpenRead(@"Assets/logcat.bin"); + await using FileStream stream = File.OpenRead(@"Assets/logcat.bin"); await using ShellStream shellStream = new(stream, false); LogReader reader = new(shellStream); @@ -71,7 +72,7 @@ public void ReadEventLogTest() { // The data in this stream was read using a ShellStream, so the CRLF fixing // has already taken place. - using Stream stream = File.OpenRead(@"Assets/logcatevents.bin"); + using FileStream stream = File.OpenRead(@"Assets/logcatevents.bin"); LogReader reader = new(stream); LogEntry entry = reader.ReadEntry(); @@ -88,9 +89,9 @@ public void ReadEventLogTest() Assert.NotNull(eventLog.Values); Assert.Single(eventLog.Values); Assert.NotNull(eventLog.Values[0]); - Assert.IsType>(eventLog.Values[0]); + Assert.IsType>(eventLog.Values[0]); - Collection list = (Collection)eventLog.Values[0]; + List list = (List)eventLog.Values[0]; Assert.Equal(3, list.Count); Assert.Equal(0, list[0]); Assert.Equal(19512, list[1]); @@ -105,7 +106,7 @@ public async void ReadEventLogAsyncTest() { // The data in this stream was read using a ShellStream, so the CRLF fixing // has already taken place. - await using Stream stream = File.OpenRead(@"Assets/logcatevents.bin"); + await using FileStream stream = File.OpenRead(@"Assets/logcatevents.bin"); LogReader reader = new(stream); LogEntry entry = await reader.ReadEntryAsync(CancellationToken.None); @@ -122,9 +123,9 @@ public async void ReadEventLogAsyncTest() Assert.NotNull(eventLog.Values); Assert.Single(eventLog.Values); Assert.NotNull(eventLog.Values[0]); - Assert.IsType>(eventLog.Values[0]); + Assert.IsType>(eventLog.Values[0]); - Collection list = (Collection)eventLog.Values[0]; + List list = (List)eventLog.Values[0]; Assert.Equal(3, list.Count); Assert.Equal(0, list[0]); Assert.Equal(19512, list[1]); diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs index 8e0ea66c..85064140 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs @@ -206,7 +206,7 @@ public async void PushAsyncTest() State = DeviceState.Online }; - Stream stream = File.OpenRead("Assets/fstab.bin"); + FileStream stream = File.OpenRead("Assets/fstab.bin"); byte[] content = File.ReadAllBytes("Assets/fstab.bin"); List contentMessage = [ diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs index 21734e42..e536e336 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs @@ -154,7 +154,7 @@ public void PushTest() State = DeviceState.Online }; - Stream stream = File.OpenRead("Assets/fstab.bin"); + FileStream stream = File.OpenRead("Assets/fstab.bin"); byte[] content = File.ReadAllBytes("Assets/fstab.bin"); List contentMessage = [ diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index 9ea0f224..f874126a 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -26,9 +26,9 @@ public partial class AdbClient public async Task GetAdbVersionAsync(CancellationToken cancellationToken = default) { using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SendAdbRequestAsync("host:version", cancellationToken); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken); - string version = await socket.ReadStringAsync(cancellationToken); + await socket.SendAdbRequestAsync("host:version", cancellationToken).ConfigureAwait(false); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + string version = await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); return int.Parse(version, NumberStyles.HexNumber); } @@ -37,7 +37,7 @@ public async Task GetAdbVersionAsync(CancellationToken cancellationToken = public async Task KillAdbAsync(CancellationToken cancellationToken = default) { using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SendAdbRequestAsync("host:kill", cancellationToken); + await socket.SendAdbRequestAsync("host:kill", cancellationToken).ConfigureAwait(false); // The host will immediately close the connection after the kill // command has been sent; no need to read the response. @@ -47,9 +47,9 @@ public async Task KillAdbAsync(CancellationToken cancellationToken = default) public async Task> GetDevicesAsync(CancellationToken cancellationToken = default) { using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SendAdbRequestAsync("host:devices-l", cancellationToken); - await socket.ReadAdbResponseAsync(cancellationToken); - string reply = await socket.ReadStringAsync(cancellationToken); + await socket.SendAdbRequestAsync("host:devices-l", cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + string reply = await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); string[] data = reply.Split(separator, StringSplitOptions.RemoveEmptyEntries); @@ -64,10 +64,10 @@ public async Task CreateForwardAsync(DeviceData device, string local, strin using IAdbSocket socket = adbSocketFactory(EndPoint); string rebind = allowRebind ? string.Empty : "norebind:"; - await socket.SendAdbRequestAsync($"host-serial:{device.Serial}:forward:{rebind}{local};{remote}", cancellationToken); - _ = await socket.ReadAdbResponseAsync(cancellationToken); - _ = await socket.ReadAdbResponseAsync(cancellationToken); - string portString = await socket.ReadStringAsync(cancellationToken); + await socket.SendAdbRequestAsync($"host-serial:{device.Serial}:forward:{rebind}{local};{remote}", cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + string portString = await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); return portString != null && int.TryParse(portString, out int port) ? port : 0; } @@ -82,14 +82,14 @@ public async Task CreateReverseForwardAsync(DeviceData device, string remot EnsureDevice(device); using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SetDeviceAsync(device, cancellationToken); + await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); string rebind = allowRebind ? string.Empty : "norebind:"; - await socket.SendAdbRequestAsync($"reverse:forward:{rebind}{remote};{local}", cancellationToken); - _ = await socket.ReadAdbResponseAsync(cancellationToken); - _ = await socket.ReadAdbResponseAsync(cancellationToken); - string portString = await socket.ReadStringAsync(cancellationToken); + await socket.SendAdbRequestAsync($"reverse:forward:{rebind}{remote};{local}", cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + string portString = await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); return portString != null && int.TryParse(portString, out int port) ? port : 0; } @@ -100,10 +100,10 @@ public async Task RemoveReverseForwardAsync(DeviceData device, string remote, Ca EnsureDevice(device); using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SetDeviceAsync(device, cancellationToken); + await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); - await socket.SendAdbRequestAsync($"reverse:killforward:{remote}", cancellationToken); - AdbResponse response = socket.ReadAdbResponse(); + await socket.SendAdbRequestAsync($"reverse:killforward:{remote}", cancellationToken).ConfigureAwait(false); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); } /// @@ -112,10 +112,10 @@ public async Task RemoveAllReverseForwardsAsync(DeviceData device, CancellationT EnsureDevice(device); using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SetDeviceAsync(device, cancellationToken); + await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); - await socket.SendAdbRequestAsync($"reverse:killforward-all", cancellationToken); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken); + await socket.SendAdbRequestAsync($"reverse:killforward-all", cancellationToken).ConfigureAwait(false); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); } /// @@ -124,8 +124,8 @@ public async Task RemoveForwardAsync(DeviceData device, int localPort, Cancellat EnsureDevice(device); using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SendAdbRequestAsync($"host-serial:{device.Serial}:killforward:tcp:{localPort}", cancellationToken); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken); + await socket.SendAdbRequestAsync($"host-serial:{device.Serial}:killforward:tcp:{localPort}", cancellationToken).ConfigureAwait(false); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); } /// @@ -134,8 +134,8 @@ public async Task RemoveAllForwardsAsync(DeviceData device, CancellationToken ca EnsureDevice(device); using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SendAdbRequestAsync($"host-serial:{device.Serial}:killforward-all", cancellationToken); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken); + await socket.SendAdbRequestAsync($"host-serial:{device.Serial}:killforward-all", cancellationToken).ConfigureAwait(false); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); } /// @@ -144,10 +144,10 @@ public async Task> ListForwardAsync(DeviceData device, EnsureDevice(device); using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SendAdbRequestAsync($"host-serial:{device.Serial}:list-forward", cancellationToken); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken); + await socket.SendAdbRequestAsync($"host-serial:{device.Serial}:list-forward", cancellationToken).ConfigureAwait(false); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); - string data = await socket.ReadStringAsync(cancellationToken); + string data = await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); string[] parts = data.Split(separator, StringSplitOptions.RemoveEmptyEntries); @@ -160,12 +160,12 @@ public async Task> ListReverseForwardAsync(DeviceData d EnsureDevice(device); using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SetDeviceAsync(device, cancellationToken); + await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); - await socket.SendAdbRequestAsync($"reverse:list-forward", cancellationToken); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken); + await socket.SendAdbRequestAsync($"reverse:list-forward", cancellationToken).ConfigureAwait(false); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); - string data = await socket.ReadStringAsync(cancellationToken); + string data = await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); string[] parts = data.Split(separator, StringSplitOptions.RemoveEmptyEntries); @@ -184,9 +184,9 @@ public async Task ExecuteRemoteCommandAsync(string command, DeviceData device, I using IAdbSocket socket = adbSocketFactory(EndPoint); cancellationToken.Register(socket.Dispose); - await socket.SetDeviceAsync(device, cancellationToken); - await socket.SendAdbRequestAsync($"shell:{command}", cancellationToken); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken); + await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); + await socket.SendAdbRequestAsync($"shell:{command}", cancellationToken).ConfigureAwait(false); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); try { @@ -246,7 +246,7 @@ public async Task RunLogServiceAsync(DeviceData device, Action message // The 'log' service has been deprecated, see // https://android.googlesource.com/platform/system/core/+/7aa39a7b199bb9803d3fd47246ee9530b4a96177 using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SetDeviceAsync(device, cancellationToken); + await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); StringBuilder request = new StringBuilder().Append("shell:logcat -B"); @@ -255,8 +255,8 @@ public async Task RunLogServiceAsync(DeviceData device, Action message _ = request.Append($" -b {logName.ToString().ToLowerInvariant()}"); } - await socket.SendAdbRequestAsync(request.ToString(), cancellationToken); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken); + await socket.SendAdbRequestAsync(request.ToString(), cancellationToken).ConfigureAwait(false); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); #if NETCOREAPP3_0_OR_GREATER await @@ -296,9 +296,9 @@ public async Task RebootAsync(string into, DeviceData device, CancellationToken string request = $"reboot:{into}"; using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SetDeviceAsync(device, cancellationToken); - await socket.SendAdbRequestAsync(request, cancellationToken); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken); + await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); + await socket.SendAdbRequestAsync(request, cancellationToken).ConfigureAwait(false); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); } /// @@ -307,9 +307,9 @@ public async Task PairAsync(DnsEndPoint endpoint, string code, Cancellat ExceptionExtensions.ThrowIfNull(endpoint); using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SendAdbRequestAsync($"host:pair:{code}:{endpoint.Host}:{endpoint.Port}", cancellationToken); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken); - string results = await socket.ReadStringAsync(cancellationToken); + await socket.SendAdbRequestAsync($"host:pair:{code}:{endpoint.Host}:{endpoint.Port}", cancellationToken).ConfigureAwait(false); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + string results = await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); return results; } @@ -319,9 +319,9 @@ public async Task ConnectAsync(DnsEndPoint endpoint, CancellationToken c ExceptionExtensions.ThrowIfNull(endpoint); using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SendAdbRequestAsync($"host:connect:{endpoint.Host}:{endpoint.Port}", cancellationToken); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken); - string results = await socket.ReadStringAsync(cancellationToken); + await socket.SendAdbRequestAsync($"host:connect:{endpoint.Host}:{endpoint.Port}", cancellationToken).ConfigureAwait(false); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + string results = await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); return results; } @@ -331,9 +331,9 @@ public async Task DisconnectAsync(DnsEndPoint endpoint, CancellationToke ExceptionExtensions.ThrowIfNull(endpoint); using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SendAdbRequestAsync($"host:disconnect:{endpoint.Host}:{endpoint.Port}", cancellationToken); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken); - string results = await socket.ReadStringAsync(cancellationToken); + await socket.SendAdbRequestAsync($"host:disconnect:{endpoint.Host}:{endpoint.Port}", cancellationToken).ConfigureAwait(false); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + string results = await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); return results; } @@ -355,13 +355,13 @@ protected async Task RootAsync(string request, DeviceData device, CancellationTo EnsureDevice(device); using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SetDeviceAsync(device, cancellationToken); - await socket.SendAdbRequestAsync(request, cancellationToken); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken); + await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); + await socket.SendAdbRequestAsync(request, cancellationToken).ConfigureAwait(false); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); // ADB will send some additional data byte[] buffer = new byte[1024]; - int read = socket.Read(buffer); + int read = await socket.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); string responseMessage = #if HAS_BUFFERS @@ -380,7 +380,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. - Extensions.Delay(3000, cancellationToken).GetAwaiter().GetResult(); + await Extensions.Delay(3000, cancellationToken).ConfigureAwait(false); } } @@ -414,24 +414,24 @@ public async Task InstallAsync(DeviceData device, Stream apk, CancellationToken _ = requestBuilder.Append($" -S {apk.Length}"); using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SetDeviceAsync(device, cancellationToken); + await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); - await socket.SendAdbRequestAsync(requestBuilder.ToString(), cancellationToken); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken); + await socket.SendAdbRequestAsync(requestBuilder.ToString(), cancellationToken).ConfigureAwait(false); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); byte[] buffer = new byte[32 * 1024]; int read = 0; - while ((read = await apk.ReadAsync(buffer, cancellationToken)) > 0) + while ((read = await apk.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) > 0) { #if HAS_BUFFERS - await socket.SendAsync(buffer.AsMemory(0, read), cancellationToken); + await socket.SendAsync(buffer.AsMemory(0, read), cancellationToken).ConfigureAwait(false); #else - await socket.SendAsync(buffer, read, cancellationToken); + await socket.SendAsync(buffer, read, cancellationToken).ConfigureAwait(false); #endif } - read = await socket.ReadAsync(buffer, cancellationToken); + read = await socket.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); string value = #if HAS_BUFFERS Encoding.UTF8.GetString(buffer.AsSpan(0, read)); @@ -456,10 +456,10 @@ public async Task InstallMultipleAsync(DeviceData device, IEnumerable sp ExceptionExtensions.ThrowIfNull(packageName); - string session = await InstallCreateAsync(device, packageName, cancellationToken, arguments); + string session = await InstallCreateAsync(device, packageName, cancellationToken, arguments).ConfigureAwait(false); int i = 0; - IEnumerable tasks = splitAPKs.Select(async (splitAPK) => + await Extensions.WhenAll(splitAPKs.Select(async (splitAPK) => { if (splitAPK == null || !splitAPK.CanRead || !splitAPK.CanSeek) { @@ -469,19 +469,15 @@ public async Task InstallMultipleAsync(DeviceData device, IEnumerable sp try { - await InstallWriteAsync(device, splitAPK, $"{nameof(splitAPK)}{i++}", session, cancellationToken); + await InstallWriteAsync(device, splitAPK, $"{nameof(splitAPK)}{i++}", session, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { Debug.WriteLine(ex.Message); } - }); - foreach (Task task in tasks) - { - await task; - } + })).ConfigureAwait(false); - await InstallCommitAsync(device, session, cancellationToken); + await InstallCommitAsync(device, session, cancellationToken).ConfigureAwait(false); } /// @@ -500,12 +496,12 @@ public async Task InstallMultipleAsync(DeviceData device, Stream baseAPK, IEnume throw new ArgumentOutOfRangeException(nameof(baseAPK), "The apk stream must be a readable and seekable stream"); } - string session = await InstallCreateAsync(device, null, cancellationToken, arguments); + string session = await InstallCreateAsync(device, null, cancellationToken, arguments).ConfigureAwait(false); - await InstallWriteAsync(device, baseAPK, nameof(baseAPK), session, cancellationToken); + await InstallWriteAsync(device, baseAPK, nameof(baseAPK), session, cancellationToken).ConfigureAwait(false); int i = 0; - IEnumerable tasks = splitAPKs.Select(async (splitAPK) => + await Extensions.WhenAll(splitAPKs.Select(async (splitAPK) => { if (splitAPK == null || !splitAPK.CanRead || !splitAPK.CanSeek) { @@ -515,19 +511,15 @@ public async Task InstallMultipleAsync(DeviceData device, Stream baseAPK, IEnume try { - await InstallWriteAsync(device, splitAPK, $"{nameof(splitAPK)}{i++}", session, cancellationToken); + await InstallWriteAsync(device, splitAPK, $"{nameof(splitAPK)}{i++}", session, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { Debug.WriteLine(ex.Message); } - }); - foreach (Task task in tasks) - { - await task; - } + })).ConfigureAwait(false); - await InstallCommitAsync(device, session, cancellationToken); + await InstallCommitAsync(device, session, cancellationToken).ConfigureAwait(false); } /// @@ -540,9 +532,9 @@ public async Task InstallCreateAsync(DeviceData device, string packageNa { EnsureDevice(device); - StringBuilder requestBuilder = new(); - _ = requestBuilder.Append("exec:cmd package 'install-create'"); - _ = requestBuilder.Append(packageName.IsNullOrWhiteSpace() ? string.Empty : $" -p {packageName}"); + StringBuilder requestBuilder = + new StringBuilder().Append("exec:cmd package 'install-create'") + .Append(packageName.IsNullOrWhiteSpace() ? string.Empty : $" -p {packageName}"); if (arguments != null) { @@ -553,17 +545,17 @@ public async Task InstallCreateAsync(DeviceData device, string packageNa } using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SetDeviceAsync(device, cancellationToken); + await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); - await socket.SendAdbRequestAsync(requestBuilder.ToString(), cancellationToken); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken); + await socket.SendAdbRequestAsync(requestBuilder.ToString(), cancellationToken).ConfigureAwait(false); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); using StreamReader reader = new(socket.GetShellStream(), Encoding); string result = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false); if (!result.Contains("Success")) { - throw new AdbException(await reader.ReadToEndAsync(cancellationToken)); + throw new AdbException(await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false)); } int arr = result.IndexOf(']') - 1 - result.IndexOf('['); @@ -587,34 +579,32 @@ public async Task InstallWriteAsync(DeviceData device, Stream apk, string apkNam ExceptionExtensions.ThrowIfNull(apkName); - StringBuilder requestBuilder = new(); - requestBuilder.Append($"exec:cmd package 'install-write'"); - - // add size parameter [required for streaming installs] - // do last to override any user specified value - requestBuilder.Append($" -S {apk.Length}"); - - requestBuilder.Append($" {session} {apkName}.apk"); + StringBuilder requestBuilder = + new StringBuilder().Append($"exec:cmd package 'install-write'") + // add size parameter [required for streaming installs] + // do last to override any user specified value + .Append($" -S {apk.Length}") + .Append($" {session} {apkName}.apk"); using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SetDeviceAsync(device, cancellationToken); + await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); - await socket.SendAdbRequestAsync(requestBuilder.ToString(), cancellationToken); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken); + await socket.SendAdbRequestAsync(requestBuilder.ToString(), cancellationToken).ConfigureAwait(false); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); byte[] buffer = new byte[32 * 1024]; int read = 0; - while ((read = await apk.ReadAsync(buffer, cancellationToken)) > 0) + while ((read = await apk.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) > 0) { #if HAS_BUFFERS - await socket.SendAsync(buffer.AsMemory(0, read), cancellationToken); + await socket.SendAsync(buffer.AsMemory(0, read), cancellationToken).ConfigureAwait(false); #else - await socket.SendAsync(buffer, read, cancellationToken); + await socket.SendAsync(buffer, read, cancellationToken).ConfigureAwait(false); #endif } - read = await socket.ReadAsync(buffer, cancellationToken); + read = await socket.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); string value = #if HAS_BUFFERS Encoding.UTF8.GetString(buffer.AsSpan(0, read)); @@ -632,16 +622,16 @@ public async Task InstallWriteAsync(DeviceData device, Stream apk, string apkNam public async Task InstallCommitAsync(DeviceData device, string session, CancellationToken cancellationToken = default) { using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SetDeviceAsync(device, cancellationToken); + await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); - await socket.SendAdbRequestAsync($"exec:cmd package 'install-commit' {session}", cancellationToken); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken); + await socket.SendAdbRequestAsync($"exec:cmd package 'install-commit' {session}", cancellationToken).ConfigureAwait(false); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); using StreamReader reader = new(socket.GetShellStream(), Encoding); string result = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false); if (!result.Contains("Success")) { - throw new AdbException(await reader.ReadToEndAsync(cancellationToken)); + throw new AdbException(await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false)); } } @@ -649,10 +639,10 @@ public async Task InstallCommitAsync(DeviceData device, string session, Cancella public async Task> GetFeatureSetAsync(DeviceData device, CancellationToken cancellationToken = default) { using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SendAdbRequestAsync($"host-serial:{device.Serial}:features", cancellationToken); + await socket.SendAdbRequestAsync($"host-serial:{device.Serial}:features", cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken); - string features = await socket.ReadStringAsync(cancellationToken); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + string features = await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); IEnumerable featureList = features.Trim().Split('\n', ','); return featureList; @@ -663,14 +653,15 @@ public async Task DumpScreenStringAsync(DeviceData device, CancellationT { EnsureDevice(device); using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SetDeviceAsync(device, cancellationToken); - await socket.SendAdbRequestAsync("shell:uiautomator dump /dev/tty", cancellationToken); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken); + await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); + await socket.SendAdbRequestAsync("shell:uiautomator dump /dev/tty", cancellationToken).ConfigureAwait(false); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); using StreamReader reader = new(socket.GetShellStream(), Encoding); - string xmlString = reader.ReadToEnd() - .Replace("Events injected: 1\r\n", string.Empty) - .Replace("UI hierchary dumped to: /dev/tty", string.Empty) - .Trim(); + string xmlString = + reader.ReadToEnd() + .Replace("Events injected: 1\r\n", string.Empty) + .Replace("UI hierchary dumped to: /dev/tty", string.Empty) + .Trim(); if (string.IsNullOrEmpty(xmlString) || xmlString.StartsWith(" DumpScreenStringAsync(DeviceData device, CancellationT /// public async Task DumpScreenAsync(DeviceData device, CancellationToken cancellationToken = default) { + string xmlString = await DumpScreenStringAsync(device, cancellationToken).ConfigureAwait(false); XmlDocument doc = new(); - string xmlString = await DumpScreenStringAsync(device, cancellationToken); if (!string.IsNullOrEmpty(xmlString)) { doc.LoadXml(xmlString); @@ -696,8 +687,8 @@ public async Task DumpScreenAsync(DeviceData device, CancellationTo /// public async Task DumpScreenWinRTAsync(DeviceData device, CancellationToken cancellationToken = default) { + string xmlString = await DumpScreenStringAsync(device, cancellationToken).ConfigureAwait(false); Windows.Data.Xml.Dom.XmlDocument doc = new(); - string xmlString = await DumpScreenStringAsync(device, cancellationToken); if (!string.IsNullOrEmpty(xmlString)) { doc.LoadXml(xmlString); @@ -713,9 +704,9 @@ public async Task ClickAsync(DeviceData device, Cords cords, CancellationToken c EnsureDevice(device); using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SetDeviceAsync(device, cancellationToken); - await socket.SendAdbRequestAsync($"shell:input tap {cords.X} {cords.Y}", cancellationToken); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken); + await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); + await socket.SendAdbRequestAsync($"shell:input tap {cords.X} {cords.Y}", cancellationToken).ConfigureAwait(false); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); using StreamReader reader = new(socket.GetShellStream(), Encoding); string result = (await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false)).TrimStart(); if (result.StartsWith("java.lang.")) @@ -734,9 +725,9 @@ public async Task ClickAsync(DeviceData device, int x, int y, CancellationToken EnsureDevice(device); using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SetDeviceAsync(device, cancellationToken); - await socket.SendAdbRequestAsync($"shell:input tap {x} {y}", cancellationToken); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken); + await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); + await socket.SendAdbRequestAsync($"shell:input tap {x} {y}", cancellationToken).ConfigureAwait(false); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); using StreamReader reader = new(socket.GetShellStream(), Encoding); string result = (await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false)).TrimStart(); if (result.StartsWith("java.lang.")) @@ -755,9 +746,9 @@ public async Task SwipeAsync(DeviceData device, Element first, Element second, l EnsureDevice(device); using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SetDeviceAsync(device, cancellationToken); - await socket.SendAdbRequestAsync($"shell:input swipe {first.Cords.X} {first.Cords.Y} {second.Cords.X} {second.Cords.Y} {speed}", cancellationToken); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken); + await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); + await socket.SendAdbRequestAsync($"shell:input swipe {first.Cords.X} {first.Cords.Y} {second.Cords.X} {second.Cords.Y} {speed}", cancellationToken).ConfigureAwait(false); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); using StreamReader reader = new(socket.GetShellStream(), Encoding); string result = (await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false)).TrimStart(); if (result.StartsWith("java.lang.")) @@ -776,9 +767,9 @@ public async Task SwipeAsync(DeviceData device, int x1, int y1, int x2, int y2, EnsureDevice(device); using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SetDeviceAsync(device, cancellationToken); - await socket.SendAdbRequestAsync($"shell:input swipe {x1} {y1} {x2} {y2} {speed}", cancellationToken); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken); + await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); + await socket.SendAdbRequestAsync($"shell:input swipe {x1} {y1} {x2} {y2} {speed}", cancellationToken).ConfigureAwait(false); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); using StreamReader reader = new(socket.GetShellStream(), Encoding); string result = (await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false)).TrimStart(); if (result.StartsWith("java.lang.")) @@ -795,7 +786,7 @@ public async Task SwipeAsync(DeviceData device, int x1, int y1, int x2, int y2, public async Task IsCurrentAppAsync(DeviceData device, string packageName, CancellationToken cancellationToken = default) { ConsoleOutputReceiver receiver = new(); - await ExecuteRemoteCommandAsync($"dumpsys activity activities | grep mResumedActivity", device, receiver, cancellationToken); + await ExecuteRemoteCommandAsync($"dumpsys activity activities | grep mResumedActivity", device, receiver, cancellationToken).ConfigureAwait(false); string response = receiver.ToString().Trim(); return response.ToString().Contains(packageName); } @@ -804,7 +795,7 @@ public async Task IsCurrentAppAsync(DeviceData device, string packageName, public async Task IsAppRunningAsync(DeviceData device, string packageName, CancellationToken cancellationToken = default) { ConsoleOutputReceiver receiver = new(); - await ExecuteRemoteCommandAsync($"pidof {packageName}", device, receiver, cancellationToken); + await ExecuteRemoteCommandAsync($"pidof {packageName}", device, receiver, cancellationToken).ConfigureAwait(false); string response = receiver.ToString().Trim(); bool intParsed = int.TryParse(response, out int pid); return intParsed && pid > 0; @@ -814,14 +805,14 @@ public async Task IsAppRunningAsync(DeviceData device, string packageName, public async Task GetAppStatusAsync(DeviceData device, string packageName, CancellationToken cancellationToken = default) { // Check if the app is in foreground - bool currentApp = await IsCurrentAppAsync(device, packageName, cancellationToken); + bool currentApp = await IsCurrentAppAsync(device, packageName, cancellationToken).ConfigureAwait(false); if (currentApp) { return AppStatus.Foreground; } // Check if the app is running in background - bool isAppRunning = await IsAppRunningAsync(device, packageName, cancellationToken); + bool isAppRunning = await IsAppRunningAsync(device, packageName, cancellationToken).ConfigureAwait(false); return isAppRunning ? AppStatus.Background : AppStatus.Stopped; } @@ -832,7 +823,7 @@ public async Task FindElementAsync(DeviceData device, string xpath = "h { while (!cancellationToken.IsCancellationRequested) { - XmlDocument doc = await DumpScreenAsync(device, cancellationToken); + XmlDocument doc = await DumpScreenAsync(device, cancellationToken).ConfigureAwait(false); if (doc != null) { XmlNode xmlNode = doc.SelectSingleNode(xpath); @@ -871,7 +862,7 @@ public async Task> FindElementsAsync(DeviceData device, string xpa { while (!cancellationToken.IsCancellationRequested) { - XmlDocument doc = await DumpScreenAsync(device, cancellationToken); + XmlDocument doc = await DumpScreenAsync(device, cancellationToken).ConfigureAwait(false); if (doc != null) { XmlNodeList xmlNodes = doc.SelectNodes(xpath); @@ -914,7 +905,7 @@ public async IAsyncEnumerable FindAsyncElements(DeviceData device, stri { while (!cancellationToken.IsCancellationRequested) { - XmlDocument doc = await DumpScreenAsync(device, cancellationToken); + XmlDocument doc = await DumpScreenAsync(device, cancellationToken).ConfigureAwait(false); if (doc != null) { XmlNodeList xmlNodes = doc.SelectNodes(xpath); @@ -950,9 +941,9 @@ public async Task SendKeyEventAsync(DeviceData device, string key, CancellationT EnsureDevice(device); using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SetDeviceAsync(device, cancellationToken); - await socket.SendAdbRequestAsync($"shell:input keyevent {key}", cancellationToken); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken); + await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); + await socket.SendAdbRequestAsync($"shell:input keyevent {key}", cancellationToken).ConfigureAwait(false); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); using StreamReader reader = new(socket.GetShellStream(), Encoding); string result = (await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false)).TrimStart(); if (result.StartsWith("java.lang.")) @@ -971,9 +962,9 @@ public async Task SendTextAsync(DeviceData device, string text, CancellationToke EnsureDevice(device); using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SetDeviceAsync(device, cancellationToken); - await socket.SendAdbRequestAsync($"shell:input text {text}", cancellationToken); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken); + await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); + await socket.SendAdbRequestAsync($"shell:input text {text}", cancellationToken).ConfigureAwait(false); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); using StreamReader reader = new(socket.GetShellStream(), Encoding); string result = (await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false)).TrimStart(); if (result.StartsWith("java.lang.")) @@ -989,23 +980,23 @@ public async Task SendTextAsync(DeviceData device, string text, CancellationToke /// public async Task ClearInputAsync(DeviceData device, int charCount, CancellationToken cancellationToken = default) { - await SendKeyEventAsync(device, "KEYCODE_MOVE_END", cancellationToken); - await ExecuteRemoteCommandAsync("input keyevent " + Extensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL ", charCount)), device, null, cancellationToken); + await SendKeyEventAsync(device, "KEYCODE_MOVE_END", cancellationToken).ConfigureAwait(false); + await ExecuteRemoteCommandAsync("input keyevent " + Extensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL ", charCount)), device, null, cancellationToken).ConfigureAwait(false); } /// - public async Task StartAppAsync(DeviceData device, string packageName, CancellationToken cancellationToken = default) => - await ExecuteRemoteCommandAsync($"monkey -p {packageName} 1", device, null, cancellationToken); + public Task StartAppAsync(DeviceData device, string packageName, CancellationToken cancellationToken = default) => + ExecuteRemoteCommandAsync($"monkey -p {packageName} 1", device, null, cancellationToken); /// - public async Task StopAppAsync(DeviceData device, string packageName, CancellationToken cancellationToken = default) => - await ExecuteRemoteCommandAsync($"am force-stop {packageName}", device, null, cancellationToken); + public Task StopAppAsync(DeviceData device, string packageName, CancellationToken cancellationToken = default) => + ExecuteRemoteCommandAsync($"am force-stop {packageName}", device, null, cancellationToken); /// - public Task BackBtnAsync(DeviceData device) => SendKeyEventAsync(device, "KEYCODE_BACK"); + public Task BackBtnAsync(DeviceData device, CancellationToken cancellationToken = default) => SendKeyEventAsync(device, "KEYCODE_BACK", cancellationToken); /// - public Task HomeBtnAsync(DeviceData device) => SendKeyEventAsync(device, "KEYCODE_HOME"); + public Task HomeBtnAsync(DeviceData device, CancellationToken cancellationToken = default) => SendKeyEventAsync(device, "KEYCODE_HOME", cancellationToken); } } #endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs index 1f003f11..5bb49452 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs @@ -24,7 +24,7 @@ public virtual async Task GetVersionAsync(CancellationToken cancellatio // Run the adb.exe version command and capture the output. List standardOutput = []; - await RunAdbProcessAsync("version", null, standardOutput, cancellationToken); + await RunAdbProcessAsync("version", null, standardOutput, cancellationToken).ConfigureAwait(false); // Parse the output to get the version. Version version = GetVersionFromOutput(standardOutput) ?? throw new AdbException($"The version of the adb executable at {AdbPath} could not be determined."); @@ -46,7 +46,7 @@ public virtual async Task GetVersionAsync(CancellationToken cancellatio /// A which represents the asynchronous operation. public virtual async Task StartServerAsync(CancellationToken cancellationToken = default) { - int status = await RunAdbProcessInnerAsync("start-server", null, null, cancellationToken); + int status = await RunAdbProcessInnerAsync("start-server", null, null, cancellationToken).ConfigureAwait(false); if (status == 0) { @@ -79,7 +79,7 @@ public virtual async Task StartServerAsync(CancellationToken cancellationToken = // Try again. This time, we don't call "Inner", and an exception will be thrown if the start operation fails // again. We'll let that exception bubble up the stack. - await RunAdbProcessAsync("start-server", null, null, cancellationToken); + await RunAdbProcessAsync("start-server", null, null, cancellationToken).ConfigureAwait(false); } /// @@ -98,7 +98,7 @@ public virtual async Task StartServerAsync(CancellationToken cancellationToken = /// The process exited with an exit code other than 0. protected virtual async Task RunAdbProcessAsync(string command, List errorOutput, List standardOutput, CancellationToken cancellationToken = default) { - int status = await RunAdbProcessInnerAsync(command, errorOutput, standardOutput, cancellationToken); + int status = await RunAdbProcessInnerAsync(command, errorOutput, standardOutput, cancellationToken).ConfigureAwait(false); if (status != 0) { @@ -123,7 +123,7 @@ protected virtual async Task RunAdbProcessInnerAsync(string command, List RunProcessAsync(string filename, string comman #if NET5_0_OR_GREATER using (CancellationTokenSource completionSource = new(TimeSpan.FromMilliseconds(5000))) { - await process.WaitForExitAsync(completionSource.Token); + await process.WaitForExitAsync(completionSource.Token).ConfigureAwait(false); if (!process.HasExited) { process.Kill(); diff --git a/AdvancedSharpAdbClient/AdbServer.Async.cs b/AdvancedSharpAdbClient/AdbServer.Async.cs index 30ef97f3..0c8279af 100644 --- a/AdvancedSharpAdbClient/AdbServer.Async.cs +++ b/AdvancedSharpAdbClient/AdbServer.Async.cs @@ -15,7 +15,7 @@ public partial class AdbServer /// public virtual async Task StartServerAsync(string adbPath, bool restartServerIfNewer, CancellationToken cancellationToken = default) { - AdbServerStatus serverStatus = await GetStatusAsync(cancellationToken); + AdbServerStatus serverStatus = await GetStatusAsync(cancellationToken).ConfigureAwait(false); Version commandLineVersion = null; IAdbCommandLineClient commandLineClient = adbCommandLineClientFactory(adbPath); @@ -24,7 +24,7 @@ public virtual async Task StartServerAsync(string adbPath, bo if (commandLineClient.IsValidAdbFile(adbPath)) { CachedAdbPath = adbPath; - commandLineVersion = await commandLineClient.GetVersionAsync(cancellationToken); + commandLineVersion = await commandLineClient.GetVersionAsync(cancellationToken).ConfigureAwait(false); } // If the server is running, and no adb path is provided, check if we have the minimum version @@ -43,15 +43,15 @@ public virtual async Task StartServerAsync(string adbPath, bo { ExceptionExtensions.ThrowIfNull(adbPath); - await adbClient.KillAdbAsync(cancellationToken); - await commandLineClient.StartServerAsync(cancellationToken); + await adbClient.KillAdbAsync(cancellationToken).ConfigureAwait(false); + await commandLineClient.StartServerAsync(cancellationToken).ConfigureAwait(false); return StartServerResult.RestartedOutdatedDaemon; } else if (!serverStatus.IsRunning) { ExceptionExtensions.ThrowIfNull(adbPath); - await commandLineClient.StartServerAsync(cancellationToken); + await commandLineClient.StartServerAsync(cancellationToken).ConfigureAwait(false); return StartServerResult.Started; } else @@ -80,7 +80,7 @@ await Extensions.Run(() => { return new ManualResetEvent(false); } - }, cancellationToken); + }, cancellationToken).ConfigureAwait(false); _ = Extensions.Run(() => { @@ -90,7 +90,7 @@ await Extensions.Run(() => } }, cancellationToken); - StartServerResult result = await StartServerAsync(adbPath, false, cancellationToken); + StartServerResult result = await StartServerAsync(adbPath, false, cancellationToken).ConfigureAwait(false); manualResetEvent.Set(); return result; } @@ -101,7 +101,7 @@ public virtual async Task GetStatusAsync(CancellationToken canc // Try to connect to a running instance of the adb server try { - int versionCode = await adbClient.GetAdbVersionAsync(cancellationToken); + int versionCode = await adbClient.GetAdbVersionAsync(cancellationToken).ConfigureAwait(false); return new AdbServerStatus(true, new Version(1, 0, versionCode)); } catch (AggregateException ex) diff --git a/AdvancedSharpAdbClient/AdbSocket.Async.cs b/AdvancedSharpAdbClient/AdbSocket.Async.cs index cebc72b8..7f4a097e 100644 --- a/AdvancedSharpAdbClient/AdbSocket.Async.cs +++ b/AdvancedSharpAdbClient/AdbSocket.Async.cs @@ -24,7 +24,7 @@ public virtual async Task SendAsync(byte[] data, int length, CancellationToken c { try { - int count = await socket.SendAsync(data, length != -1 ? length : data.Length, SocketFlags.None, cancellationToken); + int count = await socket.SendAsync(data, length != -1 ? length : data.Length, SocketFlags.None, cancellationToken).ConfigureAwait(false); if (count < 0) { throw new AdbException("channel EOF"); @@ -42,7 +42,7 @@ public virtual async Task SendAsync(byte[] data, int offset, int length, Cancell { try { - int count = await socket.SendAsync(data, offset, length != -1 ? length : data.Length - offset, SocketFlags.None, cancellationToken); + int count = await socket.SendAsync(data, offset, length != -1 ? length : data.Length - offset, SocketFlags.None, cancellationToken).ConfigureAwait(false); if (count < 0) { throw new AdbException("channel EOF"); @@ -65,8 +65,8 @@ public virtual async Task SendSyncRequestAsync(SyncCommand command, string path, ExceptionExtensions.ThrowIfNull(path); byte[] pathBytes = AdbClient.Encoding.GetBytes(path); - await SendSyncRequestAsync(command, pathBytes.Length, cancellationToken); - _ = await WriteAsync(pathBytes, cancellationToken); + await SendSyncRequestAsync(command, pathBytes.Length, cancellationToken).ConfigureAwait(false); + _ = await WriteAsync(pathBytes, cancellationToken).ConfigureAwait(false); } /// @@ -86,8 +86,8 @@ public virtual async Task SendSyncRequestAsync(SyncCommand command, int length, Array.Reverse(lengthBytes); } - _ = await WriteAsync(commandBytes, cancellationToken); - _ = await WriteAsync(lengthBytes, cancellationToken); + _ = await WriteAsync(commandBytes, cancellationToken).ConfigureAwait(false); + _ = await WriteAsync(lengthBytes, cancellationToken).ConfigureAwait(false); } /// @@ -95,7 +95,7 @@ public virtual async Task SendAdbRequestAsync(string request, CancellationToken { byte[] data = AdbClient.FormAdbRequest(request); - if (!await WriteAsync(data, cancellationToken)) + if (!await WriteAsync(data, cancellationToken).ConfigureAwait(false)) { throw new IOException($"Failed sending the request '{request}' to ADB"); } @@ -175,7 +175,7 @@ public virtual async Task ReadSyncStringAsync(CancellationToken cancella { // The first 4 bytes contain the length of the string byte[] reply = new byte[4]; - _ = await ReadAsync(reply, cancellationToken); + _ = await ReadAsync(reply, cancellationToken).ConfigureAwait(false); if (!BitConverter.IsLittleEndian) { @@ -186,7 +186,7 @@ public virtual async Task ReadSyncStringAsync(CancellationToken cancella // And get the string reply = new byte[len]; - _ = await ReadAsync(reply, cancellationToken); + _ = await ReadAsync(reply, cancellationToken).ConfigureAwait(false); string value = AdbClient.Encoding.GetString(reply); return value; @@ -196,7 +196,7 @@ public virtual async Task ReadSyncStringAsync(CancellationToken cancella public virtual async Task ReadSyncResponseAsync(CancellationToken cancellationToken = default) { byte[] data = new byte[4]; - _ = await ReadAsync(data, cancellationToken); + _ = await ReadAsync(data, cancellationToken).ConfigureAwait(false); return SyncCommandConverter.GetCommand(data); } @@ -204,7 +204,7 @@ public virtual async Task ReadSyncResponseAsync(CancellationToken c /// public virtual async Task ReadAdbResponseAsync(CancellationToken cancellationToken = default) { - AdbResponse response = await ReadAdbResponseInnerAsync(cancellationToken); + AdbResponse response = await ReadAdbResponseInnerAsync(cancellationToken).ConfigureAwait(false); if (!response.IOSuccess || !response.Okay) { @@ -222,11 +222,11 @@ public virtual async Task SetDeviceAsync(DeviceData device, CancellationToken ca // to a specific device if (device != null) { - await SendAdbRequestAsync($"host:transport:{device.Serial}", cancellationToken); + await SendAdbRequestAsync($"host:transport:{device.Serial}", cancellationToken).ConfigureAwait(false); try { - AdbResponse response = await ReadAdbResponseAsync(cancellationToken); + AdbResponse response = await ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); } catch (AdbException e) { @@ -248,7 +248,7 @@ public virtual async ValueTask SendAsync(ReadOnlyMemory data, Cancellation { try { - int count = await socket.SendAsync(data, SocketFlags.None, cancellationToken); + int count = await socket.SendAsync(data, SocketFlags.None, cancellationToken).ConfigureAwait(false); if (count < 0) { throw new AdbException("channel EOF"); @@ -309,7 +309,7 @@ public virtual async Task SendAsync(byte[] data, CancellationToken cancellationT { try { - int count = await socket.SendAsync(data, SocketFlags.None, cancellationToken); + int count = await socket.SendAsync(data, SocketFlags.None, cancellationToken).ConfigureAwait(false); if (count < 0) { throw new AdbException("channel EOF"); @@ -338,7 +338,7 @@ protected virtual async Task WriteAsync(byte[] data, CancellationToken can { try { - await SendAsync(data, cancellationToken); + await SendAsync(data, cancellationToken).ConfigureAwait(false); } catch (IOException e) { @@ -359,7 +359,7 @@ protected virtual async Task ReadAdbResponseInnerAsync(Cancellation AdbResponse rasps = new(); byte[] reply = new byte[4]; - _ = await ReadAsync(reply, cancellationToken); + _ = await ReadAsync(reply, cancellationToken).ConfigureAwait(false); rasps.IOSuccess = true; @@ -367,7 +367,7 @@ protected virtual async Task ReadAdbResponseInnerAsync(Cancellation if (!rasps.Okay) { - string message = await ReadStringAsync(cancellationToken); + string message = await ReadStringAsync(cancellationToken).ConfigureAwait(false); rasps.Message = message; logger.LogError("Got reply '{0}', diag='{1}'", ReplyToString(reply), rasps.Message); } diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs index e2a67362..32ea11e2 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Text; @@ -38,7 +37,7 @@ public static Task ExecuteShellCommandAsync(this IAdbClient client, DeviceData d public static async Task StatAsync(this IAdbClient client, DeviceData device, string path, CancellationToken cancellationToken = default) { using ISyncService service = Factories.SyncServiceFactory(client, device); - return await service.StatAsync(path, cancellationToken); + return await service.StatAsync(path, cancellationToken).ConfigureAwait(false); } /// @@ -52,7 +51,7 @@ public static async Task StatAsync(this IAdbClient client, Devic public static async Task> List(this IAdbClient client, DeviceData device, string remotePath, CancellationToken cancellationToken = default) { using ISyncService service = Factories.SyncServiceFactory(client, device); - return await service.GetDirectoryListingAsync(remotePath, cancellationToken); + return await service.GetDirectoryListingAsync(remotePath, cancellationToken).ConfigureAwait(false); } /// @@ -76,7 +75,7 @@ public static async Task PullAsync(this IAdbClient client, DeviceData device, { service.SyncProgressChanged += syncProgressEventHandler; } - await service.PullAsync(remotePath, stream, progress, cancellationToken); + await service.PullAsync(remotePath, stream, progress, cancellationToken).ConfigureAwait(false); } /// @@ -102,7 +101,7 @@ public static async Task PushAsync(this IAdbClient client, DeviceData device, { service.SyncProgressChanged += syncProgressEventHandler; } - await service.PushAsync(stream, remotePath, permissions, timestamp, progress, cancellationToken); + await service.PushAsync(stream, remotePath, permissions, timestamp, progress, cancellationToken).ConfigureAwait(false); } /// @@ -116,7 +115,7 @@ public static async Task PushAsync(this IAdbClient client, DeviceData device, public static async Task GetPropertyAsync(this IAdbClient client, DeviceData device, string property, CancellationToken cancellationToken = default) { ConsoleOutputReceiver receiver = new(); - await client.ExecuteRemoteCommandAsync($"{GetPropReceiver.GetPropCommand} {property}", device, receiver, cancellationToken); + await client.ExecuteRemoteCommandAsync($"{GetPropReceiver.GetPropCommand} {property}", device, receiver, cancellationToken).ConfigureAwait(false); return receiver.ToString(); } @@ -130,7 +129,7 @@ public static async Task GetPropertyAsync(this IAdbClient client, Device public static async Task> GetPropertiesAsync(this IAdbClient client, DeviceData device, CancellationToken cancellationToken = default) { GetPropReceiver receiver = new(); - await client.ExecuteRemoteCommandAsync(GetPropReceiver.GetPropCommand, device, receiver, cancellationToken); + await client.ExecuteRemoteCommandAsync(GetPropReceiver.GetPropCommand, device, receiver, cancellationToken).ConfigureAwait(false); return receiver.Properties; } @@ -144,7 +143,7 @@ public static async Task> GetPropertiesAsync(this IAd public static async Task> GetEnvironmentVariablesAsync(this IAdbClient client, DeviceData device, CancellationToken cancellationToken = default) { EnvironmentVariablesReceiver receiver = new(); - await client.ExecuteRemoteCommandAsync(EnvironmentVariablesReceiver.PrintEnvCommand, device, receiver, cancellationToken); + await client.ExecuteRemoteCommandAsync(EnvironmentVariablesReceiver.PrintEnvCommand, device, receiver, cancellationToken).ConfigureAwait(false); return receiver.EnvironmentVariables; } @@ -220,9 +219,9 @@ await client.ExecuteShellCommandAsync(device, @"SDK=""$(/system/bin/getprop ro.b /system/bin/ls /proc/ else /system/bin/ls -1 /proc/ -fi".Replace("\r\n", "\n"), receiver, cancellationToken); +fi".Replace("\r\n", "\n"), receiver, cancellationToken).ConfigureAwait(false); - Collection pids = []; + List pids = []; string output = receiver.ToString(); using (StringReader reader = new(output)) @@ -258,9 +257,8 @@ await client.ExecuteShellCommandAsync(device, @"SDK=""$(/system/bin/getprop ro.b if (i > 0 && (i % 25 == 0 || i == pids.Count - 1)) { - await client.ExecuteShellCommandAsync(device, catBuilder.ToString(), processOutputReceiver, cancellationToken); - _ = catBuilder.Clear(); - _ = catBuilder.Append("cat "); + await client.ExecuteShellCommandAsync(device, catBuilder.ToString(), processOutputReceiver, cancellationToken).ConfigureAwait(false); + _ = catBuilder.Clear().Append("cat "); } } diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs index 7ce21581..98f44dc1 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs @@ -4,11 +4,9 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Text; -using System.Threading; namespace AdvancedSharpAdbClient.DeviceCommands { @@ -213,7 +211,7 @@ public static IEnumerable ListProcesses(this IAdbClient client, /system/bin/ls -1 /proc/ fi".Replace("\r\n", "\n"), receiver); - Collection pids = []; + List pids = []; string output = receiver.ToString(); using (StringReader reader = new(output)) diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs index 5ec44a81..90bcf2f9 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs @@ -41,12 +41,12 @@ public virtual async Task InstallPackageAsync(string packageFilePath, bool reins { ValidateDevice(); - string remoteFilePath = await SyncPackageToDeviceAsync(packageFilePath, OnSyncProgressChanged, cancellationToken); + string remoteFilePath = await SyncPackageToDeviceAsync(packageFilePath, OnSyncProgressChanged, cancellationToken).ConfigureAwait(false); - await InstallRemotePackageAsync(remoteFilePath, reinstall, cancellationToken); + await InstallRemotePackageAsync(remoteFilePath, reinstall, cancellationToken).ConfigureAwait(false); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, 1, PackageInstallProgressState.PostInstall)); - await RemoveRemotePackageAsync(remoteFilePath, cancellationToken); + await RemoveRemotePackageAsync(remoteFilePath, cancellationToken).ConfigureAwait(false); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(1, 1, PackageInstallProgressState.PostInstall)); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.Finished)); @@ -72,7 +72,7 @@ public virtual async Task InstallRemotePackageAsync(string remoteFilePath, bool string reinstallSwitch = reinstall ? "-r " : string.Empty; string cmd = $"pm install {reinstallSwitch}\"{remoteFilePath}\""; - await client.ExecuteShellCommandAsync(Device, cmd, receiver, cancellationToken); + await client.ExecuteShellCommandAsync(Device, cmd, receiver, cancellationToken).ConfigureAwait(false); if (!string.IsNullOrEmpty(receiver.ErrorMessage)) { @@ -88,14 +88,14 @@ public virtual async Task InstallRemotePackageAsync(string remoteFilePath, bool /// Set to if re-install of app should be performed. /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. - public virtual async Task InstallMultiplePackageAsync(string basePackageFilePath, IList splitPackageFilePaths, bool reinstall, CancellationToken cancellationToken = default) + public virtual async Task InstallMultiplePackageAsync(string basePackageFilePath, ICollection splitPackageFilePaths, bool reinstall, CancellationToken cancellationToken = default) { ValidateDevice(); void OnMainSyncProgressChanged(object sender, SyncProgressChangedEventArgs args) => InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(sender is true ? 1 : 0, splitPackageFilePaths.Count + 1, args.ProgressPercentage / 2)); - string baseRemoteFilePath = await SyncPackageToDeviceAsync(basePackageFilePath, OnMainSyncProgressChanged, cancellationToken); + string baseRemoteFilePath = await SyncPackageToDeviceAsync(basePackageFilePath, OnMainSyncProgressChanged, cancellationToken).ConfigureAwait(false); Dictionary progress = new(splitPackageFilePaths.Count); void OnSplitSyncProgressChanged(object sender, SyncProgressChangedEventArgs args) @@ -109,38 +109,24 @@ void OnSplitSyncProgressChanged(object sender, SyncProgressChangedEventArgs args { count++; } - - double present = 0; - foreach (KeyValuePair info in progress) - { - present += info.Value / splitPackageFilePaths.Count / 2; - } - + double present = progress.Values.Select((x) => x / splitPackageFilePaths.Count / 2).Sum(); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitPackageFilePaths.Count + 1, present)); } List splitRemoteFilePaths = new(splitPackageFilePaths.Count); - IEnumerable tasks = splitPackageFilePaths.Select(async (x) => splitRemoteFilePaths.Add(await SyncPackageToDeviceAsync(x, OnSplitSyncProgressChanged, cancellationToken))); - foreach (Task task in tasks) - { - await task; - } + await Extensions.WhenAll(splitPackageFilePaths.Select(async (x) => splitRemoteFilePaths.Add(await SyncPackageToDeviceAsync(x, OnSplitSyncProgressChanged, cancellationToken).ConfigureAwait(false)))).ConfigureAwait(false); await InstallMultipleRemotePackageAsync(baseRemoteFilePath, splitRemoteFilePaths, reinstall, cancellationToken); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFilePaths.Count + 1, PackageInstallProgressState.PostInstall)); int count = 0; - tasks = splitRemoteFilePaths.Select(async (x) => + await Extensions.WhenAll(splitRemoteFilePaths.Select(async (x) => { - await RemoveRemotePackageAsync(x, cancellationToken); + await RemoveRemotePackageAsync(x, cancellationToken).ConfigureAwait(false); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(++count, splitRemoteFilePaths.Count + 1, PackageInstallProgressState.PostInstall)); - }); - foreach (Task task in tasks) - { - await task; - } + })).ConfigureAwait(false); - await RemoveRemotePackageAsync(baseRemoteFilePath, cancellationToken); + await RemoveRemotePackageAsync(baseRemoteFilePath, cancellationToken).ConfigureAwait(false); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(++count, splitRemoteFilePaths.Count + 1, PackageInstallProgressState.PostInstall)); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.Finished)); @@ -154,7 +140,7 @@ void OnSplitSyncProgressChanged(object sender, SyncProgressChangedEventArgs args /// Set to if re-install of app should be performed. /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. - public virtual async Task InstallMultiplePackageAsync(IList splitPackageFilePaths, string packageName, bool reinstall, CancellationToken cancellationToken = default) + public virtual async Task InstallMultiplePackageAsync(ICollection splitPackageFilePaths, string packageName, bool reinstall, CancellationToken cancellationToken = default) { ValidateDevice(); @@ -170,36 +156,22 @@ void OnSyncProgressChanged(object sender, SyncProgressChangedEventArgs args) { count++; } - - double present = 0; - foreach (KeyValuePair info in progress) - { - present += info.Value / splitPackageFilePaths.Count / 2; - } - + double present = progress.Values.Select((x) => x / splitPackageFilePaths.Count / 2).Sum(); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitPackageFilePaths.Count, present)); } List splitRemoteFilePaths = new(splitPackageFilePaths.Count); - IEnumerable tasks = splitPackageFilePaths.Select(async (x) => splitRemoteFilePaths.Add(await SyncPackageToDeviceAsync(x, OnSyncProgressChanged, cancellationToken))); - foreach (Task task in tasks) - { - await task; - } + await Extensions.WhenAll(splitPackageFilePaths.Select(async (x) => splitRemoteFilePaths.Add(await SyncPackageToDeviceAsync(x, OnSyncProgressChanged, cancellationToken)))).ConfigureAwait(false); await InstallMultipleRemotePackageAsync(splitRemoteFilePaths, packageName, reinstall, cancellationToken); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFilePaths.Count, PackageInstallProgressState.PostInstall)); int count = 0; - tasks = splitRemoteFilePaths.Select(async (x) => + await Extensions.WhenAll(splitRemoteFilePaths.Select(async (x) => { - await RemoveRemotePackageAsync(x, cancellationToken); + await RemoveRemotePackageAsync(x, cancellationToken).ConfigureAwait(false); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(++count, splitRemoteFilePaths.Count, PackageInstallProgressState.PostInstall)); - }); - foreach (Task task in tasks) - { - await task; - } + })).ConfigureAwait(false); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.Finished)); } @@ -212,42 +184,38 @@ void OnSyncProgressChanged(object sender, SyncProgressChangedEventArgs args) /// Set to if re-install of app should be performed. /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. - public virtual async Task InstallMultipleRemotePackageAsync(string baseRemoteFilePath, IList splitRemoteFilePaths, bool reinstall, CancellationToken cancellationToken = default) + public virtual async Task InstallMultipleRemotePackageAsync(string baseRemoteFilePath, ICollection splitRemoteFilePaths, bool reinstall, CancellationToken cancellationToken = default) { InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.CreateSession)); ValidateDevice(); - string session = await CreateInstallSessionAsync(reinstall, cancellationToken: cancellationToken); + string session = await CreateInstallSessionAsync(reinstall, cancellationToken: cancellationToken).ConfigureAwait(false); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFilePaths.Count + 1, PackageInstallProgressState.WriteSession)); - await WriteInstallSessionAsync(session, "base", baseRemoteFilePath, cancellationToken); + await WriteInstallSessionAsync(session, "base", baseRemoteFilePath, cancellationToken).ConfigureAwait(false); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(1, splitRemoteFilePaths.Count + 1, PackageInstallProgressState.WriteSession)); int i = 0, count = 0; - IEnumerable tasks = splitRemoteFilePaths.Select(async (splitRemoteFilePath) => + await Extensions.WhenAll(splitRemoteFilePaths.Select(async (splitRemoteFilePath) => { try { - await WriteInstallSessionAsync(session, $"splitapp{i++}", splitRemoteFilePath, cancellationToken); + await WriteInstallSessionAsync(session, $"splitapp{i++}", splitRemoteFilePath, cancellationToken).ConfigureAwait(false); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(++count, splitRemoteFilePaths.Count + 1, PackageInstallProgressState.WriteSession)); } catch (Exception ex) { Debug.WriteLine(ex.Message); } - }); - foreach (Task task in tasks) - { - await task; - } + })).ConfigureAwait(false); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.Installing)); InstallOutputReceiver receiver = new(); - await client.ExecuteShellCommandAsync(Device, $"pm install-commit {session}", receiver, cancellationToken); + await client.ExecuteShellCommandAsync(Device, $"pm install-commit {session}", receiver, cancellationToken).ConfigureAwait(false); if (!string.IsNullOrEmpty(receiver.ErrorMessage)) { @@ -263,38 +231,34 @@ public virtual async Task InstallMultipleRemotePackageAsync(string baseRemoteFil /// Set to if re-install of app should be performed. /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. - public virtual async Task InstallMultipleRemotePackageAsync(IList splitRemoteFilePaths, string packageName, bool reinstall, CancellationToken cancellationToken = default) + public virtual async Task InstallMultipleRemotePackageAsync(ICollection splitRemoteFilePaths, string packageName, bool reinstall, CancellationToken cancellationToken = default) { InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.CreateSession)); ValidateDevice(); - string session = await CreateInstallSessionAsync(reinstall, packageName, cancellationToken); + string session = await CreateInstallSessionAsync(reinstall, packageName, cancellationToken).ConfigureAwait(false); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFilePaths.Count, PackageInstallProgressState.WriteSession)); int i = 0, count = 0; - IEnumerable tasks = splitRemoteFilePaths.Select(async (splitRemoteFilePath) => + await Extensions.WhenAll(splitRemoteFilePaths.Select(async (splitRemoteFilePath) => { try { - await WriteInstallSessionAsync(session, $"splitapp{i++}", splitRemoteFilePath, cancellationToken); + await WriteInstallSessionAsync(session, $"splitapp{i++}", splitRemoteFilePath, cancellationToken).ConfigureAwait(false); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(++count, splitRemoteFilePaths.Count, PackageInstallProgressState.WriteSession)); } catch (Exception ex) { Debug.WriteLine(ex.Message); } - }); - foreach (Task task in tasks) - { - await task; - } + })).ConfigureAwait(false); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.Installing)); InstallOutputReceiver receiver = new(); - await client.ExecuteShellCommandAsync(Device, $"pm install-commit {session}", receiver, cancellationToken); + await client.ExecuteShellCommandAsync(Device, $"pm install-commit {session}", receiver, cancellationToken).ConfigureAwait(false); if (!string.IsNullOrEmpty(receiver.ErrorMessage)) { @@ -313,7 +277,7 @@ public virtual async Task UninstallPackageAsync(string packageName, Cancellation ValidateDevice(); InstallOutputReceiver receiver = new(); - await client.ExecuteShellCommandAsync(Device, $"pm uninstall {packageName}", receiver, cancellationToken); + await client.ExecuteShellCommandAsync(Device, $"pm uninstall {packageName}", receiver, cancellationToken).ConfigureAwait(false); if (!string.IsNullOrEmpty(receiver.ErrorMessage)) { throw new PackageInstallationException(receiver.ErrorMessage); @@ -331,7 +295,7 @@ public virtual async Task GetVersionInfoAsync(string packageName, C ValidateDevice(); VersionInfoReceiver receiver = new(); - await client.ExecuteShellCommandAsync(Device, $"dumpsys package {packageName}", receiver, cancellationToken); + await client.ExecuteShellCommandAsync(Device, $"dumpsys package {packageName}", receiver, cancellationToken).ConfigureAwait(false); return receiver.VersionInfo; } @@ -370,12 +334,12 @@ protected virtual async Task SyncPackageToDeviceAsync(string localFilePa #if NETCOREAPP3_0_OR_GREATER await #endif - using Stream stream = File.OpenRead(localFilePath); + using FileStream stream = File.OpenRead(localFilePath); logger.LogDebug("Uploading file onto device '{0}'", Device.Serial); // As C# can't use octal, the octal literal 666 (rw-Permission) is here converted to decimal (438) - await sync.PushAsync(stream, remoteFilePath, 438, File.GetLastWriteTime(localFilePath), null, cancellationToken); + await sync.PushAsync(stream, remoteFilePath, 438, File.GetLastWriteTime(localFilePath), null, cancellationToken).ConfigureAwait(false); } return remoteFilePath; @@ -403,7 +367,7 @@ protected virtual async Task RemoveRemotePackageAsync(string remoteFilePath, Can // now we delete the app we synced try { - await client.ExecuteShellCommandAsync(Device, $"rm \"{remoteFilePath}\"", null, cancellationToken); + await client.ExecuteShellCommandAsync(Device, $"rm \"{remoteFilePath}\"", null, cancellationToken).ConfigureAwait(false); } catch (IOException e) { @@ -428,7 +392,7 @@ protected virtual async Task CreateInstallSessionAsync(bool reinstall, s string addon = packageName.IsNullOrWhiteSpace() ? string.Empty : $" -p {packageName}"; string cmd = $"pm install-create{reinstallSwitch}{addon}"; - await client.ExecuteShellCommandAsync(Device, cmd, receiver, cancellationToken); + await client.ExecuteShellCommandAsync(Device, cmd, receiver, cancellationToken).ConfigureAwait(false); if (string.IsNullOrEmpty(receiver.SuccessMessage)) { @@ -455,7 +419,7 @@ protected virtual async Task WriteInstallSessionAsync(string session, string apk ValidateDevice(); InstallOutputReceiver receiver = new(); - await client.ExecuteShellCommandAsync(Device, $"pm install-write {session} {apkName}.apk \"{path}\"", receiver, cancellationToken); + await client.ExecuteShellCommandAsync(Device, $"pm install-write {session} {apkName}.apk \"{path}\"", receiver, cancellationToken).ConfigureAwait(false); if (!string.IsNullOrEmpty(receiver.ErrorMessage)) { diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs index ddd76390..b84d0a44 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs @@ -434,7 +434,7 @@ protected virtual string SyncPackageToDevice(string localFilePath, Action progress(localFilePath, e); } - using Stream stream = File.OpenRead(localFilePath); + using FileStream stream = File.OpenRead(localFilePath); logger.LogDebug("Uploading file onto device '{0}'", Device.Serial); diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs index aff2b399..93ac6026 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; namespace AdvancedSharpAdbClient.DeviceCommands { @@ -21,7 +20,7 @@ public ProcessOutputReceiver() { } /// /// Gets a list of all processes that have been received. /// - public Collection Processes { get; private set; } = new Collection(); + public List Processes { get; private set; } = new List(); /// protected override void ProcessNewLines(IEnumerable lines) diff --git a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs index 0afff082..fd2830ae 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs @@ -38,7 +38,7 @@ public virtual async Task StartAsync(CancellationToken cancellationToken = defau monitorTask = Extensions.Run(() => DeviceMonitorLoopAsync(monitorTaskCancellationTokenSource.Token), cancellationToken); // Wait for the worker thread to have read the first list of devices. - _ = await Extensions.Run(firstDeviceListParsed.WaitOne, cancellationToken); + _ = await Extensions.Run(firstDeviceListParsed.WaitOne, cancellationToken).ConfigureAwait(false); } } @@ -64,7 +64,7 @@ protected virtual async // 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; + await monitorTask.ConfigureAwait(false); #if HAS_PROCESS monitorTask.Dispose(); #endif @@ -112,7 +112,7 @@ protected virtual async Task DeviceMonitorLoopAsync(CancellationToken cancellati IsRunning = true; // Set up the connection to track the list of devices. - await InitializeSocketAsync(cancellationToken); + await InitializeSocketAsync(cancellationToken).ConfigureAwait(false); do { @@ -160,9 +160,9 @@ protected virtual async Task DeviceMonitorLoopAsync(CancellationToken cancellati if (adbException.ConnectionReset) { // The adb server was killed, for whatever reason. Try to restart it and recover from this. - await AdbServer.Instance.RestartServerAsync(cancellationToken); + await AdbServer.Instance.RestartServerAsync(cancellationToken).ConfigureAwait(false); Socket.Reconnect(); - await InitializeSocketAsync(cancellationToken); + await InitializeSocketAsync(cancellationToken).ConfigureAwait(false); } else { @@ -182,8 +182,8 @@ protected virtual async Task DeviceMonitorLoopAsync(CancellationToken cancellati private async Task InitializeSocketAsync(CancellationToken cancellationToken) { // Set up the connection to track the list of devices. - await Socket.SendAdbRequestAsync("host:track-devices", cancellationToken); - _ = await Socket.ReadAdbResponseAsync(cancellationToken); + await Socket.SendAdbRequestAsync("host:track-devices", cancellationToken).ConfigureAwait(false); + _ = await Socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); } } } diff --git a/AdvancedSharpAdbClient/Extensions/Extensions.cs b/AdvancedSharpAdbClient/Extensions/Extensions.cs index 371d29d9..3d49af5c 100644 --- a/AdvancedSharpAdbClient/Extensions/Extensions.cs +++ b/AdvancedSharpAdbClient/Extensions/Extensions.cs @@ -255,6 +255,19 @@ public static Task Run(Func function, CancellationTok #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); + #if !NET7_0_OR_GREATER /// /// Reads a line of characters asynchronously and returns the data as a string. diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs b/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs index 886022de..d321e899 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs @@ -590,15 +590,17 @@ public partial interface IAdbClient /// Click BACK button. /// /// The device on which to click BACK button. + /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. - Task BackBtnAsync(DeviceData device); + Task BackBtnAsync(DeviceData device, CancellationToken cancellationToken); /// /// Click HOME button. /// /// The device on which to click HOME button. + /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. - Task HomeBtnAsync(DeviceData device); + Task HomeBtnAsync(DeviceData device, CancellationToken cancellationToken); } } #endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Logs/EventLogEntry.cs b/AdvancedSharpAdbClient/Logs/EventLogEntry.cs index 61beb24c..2ef2054c 100644 --- a/AdvancedSharpAdbClient/Logs/EventLogEntry.cs +++ b/AdvancedSharpAdbClient/Logs/EventLogEntry.cs @@ -2,7 +2,7 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using System.Collections.ObjectModel; +using System.Collections.Generic; namespace AdvancedSharpAdbClient.Logs { @@ -25,6 +25,6 @@ public EventLogEntry() { } /// /// Gets or sets the values of this event log entry. /// - public Collection Values { get; set; } = new Collection(); + public List Values { get; set; } = new List(); } } diff --git a/AdvancedSharpAdbClient/Logs/LogReader.cs b/AdvancedSharpAdbClient/Logs/LogReader.cs index 51901bb1..89364114 100644 --- a/AdvancedSharpAdbClient/Logs/LogReader.cs +++ b/AdvancedSharpAdbClient/Logs/LogReader.cs @@ -4,7 +4,7 @@ using AdvancedSharpAdbClient.Exceptions; using System; -using System.Collections.ObjectModel; +using System.Collections.Generic; using System.IO; using System.Text; @@ -186,7 +186,7 @@ or LogId.Radio /// /// Reads a single log entry from the stream. /// - protected void ReadLogEntry(BinaryReader reader, Collection parent) + protected void ReadLogEntry(BinaryReader reader, IList parent) { EventLogType type = (EventLogType)reader.ReadByte(); @@ -207,7 +207,7 @@ protected void ReadLogEntry(BinaryReader reader, Collection parent) case EventLogType.List: byte listLength = reader.ReadByte(); - Collection list = []; + List list = []; for (int i = 0; i < listLength; i++) { diff --git a/AdvancedSharpAdbClient/Models/Element.cs b/AdvancedSharpAdbClient/Models/Element.cs index 315a13e6..486afb27 100644 --- a/AdvancedSharpAdbClient/Models/Element.cs +++ b/AdvancedSharpAdbClient/Models/Element.cs @@ -228,8 +228,8 @@ public virtual int GetChildCount() /// Clicks on this coordinates. /// /// A which can be used to cancel the asynchronous operation. - public async Task ClickAsync(CancellationToken cancellationToken = default) => - await Client.ClickAsync(Device, Cords, cancellationToken); + public Task ClickAsync(CancellationToken cancellationToken = default) => + Client.ClickAsync(Device, Cords, cancellationToken); #endif /// @@ -251,8 +251,8 @@ public void SendText(string text) /// A which represents the asynchronous operation. public async Task SendTextAsync(string text, CancellationToken cancellationToken = default) { - await ClickAsync(cancellationToken); - await Client.SendTextAsync(Device, text, cancellationToken); + await ClickAsync(cancellationToken).ConfigureAwait(false); + await Client.SendTextAsync(Device, text, cancellationToken).ConfigureAwait(false); } #endif @@ -282,14 +282,14 @@ public void ClearInput(int charCount = 0) /// A which represents the asynchronous operation. public async Task ClearInputAsync(int charCount = 0, CancellationToken cancellationToken = default) { - await ClickAsync(cancellationToken); // focuses + await ClickAsync(cancellationToken).ConfigureAwait(false); // focuses if (charCount == 0) { - await Client.ClearInputAsync(Device, Attributes["text"].Length, cancellationToken); + await Client.ClearInputAsync(Device, Attributes["text"].Length, cancellationToken).ConfigureAwait(false); } else { - await Client.ClearInputAsync(Device, charCount, cancellationToken); + await Client.ClearInputAsync(Device, charCount, cancellationToken).ConfigureAwait(false); } } #endif diff --git a/AdvancedSharpAdbClient/Models/Framebuffer.cs b/AdvancedSharpAdbClient/Models/Framebuffer.cs index 21383e52..e25e6d3c 100644 --- a/AdvancedSharpAdbClient/Models/Framebuffer.cs +++ b/AdvancedSharpAdbClient/Models/Framebuffer.cs @@ -141,11 +141,11 @@ public virtual async Task RefreshAsync(bool reset = false, CancellationToken can using IAdbSocket socket = Factories.AdbSocketFactory(EndPoint); // Select the target device - socket.SetDevice(Device); + await socket.SetDeviceAsync(Device, cancellationToken).ConfigureAwait(false); // Send the framebuffer command - socket.SendAdbRequest("framebuffer:"); - socket.ReadAdbResponse(); + await socket.SendAdbRequestAsync("framebuffer:", cancellationToken).ConfigureAwait(false); + await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); // The result first is a FramebufferHeader object, _ = await socket.ReadAsync(headerData, cancellationToken).ConfigureAwait(false); diff --git a/AdvancedSharpAdbClient/SyncService.Async.cs b/AdvancedSharpAdbClient/SyncService.Async.cs index 2b957203..6b94bf88 100644 --- a/AdvancedSharpAdbClient/SyncService.Async.cs +++ b/AdvancedSharpAdbClient/SyncService.Async.cs @@ -6,7 +6,6 @@ using AdvancedSharpAdbClient.Exceptions; using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.IO; using System.Runtime.CompilerServices; using System.Threading; @@ -19,10 +18,10 @@ public partial class SyncService public virtual async Task OpenAsync(CancellationToken cancellationToken = default) { // target a specific device - await Socket.SetDeviceAsync(Device, cancellationToken); + await Socket.SetDeviceAsync(Device, cancellationToken).ConfigureAwait(false); - await Socket.SendAdbRequestAsync("sync:", cancellationToken); - _ = await Socket.ReadAdbResponseAsync(cancellationToken); + await Socket.SendAdbRequestAsync("sync:", cancellationToken).ConfigureAwait(false); + _ = await Socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); } /// @@ -62,7 +61,7 @@ public virtual async Task PushAsync(Stream stream, string remotePath, int permis throw new ArgumentOutOfRangeException(nameof(remotePath), $"The remote path {remotePath} exceeds the maximum path size {MaxPathLength}"); } - await Socket.SendSyncRequestAsync(SyncCommand.SEND, remotePath, permissions, cancellationToken); + await Socket.SendSyncRequestAsync(SyncCommand.SEND, remotePath, permissions, cancellationToken).ConfigureAwait(false); // create the buffer used to read. // we read max SYNC_DATA_MAX. @@ -92,9 +91,9 @@ public virtual async Task PushAsync(Stream stream, string remotePath, int permis // read up to SYNC_DATA_MAX int read = #if HAS_BUFFERS - await stream.ReadAsync(buffer.AsMemory(headerSize, maxDataSize), cancellationToken); + await stream.ReadAsync(buffer.AsMemory(headerSize, maxDataSize), cancellationToken).ConfigureAwait(false); #else - await stream.ReadAsync(buffer, headerSize, maxDataSize, cancellationToken); + await stream.ReadAsync(buffer, headerSize, maxDataSize, cancellationToken).ConfigureAwait(false); #endif totalBytesRead += read; @@ -117,9 +116,9 @@ public virtual async Task PushAsync(Stream stream, string remotePath, int permis // now send the data to the device #if HAS_BUFFERS - await Socket.SendAsync(buffer.AsMemory(startPosition, read + dataBytes.Length + lengthBytes.Length), cancellationToken); + await Socket.SendAsync(buffer.AsMemory(startPosition, read + dataBytes.Length + lengthBytes.Length), cancellationToken).ConfigureAwait(false); #else - await Socket.SendAsync(buffer, startPosition, read + dataBytes.Length + lengthBytes.Length, cancellationToken); + await Socket.SendAsync(buffer, startPosition, read + dataBytes.Length + lengthBytes.Length, cancellationToken).ConfigureAwait(false); #endif SyncProgressChanged?.Invoke(this, new SyncProgressChangedEventArgs(totalBytesRead, totalBytesToProcess)); @@ -133,15 +132,15 @@ public virtual async Task PushAsync(Stream stream, string remotePath, int permis // create the DONE message int time = (int)timestamp.ToUnixTimeSeconds(); - await Socket.SendSyncRequestAsync(SyncCommand.DONE, time, cancellationToken); + await Socket.SendSyncRequestAsync(SyncCommand.DONE, time, cancellationToken).ConfigureAwait(false); // read the result, in a byte array containing 2 int // (id, size) - SyncCommand result = await Socket.ReadSyncResponseAsync(cancellationToken); + SyncCommand result = await Socket.ReadSyncResponseAsync(cancellationToken).ConfigureAwait(false); if (result == SyncCommand.FAIL) { - string message = await Socket.ReadSyncStringAsync(cancellationToken); + string message = await Socket.ReadSyncStringAsync(cancellationToken).ConfigureAwait(false); throw new AdbException(message); } @@ -159,17 +158,17 @@ public virtual async Task PullAsync(string remoteFilePath, Stream stream, IProgr ExceptionExtensions.ThrowIfNull(stream); // Get file information, including the file size, used to calculate the total amount of bytes to receive. - FileStatistics stat = await StatAsync(remoteFilePath, cancellationToken); + FileStatistics stat = await StatAsync(remoteFilePath, cancellationToken).ConfigureAwait(false); long totalBytesToProcess = stat.Size; long totalBytesRead = 0; byte[] buffer = new byte[MaxBufferSize]; - await Socket.SendSyncRequestAsync(SyncCommand.RECV, remoteFilePath, cancellationToken); + await Socket.SendSyncRequestAsync(SyncCommand.RECV, remoteFilePath, cancellationToken).ConfigureAwait(false); while (true) { - SyncCommand response = await Socket.ReadSyncResponseAsync(cancellationToken); + SyncCommand response = await Socket.ReadSyncResponseAsync(cancellationToken).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); if (response == SyncCommand.DONE) @@ -178,7 +177,7 @@ public virtual async Task PullAsync(string remoteFilePath, Stream stream, IProgr } else if (response == SyncCommand.FAIL) { - string message = await Socket.ReadSyncStringAsync(cancellationToken); + string message = await Socket.ReadSyncStringAsync(cancellationToken).ConfigureAwait(false); throw new AdbException($"Failed to pull '{remoteFilePath}'. {message}"); } else if (response != SyncCommand.DATA) @@ -188,7 +187,7 @@ public virtual async Task PullAsync(string remoteFilePath, Stream stream, IProgr // The first 4 bytes contain the length of the data packet byte[] reply = new byte[4]; - _ = await Socket.ReadAsync(reply, cancellationToken); + _ = await Socket.ReadAsync(reply, cancellationToken).ConfigureAwait(false); if (!BitConverter.IsLittleEndian) { @@ -204,11 +203,11 @@ public virtual async Task PullAsync(string remoteFilePath, Stream stream, IProgr // now read the length we received #if HAS_BUFFERS - await Socket.ReadAsync(buffer.AsMemory(0, size), cancellationToken); - await stream.WriteAsync(buffer.AsMemory(0, size), cancellationToken); + await Socket.ReadAsync(buffer.AsMemory(0, size), cancellationToken).ConfigureAwait(false); + await stream.WriteAsync(buffer.AsMemory(0, size), cancellationToken).ConfigureAwait(false); #else - await Socket.ReadAsync(buffer, size, cancellationToken); - await stream.WriteAsync(buffer, size, cancellationToken); + await Socket.ReadAsync(buffer, size, cancellationToken).ConfigureAwait(false); + await stream.WriteAsync(buffer, size, cancellationToken).ConfigureAwait(false); #endif totalBytesRead += size; @@ -226,9 +225,9 @@ public virtual async Task PullAsync(string remoteFilePath, Stream stream, IProgr public virtual async Task StatAsync(string remotePath, CancellationToken cancellationToken = default) { // create the stat request message. - await Socket.SendSyncRequestAsync(SyncCommand.STAT, remotePath, cancellationToken); + await Socket.SendSyncRequestAsync(SyncCommand.STAT, remotePath, cancellationToken).ConfigureAwait(false); - if (await Socket.ReadSyncResponseAsync(cancellationToken) != SyncCommand.STAT) + if (await Socket.ReadSyncResponseAsync(cancellationToken).ConfigureAwait(false) != SyncCommand.STAT) { throw new AdbException($"The server returned an invalid sync response."); } @@ -240,7 +239,7 @@ public virtual async Task StatAsync(string remotePath, Cancellat Path = remotePath }; - await ReadStatisticsAsync(value, cancellationToken); + await ReadStatisticsAsync(value, cancellationToken).ConfigureAwait(false); return value; } @@ -248,14 +247,14 @@ public virtual async Task StatAsync(string remotePath, Cancellat /// public virtual async Task> GetDirectoryListingAsync(string remotePath, CancellationToken cancellationToken = default) { - Collection value = []; + List value = []; // create the stat request message. - await Socket.SendSyncRequestAsync(SyncCommand.LIST, remotePath, cancellationToken); + await Socket.SendSyncRequestAsync(SyncCommand.LIST, remotePath, cancellationToken).ConfigureAwait(false); while (true) { - SyncCommand response = await Socket.ReadSyncResponseAsync(cancellationToken); + SyncCommand response = await Socket.ReadSyncResponseAsync(cancellationToken).ConfigureAwait(false); if (response == SyncCommand.DONE) { @@ -267,8 +266,8 @@ public virtual async Task> GetDirectoryListingAsync( } FileStatistics entry = new(); - await ReadStatisticsAsync(entry, cancellationToken); - entry.Path = await Socket.ReadSyncStringAsync(cancellationToken); + await ReadStatisticsAsync(entry, cancellationToken).ConfigureAwait(false); + entry.Path = await Socket.ReadSyncStringAsync(cancellationToken).ConfigureAwait(false); value.Add(entry); } @@ -281,11 +280,11 @@ public virtual async Task> GetDirectoryListingAsync( public virtual async IAsyncEnumerable GetDirectoryAsyncListing(string remotePath, [EnumeratorCancellation] CancellationToken cancellationToken = default) { // create the stat request message. - await Socket.SendSyncRequestAsync(SyncCommand.LIST, remotePath, cancellationToken); + await Socket.SendSyncRequestAsync(SyncCommand.LIST, remotePath, cancellationToken).ConfigureAwait(false); while (true) { - SyncCommand response = await Socket.ReadSyncResponseAsync(cancellationToken); + SyncCommand response = await Socket.ReadSyncResponseAsync(cancellationToken).ConfigureAwait(false); if (response == SyncCommand.DONE) { @@ -297,8 +296,8 @@ public virtual async IAsyncEnumerable GetDirectoryAsyncListing(s } FileStatistics entry = new(); - await ReadStatisticsAsync(entry, cancellationToken); - entry.Path = await Socket.ReadSyncStringAsync(cancellationToken); + await ReadStatisticsAsync(entry, cancellationToken).ConfigureAwait(false); + entry.Path = await Socket.ReadSyncStringAsync(cancellationToken).ConfigureAwait(false); yield return entry; } @@ -308,7 +307,7 @@ public virtual async IAsyncEnumerable GetDirectoryAsyncListing(s private async Task ReadStatisticsAsync(FileStatistics value, CancellationToken cancellationToken = default) { byte[] statResult = new byte[12]; - _ = await Socket.ReadAsync(statResult, cancellationToken); + _ = await Socket.ReadAsync(statResult, cancellationToken).ConfigureAwait(false); if (!BitConverter.IsLittleEndian) { diff --git a/AdvancedSharpAdbClient/TcpSocket.Async.cs b/AdvancedSharpAdbClient/TcpSocket.Async.cs index b04a8aa2..753dfc41 100644 --- a/AdvancedSharpAdbClient/TcpSocket.Async.cs +++ b/AdvancedSharpAdbClient/TcpSocket.Async.cs @@ -21,7 +21,7 @@ public virtual async ValueTask ConnectAsync(EndPoint endPoint, CancellationToken throw new NotSupportedException("Only TCP endpoints are supported"); } - await socket.ConnectAsync(endPoint, cancellationToken); + await socket.ConnectAsync(endPoint, cancellationToken).ConfigureAwait(false); socket.Blocking = true; this.endPoint = endPoint; } From e5197461aad60f30fb5fd2a1dd9c5279a2195dce Mon Sep 17 00:00:00 2001 From: wherewhere Date: Sun, 1 Oct 2023 22:23:52 +0800 Subject: [PATCH 19/66] Rename IsValidAdbFile to CheckFileExists Change some IList to ICollection Change (x) to x --- .../Dummys/DummyAdbCommandLineClient.cs | 42 ++++++------ .../AdbCommandLineClientExtensionsTests.cs | 34 ---------- .../Extensions/ExtensionsTests.cs | 25 +++++++ AdvancedSharpAdbClient/AdbClient.Async.cs | 10 +-- AdvancedSharpAdbClient/AdbClient.cs | 6 +- .../AdbCommandLineClient.Async.cs | 6 +- .../AdbCommandLineClient.cs | 68 +++++++++++-------- AdvancedSharpAdbClient/AdbServer.Async.cs | 6 +- AdvancedSharpAdbClient/AdbServer.cs | 8 +-- .../DeviceCommands/PackageManager.Async.cs | 12 ++-- .../DeviceCommands/PackageManager.cs | 22 ++---- AdvancedSharpAdbClient/DeviceMonitor.cs | 2 +- .../AdbCommandLineClientExtensions.cs | 30 -------- .../Extensions/Extensions.cs | 34 ++++++++++ .../Extensions/Factories.cs | 6 +- .../Extensions/SocketExtensions.cs | 4 +- .../Extensions/SteamExtensions.cs | 4 +- .../Interfaces/IAdbCommandLineClient.cs | 4 +- AdvancedSharpAdbClient/Logs/LogReader.cs | 2 +- AdvancedSharpAdbClient/Models/Element.cs | 7 +- 20 files changed, 162 insertions(+), 170 deletions(-) delete mode 100644 AdvancedSharpAdbClient.Tests/Extensions/AdbCommandLineClientExtensionsTests.cs delete mode 100644 AdvancedSharpAdbClient/Extensions/AdbCommandLineClientExtensions.cs diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbCommandLineClient.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbCommandLineClient.cs index ff2b269f..f6837a06 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbCommandLineClient.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbCommandLineClient.cs @@ -19,38 +19,40 @@ public DummyAdbCommandLineClient() : base(ServerName) public bool ServerStarted { get; private set; } - public override bool IsValidAdbFile(string adbPath) => - // No validation done in the dummy adb client. - true; + // No validation done in the dummy adb client. + public override bool CheckFileExists(string adbPath) => true; - protected override int RunAdbProcessInner(string command, List errorOutput, List standardOutput) + protected override int RunProcess(string filename, string command, ICollection errorOutput, ICollection standardOutput) { - errorOutput?.Add(null); + if (filename == AdbPath) + { + errorOutput?.Add(null); - standardOutput?.Add(null); + standardOutput?.Add(null); - if (command == "start-server") - { - ServerStarted = true; - } - else if (command == "version") - { - if (standardOutput != null && Version != null) + if (command == "start-server") { - standardOutput.Add($"Android Debug Bridge version {Version.ToString(3)}"); + ServerStarted = true; + } + else if (command == "version") + { + if (standardOutput != null && Version != null) + { + standardOutput.Add($"Android Debug Bridge version {Version.ToString(3)}"); + } + } + else + { + throw new ArgumentOutOfRangeException(nameof(command)); } - } - else - { - throw new ArgumentOutOfRangeException(nameof(command)); } return 0; } - protected override Task RunAdbProcessInnerAsync(string command, List errorOutput, List standardOutput, CancellationToken cancellationToken = default) + protected override Task RunProcessAsync(string filename, string command, ICollection errorOutput, ICollection standardOutput, CancellationToken cancellationToken = default) { - int result = RunAdbProcessInner(command, errorOutput, standardOutput); + int result = RunProcess(filename, command, errorOutput, standardOutput); TaskCompletionSource tcs = new(); tcs.SetResult(result); return tcs.Task; diff --git a/AdvancedSharpAdbClient.Tests/Extensions/AdbCommandLineClientExtensionsTests.cs b/AdvancedSharpAdbClient.Tests/Extensions/AdbCommandLineClientExtensionsTests.cs deleted file mode 100644 index 5b9e2e1e..00000000 --- a/AdvancedSharpAdbClient.Tests/Extensions/AdbCommandLineClientExtensionsTests.cs +++ /dev/null @@ -1,34 +0,0 @@ -using NSubstitute; -using System; -using System.IO; -using Xunit; - -namespace AdvancedSharpAdbClient.Tests -{ - /// - /// Tests the class. - /// - public class AdbCommandLineClientExtensionsTests - { - /// - /// Tests the method. - /// - [Fact] - public void EnsureIsValidAdbFileNullValueTest() => - _ = Assert.Throws(() => AdbCommandLineClientExtensions.EnsureIsValidAdbFile(null, "adb.exe")); - - /// - /// Tests the method. - /// - [Fact] - public void EnsureIsValidAdbFileInvalidFileTest() - { - IAdbCommandLineClient clientMock = Substitute.For(); - clientMock.IsValidAdbFile(Arg.Any()).Returns(false); - - IAdbCommandLineClient client = clientMock; - - _ = Assert.Throws(() => client.EnsureIsValidAdbFile("xyz.exe")); - } - } -} diff --git a/AdvancedSharpAdbClient.Tests/Extensions/ExtensionsTests.cs b/AdvancedSharpAdbClient.Tests/Extensions/ExtensionsTests.cs index dc23e4b8..91fd45cb 100644 --- a/AdvancedSharpAdbClient.Tests/Extensions/ExtensionsTests.cs +++ b/AdvancedSharpAdbClient.Tests/Extensions/ExtensionsTests.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Collections.ObjectModel; using Xunit; namespace AdvancedSharpAdbClient.Tests @@ -8,6 +9,30 @@ namespace AdvancedSharpAdbClient.Tests /// public class ExtensionsTests { + /// + /// Tests the method. + /// + [Fact] + public void AddRangeTest() + { + int[] numbs = [6, 7, 8, 9, 10]; + + List list = [1, 2, 3, 4, 5]; + list.AddRange(numbs); + Assert.Equal(10, list.Count); + Assert.Equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], list); + + HashSet hashSet = [1, 2, 3, 4, 5]; + hashSet.AddRange(numbs); + Assert.Equal(10, hashSet.Count); + Assert.Equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], hashSet); + + Collection collection = [1, 2, 3, 4, 5]; + collection.AddRange(numbs); + Assert.Equal(10, collection.Count); + Assert.Equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], collection); + } + /// /// Tests the method. /// diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index f874126a..d20f7050 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -53,7 +53,7 @@ public async Task> GetDevicesAsync(CancellationToken can string[] data = reply.Split(separator, StringSplitOptions.RemoveEmptyEntries); - return data.Select((x) => new DeviceData(x)); + return data.Select(x => new DeviceData(x)); } /// @@ -151,7 +151,7 @@ public async Task> ListForwardAsync(DeviceData device, string[] parts = data.Split(separator, StringSplitOptions.RemoveEmptyEntries); - return parts.Select((x) => new ForwardData(x)); + return parts.Select(x => new ForwardData(x)); } /// @@ -169,7 +169,7 @@ public async Task> ListReverseForwardAsync(DeviceData d string[] parts = data.Split(separator, StringSplitOptions.RemoveEmptyEntries); - return parts.Select((x) => new ForwardData(x)); + return parts.Select(x => new ForwardData(x)); } /// @@ -459,7 +459,7 @@ public async Task InstallMultipleAsync(DeviceData device, IEnumerable sp string session = await InstallCreateAsync(device, packageName, cancellationToken, arguments).ConfigureAwait(false); int i = 0; - await Extensions.WhenAll(splitAPKs.Select(async (splitAPK) => + await Extensions.WhenAll(splitAPKs.Select(async splitAPK => { if (splitAPK == null || !splitAPK.CanRead || !splitAPK.CanSeek) { @@ -501,7 +501,7 @@ public async Task InstallMultipleAsync(DeviceData device, Stream baseAPK, IEnume await InstallWriteAsync(device, baseAPK, nameof(baseAPK), session, cancellationToken).ConfigureAwait(false); int i = 0; - await Extensions.WhenAll(splitAPKs.Select(async (splitAPK) => + await Extensions.WhenAll(splitAPKs.Select(async splitAPK => { if (splitAPK == null || !splitAPK.CanRead || !splitAPK.CanSeek) { diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index dc2e8261..d1d58849 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -197,7 +197,7 @@ public IEnumerable GetDevices() string[] data = reply.Split(separator, StringSplitOptions.RemoveEmptyEntries); - return data.Select((x) => new DeviceData(x)); + return data.Select(x => new DeviceData(x)); } /// @@ -295,7 +295,7 @@ public IEnumerable ListForward(DeviceData device) string[] parts = data.Split(separator, StringSplitOptions.RemoveEmptyEntries); - return parts.Select((x) => new ForwardData(x)); + return parts.Select(x => new ForwardData(x)); } /// @@ -313,7 +313,7 @@ public IEnumerable ListReverseForward(DeviceData device) string[] parts = data.Split(separator, StringSplitOptions.RemoveEmptyEntries); - return parts.Select((x) => new ForwardData(x)); + return parts.Select(x => new ForwardData(x)); } /// diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs index 5bb49452..5fd47357 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs @@ -96,7 +96,7 @@ public virtual async Task StartServerAsync(CancellationToken cancellationToken = /// Use this command only for adb commands that return immediately, such as /// adb version. This operation times out after 5 seconds. /// The process exited with an exit code other than 0. - protected virtual async Task RunAdbProcessAsync(string command, List errorOutput, List standardOutput, CancellationToken cancellationToken = default) + protected virtual async Task RunAdbProcessAsync(string command, ICollection errorOutput, ICollection standardOutput, CancellationToken cancellationToken = default) { int status = await RunAdbProcessInnerAsync(command, errorOutput, standardOutput, cancellationToken).ConfigureAwait(false); @@ -119,7 +119,7 @@ protected virtual async Task RunAdbProcessAsync(string command, List err /// A which return the return code of the adb process. /// Use this command only for adb commands that return immediately, such as /// adb version. This operation times out after 5 seconds. - protected virtual async Task RunAdbProcessInnerAsync(string command, List errorOutput, List standardOutput, CancellationToken cancellationToken = default) + protected virtual async Task RunAdbProcessInnerAsync(string command, ICollection errorOutput, ICollection standardOutput, CancellationToken cancellationToken = default) { ExceptionExtensions.ThrowIfNull(command); @@ -132,7 +132,7 @@ protected virtual async Task RunAdbProcessInnerAsync(string command, List /// The return code of the process. - protected virtual async Task RunProcessAsync(string filename, string command, List errorOutput, List standardOutput, CancellationToken cancellationToken = default) + protected virtual async Task RunProcessAsync(string filename, string command, ICollection errorOutput, ICollection standardOutput, CancellationToken cancellationToken = default) { #if HAS_PROCESS ProcessStartInfo psi = new(filename, command) diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.cs index 869b76f6..b2d2fac1 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.cs @@ -45,30 +45,13 @@ public AdbCommandLineClient(string adbPath, bool isForce = false, ILogger(); @@ -77,7 +60,7 @@ public AdbCommandLineClient(string adbPath, bool isForce = false, ILogger /// Gets the path to the adb.exe executable. /// - public string AdbPath { get; private set; } + public string AdbPath { get; protected set; } /// /// Queries adb for its version number and checks it against . @@ -145,14 +128,43 @@ public virtual void StartServer() } /// - public virtual bool IsValidAdbFile(string adbPath) => Factories.CheckFileExists(adbPath); + public virtual bool CheckFileExists(string adbPath) => Factories.CheckFileExists(adbPath); + + /// + /// Throws an error if the path does not point to a valid instance of adb.exe. + /// + /// The path to validate. + protected virtual void EnsureIsValidAdbFile(string adbPath) + { + bool isWindows = Extensions.IsWindowsPlatform(); + bool isUnix = Extensions.IsUnixPlatform(); + + if (isWindows) + { + if (!string.Equals(Path.GetFileName(adbPath), "adb.exe", StringComparison.OrdinalIgnoreCase)) + { + throw new ArgumentOutOfRangeException(nameof(adbPath), $"{adbPath} does not seem to be a valid adb.exe executable. The path must end with `adb.exe`"); + } + } + else if (isUnix) + { + if (!string.Equals(Path.GetFileName(adbPath), "adb", StringComparison.OrdinalIgnoreCase)) + { + throw new ArgumentOutOfRangeException(nameof(adbPath), $"{adbPath} does not seem to be a valid adb executable. The path must end with `adb`"); + } + } + else + { + throw new NotSupportedException("SharpAdbClient only supports launching adb.exe on Windows, Mac OS and Linux"); + } + } /// /// Parses the output of the adb.exe version command and determines the adb version. /// /// The output of the adb.exe version command. /// A object that represents the version of the adb command line client. - internal static Version GetVersionFromOutput(IEnumerable output) + protected static Version GetVersionFromOutput(IEnumerable output) { Regex regex = AdbVersionRegex(); foreach (string line in output) @@ -189,7 +201,7 @@ internal static Version GetVersionFromOutput(IEnumerable output) /// Use this command only for adb commands that return immediately, such as /// adb version. This operation times out after 5 seconds. /// The process exited with an exit code other than 0. - protected virtual void RunAdbProcess(string command, List errorOutput, List standardOutput) + protected virtual void RunAdbProcess(string command, ICollection errorOutput, ICollection standardOutput) { int status = RunAdbProcessInner(command, errorOutput, standardOutput); @@ -211,7 +223,7 @@ protected virtual void RunAdbProcess(string command, List errorOutput, L /// The return code of the adb process. /// Use this command only for adb commands that return immediately, such as /// adb version. This operation times out after 5 seconds. - protected virtual int RunAdbProcessInner(string command, List errorOutput, List standardOutput) + protected virtual int RunAdbProcessInner(string command, ICollection errorOutput, ICollection standardOutput) { ExceptionExtensions.ThrowIfNull(command); @@ -224,7 +236,7 @@ protected virtual int RunAdbProcessInner(string command, List errorOutpu /// Runs process, invoking a specific command, and reads the standard output and standard error output. /// /// The return code of the process. - protected virtual int RunProcess(string filename, string command, List errorOutput, List standardOutput) + protected virtual int RunProcess(string filename, string command, ICollection errorOutput, ICollection standardOutput) { #if HAS_PROCESS ProcessStartInfo psi = new(filename, command) diff --git a/AdvancedSharpAdbClient/AdbServer.Async.cs b/AdvancedSharpAdbClient/AdbServer.Async.cs index 0c8279af..70221dc3 100644 --- a/AdvancedSharpAdbClient/AdbServer.Async.cs +++ b/AdvancedSharpAdbClient/AdbServer.Async.cs @@ -19,9 +19,9 @@ public virtual async Task StartServerAsync(string adbPath, bo Version commandLineVersion = null; IAdbCommandLineClient commandLineClient = adbCommandLineClientFactory(adbPath); - IsValidAdbFile = commandLineClient.IsValidAdbFile; + CheckFileExists = commandLineClient.CheckFileExists; - if (commandLineClient.IsValidAdbFile(adbPath)) + if (commandLineClient.CheckFileExists(adbPath)) { CachedAdbPath = adbPath; commandLineVersion = await commandLineClient.GetVersionAsync(cancellationToken).ConfigureAwait(false); @@ -68,7 +68,7 @@ public virtual async Task RestartServerAsync(string adbPath, { adbPath ??= CachedAdbPath; - if (!IsValidAdbFile(adbPath)) + if (!CheckFileExists(adbPath)) { throw new InvalidOperationException($"The adb server was not started via {nameof(AdbServer)}.{nameof(this.StartServer)} or no path to adb was specified. The adb server cannot be restarted."); } diff --git a/AdvancedSharpAdbClient/AdbServer.cs b/AdvancedSharpAdbClient/AdbServer.cs index 74813ad0..33697502 100644 --- a/AdvancedSharpAdbClient/AdbServer.cs +++ b/AdvancedSharpAdbClient/AdbServer.cs @@ -97,7 +97,7 @@ public AdbServer(Func adbCommandLineClientFactory /// /// Throws an error if the path does not point to a valid instance of adb.exe. /// - protected static Func IsValidAdbFile { get; set; } = Factories.CheckFileExists; + protected static Func CheckFileExists { get; set; } = Factories.CheckFileExists; /// public virtual StartServerResult StartServer(string adbPath, bool restartServerIfNewer) @@ -106,9 +106,9 @@ public virtual StartServerResult StartServer(string adbPath, bool restartServerI Version commandLineVersion = null; IAdbCommandLineClient commandLineClient = adbCommandLineClientFactory(adbPath); - IsValidAdbFile = commandLineClient.IsValidAdbFile; + CheckFileExists = commandLineClient.CheckFileExists; - if (commandLineClient.IsValidAdbFile(adbPath)) + if (commandLineClient.CheckFileExists(adbPath)) { CachedAdbPath = adbPath; commandLineVersion = commandLineClient.GetVersion(); @@ -152,7 +152,7 @@ public virtual StartServerResult RestartServer(string adbPath = null) { adbPath ??= CachedAdbPath; - if (!IsValidAdbFile(adbPath)) + if (!CheckFileExists(adbPath)) { throw new InvalidOperationException($"The adb server was not started via {nameof(AdbServer)}.{nameof(this.StartServer)} or no path to adb was specified. The adb server cannot be restarted."); } diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs index 90bcf2f9..f1c1f587 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs @@ -109,18 +109,18 @@ void OnSplitSyncProgressChanged(object sender, SyncProgressChangedEventArgs args { count++; } - double present = progress.Values.Select((x) => x / splitPackageFilePaths.Count / 2).Sum(); + double present = progress.Values.Select(x => x / splitPackageFilePaths.Count / 2).Sum(); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitPackageFilePaths.Count + 1, present)); } List splitRemoteFilePaths = new(splitPackageFilePaths.Count); - await Extensions.WhenAll(splitPackageFilePaths.Select(async (x) => splitRemoteFilePaths.Add(await SyncPackageToDeviceAsync(x, OnSplitSyncProgressChanged, cancellationToken).ConfigureAwait(false)))).ConfigureAwait(false); + await Extensions.WhenAll(splitPackageFilePaths.Select(async x => splitRemoteFilePaths.Add(await SyncPackageToDeviceAsync(x, OnSplitSyncProgressChanged, cancellationToken).ConfigureAwait(false)))).ConfigureAwait(false); await InstallMultipleRemotePackageAsync(baseRemoteFilePath, splitRemoteFilePaths, reinstall, cancellationToken); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFilePaths.Count + 1, PackageInstallProgressState.PostInstall)); int count = 0; - await Extensions.WhenAll(splitRemoteFilePaths.Select(async (x) => + await Extensions.WhenAll(splitRemoteFilePaths.Select(async x => { await RemoveRemotePackageAsync(x, cancellationToken).ConfigureAwait(false); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(++count, splitRemoteFilePaths.Count + 1, PackageInstallProgressState.PostInstall)); @@ -156,18 +156,18 @@ void OnSyncProgressChanged(object sender, SyncProgressChangedEventArgs args) { count++; } - double present = progress.Values.Select((x) => x / splitPackageFilePaths.Count / 2).Sum(); + double present = progress.Values.Select(x => x / splitPackageFilePaths.Count).Sum(); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitPackageFilePaths.Count, present)); } List splitRemoteFilePaths = new(splitPackageFilePaths.Count); - await Extensions.WhenAll(splitPackageFilePaths.Select(async (x) => splitRemoteFilePaths.Add(await SyncPackageToDeviceAsync(x, OnSyncProgressChanged, cancellationToken)))).ConfigureAwait(false); + await Extensions.WhenAll(splitPackageFilePaths.Select(async x => splitRemoteFilePaths.Add(await SyncPackageToDeviceAsync(x, OnSyncProgressChanged, cancellationToken)))).ConfigureAwait(false); await InstallMultipleRemotePackageAsync(splitRemoteFilePaths, packageName, reinstall, cancellationToken); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFilePaths.Count, PackageInstallProgressState.PostInstall)); int count = 0; - await Extensions.WhenAll(splitRemoteFilePaths.Select(async (x) => + await Extensions.WhenAll(splitRemoteFilePaths.Select(async x => { await RemoveRemotePackageAsync(x, cancellationToken).ConfigureAwait(false); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(++count, splitRemoteFilePaths.Count, PackageInstallProgressState.PostInstall)); diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs index b84d0a44..89b53a5a 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs @@ -8,7 +8,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Threading; +using System.Linq; namespace AdvancedSharpAdbClient.DeviceCommands { @@ -193,13 +193,7 @@ void OnSplitSyncProgressChanged(object sender, SyncProgressChangedEventArgs args { count++; } - - double present = 0; - foreach (KeyValuePair info in progress) - { - present += info.Value / splitPackageFilePaths.Count / 2; - } - + double present = progress.Values.Select(x => x / splitPackageFilePaths.Count / 2).Sum(); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitPackageFilePaths.Count + 1, present)); } @@ -247,13 +241,7 @@ void OnSyncProgressChanged(object sender, SyncProgressChangedEventArgs args) { count++; } - - double present = 0; - foreach (KeyValuePair info in progress) - { - present += info.Value / splitPackageFilePaths.Count; - } - + double present = progress.Values.Select(x => x / splitPackageFilePaths.Count).Sum(); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitPackageFilePaths.Count, present)); } @@ -282,7 +270,7 @@ void OnSyncProgressChanged(object sender, SyncProgressChangedEventArgs args) /// The absolute base app file path to package file on device. /// The absolute split app file paths to package file on device. /// Set to if re-install of app should be performed. - public virtual void InstallMultipleRemotePackage(string baseRemoteFilePath, IList splitRemoteFilePaths, bool reinstall) + public virtual void InstallMultipleRemotePackage(string baseRemoteFilePath, ICollection splitRemoteFilePaths, bool reinstall) { InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.CreateSession)); @@ -327,7 +315,7 @@ public virtual void InstallMultipleRemotePackage(string baseRemoteFilePath, ILis /// The absolute split app file paths to package file on device. /// The absolute package name of the base app. /// Set to if re-install of app should be performed. - public virtual void InstallMultipleRemotePackage(IList splitRemoteFilePaths, string packageName, bool reinstall) + public virtual void InstallMultipleRemotePackage(ICollection splitRemoteFilePaths, string packageName, bool reinstall) { InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.CreateSession)); diff --git a/AdvancedSharpAdbClient/DeviceMonitor.cs b/AdvancedSharpAdbClient/DeviceMonitor.cs index b5f8a470..45afac90 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.cs @@ -276,7 +276,7 @@ private void ProcessIncomingDeviceData(string result) { string[] deviceValues = result.Split(separator, StringSplitOptions.RemoveEmptyEntries); - IEnumerable currentDevices = deviceValues.Select((x) => new DeviceData(x)); + IEnumerable currentDevices = deviceValues.Select(x => new DeviceData(x)); UpdateDevices(currentDevices); } diff --git a/AdvancedSharpAdbClient/Extensions/AdbCommandLineClientExtensions.cs b/AdvancedSharpAdbClient/Extensions/AdbCommandLineClientExtensions.cs deleted file mode 100644 index 52d7a4da..00000000 --- a/AdvancedSharpAdbClient/Extensions/AdbCommandLineClientExtensions.cs +++ /dev/null @@ -1,30 +0,0 @@ -// -// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. -// - -using AdvancedSharpAdbClient.Exceptions; -using System.IO; - -namespace AdvancedSharpAdbClient -{ - /// - /// Provides extension methods for the class. - /// - public static class AdbCommandLineClientExtensions - { - /// - /// Throws an error if the path does not point to a valid instance of adb.exe. - /// - /// An instance of a class that implements the interface. - /// The path to validate. - public static void EnsureIsValidAdbFile(this IAdbCommandLineClient client, string adbPath) - { - ExceptionExtensions.ThrowIfNull(client); - - if (!client.IsValidAdbFile(adbPath)) - { - throw new FileNotFoundException($"The adb.exe executable could not be found at {adbPath}"); - } - } - } -} diff --git a/AdvancedSharpAdbClient/Extensions/Extensions.cs b/AdvancedSharpAdbClient/Extensions/Extensions.cs index 3d49af5c..b5b8829b 100644 --- a/AdvancedSharpAdbClient/Extensions/Extensions.cs +++ b/AdvancedSharpAdbClient/Extensions/Extensions.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Net.Sockets; using System.Runtime.InteropServices; using System.Text; @@ -16,6 +17,39 @@ internal static class Extensions { public static char[] NewLineSeparator { get; } = ['\r', '\n']; + /// + /// Adds the elements of the specified collection to the end of the . + /// + /// The type of the elements of . + /// The to be added. + /// The collection whose elements should be added to the end of the . + /// The collection itself cannot be , but it can contain elements that are + /// , if type is a reference type. + /// or is null. + public static void AddRange(this ICollection source, IEnumerable collection) + { + ExceptionExtensions.ThrowIfNull(source); + ExceptionExtensions.ThrowIfNull(collection); + + if (source is List list) + { + list.AddRange(collection); + } +#if !NETFRAMEWORK || NET40_OR_GREATER + else if (source is ISet set) + { + set.UnionWith(collection); + } +#endif + else + { + foreach (TSource item in collection) + { + source.Add(item); + } + } + } + /// /// Converts the string representation of the name or numeric value of one or more /// enumerated constants to an equivalent enumerated object. A parameter specifies diff --git a/AdvancedSharpAdbClient/Extensions/Factories.cs b/AdvancedSharpAdbClient/Extensions/Factories.cs index 036293c9..33cc5889 100644 --- a/AdvancedSharpAdbClient/Extensions/Factories.cs +++ b/AdvancedSharpAdbClient/Extensions/Factories.cs @@ -52,9 +52,9 @@ public static class Factories public static void Reset() { CheckFileExists = File.Exists; - AdbSocketFactory = (endPoint) => new AdbSocket(endPoint); - AdbClientFactory = (endPoint) => new AdbClient(endPoint, AdbSocketFactory); - AdbCommandLineClientFactory = (path) => new AdbCommandLineClient(path); + AdbSocketFactory = endPoint => new AdbSocket(endPoint); + AdbClientFactory = endPoint => new AdbClient(endPoint, AdbSocketFactory); + AdbCommandLineClientFactory = path => new AdbCommandLineClient(path); SyncServiceFactory = (client, device) => new SyncService(client, device); } } diff --git a/AdvancedSharpAdbClient/Extensions/SocketExtensions.cs b/AdvancedSharpAdbClient/Extensions/SocketExtensions.cs index 71ac861b..2471c0ed 100644 --- a/AdvancedSharpAdbClient/Extensions/SocketExtensions.cs +++ b/AdvancedSharpAdbClient/Extensions/SocketExtensions.cs @@ -65,7 +65,7 @@ public static Task ReceiveAsync(this Socket socket, byte[] buffer, int offs TaskCompletionSource taskCompletionSource = new(socket); - IAsyncResult asyncResult = socket.BeginReceive(buffer, offset, size, socketFlags, (iar) => + IAsyncResult asyncResult = socket.BeginReceive(buffer, offset, size, socketFlags, iar => { // this is the callback @@ -146,7 +146,7 @@ public static Task SendAsync(this Socket socket, byte[] buffer, int offset, TaskCompletionSource taskCompletionSource = new(socket); - _ = socket.BeginSend(buffer, offset, size, socketFlags, (iar) => + _ = socket.BeginSend(buffer, offset, size, socketFlags, iar => { // this is the callback diff --git a/AdvancedSharpAdbClient/Extensions/SteamExtensions.cs b/AdvancedSharpAdbClient/Extensions/SteamExtensions.cs index 3bbb9e4a..43bff95d 100644 --- a/AdvancedSharpAdbClient/Extensions/SteamExtensions.cs +++ b/AdvancedSharpAdbClient/Extensions/SteamExtensions.cs @@ -134,7 +134,7 @@ public static Task ReadAsync(this Stream stream, byte[] buffer, int offset, TaskCompletionSource taskCompletionSource = new(stream); - IAsyncResult asyncResult = stream.BeginRead(buffer, offset, count, (iar) => + IAsyncResult asyncResult = stream.BeginRead(buffer, offset, count, iar => { // this is the callback @@ -182,7 +182,7 @@ public static Task WriteAsync(this Stream stream, byte[] buffer, int offset, int TaskCompletionSource taskCompletionSource = new(stream); - IAsyncResult asyncResult = stream.BeginWrite(buffer, offset, count, (iar) => + IAsyncResult asyncResult = stream.BeginWrite(buffer, offset, count, iar => { // this is the callback diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbCommandLineClient.cs b/AdvancedSharpAdbClient/Interfaces/IAdbCommandLineClient.cs index a26e30a9..f5eaffc0 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbCommandLineClient.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbCommandLineClient.cs @@ -22,9 +22,9 @@ public partial interface IAdbCommandLineClient void StartServer(); /// - /// Throws an error if the path does not point to a valid instance of adb.exe. + /// Determines whether the adb.exe file exists. /// /// The path to validate. - bool IsValidAdbFile(string adbPath); + bool CheckFileExists(string adbPath); } } diff --git a/AdvancedSharpAdbClient/Logs/LogReader.cs b/AdvancedSharpAdbClient/Logs/LogReader.cs index 89364114..7dcca3d1 100644 --- a/AdvancedSharpAdbClient/Logs/LogReader.cs +++ b/AdvancedSharpAdbClient/Logs/LogReader.cs @@ -186,7 +186,7 @@ or LogId.Radio /// /// Reads a single log entry from the stream. /// - protected void ReadLogEntry(BinaryReader reader, IList parent) + protected void ReadLogEntry(BinaryReader reader, ICollection parent) { EventLogType type = (EventLogType)reader.ReadByte(); diff --git a/AdvancedSharpAdbClient/Models/Element.cs b/AdvancedSharpAdbClient/Models/Element.cs index 486afb27..72c9271d 100644 --- a/AdvancedSharpAdbClient/Models/Element.cs +++ b/AdvancedSharpAdbClient/Models/Element.cs @@ -211,12 +211,7 @@ public static Element FromIXmlNode(IAdbClient client, DeviceData device, Windows /// /// Gets the count of in this element. /// - public virtual int GetChildCount() - { - int count = Children.Count; - Children.ForEach(x => count += x.GetChildCount()); - return count; - } + public virtual int GetChildCount() => Children.Count + Children.Select(x => x.GetChildCount()).Sum(); /// /// Clicks on this coordinates. From cc8da0ece04c8f13aae021cf7f6bc5f06ad31649 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Sun, 1 Oct 2023 23:39:33 +0800 Subject: [PATCH 20/66] Add logger tests --- .../AdbClientTests.Async.cs | 3 +- .../AdbClientTests.cs | 1 - .../DeviceCommands/PackageManagerTests.cs | 1 - .../Dummys/DummyAdbClient.cs | 1 - .../Dummys/DummySyncService.cs | 2 +- .../Dummys/DummyTcpSocket.cs | 1 - .../Logs/Dummys/DummyLogger.cs | 29 +++++++++++++++++++ .../Logs/Dummys/DummyLoggerFactory.cs | 9 ++++++ .../Logs/{LoggerTests.cs => LogTests.cs} | 3 +- .../SyncServiceTests.cs | 1 - .../DeviceCommands/LinuxPath.cs | 1 - .../DeviceCommands/Models/AndroidProcess.cs | 1 - .../DeviceCommands/PackageManager.cs | 2 +- .../Extensions/Extensions.cs | 1 - .../Extensions/LoggerExtensions.cs | 2 +- .../Extensions/SyncCommandConverter.cs | 1 - .../Interfaces/ISyncService.cs | 1 - .../Logs/Interfaces/ILoggerFactory.cs | 4 +-- AdvancedSharpAdbClient/Models/ForwardSpec.cs | 1 - AdvancedSharpAdbClient/Models/Framebuffer.cs | 11 ++++--- .../Models/FramebufferHeader.cs | 2 -- AdvancedSharpAdbClient/SyncService.cs | 1 - AdvancedSharpAdbClient/TcpSocket.Async.cs | 2 +- 23 files changed, 50 insertions(+), 31 deletions(-) create mode 100644 AdvancedSharpAdbClient.Tests/Logs/Dummys/DummyLogger.cs create mode 100644 AdvancedSharpAdbClient.Tests/Logs/Dummys/DummyLoggerFactory.cs rename AdvancedSharpAdbClient.Tests/Logs/{LoggerTests.cs => LogTests.cs} (98%) diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs index c2d02d2b..7e9eb62a 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs @@ -2,7 +2,6 @@ using AdvancedSharpAdbClient.Logs; using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Drawing; using System.Drawing.Imaging; using System.IO; @@ -1278,7 +1277,7 @@ await RunTestAsync( async () => { elements = []; - await foreach(Element element in TestClient.FindAsyncElements(device)) + await foreach (Element element in TestClient.FindAsyncElements(device)) { elements.Add(element); } diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs index 41df0010..b3768455 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs @@ -2,7 +2,6 @@ using AdvancedSharpAdbClient.Logs; using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Drawing; using System.Drawing.Imaging; using System.IO; diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs index bbefd19d..7ab490d1 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs @@ -6,7 +6,6 @@ namespace AdvancedSharpAdbClient.DeviceCommands.Tests { - public partial class PackageManagerTests { [Fact] diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs index 28563176..05f8e41d 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs @@ -1,7 +1,6 @@ using AdvancedSharpAdbClient.Logs; using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.IO; using System.Net; using System.Text; diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs index d684eaea..1640d984 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs @@ -10,7 +10,7 @@ internal class DummySyncService : ISyncService { public Dictionary UploadedFiles { get; private set; } = new Dictionary(); - public bool IsOpen { get; private set; }= true; + public bool IsOpen { get; private set; } = true; public event EventHandler SyncProgressChanged; diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyTcpSocket.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyTcpSocket.cs index ead0a901..4f06eb19 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyTcpSocket.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyTcpSocket.cs @@ -1,5 +1,4 @@ using System; -using System.Drawing; using System.IO; using System.Net; using System.Net.Sockets; diff --git a/AdvancedSharpAdbClient.Tests/Logs/Dummys/DummyLogger.cs b/AdvancedSharpAdbClient.Tests/Logs/Dummys/DummyLogger.cs new file mode 100644 index 00000000..0efff112 --- /dev/null +++ b/AdvancedSharpAdbClient.Tests/Logs/Dummys/DummyLogger.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; + +namespace AdvancedSharpAdbClient.Logs.Tests +{ + public class DummyLogger(string name) : ILogger + { + public string Name { get; } = name; + + public List LogMessages { get; } = new(); + + public void Log(LogLevel logLevel, Exception exception, string message, params object[] args) => + LogMessages.Add(new LogMessage(logLevel, exception, message, args)); + } + + public class DummyLogger : DummyLogger, ILogger + { + public Type Type { get; } = typeof(T); + + public DummyLogger() : base(typeof(T).Name) { } + } + + public record class LogMessage(LogLevel LogLevel, Exception Exception, string Message, params object[] Args) + { + public bool HasFormat => Args.Length > 0; + public bool HasException => Exception != null; + public string FormattedMessage => HasFormat ? string.Format(Message, Args) : Message; + } +} diff --git a/AdvancedSharpAdbClient.Tests/Logs/Dummys/DummyLoggerFactory.cs b/AdvancedSharpAdbClient.Tests/Logs/Dummys/DummyLoggerFactory.cs new file mode 100644 index 00000000..874de3e8 --- /dev/null +++ b/AdvancedSharpAdbClient.Tests/Logs/Dummys/DummyLoggerFactory.cs @@ -0,0 +1,9 @@ +namespace AdvancedSharpAdbClient.Logs.Tests +{ + public class DummyLoggerFactory : ILoggerFactory + { + public ILogger CreateLogger(string categoryName) => new DummyLogger(categoryName); + + public ILogger CreateLogger() => new DummyLogger(); + } +} diff --git a/AdvancedSharpAdbClient.Tests/Logs/LoggerTests.cs b/AdvancedSharpAdbClient.Tests/Logs/LogTests.cs similarity index 98% rename from AdvancedSharpAdbClient.Tests/Logs/LoggerTests.cs rename to AdvancedSharpAdbClient.Tests/Logs/LogTests.cs index 89598486..4802c3c7 100644 --- a/AdvancedSharpAdbClient.Tests/Logs/LoggerTests.cs +++ b/AdvancedSharpAdbClient.Tests/Logs/LogTests.cs @@ -1,13 +1,12 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.IO; using System.Threading; using Xunit; namespace AdvancedSharpAdbClient.Logs.Tests { - public class LoggerTests + public class LogTests { [Fact] public void ReadLogTest() diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs index e536e336..0bcc2a00 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Threading; using Xunit; namespace AdvancedSharpAdbClient.Tests diff --git a/AdvancedSharpAdbClient/DeviceCommands/LinuxPath.cs b/AdvancedSharpAdbClient/DeviceCommands/LinuxPath.cs index ea7239b3..ad1f95ca 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/LinuxPath.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/LinuxPath.cs @@ -2,7 +2,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; using System; using System.IO; using System.Linq; diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs index 7d9e4bfc..e8176785 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs @@ -2,7 +2,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; using System; namespace AdvancedSharpAdbClient.DeviceCommands diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs index 89b53a5a..b44a42a7 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs @@ -67,7 +67,7 @@ public partial class PackageManager /// A value indicating whether to skip the initial refresh of the package list or not. /// Used mainly by unit tests. /// The logger to use when logging. - public PackageManager(IAdbClient client, DeviceData device, bool thirdPartyOnly = false, Func syncServiceFactory = null, bool skipInit = false , ILogger logger = null ) + public PackageManager(IAdbClient client, DeviceData device, bool thirdPartyOnly = false, Func syncServiceFactory = null, bool skipInit = false, ILogger logger = null) { Device = device ?? throw new ArgumentNullException(nameof(device)); Packages = []; diff --git a/AdvancedSharpAdbClient/Extensions/Extensions.cs b/AdvancedSharpAdbClient/Extensions/Extensions.cs index b5b8829b..7038620f 100644 --- a/AdvancedSharpAdbClient/Extensions/Extensions.cs +++ b/AdvancedSharpAdbClient/Extensions/Extensions.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Net.Sockets; using System.Runtime.InteropServices; using System.Text; diff --git a/AdvancedSharpAdbClient/Extensions/LoggerExtensions.cs b/AdvancedSharpAdbClient/Extensions/LoggerExtensions.cs index e54c84c7..69fb4c58 100644 --- a/AdvancedSharpAdbClient/Extensions/LoggerExtensions.cs +++ b/AdvancedSharpAdbClient/Extensions/LoggerExtensions.cs @@ -2,8 +2,8 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using System; using AdvancedSharpAdbClient.Logs; +using System; namespace AdvancedSharpAdbClient { diff --git a/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs b/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs index 91d171d0..86178d1e 100644 --- a/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs +++ b/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs @@ -2,7 +2,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; using System; using System.Collections.Generic; using System.Linq; diff --git a/AdvancedSharpAdbClient/Interfaces/ISyncService.cs b/AdvancedSharpAdbClient/Interfaces/ISyncService.cs index 47ef6200..6118e67f 100644 --- a/AdvancedSharpAdbClient/Interfaces/ISyncService.cs +++ b/AdvancedSharpAdbClient/Interfaces/ISyncService.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Threading; namespace AdvancedSharpAdbClient { diff --git a/AdvancedSharpAdbClient/Logs/Interfaces/ILoggerFactory.cs b/AdvancedSharpAdbClient/Logs/Interfaces/ILoggerFactory.cs index 62f1096b..7228f002 100644 --- a/AdvancedSharpAdbClient/Logs/Interfaces/ILoggerFactory.cs +++ b/AdvancedSharpAdbClient/Logs/Interfaces/ILoggerFactory.cs @@ -2,14 +2,12 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using System; - namespace AdvancedSharpAdbClient.Logs { /// /// Represents a type used to configure the logging system and create instances of . /// - public interface ILoggerFactory : IDisposable + public interface ILoggerFactory { /// /// Creates a new instance. diff --git a/AdvancedSharpAdbClient/Models/ForwardSpec.cs b/AdvancedSharpAdbClient/Models/ForwardSpec.cs index bcdeb66f..0482df36 100644 --- a/AdvancedSharpAdbClient/Models/ForwardSpec.cs +++ b/AdvancedSharpAdbClient/Models/ForwardSpec.cs @@ -2,7 +2,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; using System; using System.Collections.Generic; using System.Linq; diff --git a/AdvancedSharpAdbClient/Models/Framebuffer.cs b/AdvancedSharpAdbClient/Models/Framebuffer.cs index e25e6d3c..e8ffa9d3 100644 --- a/AdvancedSharpAdbClient/Models/Framebuffer.cs +++ b/AdvancedSharpAdbClient/Models/Framebuffer.cs @@ -2,7 +2,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; using System; using System.Net; using System.Runtime.InteropServices; @@ -181,12 +180,12 @@ public virtual async Task RefreshAsync(bool reset = false, CancellationToken can #endif #if HAS_DRAWING - /// - /// Converts the framebuffer data to a . - /// - /// An which represents the framebuffer data. + /// + /// Converts the framebuffer data to a . + /// + /// An which represents the framebuffer data. #if NET - [SupportedOSPlatform("windows")] + [SupportedOSPlatform("windows")] #endif public virtual Bitmap ToImage() { diff --git a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs index fc081f4e..b5b03d0b 100644 --- a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs +++ b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs @@ -2,12 +2,10 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; using System; using System.IO; using System.Runtime.InteropServices; using System.Text; -using System.Threading; namespace AdvancedSharpAdbClient { diff --git a/AdvancedSharpAdbClient/SyncService.cs b/AdvancedSharpAdbClient/SyncService.cs index b9461236..cb602164 100644 --- a/AdvancedSharpAdbClient/SyncService.cs +++ b/AdvancedSharpAdbClient/SyncService.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Threading; namespace AdvancedSharpAdbClient { diff --git a/AdvancedSharpAdbClient/TcpSocket.Async.cs b/AdvancedSharpAdbClient/TcpSocket.Async.cs index 753dfc41..8eeba38d 100644 --- a/AdvancedSharpAdbClient/TcpSocket.Async.cs +++ b/AdvancedSharpAdbClient/TcpSocket.Async.cs @@ -53,7 +53,7 @@ public virtual Task SendAsync(byte[] buffer, int offset, int size, SocketFl socket.SendAsync(buffer.AsMemory(offset, size), socketFlags, cancellationToken).AsTask(); /// - public ValueTask SendAsync(ReadOnlyMemory buffer, SocketFlags socketFlags, CancellationToken cancellationToken = default)=> + public ValueTask SendAsync(ReadOnlyMemory buffer, SocketFlags socketFlags, CancellationToken cancellationToken = default) => socket.SendAsync(buffer, socketFlags, cancellationToken); /// From 805e0e07e321002c9c8315260db295f66f7db388 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Mon, 2 Oct 2023 17:42:52 +0800 Subject: [PATCH 21/66] Change Array parameter to Span --- .../Extensions/ExtensionsTests.cs | 31 ---- .../Extensions/SyncCommandConverterTests.cs | 4 +- AdvancedSharpAdbClient/AdbClient.Async.cs | 11 +- AdvancedSharpAdbClient/AdbClient.cs | 11 +- .../AdbCommandLineClient.cs | 2 +- AdvancedSharpAdbClient/AdbSocket.Async.cs | 25 ++- AdvancedSharpAdbClient/AdbSocket.cs | 29 +++- .../DeviceCommands/LinuxPath.cs | 17 +- .../DeviceCommands/PackageManager.Async.cs | 2 +- .../DeviceCommands/PackageManager.cs | 2 +- .../Receivers/EnvironmentVariablesReceiver.cs | 2 +- .../Receivers/GetPropReceiver.cs | 2 +- .../Receivers/InstallOutputReceiver.cs | 4 +- .../Receivers/VersionInfoReceiver.cs | 2 +- .../Exceptions/JavaException.cs | 4 +- .../Extensions/Extensions.cs | 146 +----------------- .../Extensions/SocketExtensions.cs | 22 ++- .../Extensions/StringExtensions.cs | 135 ++++++++++++++++ .../Extensions/SyncCommandConverter.cs | 6 + .../Models/FramebufferHeader.cs | 22 +-- AdvancedSharpAdbClient/TcpSocket.cs | 16 ++ 21 files changed, 265 insertions(+), 230 deletions(-) create mode 100644 AdvancedSharpAdbClient/Extensions/StringExtensions.cs diff --git a/AdvancedSharpAdbClient.Tests/Extensions/ExtensionsTests.cs b/AdvancedSharpAdbClient.Tests/Extensions/ExtensionsTests.cs index 91fd45cb..0e443155 100644 --- a/AdvancedSharpAdbClient.Tests/Extensions/ExtensionsTests.cs +++ b/AdvancedSharpAdbClient.Tests/Extensions/ExtensionsTests.cs @@ -32,36 +32,5 @@ public void AddRangeTest() Assert.Equal(10, collection.Count); Assert.Equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], collection); } - - /// - /// Tests the method. - /// - [Fact] - public void TryParseTest() - { - Assert.True(Extensions.TryParse("BootLoader", false, out DeviceState result)); - Assert.Equal(DeviceState.BootLoader, result); - Assert.True(Extensions.TryParse("Bootloader", true, out result)); - Assert.Equal(DeviceState.BootLoader, result); - Assert.False(Extensions.TryParse("Bootloader", false, out _)); - Assert.False(Extensions.TryParse("Reset", true, out _)); - } - - /// - /// Tests the method. - /// - [Fact] - public void IsNullOrWhiteSpaceTest() - { - Assert.True(" ".IsNullOrWhiteSpace()); - Assert.False(" test ".IsNullOrWhiteSpace()); - } - - /// - /// Tests the method. - /// - [Fact] - public void JoinTest() => - Assert.Equal("Hello World!", Extensions.Join(" ", ["Hello", "World!"])); } } diff --git a/AdvancedSharpAdbClient.Tests/Extensions/SyncCommandConverterTests.cs b/AdvancedSharpAdbClient.Tests/Extensions/SyncCommandConverterTests.cs index 1840e85d..88ea2592 100644 --- a/AdvancedSharpAdbClient.Tests/Extensions/SyncCommandConverterTests.cs +++ b/AdvancedSharpAdbClient.Tests/Extensions/SyncCommandConverterTests.cs @@ -10,7 +10,7 @@ public class SyncCommandConverterTests { [Fact] public void GetCommandNullTest() => - _ = Assert.Throws(() => SyncCommandConverter.GetCommand(null)); + _ = Assert.Throws(() => SyncCommandConverter.GetCommand(null)); [Fact] public void GetCommandInvalidNumberOfBytesTest() => @@ -18,7 +18,7 @@ public void GetCommandInvalidNumberOfBytesTest() => [Fact] public void GetCommandInvalidCommandTest() => - _ = Assert.Throws(() => SyncCommandConverter.GetCommand("QMTV"u8.ToArray())); + _ = Assert.Throws(() => SyncCommandConverter.GetCommand("QMTV"u8)); [Fact] public void GetBytesInvalidCommandTest() => diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index d20f7050..971ebb97 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -532,9 +532,12 @@ public async Task InstallCreateAsync(DeviceData device, string packageNa { EnsureDevice(device); - StringBuilder requestBuilder = - new StringBuilder().Append("exec:cmd package 'install-create'") - .Append(packageName.IsNullOrWhiteSpace() ? string.Empty : $" -p {packageName}"); + StringBuilder requestBuilder = new StringBuilder().Append("exec:cmd package 'install-create'"); + + if (!StringExtensions.IsNullOrWhiteSpace(packageName)) + { + requestBuilder.Append($" -p {packageName}"); + } if (arguments != null) { @@ -981,7 +984,7 @@ public async Task SendTextAsync(DeviceData device, string text, CancellationToke public async Task ClearInputAsync(DeviceData device, int charCount, CancellationToken cancellationToken = default) { await SendKeyEventAsync(device, "KEYCODE_MOVE_END", cancellationToken).ConfigureAwait(false); - await ExecuteRemoteCommandAsync("input keyevent " + Extensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL ", charCount)), device, null, cancellationToken).ConfigureAwait(false); + await ExecuteRemoteCommandAsync("input keyevent " + StringExtensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL ", charCount)), device, null, cancellationToken).ConfigureAwait(false); } /// diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index d1d58849..07a80fd9 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -656,9 +656,12 @@ public string InstallCreate(DeviceData device, string packageName = null, params { EnsureDevice(device); - StringBuilder requestBuilder = new(); - _ = requestBuilder.Append("exec:cmd package 'install-create'"); - _ = requestBuilder.Append(packageName.IsNullOrWhiteSpace() ? string.Empty : $" -p {packageName}"); + StringBuilder requestBuilder = new StringBuilder().Append("exec:cmd package 'install-create'"); + + if (!StringExtensions.IsNullOrWhiteSpace(packageName)) + { + requestBuilder.Append($" -p {packageName}"); + } if (arguments != null) { @@ -1059,7 +1062,7 @@ public void SendText(DeviceData device, string text) public void ClearInput(DeviceData device, int charCount) { SendKeyEvent(device, "KEYCODE_MOVE_END"); - ExecuteRemoteCommand("input keyevent " + Extensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL ", charCount)), device, null); + ExecuteRemoteCommand("input keyevent " + StringExtensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL ", charCount)), device, null); } /// diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.cs index b2d2fac1..57e177af 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.cs @@ -38,7 +38,7 @@ public partial class AdbCommandLineClient : IAdbCommandLineClient /// The logger to use when logging. public AdbCommandLineClient(string adbPath, bool isForce = false, ILogger logger = null) { - if (adbPath.IsNullOrWhiteSpace()) + if (StringExtensions.IsNullOrWhiteSpace(adbPath)) { throw new ArgumentNullException(nameof(adbPath)); } diff --git a/AdvancedSharpAdbClient/AdbSocket.Async.cs b/AdvancedSharpAdbClient/AdbSocket.Async.cs index 7f4a097e..ebffbaa2 100644 --- a/AdvancedSharpAdbClient/AdbSocket.Async.cs +++ b/AdvancedSharpAdbClient/AdbSocket.Async.cs @@ -24,7 +24,12 @@ public virtual async Task SendAsync(byte[] data, int length, CancellationToken c { try { - int count = await socket.SendAsync(data, length != -1 ? length : data.Length, SocketFlags.None, cancellationToken).ConfigureAwait(false); + int count = +#if HAS_BUFFERS + await socket.SendAsync(data.AsMemory(0, length != -1 ? length : data.Length), SocketFlags.None, cancellationToken).ConfigureAwait(false); +#else + await socket.SendAsync(data, length != -1 ? length : data.Length, SocketFlags.None, cancellationToken).ConfigureAwait(false); +#endif if (count < 0) { throw new AdbException("channel EOF"); @@ -42,7 +47,12 @@ public virtual async Task SendAsync(byte[] data, int offset, int length, Cancell { try { - int count = await socket.SendAsync(data, offset, length != -1 ? length : data.Length - offset, SocketFlags.None, cancellationToken).ConfigureAwait(false); + int count = +#if HAS_BUFFERS + await socket.SendAsync(data.AsMemory(offset, length != -1 ? length : data.Length - offset), SocketFlags.None, cancellationToken).ConfigureAwait(false); +#else + await socket.SendAsync(data, offset, length != -1 ? length : data.Length - offset, SocketFlags.None, cancellationToken).ConfigureAwait(false); +#endif if (count < 0) { throw new AdbException("channel EOF"); @@ -126,7 +136,12 @@ public virtual async Task ReadAsync(byte[] data, int offset, int length, Ca int left = length - totalRead; int bufferLength = left < ReceiveBufferSize ? left : ReceiveBufferSize; - count = await socket.ReceiveAsync(data, totalRead, bufferLength, SocketFlags.None, cancellationToken).ConfigureAwait(false); + count = +#if HAS_BUFFERS + await socket.ReceiveAsync(data.AsMemory(totalRead, bufferLength), SocketFlags.None, cancellationToken).ConfigureAwait(false); +#else + await socket.ReceiveAsync(data, totalRead, bufferLength, SocketFlags.None, cancellationToken).ConfigureAwait(false); +#endif if (count < 0) { @@ -334,7 +349,11 @@ public virtual Task ReadAsync(byte[] data, CancellationToken cancellationTo /// A which can be used to cancel the asynchronous task. /// Returns if all data was written; otherwise, . /// This uses the default time out value. +#if HAS_BUFFERS + protected virtual async Task WriteAsync(Memory data, CancellationToken cancellationToken = default) +#else protected virtual async Task WriteAsync(byte[] data, CancellationToken cancellationToken = default) +#endif { try { diff --git a/AdvancedSharpAdbClient/AdbSocket.cs b/AdvancedSharpAdbClient/AdbSocket.cs index 75f51b1f..7668c023 100644 --- a/AdvancedSharpAdbClient/AdbSocket.cs +++ b/AdvancedSharpAdbClient/AdbSocket.cs @@ -99,7 +99,11 @@ public AdbSocket(ITcpSocket socket, ILogger logger = null) /// /// The reply. /// if the specified reply is okay; otherwise, . +#if HAS_BUFFERS + public static bool IsOkay(ReadOnlySpan reply) => AdbClient.Encoding.GetString(reply).Equals("OKAY"); +#else public static bool IsOkay(byte[] reply) => AdbClient.Encoding.GetString(reply).Equals("OKAY"); +#endif /// public bool Connected => socket.Connected; @@ -112,7 +116,12 @@ public virtual void Send(byte[] data, int length) { try { - int count = socket.Send(data, length != -1 ? length : data.Length, SocketFlags.None); + int count = +#if HAS_BUFFERS + socket.Send(data.AsSpan(0, length != -1 ? length : data.Length), SocketFlags.None); +#else + socket.Send(data, length != -1 ? length : data.Length, SocketFlags.None); +#endif if (count < 0) { throw new AdbException("channel EOF"); @@ -130,7 +139,12 @@ public virtual void Send(byte[] data, int offset, int length) { try { - int count = socket.Send(data, offset, length != -1 ? length : data.Length, SocketFlags.None); + int count = +#if HAS_BUFFERS + socket.Send(data.AsSpan(offset, length != -1 ? length : data.Length), SocketFlags.None); +#else + socket.Send(data, offset, length != -1 ? length : data.Length, SocketFlags.None); +#endif if (count < 0) { throw new AdbException("channel EOF"); @@ -211,7 +225,12 @@ public virtual int Read(byte[] data, int offset, int length) int left = length - totalRead; int bufferLength = left < ReceiveBufferSize ? left : ReceiveBufferSize; - count = socket.Receive(data, totalRead, bufferLength, SocketFlags.None); + count = +#if HAS_BUFFERS + socket.Receive(data.AsSpan(totalRead, bufferLength), SocketFlags.None); +#else + socket.Receive(data, totalRead, bufferLength, SocketFlags.None); +#endif if (count < 0) { @@ -475,7 +494,11 @@ protected virtual AdbResponse ReadAdbResponseInner() /// /// A array that represents the ADB reply. /// A that represents the ADB reply. +#if HAS_BUFFERS + protected virtual string ReplyToString(ReadOnlySpan reply) +#else protected virtual string ReplyToString(byte[] reply) +#endif { string result; try diff --git a/AdvancedSharpAdbClient/DeviceCommands/LinuxPath.cs b/AdvancedSharpAdbClient/DeviceCommands/LinuxPath.cs index ad1f95ca..87d32e60 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/LinuxPath.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/LinuxPath.cs @@ -119,11 +119,7 @@ public static string GetDirectoryName(string path) string tpath = path; if (tpath.Length > 1) { -#if NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER if (tpath.EndsWith(DirectorySeparatorChar)) -#else - if (tpath.EndsWith(new string([DirectorySeparatorChar]))) -#endif { return tpath; } @@ -186,7 +182,7 @@ public static bool IsPathRooted(string path) CheckInvalidPathChars(path); int length = path.Length; if ((length >= 1 && (path[0] == DirectorySeparatorChar)) || - (length == 1)) + length == 1) { return true; } @@ -230,23 +226,14 @@ internal static void CheckInvalidPathChars(string path) /// The fixup path private static string FixupPath(string path) { - string sb = path; - sb = sb.Replace(Path.DirectorySeparatorChar, DirectorySeparatorChar); + string sb = path.Replace(Path.DirectorySeparatorChar, DirectorySeparatorChar); -#if NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER if (sb != "." && !sb.StartsWith(DirectorySeparatorChar)) -#else - if (sb != "." && !sb.StartsWith(new string([DirectorySeparatorChar]))) -#endif { sb = $".{DirectorySeparatorChar}{sb}"; } -#if NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER if (!sb.EndsWith(DirectorySeparatorChar)) -#else - if (!sb.EndsWith(new string([DirectorySeparatorChar]))) -#endif { sb = $"{sb}{DirectorySeparatorChar}"; } diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs index f1c1f587..54a420f5 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs @@ -389,7 +389,7 @@ protected virtual async Task CreateInstallSessionAsync(bool reinstall, s InstallOutputReceiver receiver = new(); string reinstallSwitch = reinstall ? " -r" : string.Empty; - string addon = packageName.IsNullOrWhiteSpace() ? string.Empty : $" -p {packageName}"; + string addon = StringExtensions.IsNullOrWhiteSpace(packageName) ? string.Empty : $" -p {packageName}"; string cmd = $"pm install-create{reinstallSwitch}{addon}"; await client.ExecuteShellCommandAsync(Device, cmd, receiver, cancellationToken).ConfigureAwait(false); diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs index b44a42a7..0b4cb46d 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs @@ -474,7 +474,7 @@ protected virtual string CreateInstallSession(bool reinstall, string packageName InstallOutputReceiver receiver = new(); string reinstallSwitch = reinstall ? " -r" : string.Empty; - string addon = packageName.IsNullOrWhiteSpace() ? string.Empty : $" -p {packageName}"; + string addon = StringExtensions.IsNullOrWhiteSpace(packageName) ? string.Empty : $" -p {packageName}"; string cmd = $"pm install-create{reinstallSwitch}{addon}"; client.ExecuteShellCommand(Device, cmd, receiver); diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/EnvironmentVariablesReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/EnvironmentVariablesReceiver.cs index 4df27c4d..a94514e7 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/EnvironmentVariablesReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/EnvironmentVariablesReceiver.cs @@ -41,7 +41,7 @@ protected override void ProcessNewLines(IEnumerable lines) Regex regex = EnvRegex(); foreach (string line in lines) { - if (string.IsNullOrEmpty(line) || line.StartsWith("#")) + if (string.IsNullOrEmpty(line) || line.StartsWith('#')) { continue; } diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs index 6d38e8f6..295b09dd 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs @@ -46,7 +46,7 @@ protected override void ProcessNewLines(IEnumerable lines) // after all that. foreach (string line in lines) { - if (string.IsNullOrEmpty(line) || line.StartsWith("#") || line.StartsWith("$")) + if (string.IsNullOrEmpty(line) || line.StartsWith('#') || line.StartsWith('$')) { continue; } diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs index 04c81832..ba306688 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs @@ -104,7 +104,7 @@ protected override void ProcessNewLines(IEnumerable lines) if (m.Success) { string msg = m.Groups[1].Value; - ErrorMessage = msg.IsNullOrWhiteSpace() ? UnknownError : msg; + ErrorMessage = StringExtensions.IsNullOrWhiteSpace(msg) ? UnknownError : msg; } Success = false; @@ -119,7 +119,7 @@ protected override void ProcessNewLines(IEnumerable lines) if (m.Success) { string msg = m.Groups[1].Value; - ErrorMessage = msg.IsNullOrWhiteSpace() ? UnknownError : msg; + ErrorMessage = StringExtensions.IsNullOrWhiteSpace(msg) ? UnknownError : msg; } Success = false; diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/VersionInfoReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/VersionInfoReceiver.cs index a4654f6f..3ba183ee 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/VersionInfoReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/VersionInfoReceiver.cs @@ -61,7 +61,7 @@ private void CheckPackagesSection(string line) // We check whether the line is indented. If it's not, and it's not an empty line, we take it is // a section header line and update the data accordingly. - if (line.IsNullOrWhiteSpace()) + if (StringExtensions.IsNullOrWhiteSpace(line)) { return; } diff --git a/AdvancedSharpAdbClient/Exceptions/JavaException.cs b/AdvancedSharpAdbClient/Exceptions/JavaException.cs index ac56e717..abcf1d27 100644 --- a/AdvancedSharpAdbClient/Exceptions/JavaException.cs +++ b/AdvancedSharpAdbClient/Exceptions/JavaException.cs @@ -108,10 +108,10 @@ public static JavaException Parse(IEnumerable lines) exception = m.Groups[1].Value; message = m.Groups[2].Value; - message = message.IsNullOrWhiteSpace() ? UnknownError : message; + message = StringExtensions.IsNullOrWhiteSpace(message) ? UnknownError : message; } } - else if (!line.IsNullOrWhiteSpace()) + else if (!StringExtensions.IsNullOrWhiteSpace(line)) { stackTrace.AppendLine(line.TrimEnd()); } diff --git a/AdvancedSharpAdbClient/Extensions/Extensions.cs b/AdvancedSharpAdbClient/Extensions/Extensions.cs index 7038620f..154a3df0 100644 --- a/AdvancedSharpAdbClient/Extensions/Extensions.cs +++ b/AdvancedSharpAdbClient/Extensions/Extensions.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Net.Sockets; using System.Runtime.InteropServices; using System.Text; using System.Threading; @@ -90,121 +89,6 @@ public static bool TryParse(string value, bool ignoreCase, out TEnum resu #endif } - /// - /// Indicates whether a specified string is , empty, or consists only of white-space characters. - /// - /// The string to test. - /// if the parameter is or - /// , or if consists exclusively of white-space characters. - public static bool IsNullOrWhiteSpace(this string value) - { -#if NETFRAMEWORK && !NET40_OR_GREATER - if (value == null) - { - return true; - } - - for (int i = 0; i < value.Length; i++) - { - if (!char.IsWhiteSpace(value[i])) - { - return false; - } - } - - return true; -#else - return string.IsNullOrWhiteSpace(value); -#endif - } - -#if !HAS_FULLSTRING - /// - /// Returns a value indicating whether a specified string occurs within this string, using the specified comparison rules. - /// - /// A sequence in which to locate a value. - /// The string to seek. - /// One of the enumeration values that specifies the rules to use in the comparison. - /// if the parameter occurs within this string, - /// or if is the empty string (""); otherwise, . - public static bool Contains(this string text, string value, StringComparison comparisonType) => - text.IndexOf(value, comparisonType) != -1; - - /// - /// Splits a string into substrings based on a specified delimiting character and, optionally, options. - /// - /// The string to split. - /// A character that delimits the substrings in this string. - /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings. - /// An array whose elements contain the substrings from this instance that are delimited by . - public static string[] Split(this string text, char separator, StringSplitOptions options = StringSplitOptions.None) => - text.Split(new[] { separator }, options); - - /// - /// Splits a string into a maximum number of substrings based on a specified delimiting - /// character and, optionally, options. Splits a string into a maximum number of - /// substrings based on the provided character separator, optionally omitting empty - /// substrings from the result. - /// - /// The string to split. - /// A character that delimits the substrings in this string. - /// The maximum number of elements expected in the array. - /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings. - /// An array that contains at most count substrings from this instance that are delimited by . - public static string[] Split(this string text, char separator, int count, StringSplitOptions options = StringSplitOptions.None) => - text.Split(new[] { separator }, count, options); - - /// - /// Determines whether this string instance starts with the specified character. - /// - /// A sequence in which to locate a value. - /// The character to compare. - /// if matches the beginning of this string; otherwise, . - public static bool StartsWith(this string text, char value) => text.StartsWith(new string([value])); -#endif - - /// - /// Concatenates the members of a constructed collection of type , - /// using the specified separator between each member. - /// - /// The string to use as a separator. is included - /// in the returned string only if has more than one element. - /// A collection that contains the strings to concatenate. - /// A string that consists of the elements of delimited by the - /// string.-or- if values has zero elements. - public static string Join(string separator, IEnumerable values) - { -#if NETFRAMEWORK && !NET40_OR_GREATER - ExceptionExtensions.ThrowIfNull(values); - - separator ??= string.Empty; - - using IEnumerator en = values.GetEnumerator(); - if (!en.MoveNext()) - { - return string.Empty; - } - - StringBuilder result = new(); - if (en.Current != null) - { - _ = result.Append(en.Current); - } - - while (en.MoveNext()) - { - _ = result.Append(separator); - if (en.Current != null) - { - _ = result.Append(en.Current); - } - } - return result.ToString(); -#else - return string.Join(separator, values); -#endif - } - #if NETFRAMEWORK && !NET40_OR_GREATER /// /// Removes all characters from the current instance. @@ -217,16 +101,6 @@ public static StringBuilder Clear(this StringBuilder builder) return builder; } - /// - /// Releases all resources used by the current instance of the class. - /// - /// The to release. - public static void Dispose(this Socket socket) - { - socket.Close(); - GC.SuppressFinalize(socket); - } - /// /// Releases all resources used by the current instance of the class. /// @@ -310,19 +184,9 @@ public static Task WhenAll(IEnumerable tasks) => /// 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 -#if NET7_0_OR_GREATER - ValueTask -#else - Task -#endif - ReadLineAsync(this TextReader reader, CancellationToken cancellationToken) => + public static Task ReadLineAsync(this TextReader reader, CancellationToken cancellationToken) => #if !NET35 - reader.ReadLineAsync( -#if NET7_0_OR_GREATER - cancellationToken -#endif - ); + reader.ReadLineAsync(); #else Run(reader.ReadLine, cancellationToken); #endif @@ -337,11 +201,7 @@ public static /// the end of the stream. public static Task ReadToEndAsync(this TextReader reader, CancellationToken cancellationToken) => #if !NET35 - reader.ReadToEndAsync( -#if NET7_0_OR_GREATER - cancellationToken -#endif - ); + reader.ReadToEndAsync(); #else Run(reader.ReadToEnd, cancellationToken); #endif diff --git a/AdvancedSharpAdbClient/Extensions/SocketExtensions.cs b/AdvancedSharpAdbClient/Extensions/SocketExtensions.cs index 2471c0ed..3964de52 100644 --- a/AdvancedSharpAdbClient/Extensions/SocketExtensions.cs +++ b/AdvancedSharpAdbClient/Extensions/SocketExtensions.cs @@ -1,5 +1,4 @@ -#if HAS_TASK -// +// // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // @@ -14,6 +13,7 @@ namespace AdvancedSharpAdbClient /// public static class SocketExtensions { +#if HAS_TASK #if !HAS_BUFFERS /// /// Asynchronously receives data from a connected socket. @@ -92,7 +92,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)); + return Extensions.Run(() => socket.Receive(buffer, offset, size, socketFlags), cancellationToken); #endif } @@ -176,6 +176,18 @@ public static Task SendAsync(this Socket socket, byte[] buffer, int offset, return Extensions.Run(() => socket.Receive(buffer, offset, size, socketFlags), cancellationToken); #endif } +#endif + +#if NETFRAMEWORK && !NET40_OR_GREATER + /// + /// Releases all resources used by the current instance of the class. + /// + /// The to release. + public static void Dispose(this Socket socket) + { + socket.Close(); + GC.SuppressFinalize(socket); + } +#endif } -} -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Extensions/StringExtensions.cs b/AdvancedSharpAdbClient/Extensions/StringExtensions.cs new file mode 100644 index 00000000..b2fec310 --- /dev/null +++ b/AdvancedSharpAdbClient/Extensions/StringExtensions.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace AdvancedSharpAdbClient +{ + /// + /// Provides extension methods for the class. + /// + public static class StringExtensions + { + /// + /// Indicates whether a specified string is , empty, or consists only of white-space characters. + /// + /// The string to test. + /// if the parameter is or + /// , or if consists exclusively of white-space characters. + public static bool IsNullOrWhiteSpace(string value) + { +#if NETFRAMEWORK && !NET40_OR_GREATER + if (value == null) + { + return true; + } + + for (int i = 0; i < value.Length; i++) + { + if (!char.IsWhiteSpace(value[i])) + { + return false; + } + } + + return true; +#else + return string.IsNullOrWhiteSpace(value); +#endif + } + +#if !HAS_FULLSTRING + /// + /// Returns a value indicating whether a specified string occurs within this string, using the specified comparison rules. + /// + /// A sequence in which to locate a value. + /// The string to seek. + /// One of the enumeration values that specifies the rules to use in the comparison. + /// if the parameter occurs within this string, + /// or if is the empty string (""); otherwise, . + public static bool Contains(this string text, string value, StringComparison comparisonType) => + text.IndexOf(value, comparisonType) != -1; + + /// + /// Splits a string into substrings based on a specified delimiting character and, optionally, options. + /// + /// The string to split. + /// A character that delimits the substrings in this string. + /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings. + /// An array whose elements contain the substrings from this instance that are delimited by . + public static string[] Split(this string text, char separator, StringSplitOptions options = StringSplitOptions.None) => + text.Split(new[] { separator }, options); + + /// + /// Splits a string into a maximum number of substrings based on a specified delimiting + /// character and, optionally, options. Splits a string into a maximum number of + /// substrings based on the provided character separator, optionally omitting empty + /// substrings from the result. + /// + /// The string to split. + /// A character that delimits the substrings in this string. + /// The maximum number of elements expected in the array. + /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings. + /// An array that contains at most count substrings from this instance that are delimited by . + public static string[] Split(this string text, char separator, int count, StringSplitOptions options = StringSplitOptions.None) => + text.Split(new[] { separator }, count, options); + + /// + /// Determines whether this string instance starts with the specified character. + /// + /// A sequence in which to locate a value. + /// The character to compare. + /// if matches the beginning of this string; otherwise, . + public static bool StartsWith(this string text, char value) => text.StartsWith(new string([value])); + + /// + /// Determines whether the end of this string instance matches the specified character. + /// + /// A sequence in which to locate a value. + /// The character to compare to the character at the end of this instance. + /// if matches the end of this instance; otherwise, . + public static bool EndsWith(this string text, char value) => text.EndsWith(new string([value])); +#endif + + /// + /// Concatenates the members of a constructed collection of type , + /// using the specified separator between each member. + /// + /// The string to use as a separator. is included + /// in the returned string only if has more than one element. + /// A collection that contains the strings to concatenate. + /// A string that consists of the elements of delimited by the + /// string.-or- if values has zero elements. + public static string Join(string separator, IEnumerable values) + { +#if NETFRAMEWORK && !NET40_OR_GREATER + ExceptionExtensions.ThrowIfNull(values); + + separator ??= string.Empty; + + using IEnumerator en = values.GetEnumerator(); + if (!en.MoveNext()) + { + return string.Empty; + } + + StringBuilder result = new(); + if (en.Current != null) + { + _ = result.Append(en.Current); + } + + while (en.MoveNext()) + { + _ = result.Append(separator); + if (en.Current != null) + { + _ = result.Append(en.Current); + } + } + return result.ToString(); +#else + return string.Join(separator, values); +#endif + } + } +} diff --git a/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs b/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs index 86178d1e..a83b52d0 100644 --- a/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs +++ b/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs @@ -52,9 +52,15 @@ public static byte[] GetBytes(SyncCommand command) /// /// A byte array that represents a . /// The corresponding . +#if HAS_BUFFERS + public static SyncCommand GetCommand(ReadOnlySpan value) +#else public static SyncCommand GetCommand(byte[] value) +#endif { +#if !HAS_BUFFERS ExceptionExtensions.ThrowIfNull(value); +#endif if (value.Length != 4) { diff --git a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs index b5b03d0b..1682c86d 100644 --- a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs +++ b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs @@ -25,11 +25,7 @@ public FramebufferHeader(byte[] data) { // Read the data from a MemoryStream so we can use the BinaryReader to process the data. using MemoryStream stream = new(data); - using BinaryReader reader = new(stream, Encoding.ASCII -#if !NETFRAMEWORK || NET45_OR_GREATER - , leaveOpen: true -#endif - ); + using BinaryReader reader = new(stream, Encoding.ASCII); Version = reader.ReadUInt32(); if (Version > 2) @@ -41,7 +37,7 @@ public FramebufferHeader(byte[] data) Bpp = reader.ReadUInt32(); - if (Version >= 2) + if (Version == 2) { ColorSpace = reader.ReadUInt32(); } @@ -172,13 +168,19 @@ public readonly Bitmap ToImage(byte[] buffer) /// /// A byte array in which the images are stored according to this . /// A that describes how the image data is represented in this . +#if HAS_BUFFERS #if NET [SupportedOSPlatform("windows")] #endif + private readonly PixelFormat StandardizePixelFormat(Span buffer) +#else private readonly PixelFormat StandardizePixelFormat(byte[] buffer) +#endif { // Initial parameter validation. +#if !HAS_BUFFERS ExceptionExtensions.ThrowIfNull(buffer); +#endif if (buffer.Length < Width * Height * (Bpp / 8)) { @@ -208,10 +210,10 @@ private readonly PixelFormat StandardizePixelFormat(byte[] buffer) } // Get the index at which the red, bue, green and alpha values are stored. - uint redIndex = Red.Offset / 8; - uint blueIndex = Blue.Offset / 8; - uint greenIndex = Green.Offset / 8; - uint alphaIndex = Alpha.Offset / 8; + int redIndex = (int)Red.Offset / 8; + int blueIndex = (int)Blue.Offset / 8; + int greenIndex = (int)Green.Offset / 8; + int alphaIndex = (int)Alpha.Offset / 8; // Loop over the array and re-order as required for (int i = 0; i < (int)Size; i += 4) diff --git a/AdvancedSharpAdbClient/TcpSocket.cs b/AdvancedSharpAdbClient/TcpSocket.cs index 3f3aba94..7d2756a8 100644 --- a/AdvancedSharpAdbClient/TcpSocket.cs +++ b/AdvancedSharpAdbClient/TcpSocket.cs @@ -94,19 +94,35 @@ public virtual void Close() => /// public virtual int Send(byte[] buffer, int size, SocketFlags socketFlags) => +#if HAS_BUFFERS + socket.Send(buffer.AsSpan(0, size), socketFlags); +#else socket.Send(buffer, size, socketFlags); +#endif /// public virtual int Send(byte[] buffer, int offset, int size, SocketFlags socketFlags) => +#if HAS_BUFFERS + socket.Send(buffer.AsSpan(offset, size), socketFlags); +#else socket.Send(buffer, offset, size, socketFlags); +#endif /// public virtual int Receive(byte[] buffer, int size, SocketFlags socketFlags) => +#if HAS_BUFFERS + socket.Receive(buffer.AsSpan(0, size), socketFlags); +#else socket.Receive(buffer, size, socketFlags); +#endif /// public virtual int Receive(byte[] buffer, int offset, int size, SocketFlags socketFlags) => +#if HAS_BUFFERS + socket.Send(buffer.AsSpan(offset, size), socketFlags); +#else socket.Receive(buffer, offset, size, socketFlags); +#endif #if HAS_BUFFERS /// From af76a2a0dcd1943d4808a0bd3698280ce381c9ee Mon Sep 17 00:00:00 2001 From: wherewhere Date: Tue, 3 Oct 2023 00:28:47 +0800 Subject: [PATCH 22/66] Add ExecuteServerCommand --- .../AdbClientTests.Async.cs | 4 +- .../AdbClientTests.cs | 4 +- .../DeviceExtensionsTests.Async.cs | 24 ++-- .../DeviceCommands/DeviceExtensionsTests.cs | 24 ++-- .../PackageManagerTests.Async.cs | 128 ++++++++--------- .../DeviceCommands/PackageManagerTests.cs | 130 +++++++++--------- .../Receivers/GetPropReceiverTests.cs | 2 +- .../Dummys/DummyAdbClient.cs | 50 +++++-- AdvancedSharpAdbClient/AdbClient.Async.cs | 50 ++++--- AdvancedSharpAdbClient/AdbClient.cs | 54 +++++--- .../Extensions/AdbClientExtensions.Async.cs | 40 ++++++ .../Extensions/AdbClientExtensions.cs | 33 +++++ .../Extensions/SocketExtensions.cs | 20 ++- .../Interfaces/IAdbClient.Async.cs | 25 +++- .../Interfaces/IAdbClient.cs | 25 +++- .../Receivers/IShellOutputReceiver.cs | 4 +- AdvancedSharpAdbClient/TcpSocket.cs | 7 +- 17 files changed, 395 insertions(+), 229 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs index 7e9eb62a..6defa126 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs @@ -272,7 +272,7 @@ await RunTestAsync( } /// - /// Tests the method. + /// Tests the method. /// [Fact] public async void ExecuteRemoteCommandAsyncTest() @@ -313,7 +313,7 @@ await RunTestAsync( } /// - /// Tests the method. + /// Tests the method. /// [Fact] public async void ExecuteRemoteCommandAsyncUnresponsiveTest() diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs index b3768455..4408be74 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs @@ -378,7 +378,7 @@ public void ListReverseForwardTest() } /// - /// Tests the method. + /// Tests the method. /// [Fact] public void ExecuteRemoteCommandTest() @@ -419,7 +419,7 @@ public void ExecuteRemoteCommandTest() } /// - /// Tests the method. + /// Tests the method. /// [Fact] public void ExecuteRemoteCommandUnresponsiveTest() diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs index 7c013e47..ac6550ba 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs @@ -36,7 +36,7 @@ public async void GetEnvironmentVariablesAsyncTest() { DummyAdbClient adbClient = new(); - adbClient.Commands[EnvironmentVariablesReceiver.PrintEnvCommand] = "a=b"; + adbClient.Commands[$"shell:{EnvironmentVariablesReceiver.PrintEnvCommand}"] = "a=b"; DeviceData device = new(); @@ -52,8 +52,8 @@ public async void UninstallPackageAsyncTests() { DummyAdbClient adbClient = new(); - adbClient.Commands["pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; - adbClient.Commands["pm uninstall com.example"] = "Success"; + adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; + adbClient.Commands["shell:pm uninstall com.example"] = "Success"; DeviceData device = new() { @@ -62,8 +62,8 @@ public async void UninstallPackageAsyncTests() await adbClient.UninstallPackageAsync(device, "com.example"); Assert.Equal(2, adbClient.ReceivedCommands.Count); - Assert.Equal("pm list packages -f", adbClient.ReceivedCommands[0]); - Assert.Equal("pm uninstall com.example", adbClient.ReceivedCommands[1]); + Assert.Equal("shell:pm list packages -f", adbClient.ReceivedCommands[0]); + Assert.Equal("shell:pm uninstall com.example", adbClient.ReceivedCommands[1]); } [Theory] @@ -297,8 +297,8 @@ public async void GetPackageVersionAsyncTest(string command, int versionCode, st { DummyAdbClient adbClient = new(); - adbClient.Commands["pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; - adbClient.Commands[$"dumpsys package {packageName}"] = command; + adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; + adbClient.Commands[$"shell:dumpsys package {packageName}"] = command; DeviceData device = new() { @@ -310,8 +310,8 @@ public async void GetPackageVersionAsyncTest(string command, int versionCode, st Assert.Equal(versionName, version.VersionName); Assert.Equal(2, adbClient.ReceivedCommands.Count); - Assert.Equal("pm list packages -f", adbClient.ReceivedCommands[0]); - Assert.Equal($"dumpsys package {packageName}", adbClient.ReceivedCommands[1]); + Assert.Equal("shell:pm list packages -f", adbClient.ReceivedCommands[0]); + Assert.Equal($"shell:dumpsys package {packageName}", adbClient.ReceivedCommands[1]); } [Fact] @@ -319,7 +319,7 @@ public async void ListProcessesAsyncTest() { DummyAdbClient adbClient = new(); - adbClient.Commands[@"SDK=""$(/system/bin/getprop ro.build.version.sdk)"" + adbClient.Commands[@"shell:SDK=""$(/system/bin/getprop ro.build.version.sdk)"" if [ $SDK -lt 24 ] then /system/bin/ls /proc/ @@ -331,11 +331,11 @@ public async void ListProcessesAsyncTest() 3 acpi asound"; - adbClient.Commands["cat /proc/1/stat /proc/2/stat /proc/3/stat "] = + adbClient.Commands["shell:cat /proc/1/stat /proc/2/stat /proc/3/stat "] = @"1 (init) S 0 0 0 0 -1 1077944576 2680 83280 0 179 0 67 16 39 20 0 1 0 2 17735680 143 18446744073709551615 134512640 135145076 4288071392 4288070744 134658736 0 0 0 65536 18446744071580117077 0 0 17 1 0 0 0 0 0 135152736 135165080 142131200 4288073690 4288073696 4288073696 4288073714 0 2 (kthreadd) S 0 0 0 0 -1 2129984 0 0 0 0 0 0 0 0 20 0 1 0 2 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 18446744071579254310 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 (ksoftirqd/0) S 2 0 0 0 -1 69238848 0 0 0 0 0 23 0 0 20 0 1 0 7 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 18446744071579284070 0 0 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0"; - adbClient.Commands["cat /proc/1/cmdline /proc/1/stat /proc/2/cmdline /proc/2/stat /proc/3/cmdline /proc/3/stat "] = + adbClient.Commands["shell:cat /proc/1/cmdline /proc/1/stat /proc/2/cmdline /proc/2/stat /proc/3/cmdline /proc/3/stat "] = @" 1 (init) S 0 0 0 0 -1 1077944576 2680 83280 0 179 0 67 16 39 20 0 1 0 2 17735680 143 18446744073709551615 134512640 135145076 4288071392 4288070744 134658736 0 0 0 65536 18446744071580117077 0 0 17 1 0 0 0 0 0 135152736 135165080 142131200 4288073690 4288073696 4288073696 4288073714 0 diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.cs index b155e6a6..d82c9360 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.cs @@ -35,7 +35,7 @@ public void GetEnvironmentVariablesTest() { DummyAdbClient adbClient = new(); - adbClient.Commands[EnvironmentVariablesReceiver.PrintEnvCommand] = "a=b"; + adbClient.Commands[$"shell:{EnvironmentVariablesReceiver.PrintEnvCommand}"] = "a=b"; DeviceData device = new(); @@ -51,8 +51,8 @@ public void UninstallPackageTests() { DummyAdbClient adbClient = new(); - adbClient.Commands["pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; - adbClient.Commands["pm uninstall com.example"] = "Success"; + adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; + adbClient.Commands["shell:pm uninstall com.example"] = "Success"; DeviceData device = new() { @@ -61,8 +61,8 @@ public void UninstallPackageTests() adbClient.UninstallPackage(device, "com.example"); Assert.Equal(2, adbClient.ReceivedCommands.Count); - Assert.Equal("pm list packages -f", adbClient.ReceivedCommands[0]); - Assert.Equal("pm uninstall com.example", adbClient.ReceivedCommands[1]); + Assert.Equal("shell:pm list packages -f", adbClient.ReceivedCommands[0]); + Assert.Equal("shell:pm uninstall com.example", adbClient.ReceivedCommands[1]); } [Theory] @@ -296,8 +296,8 @@ public void GetPackageVersionTest(string command, int versionCode, string versio { DummyAdbClient adbClient = new(); - adbClient.Commands["pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; - adbClient.Commands[$"dumpsys package {packageName}"] = command; + adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; + adbClient.Commands[$"shell:dumpsys package {packageName}"] = command; DeviceData device = new() { @@ -309,8 +309,8 @@ public void GetPackageVersionTest(string command, int versionCode, string versio Assert.Equal(versionName, version.VersionName); Assert.Equal(2, adbClient.ReceivedCommands.Count); - Assert.Equal("pm list packages -f", adbClient.ReceivedCommands[0]); - Assert.Equal($"dumpsys package {packageName}", adbClient.ReceivedCommands[1]); + Assert.Equal("shell:pm list packages -f", adbClient.ReceivedCommands[0]); + Assert.Equal($"shell:dumpsys package {packageName}", adbClient.ReceivedCommands[1]); } [Fact] @@ -318,7 +318,7 @@ public void ListProcessesTest() { DummyAdbClient adbClient = new(); - adbClient.Commands[@"SDK=""$(/system/bin/getprop ro.build.version.sdk)"" + adbClient.Commands[@"shell:SDK=""$(/system/bin/getprop ro.build.version.sdk)"" if [ $SDK -lt 24 ] then /system/bin/ls /proc/ @@ -330,11 +330,11 @@ public void ListProcessesTest() 3 acpi asound"; - adbClient.Commands["cat /proc/1/stat /proc/2/stat /proc/3/stat "] = + adbClient.Commands["shell:cat /proc/1/stat /proc/2/stat /proc/3/stat "] = @"1 (init) S 0 0 0 0 -1 1077944576 2680 83280 0 179 0 67 16 39 20 0 1 0 2 17735680 143 18446744073709551615 134512640 135145076 4288071392 4288070744 134658736 0 0 0 65536 18446744071580117077 0 0 17 1 0 0 0 0 0 135152736 135165080 142131200 4288073690 4288073696 4288073696 4288073714 0 2 (kthreadd) S 0 0 0 0 -1 2129984 0 0 0 0 0 0 0 0 20 0 1 0 2 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 18446744071579254310 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 (ksoftirqd/0) S 2 0 0 0 -1 69238848 0 0 0 0 0 23 0 0 20 0 1 0 7 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 18446744071579284070 0 0 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0"; - adbClient.Commands["cat /proc/1/cmdline /proc/1/stat /proc/2/cmdline /proc/2/stat /proc/3/cmdline /proc/3/stat "] = + adbClient.Commands["shell:cat /proc/1/cmdline /proc/1/stat /proc/2/cmdline /proc/2/stat /proc/3/cmdline /proc/3/stat "] = @" 1 (init) S 0 0 0 0 -1 1077944576 2680 83280 0 179 0 67 16 39 20 0 1 0 2 17735680 143 18446744073709551615 134512640 135145076 4288071392 4288070744 134658736 0 0 0 65536 18446744071580117077 0 0 17 1 0 0 0 0 0 135152736 135165080 142131200 4288073690 4288073696 4288073696 4288073714 0 diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs index 9aa6744a..91f1564f 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs @@ -11,9 +11,9 @@ public async void InstallRemotePackageAsyncTest() { DummyAdbClient adbClient = new(); - adbClient.Commands["pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; - adbClient.Commands["pm install \"/data/test.apk\""] = "Success"; - adbClient.Commands["pm install -r \"/data/test.apk\""] = "Success"; + adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; + adbClient.Commands["shell:pm install \"/data/test.apk\""] = "Success"; + adbClient.Commands["shell:pm install -r \"/data/test.apk\""] = "Success"; DeviceData device = new() { @@ -24,12 +24,12 @@ public async void InstallRemotePackageAsyncTest() await manager.InstallRemotePackageAsync("/data/test.apk", false); Assert.Equal(2, adbClient.ReceivedCommands.Count); - Assert.Equal("pm install \"/data/test.apk\"", adbClient.ReceivedCommands[1]); + Assert.Equal("shell:pm install \"/data/test.apk\"", adbClient.ReceivedCommands[1]); await manager.InstallRemotePackageAsync("/data/test.apk", true); Assert.Equal(3, adbClient.ReceivedCommands.Count); - Assert.Equal("pm install -r \"/data/test.apk\"", adbClient.ReceivedCommands[2]); + Assert.Equal("shell:pm install -r \"/data/test.apk\"", adbClient.ReceivedCommands[2]); } [Fact] @@ -43,9 +43,9 @@ public async void InstallPackageAsyncTest() DummyAdbClient adbClient = new(); - adbClient.Commands["pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; - adbClient.Commands["pm install \"/data/local/tmp/test.txt\""] = "Success"; - adbClient.Commands["rm \"/data/local/tmp/test.txt\""] = string.Empty; + adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; + adbClient.Commands["shell:pm install \"/data/local/tmp/test.txt\""] = "Success"; + adbClient.Commands["shell:rm \"/data/local/tmp/test.txt\""] = string.Empty; DeviceData device = new() { @@ -55,8 +55,8 @@ public async void InstallPackageAsyncTest() PackageManager manager = new(adbClient, device); await manager.InstallPackageAsync("Assets/test.txt", false); Assert.Equal(3, adbClient.ReceivedCommands.Count); - Assert.Equal("pm install \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[1]); - Assert.Equal("rm \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[2]); + Assert.Equal("shell:pm install \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[1]); + Assert.Equal("shell:rm \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[2]); Assert.Single(syncService.UploadedFiles); Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/test.txt")); @@ -69,15 +69,15 @@ public async void InstallMultipleRemotePackageAsyncTest() { DummyAdbClient adbClient = new(); - adbClient.Commands["pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; - adbClient.Commands["pm install-create"] = "Success: created install session [936013062]"; - adbClient.Commands["pm install-create -r"] = "Success: created install session [936013062]"; - adbClient.Commands["pm install-create -p com.google.android.gms"] = "Success: created install session [936013062]"; - adbClient.Commands["pm install-create -r -p com.google.android.gms"] = "Success: created install session [936013062]"; - adbClient.Commands["pm install-write 936013062 base.apk \"/data/base.apk\""] = "Success"; - adbClient.Commands["pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\""] = "Success"; - adbClient.Commands["pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\""] = "Success"; - adbClient.Commands["pm install-commit 936013062"] = "Success"; + adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; + adbClient.Commands["shell:pm install-create"] = "Success: created install session [936013062]"; + adbClient.Commands["shell:pm install-create -r"] = "Success: created install session [936013062]"; + adbClient.Commands["shell:pm install-create -p com.google.android.gms"] = "Success: created install session [936013062]"; + adbClient.Commands["shell:pm install-create -r -p com.google.android.gms"] = "Success: created install session [936013062]"; + adbClient.Commands["shell:pm install-write 936013062 base.apk \"/data/base.apk\""] = "Success"; + adbClient.Commands["shell:pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\""] = "Success"; + adbClient.Commands["shell:pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\""] = "Success"; + adbClient.Commands["shell:pm install-commit 936013062"] = "Success"; DeviceData device = new() { @@ -88,36 +88,36 @@ public async void InstallMultipleRemotePackageAsyncTest() await manager.InstallMultipleRemotePackageAsync("/data/base.apk", ["/data/split-dpi.apk", "/data/split-abi.apk"], false); Assert.Equal(6, adbClient.ReceivedCommands.Count); - Assert.Equal("pm install-create", adbClient.ReceivedCommands[1]); - Assert.Equal("pm install-write 936013062 base.apk \"/data/base.apk\"", adbClient.ReceivedCommands[2]); - Assert.Equal("pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[3]); - Assert.Equal("pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[4]); - Assert.Equal("pm install-commit 936013062", adbClient.ReceivedCommands[5]); + Assert.Equal("shell:pm install-create", adbClient.ReceivedCommands[1]); + Assert.Equal("shell:pm install-write 936013062 base.apk \"/data/base.apk\"", adbClient.ReceivedCommands[2]); + Assert.Equal("shell:pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[3]); + Assert.Equal("shell:pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[4]); + Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[5]); await manager.InstallMultipleRemotePackageAsync("/data/base.apk", ["/data/split-dpi.apk", "/data/split-abi.apk"], true); Assert.Equal(11, adbClient.ReceivedCommands.Count); - Assert.Equal("pm install-create -r", adbClient.ReceivedCommands[6]); - Assert.Equal("pm install-write 936013062 base.apk \"/data/base.apk\"", adbClient.ReceivedCommands[7]); - Assert.Equal("pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[8]); - Assert.Equal("pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[9]); - Assert.Equal("pm install-commit 936013062", adbClient.ReceivedCommands[10]); + Assert.Equal("shell:pm install-create -r", adbClient.ReceivedCommands[6]); + Assert.Equal("shell:pm install-write 936013062 base.apk \"/data/base.apk\"", adbClient.ReceivedCommands[7]); + Assert.Equal("shell:pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[8]); + Assert.Equal("shell:pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[9]); + Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[10]); await manager.InstallMultipleRemotePackageAsync(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms", false); Assert.Equal(15, adbClient.ReceivedCommands.Count); - Assert.Equal("pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[11]); - Assert.Equal("pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[12]); - Assert.Equal("pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[13]); - Assert.Equal("pm install-commit 936013062", adbClient.ReceivedCommands[14]); + Assert.Equal("shell:pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[11]); + Assert.Equal("shell:pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[12]); + Assert.Equal("shell:pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[13]); + Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[14]); await manager.InstallMultipleRemotePackageAsync(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms", true); Assert.Equal(19, adbClient.ReceivedCommands.Count); - Assert.Equal("pm install-create -r -p com.google.android.gms", adbClient.ReceivedCommands[15]); - Assert.Equal("pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[16]); - Assert.Equal("pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[17]); - Assert.Equal("pm install-commit 936013062", adbClient.ReceivedCommands[18]); + Assert.Equal("shell:pm install-create -r -p com.google.android.gms", adbClient.ReceivedCommands[15]); + Assert.Equal("shell:pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[16]); + Assert.Equal("shell:pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[17]); + Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[18]); } [Fact] @@ -131,16 +131,16 @@ public async void InstallMultiplePackageAsyncTest() DummyAdbClient adbClient = new(); - adbClient.Commands["pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; - adbClient.Commands["pm install-create"] = "Success: created install session [936013062]"; - adbClient.Commands["pm install-create -p com.google.android.gms"] = "Success: created install session [936013062]"; - adbClient.Commands["pm install-write 936013062 base.apk \"/data/local/tmp/test.txt\""] = "Success"; - adbClient.Commands["pm install-write 936013062 splitapp0.apk \"/data/local/tmp/gapps.txt\""] = "Success"; - adbClient.Commands["pm install-write 936013062 splitapp1.apk \"/data/local/tmp/logcat.bin\""] = "Success"; - adbClient.Commands["pm install-commit 936013062"] = "Success"; - adbClient.Commands["rm \"/data/local/tmp/test.txt\""] = string.Empty; - adbClient.Commands["rm \"/data/local/tmp/gapps.txt\""] = string.Empty; - adbClient.Commands["rm \"/data/local/tmp/logcat.bin\""] = string.Empty; + adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; + adbClient.Commands["shell:pm install-create"] = "Success: created install session [936013062]"; + adbClient.Commands["shell:pm install-create -p com.google.android.gms"] = "Success: created install session [936013062]"; + adbClient.Commands["shell:pm install-write 936013062 base.apk \"/data/local/tmp/test.txt\""] = "Success"; + adbClient.Commands["shell:pm install-write 936013062 splitapp0.apk \"/data/local/tmp/gapps.txt\""] = "Success"; + adbClient.Commands["shell:pm install-write 936013062 splitapp1.apk \"/data/local/tmp/logcat.bin\""] = "Success"; + adbClient.Commands["shell:pm install-commit 936013062"] = "Success"; + adbClient.Commands["shell:rm \"/data/local/tmp/test.txt\""] = string.Empty; + adbClient.Commands["shell:rm \"/data/local/tmp/gapps.txt\""] = string.Empty; + adbClient.Commands["shell:rm \"/data/local/tmp/logcat.bin\""] = string.Empty; DeviceData device = new() { @@ -150,14 +150,14 @@ public async void InstallMultiplePackageAsyncTest() PackageManager manager = new(adbClient, device); await manager.InstallMultiplePackageAsync("Assets/test.txt", ["Assets/gapps.txt", "Assets/logcat.bin"], false); Assert.Equal(9, adbClient.ReceivedCommands.Count); - Assert.Equal("pm install-create", adbClient.ReceivedCommands[1]); - Assert.Equal("pm install-write 936013062 base.apk \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[2]); - Assert.Equal("pm install-write 936013062 splitapp0.apk \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[3]); - Assert.Equal("pm install-write 936013062 splitapp1.apk \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[4]); - Assert.Equal("pm install-commit 936013062", adbClient.ReceivedCommands[5]); - Assert.Equal("rm \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[6]); - Assert.Equal("rm \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[7]); - Assert.Equal("rm \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[8]); + Assert.Equal("shell:pm install-create", adbClient.ReceivedCommands[1]); + Assert.Equal("shell:pm install-write 936013062 base.apk \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[2]); + Assert.Equal("shell:pm install-write 936013062 splitapp0.apk \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[3]); + Assert.Equal("shell:pm install-write 936013062 splitapp1.apk \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[4]); + Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[5]); + Assert.Equal("shell:rm \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[6]); + Assert.Equal("shell:rm \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[7]); + Assert.Equal("shell:rm \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[8]); Assert.Equal(3, syncService.UploadedFiles.Count); Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/test.txt")); @@ -167,12 +167,12 @@ public async void InstallMultiplePackageAsyncTest() syncService.UploadedFiles.Clear(); await manager.InstallMultiplePackageAsync(["Assets/gapps.txt", "Assets/logcat.bin"], "com.google.android.gms", false); Assert.Equal(15, adbClient.ReceivedCommands.Count); - Assert.Equal("pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[9]); - Assert.Equal("pm install-write 936013062 splitapp0.apk \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[10]); - Assert.Equal("pm install-write 936013062 splitapp1.apk \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[11]); - Assert.Equal("pm install-commit 936013062", adbClient.ReceivedCommands[12]); - Assert.Equal("rm \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[6]); - Assert.Equal("rm \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[7]); + Assert.Equal("shell:pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[9]); + Assert.Equal("shell:pm install-write 936013062 splitapp0.apk \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[10]); + Assert.Equal("shell:pm install-write 936013062 splitapp1.apk \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[11]); + Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[12]); + Assert.Equal("shell:rm \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[6]); + Assert.Equal("shell:rm \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[7]); Assert.Equal(2, syncService.UploadedFiles.Count); Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/gapps.txt")); @@ -190,8 +190,8 @@ public async void UninstallPackageAsyncTest() }; DummyAdbClient client = new(); - client.Commands["pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; - client.Commands["pm uninstall com.android.gallery3d"] = "Success"; + client.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; + client.Commands["shell:pm uninstall com.android.gallery3d"] = "Success"; PackageManager manager = new(client, device); // Command should execute correctly; if the wrong command is passed an exception @@ -208,7 +208,7 @@ public async void GetPackageVersionInfoAsyncTest() }; DummyAdbClient client = new(); - client.Commands["dumpsys package com.google.android.gms"] = File.ReadAllText("Assets/gapps.txt"); + client.Commands["shell:dumpsys package com.google.android.gms"] = File.ReadAllText("Assets/gapps.txt"); PackageManager manager = new(client, device, skipInit: true); VersionInfo versionInfo = await manager.GetVersionInfoAsync("com.google.android.gms"); diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs index 7ab490d1..09e18160 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs @@ -27,7 +27,7 @@ public void PackagesPropertyTest(string command, string packageName, string path }; DummyAdbClient client = new(); - client.Commands["pm list packages -f"] = command; + client.Commands["shell:pm list packages -f"] = command; PackageManager manager = new(client, device); Assert.True(manager.Packages.ContainsKey(packageName)); @@ -39,9 +39,9 @@ public void InstallRemotePackageTest() { DummyAdbClient adbClient = new(); - adbClient.Commands["pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; - adbClient.Commands["pm install \"/data/test.apk\""] = "Success"; - adbClient.Commands["pm install -r \"/data/test.apk\""] = "Success"; + adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; + adbClient.Commands["shell:pm install \"/data/test.apk\""] = "Success"; + adbClient.Commands["shell:pm install -r \"/data/test.apk\""] = "Success"; DeviceData device = new() { @@ -52,12 +52,12 @@ public void InstallRemotePackageTest() manager.InstallRemotePackage("/data/test.apk", false); Assert.Equal(2, adbClient.ReceivedCommands.Count); - Assert.Equal("pm install \"/data/test.apk\"", adbClient.ReceivedCommands[1]); + Assert.Equal("shell:pm install \"/data/test.apk\"", adbClient.ReceivedCommands[1]); manager.InstallRemotePackage("/data/test.apk", true); Assert.Equal(3, adbClient.ReceivedCommands.Count); - Assert.Equal("pm install -r \"/data/test.apk\"", adbClient.ReceivedCommands[2]); + Assert.Equal("shell:pm install -r \"/data/test.apk\"", adbClient.ReceivedCommands[2]); } [Fact] @@ -71,9 +71,9 @@ public void InstallPackageTest() DummyAdbClient adbClient = new(); - adbClient.Commands["pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; - adbClient.Commands["pm install \"/data/local/tmp/test.txt\""] = "Success"; - adbClient.Commands["rm \"/data/local/tmp/test.txt\""] = string.Empty; + adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; + adbClient.Commands["shell:pm install \"/data/local/tmp/test.txt\""] = "Success"; + adbClient.Commands["shell:rm \"/data/local/tmp/test.txt\""] = string.Empty; DeviceData device = new() { @@ -83,8 +83,8 @@ public void InstallPackageTest() PackageManager manager = new(adbClient, device); manager.InstallPackage("Assets/test.txt", false); Assert.Equal(3, adbClient.ReceivedCommands.Count); - Assert.Equal("pm install \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[1]); - Assert.Equal("rm \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[2]); + Assert.Equal("shell:pm install \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[1]); + Assert.Equal("shell:rm \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[2]); Assert.Single(syncService.UploadedFiles); Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/test.txt")); @@ -97,15 +97,15 @@ public void InstallMultipleRemotePackageTest() { DummyAdbClient adbClient = new(); - adbClient.Commands["pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; - adbClient.Commands["pm install-create"] = "Success: created install session [936013062]"; - adbClient.Commands["pm install-create -r"] = "Success: created install session [936013062]"; - adbClient.Commands["pm install-create -p com.google.android.gms"] = "Success: created install session [936013062]"; - adbClient.Commands["pm install-create -r -p com.google.android.gms"] = "Success: created install session [936013062]"; - adbClient.Commands["pm install-write 936013062 base.apk \"/data/base.apk\""] = "Success"; - adbClient.Commands["pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\""] = "Success"; - adbClient.Commands["pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\""] = "Success"; - adbClient.Commands["pm install-commit 936013062"] = "Success"; + adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; + adbClient.Commands["shell:pm install-create"] = "Success: created install session [936013062]"; + adbClient.Commands["shell:pm install-create -r"] = "Success: created install session [936013062]"; + adbClient.Commands["shell:pm install-create -p com.google.android.gms"] = "Success: created install session [936013062]"; + adbClient.Commands["shell:pm install-create -r -p com.google.android.gms"] = "Success: created install session [936013062]"; + adbClient.Commands["shell:pm install-write 936013062 base.apk \"/data/base.apk\""] = "Success"; + adbClient.Commands["shell:pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\""] = "Success"; + adbClient.Commands["shell:pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\""] = "Success"; + adbClient.Commands["shell:pm install-commit 936013062"] = "Success"; DeviceData device = new() { @@ -116,36 +116,36 @@ public void InstallMultipleRemotePackageTest() manager.InstallMultipleRemotePackage("/data/base.apk", ["/data/split-dpi.apk", "/data/split-abi.apk"], false); Assert.Equal(6, adbClient.ReceivedCommands.Count); - Assert.Equal("pm install-create", adbClient.ReceivedCommands[1]); - Assert.Equal("pm install-write 936013062 base.apk \"/data/base.apk\"", adbClient.ReceivedCommands[2]); - Assert.Equal("pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[3]); - Assert.Equal("pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[4]); - Assert.Equal("pm install-commit 936013062", adbClient.ReceivedCommands[5]); + Assert.Equal("shell:pm install-create", adbClient.ReceivedCommands[1]); + Assert.Equal("shell:pm install-write 936013062 base.apk \"/data/base.apk\"", adbClient.ReceivedCommands[2]); + Assert.Equal("shell:pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[3]); + Assert.Equal("shell:pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[4]); + Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[5]); manager.InstallMultipleRemotePackage("/data/base.apk", ["/data/split-dpi.apk", "/data/split-abi.apk"], true); Assert.Equal(11, adbClient.ReceivedCommands.Count); - Assert.Equal("pm install-create -r", adbClient.ReceivedCommands[6]); - Assert.Equal("pm install-write 936013062 base.apk \"/data/base.apk\"", adbClient.ReceivedCommands[7]); - Assert.Equal("pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[8]); - Assert.Equal("pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[9]); - Assert.Equal("pm install-commit 936013062", adbClient.ReceivedCommands[10]); + Assert.Equal("shell:pm install-create -r", adbClient.ReceivedCommands[6]); + Assert.Equal("shell:pm install-write 936013062 base.apk \"/data/base.apk\"", adbClient.ReceivedCommands[7]); + Assert.Equal("shell:pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[8]); + Assert.Equal("shell:pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[9]); + Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[10]); manager.InstallMultipleRemotePackage(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms", false); Assert.Equal(15, adbClient.ReceivedCommands.Count); - Assert.Equal("pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[11]); - Assert.Equal("pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[12]); - Assert.Equal("pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[13]); - Assert.Equal("pm install-commit 936013062", adbClient.ReceivedCommands[14]); + Assert.Equal("shell:pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[11]); + Assert.Equal("shell:pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[12]); + Assert.Equal("shell:pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[13]); + Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[14]); manager.InstallMultipleRemotePackage(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms", true); Assert.Equal(19, adbClient.ReceivedCommands.Count); - Assert.Equal("pm install-create -r -p com.google.android.gms", adbClient.ReceivedCommands[15]); - Assert.Equal("pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[16]); - Assert.Equal("pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[17]); - Assert.Equal("pm install-commit 936013062", adbClient.ReceivedCommands[18]); + Assert.Equal("shell:pm install-create -r -p com.google.android.gms", adbClient.ReceivedCommands[15]); + Assert.Equal("shell:pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[16]); + Assert.Equal("shell:pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[17]); + Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[18]); } [Fact] @@ -159,16 +159,16 @@ public void InstallMultiplePackageTest() DummyAdbClient adbClient = new(); - adbClient.Commands["pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; - adbClient.Commands["pm install-create"] = "Success: created install session [936013062]"; - adbClient.Commands["pm install-create -p com.google.android.gms"] = "Success: created install session [936013062]"; - adbClient.Commands["pm install-write 936013062 base.apk \"/data/local/tmp/test.txt\""] = "Success"; - adbClient.Commands["pm install-write 936013062 splitapp0.apk \"/data/local/tmp/gapps.txt\""] = "Success"; - adbClient.Commands["pm install-write 936013062 splitapp1.apk \"/data/local/tmp/logcat.bin\""] = "Success"; - adbClient.Commands["pm install-commit 936013062"] = "Success"; - adbClient.Commands["rm \"/data/local/tmp/test.txt\""] = string.Empty; - adbClient.Commands["rm \"/data/local/tmp/gapps.txt\""] = string.Empty; - adbClient.Commands["rm \"/data/local/tmp/logcat.bin\""] = string.Empty; + adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; + adbClient.Commands["shell:pm install-create"] = "Success: created install session [936013062]"; + adbClient.Commands["shell:pm install-create -p com.google.android.gms"] = "Success: created install session [936013062]"; + adbClient.Commands["shell:pm install-write 936013062 base.apk \"/data/local/tmp/test.txt\""] = "Success"; + adbClient.Commands["shell:pm install-write 936013062 splitapp0.apk \"/data/local/tmp/gapps.txt\""] = "Success"; + adbClient.Commands["shell:pm install-write 936013062 splitapp1.apk \"/data/local/tmp/logcat.bin\""] = "Success"; + adbClient.Commands["shell:pm install-commit 936013062"] = "Success"; + adbClient.Commands["shell:rm \"/data/local/tmp/test.txt\""] = string.Empty; + adbClient.Commands["shell:rm \"/data/local/tmp/gapps.txt\""] = string.Empty; + adbClient.Commands["shell:rm \"/data/local/tmp/logcat.bin\""] = string.Empty; DeviceData device = new() { @@ -178,14 +178,14 @@ public void InstallMultiplePackageTest() PackageManager manager = new(adbClient, device); manager.InstallMultiplePackage("Assets/test.txt", ["Assets/gapps.txt", "Assets/logcat.bin"], false); Assert.Equal(9, adbClient.ReceivedCommands.Count); - Assert.Equal("pm install-create", adbClient.ReceivedCommands[1]); - Assert.Equal("pm install-write 936013062 base.apk \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[2]); - Assert.Equal("pm install-write 936013062 splitapp0.apk \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[3]); - Assert.Equal("pm install-write 936013062 splitapp1.apk \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[4]); - Assert.Equal("pm install-commit 936013062", adbClient.ReceivedCommands[5]); - Assert.Equal("rm \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[6]); - Assert.Equal("rm \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[7]); - Assert.Equal("rm \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[8]); + Assert.Equal("shell:pm install-create", adbClient.ReceivedCommands[1]); + Assert.Equal("shell:pm install-write 936013062 base.apk \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[2]); + Assert.Equal("shell:pm install-write 936013062 splitapp0.apk \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[3]); + Assert.Equal("shell:pm install-write 936013062 splitapp1.apk \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[4]); + Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[5]); + Assert.Equal("shell:rm \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[6]); + Assert.Equal("shell:rm \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[7]); + Assert.Equal("shell:rm \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[8]); Assert.Equal(3, syncService.UploadedFiles.Count); Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/test.txt")); @@ -195,12 +195,12 @@ public void InstallMultiplePackageTest() syncService.UploadedFiles.Clear(); manager.InstallMultiplePackage(["Assets/gapps.txt", "Assets/logcat.bin"], "com.google.android.gms", false); Assert.Equal(15, adbClient.ReceivedCommands.Count); - Assert.Equal("pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[9]); - Assert.Equal("pm install-write 936013062 splitapp0.apk \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[10]); - Assert.Equal("pm install-write 936013062 splitapp1.apk \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[11]); - Assert.Equal("pm install-commit 936013062", adbClient.ReceivedCommands[12]); - Assert.Equal("rm \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[6]); - Assert.Equal("rm \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[7]); + Assert.Equal("shell:pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[9]); + Assert.Equal("shell:pm install-write 936013062 splitapp0.apk \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[10]); + Assert.Equal("shell:pm install-write 936013062 splitapp1.apk \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[11]); + Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[12]); + Assert.Equal("shell:rm \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[6]); + Assert.Equal("shell:rm \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[7]); Assert.Equal(2, syncService.UploadedFiles.Count); Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/gapps.txt")); @@ -218,8 +218,8 @@ public void UninstallPackageTest() }; DummyAdbClient client = new(); - client.Commands["pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; - client.Commands["pm uninstall com.android.gallery3d"] = "Success"; + client.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; + client.Commands["shell:pm uninstall com.android.gallery3d"] = "Success"; PackageManager manager = new(client, device); // Command should execute correctly; if the wrong command is passed an exception @@ -236,7 +236,7 @@ public void GetPackageVersionInfoTest() }; DummyAdbClient client = new(); - client.Commands["dumpsys package com.google.android.gms"] = File.ReadAllText("Assets/gapps.txt"); + client.Commands["shell:dumpsys package com.google.android.gms"] = File.ReadAllText("Assets/gapps.txt"); PackageManager manager = new(client, device, skipInit: true); VersionInfo versionInfo = manager.GetVersionInfo("com.google.android.gms"); diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/GetPropReceiverTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/GetPropReceiverTests.cs index ec9921e4..7600e3eb 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/GetPropReceiverTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/GetPropReceiverTests.cs @@ -15,7 +15,7 @@ public void ListPropertiesTest() }; DummyAdbClient client = new(); - client.Commands["/system/bin/getprop"] = @"[init.svc.BGW]: [running] + client.Commands["shell:/system/bin/getprop"] = @"[init.svc.BGW]: [running] [init.svc.MtkCodecService]: [running] [init.svc.bootanim]: [stopped]"; diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs index 05f8e41d..5020f354 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs @@ -18,14 +18,25 @@ internal class DummyAdbClient : IAdbClient public EndPoint EndPoint { get; private set; } - public void ExecuteRemoteCommand(string command, DeviceData device, IShellOutputReceiver receiver) => - ExecuteRemoteCommand(command, device, receiver, Encoding.Default); + public void ExecuteRemoteCommand(string command, DeviceData device, IShellOutputReceiver receiver, Encoding encoding) => + ExecuteServerCommand("shell", command, receiver, encoding); - public void ExecuteRemoteCommand(string command, DeviceData device, IShellOutputReceiver receiver, Encoding encoding) + public Task ExecuteRemoteCommandAsync(string command, DeviceData device, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken = default) => + ExecuteServerCommandAsync("shell", command, receiver, encoding, cancellationToken); + + public void ExecuteServerCommand(string target, string command, IShellOutputReceiver receiver, Encoding encoding) { - ReceivedCommands.Add(command); + StringBuilder requestBuilder = new(); + if (!StringExtensions.IsNullOrWhiteSpace(target)) + { + _ = requestBuilder.AppendFormat("{0}:", target); + } + _ = requestBuilder.Append(command); - if (Commands.TryGetValue(command, out string value)) + string request = requestBuilder.ToString(); + ReceivedCommands.Add(request); + + if (Commands.TryGetValue(request, out string value)) { if (receiver != null) { @@ -41,18 +52,26 @@ public void ExecuteRemoteCommand(string command, DeviceData device, IShellOutput } else { - throw new ArgumentOutOfRangeException(nameof(command), $"The command '{command}' was unexpected"); + throw new ArgumentOutOfRangeException(nameof(command), $"The command '{request}' was unexpected"); } } - public Task ExecuteRemoteCommandAsync(string command, DeviceData device, IShellOutputReceiver receiver, CancellationToken cancellationToken = default) => - ExecuteRemoteCommandAsync(command, device, receiver, Encoding.Default, cancellationToken); + public void ExecuteServerCommand(string target, string command, IAdbSocket socket, IShellOutputReceiver receiver, Encoding encoding) => + ExecuteServerCommand(target, command, receiver, encoding); - public Task ExecuteRemoteCommandAsync(string command, DeviceData device, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken = default) + public async Task ExecuteServerCommandAsync(string target, string command, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken = default) { - ReceivedCommands.Add(command); + StringBuilder requestBuilder = new(); + if (!StringExtensions.IsNullOrWhiteSpace(target)) + { + _ = requestBuilder.AppendFormat("{0}:", target); + } + _ = requestBuilder.Append(command); + + string request = requestBuilder.ToString(); + ReceivedCommands.Add(request); - if (Commands.TryGetValue(command, out string value)) + if (Commands.TryGetValue(request, out string value)) { if (receiver != null) { @@ -60,7 +79,7 @@ public Task ExecuteRemoteCommandAsync(string command, DeviceData device, IShellO while (reader.Peek() != -1) { - receiver.AddOutput(reader.ReadLine()); + receiver.AddOutput(await reader.ReadLineAsync(cancellationToken)); } receiver.Flush(); @@ -68,12 +87,13 @@ public Task ExecuteRemoteCommandAsync(string command, DeviceData device, IShellO } else { - throw new ArgumentOutOfRangeException(nameof(command), $"The command '{command}' was unexpected"); + throw new ArgumentOutOfRangeException(nameof(command), $"The command '{request}' was unexpected"); } - - return Task.FromResult(true); } + public Task ExecuteServerCommandAsync(string target, string command, IAdbSocket socket, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken = default) => + ExecuteServerCommandAsync(target, command, receiver, encoding, cancellationToken); + #region Not Implemented public void BackBtn(DeviceData device) => throw new NotImplementedException(); diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index 971ebb97..3ed595eb 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -171,25 +171,30 @@ public async Task> ListReverseForwardAsync(DeviceData d return parts.Select(x => new ForwardData(x)); } - + /// - public Task ExecuteRemoteCommandAsync(string command, DeviceData device, IShellOutputReceiver receiver, CancellationToken cancellationToken = default) => - ExecuteRemoteCommandAsync(command, device, receiver, Encoding, cancellationToken); + public async Task ExecuteServerCommandAsync(string target, string command, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken = default) + { + using IAdbSocket socket = adbSocketFactory(EndPoint); + await ExecuteServerCommandAsync(target, command, socket, receiver, encoding, cancellationToken); + } /// - public async Task ExecuteRemoteCommandAsync(string command, DeviceData device, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken = default) + public async Task ExecuteServerCommandAsync(string target, string command, IAdbSocket socket, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken = default) { - EnsureDevice(device); - - using IAdbSocket socket = adbSocketFactory(EndPoint); - cancellationToken.Register(socket.Dispose); + StringBuilder request = new(); + if (!StringExtensions.IsNullOrWhiteSpace(target)) + { + _ = request.AppendFormat("{0}:", target); + } + _ = request.Append(command); - await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); - await socket.SendAdbRequestAsync($"shell:{command}", cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + await socket.SendAdbRequestAsync(request.ToString(), cancellationToken); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); 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 @@ -220,6 +225,17 @@ public async Task ExecuteRemoteCommandAsync(string command, DeviceData device, I } } + /// + public async Task ExecuteRemoteCommandAsync(string command, DeviceData device, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken = default) + { + EnsureDevice(device); + + using IAdbSocket socket = adbSocketFactory(EndPoint); + await socket.SetDeviceAsync(device, cancellationToken); + + await ExecuteServerCommandAsync("shell", command, socket, receiver, encoding, cancellationToken); + } + /// public async Task GetFrameBufferAsync(DeviceData device, CancellationToken cancellationToken = default) { @@ -669,7 +685,7 @@ public async Task DumpScreenStringAsync(DeviceData device, CancellationT { return xmlString; } - Match xmlMatch = GetXMLRegex().Match(xmlString); + Match xmlMatch = GetXmlRegex().Match(xmlString); return !xmlMatch.Success ? throw new XmlException("An error occurred while receiving xml: " + xmlString) : xmlMatch.Value; } @@ -789,7 +805,7 @@ public async Task SwipeAsync(DeviceData device, int x1, int y1, int x2, int y2, public async Task IsCurrentAppAsync(DeviceData device, string packageName, CancellationToken cancellationToken = default) { ConsoleOutputReceiver receiver = new(); - await ExecuteRemoteCommandAsync($"dumpsys activity activities | grep mResumedActivity", device, receiver, cancellationToken).ConfigureAwait(false); + await ExecuteRemoteCommandAsync($"dumpsys activity activities | grep mResumedActivity", device, receiver, Encoding, cancellationToken).ConfigureAwait(false); string response = receiver.ToString().Trim(); return response.ToString().Contains(packageName); } @@ -798,7 +814,7 @@ public async Task IsCurrentAppAsync(DeviceData device, string packageName, public async Task IsAppRunningAsync(DeviceData device, string packageName, CancellationToken cancellationToken = default) { ConsoleOutputReceiver receiver = new(); - await ExecuteRemoteCommandAsync($"pidof {packageName}", device, receiver, cancellationToken).ConfigureAwait(false); + await ExecuteRemoteCommandAsync($"pidof {packageName}", device, receiver, Encoding, cancellationToken).ConfigureAwait(false); string response = receiver.ToString().Trim(); bool intParsed = int.TryParse(response, out int pid); return intParsed && pid > 0; @@ -984,16 +1000,16 @@ public async Task SendTextAsync(DeviceData device, string text, CancellationToke public async Task ClearInputAsync(DeviceData device, int charCount, CancellationToken cancellationToken = default) { await SendKeyEventAsync(device, "KEYCODE_MOVE_END", cancellationToken).ConfigureAwait(false); - await ExecuteRemoteCommandAsync("input keyevent " + StringExtensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL ", charCount)), device, null, cancellationToken).ConfigureAwait(false); + await ExecuteRemoteCommandAsync("input keyevent " + StringExtensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL ", charCount)), device, null, Encoding, cancellationToken).ConfigureAwait(false); } /// public Task StartAppAsync(DeviceData device, string packageName, CancellationToken cancellationToken = default) => - ExecuteRemoteCommandAsync($"monkey -p {packageName} 1", device, null, cancellationToken); + ExecuteRemoteCommandAsync($"monkey -p {packageName} 1", device, null, Encoding, cancellationToken); /// public Task StopAppAsync(DeviceData device, string packageName, CancellationToken cancellationToken = default) => - ExecuteRemoteCommandAsync($"am force-stop {packageName}", device, null, cancellationToken); + ExecuteRemoteCommandAsync($"am force-stop {packageName}", device, null, Encoding, cancellationToken); /// public Task BackBtnAsync(DeviceData device, CancellationToken cancellationToken = default) => SendKeyEventAsync(device, "KEYCODE_BACK", cancellationToken); diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index 07a80fd9..ccdda938 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -23,7 +23,7 @@ namespace AdvancedSharpAdbClient /// adb server and devices that are connected to that adb server. /// For example, to fetch a list of all devices that are currently connected to this PC, you can /// call the method. - /// To run a command on a device, you can use the + /// To run a command on a device, you can use the /// method. /// /// @@ -192,7 +192,7 @@ public IEnumerable GetDevices() { using IAdbSocket socket = adbSocketFactory(EndPoint); socket.SendAdbRequest("host:devices-l"); - socket.ReadAdbResponse(); + _ = socket.ReadAdbResponse(); string reply = socket.ReadString(); string[] data = reply.Split(separator, StringSplitOptions.RemoveEmptyEntries); @@ -317,19 +317,24 @@ public IEnumerable ListReverseForward(DeviceData device) } /// - public void ExecuteRemoteCommand(string command, DeviceData device, IShellOutputReceiver receiver) => - ExecuteRemoteCommand(command, device, receiver, Encoding); + public void ExecuteServerCommand(string target, string command, IShellOutputReceiver receiver, Encoding encoding) + { + using IAdbSocket socket = adbSocketFactory(EndPoint); + ExecuteServerCommand(target, command, socket, receiver, encoding); + } /// - public void ExecuteRemoteCommand(string command, DeviceData device, IShellOutputReceiver receiver, Encoding encoding) + public void ExecuteServerCommand(string target, string command, IAdbSocket socket, IShellOutputReceiver receiver, Encoding encoding) { - EnsureDevice(device); - - using IAdbSocket socket = adbSocketFactory(EndPoint); + StringBuilder request = new(); + if (!StringExtensions.IsNullOrWhiteSpace(target)) + { + _ = request.AppendFormat("{0}:", target); + } + _ = request.Append(command); - socket.SetDevice(device); - socket.SendAdbRequest($"shell:{command}"); - AdbResponse response = socket.ReadAdbResponse(); + socket.SendAdbRequest(request.ToString()); + _ = socket.ReadAdbResponse(); try { @@ -357,6 +362,17 @@ public void ExecuteRemoteCommand(string command, DeviceData device, IShellOutput } } + /// + public void ExecuteRemoteCommand(string command, DeviceData device, IShellOutputReceiver receiver, Encoding encoding) + { + EnsureDevice(device); + + using IAdbSocket socket = adbSocketFactory(EndPoint); + socket.SetDevice(device); + + ExecuteServerCommand("shell", command, socket, receiver, encoding); + } + /// public Framebuffer CreateRefreshableFramebuffer(DeviceData device) { @@ -798,7 +814,7 @@ public string DumpScreenString(DeviceData device) { return xmlString; } - Match xmlMatch = GetXMLRegex().Match(xmlString); + Match xmlMatch = GetXmlRegex().Match(xmlString); return !xmlMatch.Success ? throw new XmlException("An error occurred while receiving xml: " + xmlString) : xmlMatch.Value; } @@ -918,7 +934,7 @@ public void Swipe(DeviceData device, int x1, int y1, int x2, int y2, long speed) public bool IsCurrentApp(DeviceData device, string packageName) { ConsoleOutputReceiver receiver = new(); - ExecuteRemoteCommand($"dumpsys activity activities | grep mResumedActivity", device, receiver); + ExecuteRemoteCommand($"dumpsys activity activities | grep mResumedActivity", device, receiver, Encoding); string response = receiver.ToString().Trim(); return response.ToString().Contains(packageName); } @@ -927,7 +943,7 @@ public bool IsCurrentApp(DeviceData device, string packageName) public bool IsAppRunning(DeviceData device, string packageName) { ConsoleOutputReceiver receiver = new(); - ExecuteRemoteCommand($"pidof {packageName}", device, receiver); + ExecuteRemoteCommand($"pidof {packageName}", device, receiver, Encoding); string response = receiver.ToString().Trim(); bool intParsed = int.TryParse(response, out int pid); return intParsed && pid > 0; @@ -1062,16 +1078,16 @@ public void SendText(DeviceData device, string text) public void ClearInput(DeviceData device, int charCount) { SendKeyEvent(device, "KEYCODE_MOVE_END"); - ExecuteRemoteCommand("input keyevent " + StringExtensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL ", charCount)), device, null); + ExecuteRemoteCommand("input keyevent " + StringExtensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL ", charCount)), device, null, Encoding); } /// public void StartApp(DeviceData device, string packageName) => - ExecuteRemoteCommand($"monkey -p {packageName} 1", device, null); + ExecuteRemoteCommand($"monkey -p {packageName} 1", device, null, Encoding); /// public void StopApp(DeviceData device, string packageName) => - ExecuteRemoteCommand($"am force-stop {packageName}", device, null); + ExecuteRemoteCommand($"am force-stop {packageName}", device, null, Encoding); /// public void BackBtn(DeviceData device) => SendKeyEvent(device, "KEYCODE_BACK"); @@ -1103,9 +1119,9 @@ protected static void EnsureDevice(DeviceData device) #if NET7_0_OR_GREATER [GeneratedRegex("<\\?xml(.?)*")] - private static partial Regex GetXMLRegex(); + private static partial Regex GetXmlRegex(); #else - private static Regex GetXMLRegex() => new("<\\?xml(.?)*"); + private static Regex GetXmlRegex() => new("<\\?xml(.?)*"); #endif } diff --git a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs index e3a9cf52..0c7d15e8 100644 --- a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs +++ b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs @@ -6,12 +6,52 @@ using AdvancedSharpAdbClient.Exceptions; using System; using System.Net; +using System.Text; using System.Threading; namespace AdvancedSharpAdbClient { public static partial class AdbClientExtensions { + /// + /// Executes a command on the adb server. + /// + /// An instance of a class that implements the interface. + /// The target of command, such as shell, remount, dev, tcp, local, + /// localreserved, localabstract, jdwp, track-jdwp, sync, reverse and so on. + /// The command to execute. + /// Optionally, a that processes the command output. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + public static Task ExecuteServerCommandAsync(this IAdbClient client, string target, string command, IShellOutputReceiver receiver, CancellationToken cancellationToken=default)=> + client.ExecuteServerCommandAsync(target, command, receiver, AdbClient.Encoding, cancellationToken); + + /// + /// Executes a command on the adb server. + /// + /// An instance of a class that implements the interface. + /// The target of command, such as shell, remount, dev, tcp, local, + /// localreserved, localabstract, jdwp, track-jdwp, sync, reverse and so on. + /// The command to execute. + /// The to send command. + /// Optionally, a that processes the command output. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + public static Task ExecuteServerCommandAsync(this IAdbClient client, string target, string command, IAdbSocket socket, IShellOutputReceiver receiver, CancellationToken cancellationToken = default) => + client.ExecuteServerCommandAsync(target, command, socket, receiver, AdbClient.Encoding, cancellationToken); + + /// + /// Executes a command on the device. + /// + /// An instance of a class that implements the interface. + /// The command to execute. + /// The device on which to run the command. + /// Optionally, a that processes the command output. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + public static Task ExecuteRemoteCommandAsync(this IAdbClient client, string command, DeviceData device, IShellOutputReceiver receiver, CancellationToken cancellationToken = default) => + client.ExecuteRemoteCommandAsync(command, device, receiver, AdbClient.Encoding, cancellationToken); + /// /// Creates a port forwarding between a local and a remote port. /// diff --git a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.cs b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.cs index ff149bac..bd034d12 100644 --- a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.cs +++ b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.cs @@ -13,6 +13,39 @@ namespace AdvancedSharpAdbClient /// public static partial class AdbClientExtensions { + /// + /// Executes a command on the adb server. + /// + /// An instance of a class that implements the interface. + /// The target of command, such as shell, remount, dev, tcp, local, + /// localreserved, localabstract, jdwp, track-jdwp, sync, reverse and so on. + /// The command to execute. + /// Optionally, a that processes the command output. + public static void ExecuteServerCommand(this IAdbClient client, string target, string command, IShellOutputReceiver receiver) => + client.ExecuteServerCommand(target, command, receiver, AdbClient.Encoding); + + /// + /// Executes a command on the adb server. + /// + /// An instance of a class that implements the interface. + /// The target of command, such as shell, remount, dev, tcp, local, + /// localreserved, localabstract, jdwp, track-jdwp, sync, reverse and so on. + /// The command to execute. + /// The to send command. + /// Optionally, a that processes the command output. + public static void ExecuteServerCommand(this IAdbClient client, string target, string command, IAdbSocket socket, IShellOutputReceiver receiver) => + client.ExecuteServerCommand(target, command, socket, receiver, AdbClient.Encoding); + + /// + /// Executes a shell command on the device. + /// + /// An instance of a class that implements the interface. + /// The command to execute. + /// The device on which to run the command. + /// Optionally, a that processes the command output. + public static void ExecuteRemoteCommand(this IAdbClient client, string command, DeviceData device, IShellOutputReceiver receiver) => + client.ExecuteRemoteCommand(command, device, receiver, AdbClient.Encoding); + /// /// Creates a port forwarding between a local and a remote port. /// diff --git a/AdvancedSharpAdbClient/Extensions/SocketExtensions.cs b/AdvancedSharpAdbClient/Extensions/SocketExtensions.cs index 3964de52..335fbbd5 100644 --- a/AdvancedSharpAdbClient/Extensions/SocketExtensions.cs +++ b/AdvancedSharpAdbClient/Extensions/SocketExtensions.cs @@ -39,7 +39,11 @@ public static Task ReceiveAsync(this Socket socket, byte[] buffer, SocketFl /// Cancelling the task will also close the socket. /// The number of bytes received. public static Task ReceiveAsync(this Socket socket, byte[] buffer, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) +#if HAS_BUFFERS + => socket.ReceiveAsync(buffer.AsMemory(0, size), socketFlags, cancellationToken).AsTask(); +#else => socket.ReceiveAsync(buffer, 0, size, socketFlags, cancellationToken); +#endif /// /// Asynchronously receives data from a connected socket. @@ -107,7 +111,7 @@ public static Task ReceiveAsync(this Socket socket, byte[] buffer, int offs /// Cancelling the task will also close the socket. /// The number of bytes received. public static Task SendAsync(this Socket socket, byte[] buffer, SocketFlags socketFlags, CancellationToken cancellationToken = default) - => SendAsync(socket, buffer, 0, buffer.Length, socketFlags, cancellationToken); + => socket.SendAsync(buffer, 0, buffer.Length, socketFlags, cancellationToken); #endif /// @@ -121,7 +125,11 @@ public static Task SendAsync(this Socket socket, byte[] buffer, SocketFlags /// Cancelling the task will also close the socket. /// The number of bytes received. public static Task SendAsync(this Socket socket, byte[] buffer, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) - => SendAsync(socket, buffer, 0, size, socketFlags, cancellationToken); +#if HAS_BUFFERS + => socket.SendAsync(buffer.AsMemory( 0, size), socketFlags, cancellationToken).AsTask(); +#else + => socket.SendAsync(buffer, 0, size, socketFlags, cancellationToken); +#endif /// /// Asynchronously sends data to a connected socket. @@ -178,6 +186,14 @@ public static Task SendAsync(this Socket socket, byte[] buffer, int offset, } #endif +#if !HAS_PROCESS + /// + /// Closes the connection and releases all associated resources. + /// + /// The to release. + public static void Close(this Socket socket) => socket.Dispose(); +#endif + #if NETFRAMEWORK && !NET40_OR_GREATER /// /// Releases all resources used by the current instance of the class. diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs b/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs index d321e899..da918c71 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs @@ -205,21 +205,36 @@ public partial interface IAdbClient Task> ListReverseForwardAsync(DeviceData device, CancellationToken cancellationToken); /// - /// Executes a command on the device. + /// Executes a command on the adb server. /// + /// The target of command, such as shell, remount, dev, tcp, local, + /// localreserved, localabstract, jdwp, track-jdwp, sync, reverse and so on. /// The command to execute. - /// The device on which to run the command. - /// The receiver which will get the command output. + /// Optionally, a that processes the command output. + /// The encoding to use when parsing the command output. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + Task ExecuteServerCommandAsync(string target, string command, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken); + + /// + /// Executes a command on the adb server. + /// + /// The target of command, such as shell, remount, dev, tcp, local, + /// localreserved, localabstract, jdwp, track-jdwp, sync, reverse and so on. + /// The command to execute. + /// The to send command. + /// Optionally, a that processes the command output. + /// The encoding to use when parsing the command output. /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. - Task ExecuteRemoteCommandAsync(string command, DeviceData device, IShellOutputReceiver receiver, CancellationToken cancellationToken); + Task ExecuteServerCommandAsync(string target, string command, IAdbSocket socket, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken); /// /// Executes a command on the device. /// /// The command to execute. /// The device on which to run the command. - /// The receiver which will get the command output. + /// Optionally, a that processes the command output. /// The encoding to use when parsing the command output. /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs b/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs index 7aa8927a..888fdfba 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs @@ -218,19 +218,32 @@ public partial interface IAdbClient IEnumerable ListReverseForward(DeviceData device); /// - /// Executes a command on the device. + /// Executes a command on the adb server. /// + /// The target of command, such as shell, remount, dev, tcp, local, + /// localreserved, localabstract, jdwp, track-jdwp, sync, reverse and so on. /// The command to execute. - /// The device on which to run the command. - /// The receiver which will get the command output. - void ExecuteRemoteCommand(string command, DeviceData device, IShellOutputReceiver receiver); + /// Optionally, a that processes the command output. + /// The encoding to use when parsing the command output. + void ExecuteServerCommand(string target, string command, IShellOutputReceiver receiver, Encoding encoding); + + /// + /// Executes a command on the adb server. + /// + /// The target of command, such as shell, remount, dev, tcp, local, + /// localreserved, localabstract, jdwp, track-jdwp, sync, reverse and so on. + /// The command to execute. + /// The to send command. + /// Optionally, a that processes the command output. + /// The encoding to use when parsing the command output. + void ExecuteServerCommand(string target, string command, IAdbSocket socket, IShellOutputReceiver receiver, Encoding encoding); /// - /// Executes a command on the device. + /// Executes a shell command on the device. /// /// The command to execute. /// The device on which to run the command. - /// The receiver which will get the command output. + /// Optionally, a that processes the command output. /// The encoding to use when parsing the command output. void ExecuteRemoteCommand(string command, DeviceData device, IShellOutputReceiver receiver, Encoding encoding); diff --git a/AdvancedSharpAdbClient/Receivers/IShellOutputReceiver.cs b/AdvancedSharpAdbClient/Receivers/IShellOutputReceiver.cs index b98a1454..4444d39a 100644 --- a/AdvancedSharpAdbClient/Receivers/IShellOutputReceiver.cs +++ b/AdvancedSharpAdbClient/Receivers/IShellOutputReceiver.cs @@ -2,11 +2,13 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // +using System.Text; + namespace AdvancedSharpAdbClient { /// /// This interface contains various receivers that are able to parse Android console output. You can use - /// the receivers in combination with the + /// the receivers in combination with the /// method to capture the output of any Android command. /// public interface IShellOutputReceiver diff --git a/AdvancedSharpAdbClient/TcpSocket.cs b/AdvancedSharpAdbClient/TcpSocket.cs index 7d2756a8..8a953ab3 100644 --- a/AdvancedSharpAdbClient/TcpSocket.cs +++ b/AdvancedSharpAdbClient/TcpSocket.cs @@ -85,12 +85,7 @@ public void Dispose() } /// - public virtual void Close() => -#if HAS_PROCESS - socket.Close(); -#else - socket.Dispose(); -#endif + public virtual void Close() => socket.Close(); /// public virtual int Send(byte[] buffer, int size, SocketFlags socketFlags) => From 9ba13238b304edae2e96e28756dae6f021082bc8 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Tue, 3 Oct 2023 02:08:12 +0800 Subject: [PATCH 23/66] Use IAdbSocket instead of ExecuteRemoteCommand Rename IsCurrentApp to IsAppRunning Add some tests --- .../AdbClientTests.Async.cs | 297 ++++++++++++++++- .../AdbClientTests.cs | 313 ++++++++++++++++-- .../Dummys/DummyAdbClient.cs | 4 +- AdvancedSharpAdbClient/AdbClient.Async.cs | 58 +++- AdvancedSharpAdbClient/AdbClient.cs | 58 +++- .../Interfaces/IAdbClient.Async.cs | 2 +- .../Interfaces/IAdbClient.cs | 2 +- 7 files changed, 655 insertions(+), 79 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs index 6defa126..a434a674 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs @@ -650,8 +650,7 @@ public async void RootAsyncTest() ]; byte[] expectedData = new byte[1024]; - byte[] expectedString = Encoding.UTF8.GetBytes("adbd cannot run as root in production builds\n"); - Buffer.BlockCopy(expectedString, 0, expectedData, 0, expectedString.Length); + "adbd cannot run as root in production builds\n"u8.CopyTo(expectedData); _ = await Assert.ThrowsAsync(() => RunTestAsync( @@ -684,8 +683,7 @@ public async void UnrootAsyncTest() ]; byte[] expectedData = new byte[1024]; - byte[] expectedString = Encoding.UTF8.GetBytes("adbd not running as root\n"); - Buffer.BlockCopy(expectedString, 0, expectedData, 0, expectedString.Length); + "adbd not running as root\n"u8.CopyTo(expectedData); _ = await Assert.ThrowsAsync(() => RunTestAsync( @@ -739,7 +737,7 @@ public async void InstallAsyncTest() } } - byte[] response = Encoding.UTF8.GetBytes("Success\n"); + byte[] response = "Success\n"u8.ToArray(); await using (FileStream stream = File.OpenRead("Assets/testapp.apk")) { @@ -827,7 +825,7 @@ public async void InstallWriteAsyncTest() } } - byte[] response = Encoding.UTF8.GetBytes("Success: streamed 205774 bytes\n"); + byte[] response = "Success: streamed 205774 bytes\n"u8.ToArray(); await using (FileStream stream = File.OpenRead("Assets/testapp.apk")) { @@ -985,16 +983,15 @@ public async void DumpScreenStringAsyncEmptyTest() "shell:uiautomator dump /dev/tty" ]; - byte[] emptyStreamData = Encoding.UTF8.GetBytes(string.Empty); - await using MemoryStream emptyStream = new(emptyStreamData); + await using MemoryStream emptyStream = new(); string emptyXml = string.Empty; await RunTestAsync( [AdbResponse.OK, AdbResponse.OK], - NoResponseMessages, - requests, - emptyStream, - async () => emptyXml = await TestClient.DumpScreenStringAsync(device)); + NoResponseMessages, + requests, + emptyStream, + async () => emptyXml = await TestClient.DumpScreenStringAsync(device)); Assert.True(string.IsNullOrEmpty(emptyXml)); } @@ -1085,7 +1082,7 @@ public async void ClickAsyncTest() "shell:input tap 100 100" ]; - byte[] streamData = Encoding.UTF8.GetBytes(@"java.lang.SecurityException: Injecting to another application requires INJECT_EVENTS permission + byte[] streamData = @"java.lang.SecurityException: Injecting to another application requires INJECT_EVENTS permission at android.os.Parcel.createExceptionOrNull(Parcel.java:2373) at android.os.Parcel.createException(Parcel.java:2357) at android.os.Parcel.readException(Parcel.java:2340) @@ -1106,7 +1103,7 @@ at com.android.server.input.InputManagerService.injectInputEventInternal(InputMa at com.android.server.input.InputManagerService.injectInputEvent(InputManagerService.java:651) at android.hardware.input.IInputManager$Stub.onTransact(IInputManager.java:430) at android.os.Binder.execTransactInternal(Binder.java:1165) - at android.os.Binder.execTransact(Binder.java:1134)"); + at android.os.Binder.execTransact(Binder.java:1134)"u8.ToArray(); await using MemoryStream shellStream = new(streamData); JavaException exception = await Assert.ThrowsAsync(() => @@ -1160,7 +1157,7 @@ public async void ClickCordsAsyncTest() "shell:input tap 100 100" ]; - byte[] streamData = Encoding.UTF8.GetBytes(@"Error: Injecting to another application requires INJECT_EVENTS permission"); + byte[] streamData = "Error: Injecting to another application requires INJECT_EVENTS permission\r\n"u8.ToArray(); await using MemoryStream shellStream = new(streamData); _ = await Assert.ThrowsAsync(() => @@ -1172,6 +1169,108 @@ public async void ClickCordsAsyncTest() () => TestClient.ClickAsync(device, new Cords(100, 100)))); } + /// + /// Tests the method. + /// + [Fact] + public async void SwipeAsyncTest() + { + DeviceData device = new() + { + Serial = "009d1cd696d5194a", + State = DeviceState.Online + }; + + string[] requests = + [ + "host:transport:009d1cd696d5194a", + "shell:input swipe 100 200 300 400 500" + ]; + + await using MemoryStream shellStream = new(); + + await RunTestAsync( + [AdbResponse.OK, AdbResponse.OK], + NoResponseMessages, + requests, + shellStream, + () => TestClient.SwipeAsync(device, 100, 200, 300, 400, 500)); + } + + /// + /// Tests the method. + /// + [Theory] + [InlineData("21216 27761\r\n", true)] + [InlineData(" 21216 27761\r\n", true)] + [InlineData(" \r\n", false)] + [InlineData("\r\n", false)] + [InlineData(" ", false)] + [InlineData("", false)] + public async void IsAppRunningAsyncTest(string response, bool expected) + { + DeviceData device = new() + { + Serial = "009d1cd696d5194a", + State = DeviceState.Online + }; + + string[] requests = + [ + "host:transport:009d1cd696d5194a", + "shell:pidof com.google.android.gms" + ]; + + byte[] streamData = Encoding.UTF8.GetBytes(response); + await using MemoryStream shellStream = new(streamData); + + bool result = !expected; + await RunTestAsync( + [AdbResponse.OK, AdbResponse.OK], + NoResponseMessages, + requests, + shellStream, + async () => result = await TestClient.IsAppRunningAsync(device, "com.google.android.gms")); + + Assert.Equal(expected, result); + } + + /// + /// Tests the method. + /// + [Theory] + [InlineData("app.lawnchair", true)] + [InlineData("com.android.settings", true)] + [InlineData("com.google.android.gms", false)] + public async void IsAppInForegroundAsyncTest(string packageName, bool expected) + { + DeviceData device = new() + { + Serial = "009d1cd696d5194a", + State = DeviceState.Online + }; + + string[] requests = + [ + "host:transport:009d1cd696d5194a", + "shell:dumpsys activity activities | grep mResumedActivity" + ]; + + byte[] streamData = @" mResumedActivity: ActivityRecord{1f5309a u0 com.android.settings/.homepage.SettingsHomepageActivity t61029} + mResumedActivity: ActivityRecord{896cc3 u0 app.lawnchair/.LawnchairLauncher t5}"u8.ToArray(); + await using MemoryStream shellStream = new(streamData); + + bool result = !expected; + await RunTestAsync( + [AdbResponse.OK, AdbResponse.OK], + NoResponseMessages, + requests, + shellStream, + async () => result = await TestClient.IsAppInForegroundAsync(device, packageName)); + + Assert.Equal(expected, result); + } + /// /// Tests the method. /// @@ -1291,6 +1390,174 @@ await RunTestAsync( Assert.Equal(Area.FromLTRB(45, 889, 427, 973), element.Area); } + /// + /// Tests the method. + /// + [Fact] + public async void SendKeyEventAsyncTest() + { + DeviceData device = new() + { + Serial = "009d1cd696d5194a", + State = DeviceState.Online + }; + + string[] requests = + [ + "host:transport:009d1cd696d5194a", + "shell:input keyevent KEYCODE_MOVE_END" + ]; + + await using MemoryStream shellStream = new(); + + await RunTestAsync( + [AdbResponse.OK, AdbResponse.OK], + NoResponseMessages, + requests, + shellStream, + () => TestClient.SendKeyEventAsync(device, "KEYCODE_MOVE_END")); + } + + /// + /// Tests the method. + /// + [Fact] + public async void SendTextAsyncTest() + { + DeviceData device = new() + { + Serial = "009d1cd696d5194a", + State = DeviceState.Online + }; + + string[] requests = + [ + "host:transport:009d1cd696d5194a", + "shell:input text Hello, World", + ]; + + await using MemoryStream shellStream = new(); + + await RunTestAsync( + [AdbResponse.OK, AdbResponse.OK], + NoResponseMessages, + requests, + shellStream, + () => TestClient.SendTextAsync(device, "Hello, World")); + } + + /// + /// Tests the method. + /// + [Fact] + public async void StartAppAsyncTest() + { + DeviceData device = new() + { + Serial = "009d1cd696d5194a", + State = DeviceState.Online + }; + + string[] requests = + [ + "host:transport:009d1cd696d5194a", + "shell:monkey -p com.android.settings 1", + ]; + + await using MemoryStream shellStream = new(); + + await RunTestAsync( + [AdbResponse.OK, AdbResponse.OK], + NoResponseMessages, + requests, + shellStream, + () => TestClient.StartAppAsync(device, "com.android.settings")); + } + + /// + /// Tests the method. + /// + [Fact] + public async void StopAppAsyncTest() + { + DeviceData device = new() + { + Serial = "009d1cd696d5194a", + State = DeviceState.Online + }; + + string[] requests = + [ + "host:transport:009d1cd696d5194a", + "shell:am force-stop com.android.settings", + ]; + + await using MemoryStream shellStream = new(); + + await RunTestAsync( + [AdbResponse.OK, AdbResponse.OK], + NoResponseMessages, + requests, + shellStream, + () => TestClient.StopAppAsync(device, "com.android.settings")); + } + + /// + /// Tests the method. + /// + [Fact] + public async void BackBtnAsyncTest() + { + DeviceData device = new() + { + Serial = "009d1cd696d5194a", + State = DeviceState.Online + }; + + string[] requests = + [ + "host:transport:009d1cd696d5194a", + "shell:input keyevent KEYCODE_BACK" + ]; + + await using MemoryStream shellStream = new(); + + await RunTestAsync( + [AdbResponse.OK, AdbResponse.OK], + NoResponseMessages, + requests, + shellStream, + () => TestClient.BackBtnAsync(device)); + } + + /// + /// Tests the method. + /// + [Fact] + public async void HomeBtnAsyncTest() + { + DeviceData device = new() + { + Serial = "009d1cd696d5194a", + State = DeviceState.Online + }; + + string[] requests = + [ + "host:transport:009d1cd696d5194a", + "shell:input keyevent KEYCODE_HOME" + ]; + + await using MemoryStream shellStream = new(); + + await RunTestAsync( + [AdbResponse.OK, AdbResponse.OK], + NoResponseMessages, + requests, + shellStream, + () => TestClient.HomeBtnAsync(device)); + } + private Task RunConnectAsyncTest(Func test, string connectString) { string[] requests = [$"host:connect:{connectString}"]; diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs index 4408be74..f2280cf1 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs @@ -395,8 +395,6 @@ public void ExecuteRemoteCommandTest() AdbResponse.OK ]; - string[] responseMessages = []; - string[] requests = [ "host:transport:169.254.109.177:5555", @@ -410,7 +408,7 @@ public void ExecuteRemoteCommandTest() RunTest( responses, - responseMessages, + NoResponseMessages, requests, shellStream, () => TestClient.ExecuteRemoteCommand("echo Hello, World", device, receiver)); @@ -436,8 +434,6 @@ public void ExecuteRemoteCommandUnresponsiveTest() AdbResponse.OK ]; - string[] responseMessages = []; - string[] requests = [ "host:transport:169.254.109.177:5555", @@ -449,7 +445,7 @@ public void ExecuteRemoteCommandUnresponsiveTest() _ = Assert.Throws(() => RunTest( responses, - responseMessages, + NoResponseMessages, requests, null, () => TestClient.ExecuteRemoteCommand("echo Hello, World", device, receiver))); @@ -552,8 +548,6 @@ public void RunLogServiceTest() AdbResponse.OK ]; - string[] responseMessages = []; - string[] requests = [ "host:transport:169.254.109.177:5555", @@ -569,7 +563,7 @@ public void RunLogServiceTest() RunTest( responses, - responseMessages, + NoResponseMessages, requests, shellStream, () => TestClient.RunLogService(device, sink, LogId.System)); @@ -763,8 +757,7 @@ public void RootTest() ]; byte[] expectedData = new byte[1024]; - byte[] expectedString = Encoding.UTF8.GetBytes("adbd cannot run as root in production builds\n"); - Buffer.BlockCopy(expectedString, 0, expectedData, 0, expectedString.Length); + "adbd cannot run as root in production builds\n"u8.CopyTo(expectedData); _ = Assert.Throws(() => RunTest( @@ -797,8 +790,7 @@ public void UnrootTest() ]; byte[] expectedData = new byte[1024]; - byte[] expectedString = Encoding.UTF8.GetBytes("adbd not running as root\n"); - Buffer.BlockCopy(expectedString, 0, expectedData, 0, expectedString.Length); + "adbd not running as root\n"u8.CopyTo(expectedData); _ = Assert.Throws(() => RunTest( @@ -852,7 +844,7 @@ public void InstallTest() } } - byte[] response = Encoding.UTF8.GetBytes("Success\n"); + byte[] response = "Success\n"u8.ToArray(); using (FileStream stream = File.OpenRead("Assets/testapp.apk")) { @@ -940,7 +932,7 @@ public void InstallWriteTest() } } - byte[] response = Encoding.UTF8.GetBytes("Success: streamed 205774 bytes\n"); + byte[] response = "Success: streamed 205774 bytes\n"u8.ToArray(); using (FileStream stream = File.OpenRead("Assets/testapp.apk")) { @@ -1198,7 +1190,7 @@ public void ClickTest() "shell:input tap 100 100" ]; - byte[] streamData = Encoding.UTF8.GetBytes(@"java.lang.SecurityException: Injecting to another application requires INJECT_EVENTS permission + byte[] streamData = @"java.lang.SecurityException: Injecting to another application requires INJECT_EVENTS permission at android.os.Parcel.createExceptionOrNull(Parcel.java:2373) at android.os.Parcel.createException(Parcel.java:2357) at android.os.Parcel.readException(Parcel.java:2340) @@ -1219,7 +1211,7 @@ at com.android.server.input.InputManagerService.injectInputEventInternal(InputMa at com.android.server.input.InputManagerService.injectInputEvent(InputManagerService.java:651) at android.hardware.input.IInputManager$Stub.onTransact(IInputManager.java:430) at android.os.Binder.execTransactInternal(Binder.java:1165) - at android.os.Binder.execTransact(Binder.java:1134)"); + at android.os.Binder.execTransact(Binder.java:1134)"u8.ToArray(); using MemoryStream shellStream = new(streamData); JavaException exception = Assert.Throws(() => @@ -1273,7 +1265,7 @@ public void ClickCordsTest() "shell:input tap 100 100" ]; - byte[] streamData = Encoding.UTF8.GetBytes(@"Error: Injecting to another application requires INJECT_EVENTS permission"); + byte[] streamData = "Error: Injecting to another application requires INJECT_EVENTS permission\r\n"u8.ToArray(); using MemoryStream shellStream = new(streamData); _ = Assert.Throws(() => @@ -1285,6 +1277,108 @@ public void ClickCordsTest() () => TestClient.Click(device, new Cords(100, 100)))); } + /// + /// Tests the method. + /// + [Fact] + public void SwipeTest() + { + DeviceData device = new() + { + Serial = "009d1cd696d5194a", + State = DeviceState.Online + }; + + string[] requests = + [ + "host:transport:009d1cd696d5194a", + "shell:input swipe 100 200 300 400 500" + ]; + + using MemoryStream shellStream = new(); + + RunTest( + [AdbResponse.OK, AdbResponse.OK], + NoResponseMessages, + requests, + shellStream, + () => TestClient.Swipe(device, 100, 200, 300, 400, 500)); + } + + /// + /// Tests the method. + /// + [Theory] + [InlineData("21216 27761\r\n", true)] + [InlineData(" 21216 27761\r\n", true)] + [InlineData(" \r\n", false)] + [InlineData("\r\n", false)] + [InlineData(" ", false)] + [InlineData("", false)] + public void IsAppRunningTest(string response, bool expected) + { + DeviceData device = new() + { + Serial = "009d1cd696d5194a", + State = DeviceState.Online + }; + + string[] requests = + [ + "host:transport:009d1cd696d5194a", + "shell:pidof com.google.android.gms" + ]; + + byte[] streamData = Encoding.UTF8.GetBytes(response); + using MemoryStream shellStream = new(streamData); + + bool result = !expected; + RunTest( + [AdbResponse.OK, AdbResponse.OK], + NoResponseMessages, + requests, + shellStream, + () => result = TestClient.IsAppRunning(device, "com.google.android.gms")); + + Assert.Equal(expected, result); + } + + /// + /// Tests the method. + /// + [Theory] + [InlineData("app.lawnchair", true)] + [InlineData("com.android.settings", true)] + [InlineData("com.google.android.gms", false)] + public void IsAppInForegroundTest(string packageName, bool expected) + { + DeviceData device = new() + { + Serial = "009d1cd696d5194a", + State = DeviceState.Online + }; + + string[] requests = + [ + "host:transport:009d1cd696d5194a", + "shell:dumpsys activity activities | grep mResumedActivity" + ]; + + byte[] streamData = @" mResumedActivity: ActivityRecord{1f5309a u0 com.android.settings/.homepage.SettingsHomepageActivity t61029} + mResumedActivity: ActivityRecord{896cc3 u0 app.lawnchair/.LawnchairLauncher t5}"u8.ToArray(); + using MemoryStream shellStream = new(streamData); + + bool result = !expected; + RunTest( + [AdbResponse.OK, AdbResponse.OK], + NoResponseMessages, + requests, + shellStream, + () => result = TestClient.IsAppInForeground(device, packageName)); + + Assert.Equal(expected, result); + } + /// /// Tests the method. /// @@ -1307,19 +1401,18 @@ public void FindElementTest() byte[] streamData = Encoding.UTF8.GetBytes(dump); using MemoryStream shellStream = new(streamData); + Element element = null; RunTest( [AdbResponse.OK, AdbResponse.OK], NoResponseMessages, requests, shellStream, - () => - { - Element element = TestClient.FindElement(device); - Assert.Equal(144, element.GetChildCount()); - element = element[0][0][0][0][0][0][0][0][2][1][0][0]; - Assert.Equal("where-where", element.Attributes["text"]); - Assert.Equal(Area.FromLTRB(45, 889, 427, 973), element.Area); - }); + () => element = TestClient.FindElement(device)); + + Assert.Equal(144, element.GetChildCount()); + element = element[0][0][0][0][0][0][0][0][2][1][0][0]; + Assert.Equal("where-where", element.Attributes["text"]); + Assert.Equal(Area.FromLTRB(45, 889, 427, 973), element.Area); } /// @@ -1361,6 +1454,174 @@ public void FindElementsTest() }); } + /// + /// Tests the method. + /// + [Fact] + public void SendKeyEventTest() + { + DeviceData device = new() + { + Serial = "009d1cd696d5194a", + State = DeviceState.Online + }; + + string[] requests = + [ + "host:transport:009d1cd696d5194a", + "shell:input keyevent KEYCODE_MOVE_END" + ]; + + using MemoryStream shellStream = new(); + + RunTest( + [AdbResponse.OK, AdbResponse.OK], + NoResponseMessages, + requests, + shellStream, + () => TestClient.SendKeyEvent(device, "KEYCODE_MOVE_END")); + } + + /// + /// Tests the method. + /// + [Fact] + public void SendTextTest() + { + DeviceData device = new() + { + Serial = "009d1cd696d5194a", + State = DeviceState.Online + }; + + string[] requests = + [ + "host:transport:009d1cd696d5194a", + "shell:input text Hello, World", + ]; + + using MemoryStream shellStream = new(); + + RunTest( + [AdbResponse.OK, AdbResponse.OK], + NoResponseMessages, + requests, + shellStream, + () => TestClient.SendText(device, "Hello, World")); + } + + /// + /// Tests the method. + /// + [Fact] + public void StartAppTest() + { + DeviceData device = new() + { + Serial = "009d1cd696d5194a", + State = DeviceState.Online + }; + + string[] requests = + [ + "host:transport:009d1cd696d5194a", + "shell:monkey -p com.android.settings 1", + ]; + + using MemoryStream shellStream = new(); + + RunTest( + [AdbResponse.OK, AdbResponse.OK], + NoResponseMessages, + requests, + shellStream, + () => TestClient.StartApp(device, "com.android.settings")); + } + + /// + /// Tests the method. + /// + [Fact] + public void StopAppTest() + { + DeviceData device = new() + { + Serial = "009d1cd696d5194a", + State = DeviceState.Online + }; + + string[] requests = + [ + "host:transport:009d1cd696d5194a", + "shell:am force-stop com.android.settings", + ]; + + using MemoryStream shellStream = new(); + + RunTest( + [AdbResponse.OK, AdbResponse.OK], + NoResponseMessages, + requests, + shellStream, + () => TestClient.StopApp(device, "com.android.settings")); + } + + /// + /// Tests the method. + /// + [Fact] + public void BackBtnTest() + { + DeviceData device = new() + { + Serial = "009d1cd696d5194a", + State = DeviceState.Online + }; + + string[] requests = + [ + "host:transport:009d1cd696d5194a", + "shell:input keyevent KEYCODE_BACK" + ]; + + using MemoryStream shellStream = new(); + + RunTest( + [AdbResponse.OK, AdbResponse.OK], + NoResponseMessages, + requests, + shellStream, + () => TestClient.BackBtn(device)); + } + + /// + /// Tests the method. + /// + [Fact] + public void HomeBtnTest() + { + DeviceData device = new() + { + Serial = "009d1cd696d5194a", + State = DeviceState.Online + }; + + string[] requests = + [ + "host:transport:009d1cd696d5194a", + "shell:input keyevent KEYCODE_HOME" + ]; + + using MemoryStream shellStream = new(); + + RunTest( + [AdbResponse.OK, AdbResponse.OK], + NoResponseMessages, + requests, + shellStream, + () => TestClient.HomeBtn(device)); + } + private void RunConnectTest(Action test, string connectString) { string[] requests = [$"host:connect:{connectString}"]; diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs index 5020f354..07e3da4e 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs @@ -212,9 +212,9 @@ public Task ExecuteServerCommandAsync(string target, string command, IAdbSocket public Task IsAppRunningAsync(DeviceData device, string packageName, CancellationToken cancellationToken) => throw new NotImplementedException(); - public bool IsCurrentApp(DeviceData device, string packageName) => throw new NotImplementedException(); + public bool IsAppInForeground(DeviceData device, string packageName) => throw new NotImplementedException(); - public Task IsCurrentAppAsync(DeviceData device, string packageName, CancellationToken cancellationToken) => throw new NotImplementedException(); + public Task IsAppInForegroundAsync(DeviceData device, string packageName, CancellationToken cancellationToken) => throw new NotImplementedException(); public void KillAdb() => throw new NotImplementedException(); diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index 3ed595eb..789bba32 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -802,29 +802,39 @@ public async Task SwipeAsync(DeviceData device, int x1, int y1, int x2, int y2, } /// - public async Task IsCurrentAppAsync(DeviceData device, string packageName, CancellationToken cancellationToken = default) + public async Task IsAppRunningAsync(DeviceData device, string packageName, CancellationToken cancellationToken = default) { - ConsoleOutputReceiver receiver = new(); - await ExecuteRemoteCommandAsync($"dumpsys activity activities | grep mResumedActivity", device, receiver, Encoding, cancellationToken).ConfigureAwait(false); - string response = receiver.ToString().Trim(); - return response.ToString().Contains(packageName); + EnsureDevice(device); + + using IAdbSocket socket = adbSocketFactory(EndPoint); + await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); + await socket.SendAdbRequestAsync($"shell:pidof {packageName}", cancellationToken).ConfigureAwait(false); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + using StreamReader reader = new(socket.GetShellStream(), Encoding); + string result = (await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false)).TrimStart().Split(' ', StringSplitOptions.RemoveEmptyEntries).FirstOrDefault(); + bool intParsed = int.TryParse(result, out int pid); + return intParsed && pid > 0; } /// - public async Task IsAppRunningAsync(DeviceData device, string packageName, CancellationToken cancellationToken = default) + public async Task IsAppInForegroundAsync(DeviceData device, string packageName, CancellationToken cancellationToken = default) { - ConsoleOutputReceiver receiver = new(); - await ExecuteRemoteCommandAsync($"pidof {packageName}", device, receiver, Encoding, cancellationToken).ConfigureAwait(false); - string response = receiver.ToString().Trim(); - bool intParsed = int.TryParse(response, out int pid); - return intParsed && pid > 0; + EnsureDevice(device); + + using IAdbSocket socket = adbSocketFactory(EndPoint); + await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); + await socket.SendAdbRequestAsync($"shell:dumpsys activity activities | grep mResumedActivity", cancellationToken).ConfigureAwait(false); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + using StreamReader reader = new(socket.GetShellStream(), Encoding); + string result = await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false); + return result.Contains(packageName); } /// public async Task GetAppStatusAsync(DeviceData device, string packageName, CancellationToken cancellationToken = default) { // Check if the app is in foreground - bool currentApp = await IsCurrentAppAsync(device, packageName, cancellationToken).ConfigureAwait(false); + bool currentApp = await IsAppInForegroundAsync(device, packageName, cancellationToken).ConfigureAwait(false); if (currentApp) { return AppStatus.Foreground; @@ -1000,16 +1010,30 @@ public async Task SendTextAsync(DeviceData device, string text, CancellationToke public async Task ClearInputAsync(DeviceData device, int charCount, CancellationToken cancellationToken = default) { await SendKeyEventAsync(device, "KEYCODE_MOVE_END", cancellationToken).ConfigureAwait(false); - await ExecuteRemoteCommandAsync("input keyevent " + StringExtensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL ", charCount)), device, null, Encoding, cancellationToken).ConfigureAwait(false); + await SendKeyEventAsync(device, StringExtensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL ", charCount))).ConfigureAwait(false); } /// - public Task StartAppAsync(DeviceData device, string packageName, CancellationToken cancellationToken = default) => - ExecuteRemoteCommandAsync($"monkey -p {packageName} 1", device, null, Encoding, cancellationToken); + public async Task StartAppAsync(DeviceData device, string packageName, CancellationToken cancellationToken = default) + { + EnsureDevice(device); + + using IAdbSocket socket = adbSocketFactory(EndPoint); + await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); + await socket.SendAdbRequestAsync($"shell:monkey -p {packageName} 1", cancellationToken).ConfigureAwait(false); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + } /// - public Task StopAppAsync(DeviceData device, string packageName, CancellationToken cancellationToken = default) => - ExecuteRemoteCommandAsync($"am force-stop {packageName}", device, null, Encoding, cancellationToken); + public async Task StopAppAsync(DeviceData device, string packageName, CancellationToken cancellationToken = default) + { + EnsureDevice(device); + + using IAdbSocket socket = adbSocketFactory(EndPoint); + await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); + await socket.SendAdbRequestAsync($"shell:am force-stop {packageName}", cancellationToken).ConfigureAwait(false); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + } /// public Task BackBtnAsync(DeviceData device, CancellationToken cancellationToken = default) => SendKeyEventAsync(device, "KEYCODE_BACK", cancellationToken); diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index ccdda938..884ad592 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -931,22 +931,32 @@ public void Swipe(DeviceData device, int x1, int y1, int x2, int y2, long speed) } /// - public bool IsCurrentApp(DeviceData device, string packageName) + public bool IsAppRunning(DeviceData device, string packageName) { - ConsoleOutputReceiver receiver = new(); - ExecuteRemoteCommand($"dumpsys activity activities | grep mResumedActivity", device, receiver, Encoding); - string response = receiver.ToString().Trim(); - return response.ToString().Contains(packageName); + EnsureDevice(device); + + using IAdbSocket socket = adbSocketFactory(EndPoint); + socket.SetDevice(device); + socket.SendAdbRequest($"shell:pidof {packageName}"); + AdbResponse response = socket.ReadAdbResponse(); + using StreamReader reader = new(socket.GetShellStream(), Encoding); + string result = reader.ReadToEnd().TrimStart().Split(' ', StringSplitOptions.RemoveEmptyEntries).FirstOrDefault(); + bool intParsed = int.TryParse(result, out int pid); + return intParsed && pid > 0; } /// - public bool IsAppRunning(DeviceData device, string packageName) + public bool IsAppInForeground(DeviceData device, string packageName) { - ConsoleOutputReceiver receiver = new(); - ExecuteRemoteCommand($"pidof {packageName}", device, receiver, Encoding); - string response = receiver.ToString().Trim(); - bool intParsed = int.TryParse(response, out int pid); - return intParsed && pid > 0; + EnsureDevice(device); + + using IAdbSocket socket = adbSocketFactory(EndPoint); + socket.SetDevice(device); + socket.SendAdbRequest($"shell:dumpsys activity activities | grep mResumedActivity"); + AdbResponse response = socket.ReadAdbResponse(); + using StreamReader reader = new(socket.GetShellStream(), Encoding); + string result = reader.ReadToEnd(); + return result.Contains(packageName); } /// @@ -955,7 +965,7 @@ public AppStatus GetAppStatus(DeviceData device, string packageName) EnsureDevice(device); // Check if the app is in foreground - bool currentApp = IsCurrentApp(device, packageName); + bool currentApp = IsAppInForeground(device, packageName); if (currentApp) { return AppStatus.Foreground; @@ -1078,16 +1088,30 @@ public void SendText(DeviceData device, string text) public void ClearInput(DeviceData device, int charCount) { SendKeyEvent(device, "KEYCODE_MOVE_END"); - ExecuteRemoteCommand("input keyevent " + StringExtensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL ", charCount)), device, null, Encoding); + SendKeyEvent(device, StringExtensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL", charCount))); } /// - public void StartApp(DeviceData device, string packageName) => - ExecuteRemoteCommand($"monkey -p {packageName} 1", device, null, Encoding); + public void StartApp(DeviceData device, string packageName) + { + EnsureDevice(device); + + using IAdbSocket socket = adbSocketFactory(EndPoint); + socket.SetDevice(device); + socket.SendAdbRequest($"shell:monkey -p {packageName} 1"); + AdbResponse response = socket.ReadAdbResponse(); + } /// - public void StopApp(DeviceData device, string packageName) => - ExecuteRemoteCommand($"am force-stop {packageName}", device, null, Encoding); + public void StopApp(DeviceData device, string packageName) + { + EnsureDevice(device); + + using IAdbSocket socket = adbSocketFactory(EndPoint); + socket.SetDevice(device); + socket.SendAdbRequest($"shell:am force-stop {packageName}"); + AdbResponse response = socket.ReadAdbResponse(); + } /// public void BackBtn(DeviceData device) => SendKeyEvent(device, "KEYCODE_BACK"); diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs b/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs index da918c71..ae0a6f7c 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs @@ -504,7 +504,7 @@ public partial interface IAdbClient /// The package name of the app to check. /// A which can be used to cancel the asynchronous operation. /// A which return the result. if the app is running in foreground; otherwise, . - Task IsCurrentAppAsync(DeviceData device, string packageName, CancellationToken cancellationToken); + Task IsAppInForegroundAsync(DeviceData device, string packageName, CancellationToken cancellationToken); /// /// Check if the app is running in background. diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs b/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs index 888fdfba..5f330da4 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs @@ -450,7 +450,7 @@ public partial interface IAdbClient /// The device on which to check. /// The package name of the app to check. /// if the app is running in foreground; otherwise, . - bool IsCurrentApp(DeviceData device, string packageName); + bool IsAppInForeground(DeviceData device, string packageName); /// /// Check if the app is running in background. From 93a65ff2fee23924aa776097d28f1e41a664bc49 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Tue, 3 Oct 2023 19:15:55 +0800 Subject: [PATCH 24/66] Improve SocketBasedTests --- .../AdbClientTests.Async.cs | 452 +++++------------ .../AdbClientTests.cs | 455 +++++------------- .../DeviceMonitorTests.Async.cs | 228 ++++----- .../DeviceMonitorTests.cs | 228 ++++----- .../Dummys/DummyAdbSocket.cs | 23 +- .../Dummys/IDummyAdbSocket.cs | 3 +- .../Dummys/TracingAdbSocket.cs | 31 +- .../Extensions/ExceptionExtensionsTests.cs | 2 +- .../SocketBasedTests.cs | 73 +-- .../SyncServiceTests.Async.cs | 80 ++- .../SyncServiceTests.cs | 66 +-- AdvancedSharpAdbClient/Models/Element.cs | 92 ++-- 12 files changed, 613 insertions(+), 1120 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs index a434a674..e309d720 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs @@ -161,14 +161,8 @@ public async void RemoveReverseForwardAsyncTest() "reverse:killforward:localabstract:test" ]; - AdbResponse[] responses = - [ - AdbResponse.OK, - AdbResponse.OK, - ]; - await RunTestAsync( - responses, + OkResponses(2), NoResponseMessages, requests, () => TestClient.RemoveReverseForwardAsync(Device, "localabstract:test")); @@ -201,14 +195,8 @@ public async void RemoveAllReversesAsyncTest() "reverse:killforward-all" ]; - AdbResponse[] responses = - [ - AdbResponse.OK, - AdbResponse.OK, - ]; - await RunTestAsync( - responses, + OkResponses(2), NoResponseMessages, requests, () => TestClient.RemoveAllReverseForwardsAsync(Device)); @@ -245,12 +233,6 @@ public async void ListReverseForwardAsyncTest() { string[] responseMessages = ["(reverse) localabstract:scrcpy tcp:100\n(reverse) localabstract: scrcpy2 tcp:100\n(reverse) localabstract: scrcpy3 tcp:100\n"]; - AdbResponse[] responses = - [ - AdbResponse.OK, - AdbResponse.OK, - ]; - string[] requests = [ "host:transport:169.254.109.177:5555", @@ -259,7 +241,7 @@ public async void ListReverseForwardAsyncTest() ForwardData[] forwards = null; await RunTestAsync( - responses, + OkResponses(2), responseMessages, requests, async () => forwards = (await TestClient.ListReverseForwardAsync(Device)).ToArray()); @@ -277,20 +259,6 @@ await RunTestAsync( [Fact] public async void ExecuteRemoteCommandAsyncTest() { - DeviceData device = new() - { - Serial = "169.254.109.177:5555", - State = DeviceState.Online - }; - - AdbResponse[] responses = - [ - AdbResponse.OK, - AdbResponse.OK - ]; - - string[] responseMessages = []; - string[] requests = [ "host:transport:169.254.109.177:5555", @@ -303,11 +271,11 @@ public async void ExecuteRemoteCommandAsyncTest() ConsoleOutputReceiver receiver = new(); await RunTestAsync( - responses, - responseMessages, + OkResponses(2), + NoResponseMessages, requests, shellStream, - () => TestClient.ExecuteRemoteCommandAsync("echo Hello, World", device, receiver)); + () => TestClient.ExecuteRemoteCommandAsync("echo Hello, World", Device, receiver)); Assert.Equal("Hello, World\r\n", receiver.ToString(), ignoreLineEndingDifferences: true); } @@ -318,20 +286,6 @@ await RunTestAsync( [Fact] public async void ExecuteRemoteCommandAsyncUnresponsiveTest() { - DeviceData device = new() - { - Serial = "169.254.109.177:5555", - State = DeviceState.Online - }; - - AdbResponse[] responses = - [ - AdbResponse.OK, - AdbResponse.OK - ]; - - string[] responseMessages = []; - string[] requests = [ "host:transport:169.254.109.177:5555", @@ -342,11 +296,11 @@ public async void ExecuteRemoteCommandAsyncUnresponsiveTest() _ = await Assert.ThrowsAsync(() => RunTestAsync( - responses, - responseMessages, + OkResponses(2), + NoResponseMessages, requests, null, - () => TestClient.ExecuteRemoteCommandAsync("echo Hello, World", device, receiver, CancellationToken.None))); + () => TestClient.ExecuteRemoteCommandAsync("echo Hello, World", Device, receiver, CancellationToken.None))); } /// @@ -355,12 +309,6 @@ public async void ExecuteRemoteCommandAsyncUnresponsiveTest() [Fact] public async void GetFrameBufferAsyncTest() { - DeviceData device = new() - { - Serial = "169.254.109.177:5555", - State = DeviceState.Online - }; - DummyAdbSocket socket = new(); socket.Responses.Enqueue(AdbResponse.OK); @@ -377,12 +325,12 @@ public async void GetFrameBufferAsyncTest() using (FactoriesLocker locker = await FactoriesLocker.WaitAsync()) { Factories.AdbSocketFactory = (endPoint) => socket; - framebuffer = await TestClient.GetFrameBufferAsync(device); + framebuffer = await TestClient.GetFrameBufferAsync(Device); Factories.Reset(); } Assert.NotNull(framebuffer); - Assert.Equal(device, framebuffer.Device); + Assert.Equal(Device, framebuffer.Device); Assert.Equal(16, framebuffer.Data.Length); FramebufferHeader header = framebuffer.Header; @@ -427,20 +375,6 @@ public async void GetFrameBufferAsyncTest() [Fact] public async void RunLogServiceAsyncTest() { - DeviceData device = new() - { - Serial = "169.254.109.177:5555", - State = DeviceState.Online - }; - - AdbResponse[] responses = - [ - AdbResponse.OK, - AdbResponse.OK - ]; - - string[] responseMessages = []; - string[] requests = [ "host:transport:169.254.109.177:5555", @@ -455,11 +389,11 @@ public async void RunLogServiceAsyncTest() Action sink = logs.Add; await RunTestAsync( - responses, - responseMessages, + OkResponses(2), + NoResponseMessages, requests, shellStream, - () => TestClient.RunLogServiceAsync(device, sink, CancellationToken.None, LogId.System)); + () => TestClient.RunLogServiceAsync(Device, sink, CancellationToken.None, LogId.System)); Assert.Equal(3, logs.Count); } @@ -477,7 +411,7 @@ public async void RebootAsyncTest() ]; await RunTestAsync( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, () => TestClient.RebootAsync(Device)); @@ -637,15 +571,9 @@ await RunTestAsync( [Fact] public async void RootAsyncTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "root:" ]; @@ -654,14 +582,14 @@ public async void RootAsyncTest() _ = await Assert.ThrowsAsync(() => RunTestAsync( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, - Array.Empty<(SyncCommand, string)>(), - Array.Empty(), + NoSyncRequests, + NoSyncResponses, [expectedData], - Array.Empty(), - () => TestClient.RootAsync(device))); + null, + () => TestClient.RootAsync(Device))); } /// @@ -670,15 +598,9 @@ public async void RootAsyncTest() [Fact] public async void UnrootAsyncTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "unroot:" ]; @@ -687,14 +609,14 @@ public async void UnrootAsyncTest() _ = await Assert.ThrowsAsync(() => RunTestAsync( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, - Array.Empty<(SyncCommand, string)>(), - Array.Empty(), + NoSyncRequests, + NoSyncResponses, [expectedData], - Array.Empty(), - () => TestClient.UnrootAsync(device))); + null, + () => TestClient.UnrootAsync(Device))); } /// @@ -703,15 +625,9 @@ public async void UnrootAsyncTest() [Fact] public async void InstallAsyncTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "exec:cmd package 'install' -S 205774" ]; @@ -742,14 +658,14 @@ public async void InstallAsyncTest() await using (FileStream stream = File.OpenRead("Assets/testapp.apk")) { await RunTestAsync( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, - Array.Empty<(SyncCommand, string)>(), - Array.Empty(), + NoSyncRequests, + NoSyncResponses, [response], applicationDataChunks.ToArray(), - () => TestClient.InstallAsync(device, stream)); + () => TestClient.InstallAsync(Device, stream)); } } @@ -759,15 +675,9 @@ await RunTestAsync( [Fact] public async void InstallCreateAsyncTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "exec:cmd package 'install-create' -p com.google.android.gms" ]; @@ -776,11 +686,11 @@ public async void InstallCreateAsyncTest() string session = string.Empty; await RunTestAsync( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - async () => session = await TestClient.InstallCreateAsync(device, "com.google.android.gms")); + async () => session = await TestClient.InstallCreateAsync(Device, "com.google.android.gms")); Assert.Equal("936013062", session); } @@ -791,15 +701,9 @@ await RunTestAsync( [Fact] public async void InstallWriteAsyncTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "exec:cmd package 'install-write' -S 205774 936013062 base.apk" ]; @@ -830,14 +734,14 @@ public async void InstallWriteAsyncTest() await using (FileStream stream = File.OpenRead("Assets/testapp.apk")) { await RunTestAsync( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, - Array.Empty<(SyncCommand, string)>(), - Array.Empty(), + NoSyncRequests, + NoSyncResponses, [response], applicationDataChunks.ToArray(), - () => TestClient.InstallWriteAsync(device, stream, "base", "936013062")); + () => TestClient.InstallWriteAsync(Device, stream, "base", "936013062")); } } @@ -847,15 +751,9 @@ await RunTestAsync( [Fact] public async void InstallCommitAsyncTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "exec:cmd package 'install-commit' 936013062" ]; @@ -863,11 +761,11 @@ public async void InstallCommitAsyncTest() await using MemoryStream shellStream = new(streamData); await RunTestAsync( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - () => TestClient.InstallCommitAsync(device, "936013062")); + () => TestClient.InstallCommitAsync(Device, "936013062")); } /// @@ -876,21 +774,15 @@ await RunTestAsync( [Fact] public async void GetFeatureSetAsyncTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - - string[] requests = ["host-serial:009d1cd696d5194a:features"]; + string[] requests = ["host-serial:169.254.109.177:5555:features"]; string[] responses = ["sendrecv_v2_brotli,remount_shell,sendrecv_v2,abb_exec,fixed_push_mkdir,fixed_push_symlink_timestamp,abb,shell_v2,cmd,ls_v2,apex,stat_v2\r\n"]; IEnumerable features = null; await RunTestAsync( - [AdbResponse.OK], + OkResponse, responses, requests, - async () => features = await TestClient.GetFeatureSetAsync(device)); + async () => features = await TestClient.GetFeatureSetAsync(Device)); Assert.Equal(12, features.Count()); Assert.Equal("sendrecv_v2_brotli", features.First()); @@ -903,15 +795,9 @@ await RunTestAsync( [Fact] public async void DumpScreenStringAsyncTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:uiautomator dump /dev/tty" ]; @@ -922,11 +808,11 @@ public async void DumpScreenStringAsyncTest() string xml = string.Empty; await RunTestAsync( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - async () => xml = await TestClient.DumpScreenStringAsync(device)); + async () => xml = await TestClient.DumpScreenStringAsync(Device)); Assert.Equal(cleanDump, xml); } @@ -937,15 +823,9 @@ await RunTestAsync( [Fact] public async void DumpScreenStringAsyncMIUITest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:uiautomator dump /dev/tty" ]; @@ -956,11 +836,11 @@ public async void DumpScreenStringAsyncMIUITest() string miuiXml = string.Empty; await RunTestAsync( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, miuiStream, - async () => miuiXml = await TestClient.DumpScreenStringAsync(device)); + async () => miuiXml = await TestClient.DumpScreenStringAsync(Device)); Assert.Equal(cleanMIUIDump, miuiXml); } @@ -971,15 +851,9 @@ await RunTestAsync( [Fact] public async void DumpScreenStringAsyncEmptyTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:uiautomator dump /dev/tty" ]; @@ -987,11 +861,11 @@ public async void DumpScreenStringAsyncEmptyTest() string emptyXml = string.Empty; await RunTestAsync( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, emptyStream, - async () => emptyXml = await TestClient.DumpScreenStringAsync(device)); + async () => emptyXml = await TestClient.DumpScreenStringAsync(Device)); Assert.True(string.IsNullOrEmpty(emptyXml)); } @@ -1002,15 +876,9 @@ await RunTestAsync( [Fact] public async void DumpScreenStringAsyncErrorTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:uiautomator dump /dev/tty" ]; @@ -1020,11 +888,11 @@ public async void DumpScreenStringAsyncErrorTest() await Assert.ThrowsAsync(() => RunTestAsync( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, errorStream, - () => TestClient.DumpScreenStringAsync(device))); + () => TestClient.DumpScreenStringAsync(Device))); } /// @@ -1033,15 +901,9 @@ await Assert.ThrowsAsync(() => [Fact] public async void DumpScreenAsyncTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:uiautomator dump /dev/tty" ]; @@ -1051,11 +913,11 @@ public async void DumpScreenAsyncTest() XmlDocument xml = null; await RunTestAsync( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - async () => xml = await TestClient.DumpScreenAsync(device)); + async () => xml = await TestClient.DumpScreenAsync(Device)); string cleanDump = File.ReadAllText(@"Assets/dumpscreen_clean.txt"); XmlDocument doc = new(); @@ -1070,15 +932,9 @@ await RunTestAsync( [Fact] public async void ClickAsyncTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:input tap 100 100" ]; @@ -1108,11 +964,11 @@ at android.os.Binder.execTransactInternal(Binder.java:1165) JavaException exception = await Assert.ThrowsAsync(() => RunTestAsync( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - () => TestClient.ClickAsync(device, 100, 100))); + () => TestClient.ClickAsync(Device, 100, 100))); Assert.Equal("SecurityException", exception.JavaName); Assert.Equal("Injecting to another application requires INJECT_EVENTS permission", exception.Message); @@ -1145,15 +1001,9 @@ at android.os.Binder.execTransactInternal(Binder.java:1165) [Fact] public async void ClickCordsAsyncTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:input tap 100 100" ]; @@ -1162,11 +1012,11 @@ public async void ClickCordsAsyncTest() _ = await Assert.ThrowsAsync(() => RunTestAsync( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - () => TestClient.ClickAsync(device, new Cords(100, 100)))); + () => TestClient.ClickAsync(Device, new Cords(100, 100)))); } /// @@ -1175,26 +1025,42 @@ public async void ClickCordsAsyncTest() [Fact] public async void SwipeAsyncTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; + string[] requests = + [ + "host:transport:169.254.109.177:5555", + "shell:input swipe 100 200 300 400 500" + ]; + await using MemoryStream shellStream = new(); + + await RunTestAsync( + OkResponses(2), + NoResponseMessages, + requests, + shellStream, + () => TestClient.SwipeAsync(Device, 100, 200, 300, 400, 500)); + } + + /// + /// Tests the method. + /// + [Fact] + public async void SwipeAsyncElementTest() + { string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:input swipe 100 200 300 400 500" ]; await using MemoryStream shellStream = new(); await RunTestAsync( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - () => TestClient.SwipeAsync(device, 100, 200, 300, 400, 500)); + () => TestClient.SwipeAsync(Device, new Element(TestClient, Device, new Area(0, 0, 200, 400)), new Element(TestClient, Device, new Area(0, 0, 600, 800)), 500)); } /// @@ -1209,15 +1075,9 @@ await RunTestAsync( [InlineData("", false)] public async void IsAppRunningAsyncTest(string response, bool expected) { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:pidof com.google.android.gms" ]; @@ -1226,11 +1086,11 @@ public async void IsAppRunningAsyncTest(string response, bool expected) bool result = !expected; await RunTestAsync( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - async () => result = await TestClient.IsAppRunningAsync(device, "com.google.android.gms")); + async () => result = await TestClient.IsAppRunningAsync(Device, "com.google.android.gms")); Assert.Equal(expected, result); } @@ -1244,15 +1104,9 @@ await RunTestAsync( [InlineData("com.google.android.gms", false)] public async void IsAppInForegroundAsyncTest(string packageName, bool expected) { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:dumpsys activity activities | grep mResumedActivity" ]; @@ -1262,11 +1116,11 @@ public async void IsAppInForegroundAsyncTest(string packageName, bool expected) bool result = !expected; await RunTestAsync( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - async () => result = await TestClient.IsAppInForegroundAsync(device, packageName)); + async () => result = await TestClient.IsAppInForegroundAsync(Device, packageName)); Assert.Equal(expected, result); } @@ -1277,15 +1131,9 @@ await RunTestAsync( [Fact] public async void FindElementAsyncTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:uiautomator dump /dev/tty" ]; @@ -1295,11 +1143,11 @@ public async void FindElementAsyncTest() Element element = null; await RunTestAsync( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - async () => element = await TestClient.FindElementAsync(device)); + async () => element = await TestClient.FindElementAsync(Device)); Assert.Equal(144, element.GetChildCount()); element = element[0][0][0][0][0][0][0][0][2][1][0][0]; @@ -1313,15 +1161,9 @@ await RunTestAsync( [Fact] public async void FindElementsAsyncTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:uiautomator dump /dev/tty" ]; @@ -1331,11 +1173,11 @@ public async void FindElementsAsyncTest() List elements = null; await RunTestAsync( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - async () => elements = await TestClient.FindElementsAsync(device)); + async () => elements = await TestClient.FindElementsAsync(Device)); int childCount = elements.Count; elements.ForEach(x => childCount += x.GetChildCount()); @@ -1351,15 +1193,9 @@ await RunTestAsync( [Fact] public async void FindAsyncElementsTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:uiautomator dump /dev/tty" ]; @@ -1369,14 +1205,14 @@ public async void FindAsyncElementsTest() List elements = null; await RunTestAsync( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, async () => { elements = []; - await foreach (Element element in TestClient.FindAsyncElements(device)) + await foreach (Element element in TestClient.FindAsyncElements(Device)) { elements.Add(element); } @@ -1396,26 +1232,20 @@ await RunTestAsync( [Fact] public async void SendKeyEventAsyncTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:input keyevent KEYCODE_MOVE_END" ]; await using MemoryStream shellStream = new(); await RunTestAsync( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - () => TestClient.SendKeyEventAsync(device, "KEYCODE_MOVE_END")); + () => TestClient.SendKeyEventAsync(Device, "KEYCODE_MOVE_END")); } /// @@ -1424,26 +1254,20 @@ await RunTestAsync( [Fact] public async void SendTextAsyncTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:input text Hello, World", ]; await using MemoryStream shellStream = new(); await RunTestAsync( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - () => TestClient.SendTextAsync(device, "Hello, World")); + () => TestClient.SendTextAsync(Device, "Hello, World")); } /// @@ -1452,26 +1276,20 @@ await RunTestAsync( [Fact] public async void StartAppAsyncTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:monkey -p com.android.settings 1", ]; await using MemoryStream shellStream = new(); await RunTestAsync( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - () => TestClient.StartAppAsync(device, "com.android.settings")); + () => TestClient.StartAppAsync(Device, "com.android.settings")); } /// @@ -1480,26 +1298,20 @@ await RunTestAsync( [Fact] public async void StopAppAsyncTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:am force-stop com.android.settings", ]; await using MemoryStream shellStream = new(); await RunTestAsync( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - () => TestClient.StopAppAsync(device, "com.android.settings")); + () => TestClient.StopAppAsync(Device, "com.android.settings")); } /// @@ -1508,26 +1320,20 @@ await RunTestAsync( [Fact] public async void BackBtnAsyncTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:input keyevent KEYCODE_BACK" ]; await using MemoryStream shellStream = new(); await RunTestAsync( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - () => TestClient.BackBtnAsync(device)); + () => TestClient.BackBtnAsync(Device)); } /// @@ -1536,26 +1342,20 @@ await RunTestAsync( [Fact] public async void HomeBtnAsyncTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:input keyevent KEYCODE_HOME" ]; await using MemoryStream shellStream = new(); await RunTestAsync( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - () => TestClient.HomeBtnAsync(device)); + () => TestClient.HomeBtnAsync(Device)); } private Task RunConnectAsyncTest(Func test, string connectString) @@ -1591,7 +1391,7 @@ private Task RunCreateReverseAsyncTest(Func test, string rever ]; return RunTestAsync( - [AdbResponse.OK, AdbResponse.OK, AdbResponse.OK], + OkResponses(3), [null], requests, () => test(Device)); @@ -1602,7 +1402,7 @@ private Task RunCreateForwardAsyncTest(Func test, string forwa string[] requests = [$"host-serial:169.254.109.177:5555:forward:{forwardString}"]; return RunTestAsync( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), [null], requests, () => test(Device)); diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs index f2280cf1..27e6c49f 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs @@ -10,6 +10,7 @@ using System.Runtime.InteropServices; using System.Text; using System.Xml; +using System.Xml.Linq; using Xunit; namespace AdvancedSharpAdbClient.Tests @@ -267,14 +268,8 @@ public void RemoveReverseForwardTest() "reverse:killforward:localabstract:test" ]; - AdbResponse[] responses = - [ - AdbResponse.OK, - AdbResponse.OK, - ]; - RunTest( - responses, + OkResponses(2), NoResponseMessages, requests, () => TestClient.RemoveReverseForward(Device, "localabstract:test")); @@ -307,14 +302,8 @@ public void RemoveAllReversesTest() "reverse:killforward-all" ]; - AdbResponse[] responses = - [ - AdbResponse.OK, - AdbResponse.OK, - ]; - RunTest( - responses, + OkResponses(2), NoResponseMessages, requests, () => TestClient.RemoveAllReverseForwards(Device)); @@ -351,12 +340,6 @@ public void ListReverseForwardTest() { string[] responseMessages = ["(reverse) localabstract:scrcpy tcp:100\n(reverse) localabstract: scrcpy2 tcp:100\n(reverse) localabstract: scrcpy3 tcp:100\n"]; - AdbResponse[] responses = - [ - AdbResponse.OK, - AdbResponse.OK, - ]; - string[] requests = [ "host:transport:169.254.109.177:5555", @@ -365,7 +348,7 @@ public void ListReverseForwardTest() ForwardData[] forwards = null; RunTest( - responses, + OkResponses(2), responseMessages, requests, () => forwards = TestClient.ListReverseForward(Device).ToArray()); @@ -383,18 +366,6 @@ public void ListReverseForwardTest() [Fact] public void ExecuteRemoteCommandTest() { - DeviceData device = new() - { - Serial = "169.254.109.177:5555", - State = DeviceState.Online - }; - - AdbResponse[] responses = - [ - AdbResponse.OK, - AdbResponse.OK - ]; - string[] requests = [ "host:transport:169.254.109.177:5555", @@ -407,11 +378,11 @@ public void ExecuteRemoteCommandTest() ConsoleOutputReceiver receiver = new(); RunTest( - responses, + OkResponses(2), NoResponseMessages, requests, shellStream, - () => TestClient.ExecuteRemoteCommand("echo Hello, World", device, receiver)); + () => TestClient.ExecuteRemoteCommand("echo Hello, World", Device, receiver)); Assert.Equal("Hello, World\r\n", receiver.ToString(), ignoreLineEndingDifferences: true); } @@ -422,18 +393,6 @@ public void ExecuteRemoteCommandTest() [Fact] public void ExecuteRemoteCommandUnresponsiveTest() { - DeviceData device = new() - { - Serial = "169.254.109.177:5555", - State = DeviceState.Online - }; - - AdbResponse[] responses = - [ - AdbResponse.OK, - AdbResponse.OK - ]; - string[] requests = [ "host:transport:169.254.109.177:5555", @@ -444,35 +403,24 @@ public void ExecuteRemoteCommandUnresponsiveTest() _ = Assert.Throws(() => RunTest( - responses, + OkResponses(2), NoResponseMessages, requests, null, - () => TestClient.ExecuteRemoteCommand("echo Hello, World", device, receiver))); + () => TestClient.ExecuteRemoteCommand("echo Hello, World", Device, receiver))); } [Fact] public void CreateRefreshableFramebufferTest() { - DeviceData device = new() - { - Serial = "169.254.109.177:5555", - State = DeviceState.Online - }; - Framebuffer framebuffer = TestClient.CreateRefreshableFramebuffer(device); + Framebuffer framebuffer = TestClient.CreateRefreshableFramebuffer(Device); Assert.NotNull(framebuffer); - Assert.Equal(device, framebuffer.Device); + Assert.Equal(Device, framebuffer.Device); } [Fact] public void GetFrameBufferTest() { - DeviceData device = new() - { - Serial = "169.254.109.177:5555", - State = DeviceState.Online - }; - DummyAdbSocket socket = new(); socket.Responses.Enqueue(AdbResponse.OK); @@ -489,12 +437,12 @@ public void GetFrameBufferTest() using (FactoriesLocker locker = FactoriesLocker.Wait()) { Factories.AdbSocketFactory = (endPoint) => socket; - framebuffer = TestClient.GetFrameBuffer(device); + framebuffer = TestClient.GetFrameBuffer(Device); Factories.Reset(); } Assert.NotNull(framebuffer); - Assert.Equal(device, framebuffer.Device); + Assert.Equal(Device, framebuffer.Device); Assert.Equal(16, framebuffer.Data.Length); FramebufferHeader header = framebuffer.Header; @@ -536,18 +484,6 @@ public void GetFrameBufferTest() [Fact] public void RunLogServiceTest() { - DeviceData device = new() - { - Serial = "169.254.109.177:5555", - State = DeviceState.Online - }; - - AdbResponse[] responses = - [ - AdbResponse.OK, - AdbResponse.OK - ]; - string[] requests = [ "host:transport:169.254.109.177:5555", @@ -562,11 +498,11 @@ public void RunLogServiceTest() Action sink = logs.Add; RunTest( - responses, + OkResponses(2), NoResponseMessages, requests, shellStream, - () => TestClient.RunLogService(device, sink, LogId.System)); + () => TestClient.RunLogService(Device, sink, LogId.System)); Assert.Equal(3, logs.Count); } @@ -584,7 +520,7 @@ public void RebootTest() ]; RunTest( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, () => TestClient.Reboot(Device)); @@ -744,15 +680,9 @@ public void DisconnectTest() [Fact] public void RootTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "root:" ]; @@ -761,14 +691,14 @@ public void RootTest() _ = Assert.Throws(() => RunTest( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, - Array.Empty<(SyncCommand, string)>(), - Array.Empty(), + NoSyncRequests, + NoSyncResponses, [expectedData], - Array.Empty(), - () => TestClient.Root(device))); + null, + () => TestClient.Root(Device))); } /// @@ -777,15 +707,9 @@ public void RootTest() [Fact] public void UnrootTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "unroot:" ]; @@ -794,14 +718,14 @@ public void UnrootTest() _ = Assert.Throws(() => RunTest( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, - Array.Empty<(SyncCommand, string)>(), - Array.Empty(), + NoSyncRequests, + NoSyncResponses, [expectedData], - Array.Empty(), - () => TestClient.Unroot(device))); + null, + () => TestClient.Unroot(Device))); } /// @@ -810,15 +734,9 @@ public void UnrootTest() [Fact] public void InstallTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "exec:cmd package 'install' -S 205774" ]; @@ -849,14 +767,14 @@ public void InstallTest() using (FileStream stream = File.OpenRead("Assets/testapp.apk")) { RunTest( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, - Array.Empty<(SyncCommand, string)>(), - Array.Empty(), + NoSyncRequests, + NoSyncResponses, [response], applicationDataChunks.ToArray(), - () => TestClient.Install(device, stream)); + () => TestClient.Install(Device, stream)); } } @@ -866,15 +784,9 @@ public void InstallTest() [Fact] public void InstallCreateTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "exec:cmd package 'install-create' -p com.google.android.gms" ]; @@ -883,11 +795,11 @@ public void InstallCreateTest() string session = string.Empty; RunTest( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - () => session = TestClient.InstallCreate(device, "com.google.android.gms")); + () => session = TestClient.InstallCreate(Device, "com.google.android.gms")); Assert.Equal("936013062", session); } @@ -898,15 +810,9 @@ public void InstallCreateTest() [Fact] public void InstallWriteTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "exec:cmd package 'install-write' -S 205774 936013062 base.apk" ]; @@ -937,14 +843,14 @@ public void InstallWriteTest() using (FileStream stream = File.OpenRead("Assets/testapp.apk")) { RunTest( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, - Array.Empty<(SyncCommand, string)>(), - Array.Empty(), + NoSyncRequests, + NoSyncResponses, [response], applicationDataChunks.ToArray(), - () => TestClient.InstallWrite(device, stream, "base", "936013062")); + () => TestClient.InstallWrite(Device, stream, "base", "936013062")); } } @@ -954,15 +860,9 @@ public void InstallWriteTest() [Fact] public void InstallCommitTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "exec:cmd package 'install-commit' 936013062" ]; @@ -970,11 +870,11 @@ public void InstallCommitTest() using MemoryStream shellStream = new(streamData); RunTest( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - () => TestClient.InstallCommit(device, "936013062")); + () => TestClient.InstallCommit(Device, "936013062")); } /// @@ -983,21 +883,15 @@ public void InstallCommitTest() [Fact] public void GetFeatureSetTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - - string[] requests = ["host-serial:009d1cd696d5194a:features"]; + string[] requests = ["host-serial:169.254.109.177:5555:features"]; string[] responses = ["sendrecv_v2_brotli,remount_shell,sendrecv_v2,abb_exec,fixed_push_mkdir,fixed_push_symlink_timestamp,abb,shell_v2,cmd,ls_v2,apex,stat_v2\r\n"]; IEnumerable features = null; RunTest( - [AdbResponse.OK], + OkResponse, responses, requests, - () => features = TestClient.GetFeatureSet(device)); + () => features = TestClient.GetFeatureSet(Device)); Assert.Equal(12, features.Count()); Assert.Equal("sendrecv_v2_brotli", features.First()); @@ -1010,15 +904,9 @@ public void GetFeatureSetTest() [Fact] public void DumpScreenStringTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:uiautomator dump /dev/tty" ]; @@ -1029,11 +917,11 @@ public void DumpScreenStringTest() string xml = string.Empty; RunTest( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - () => xml = TestClient.DumpScreenString(device)); + () => xml = TestClient.DumpScreenString(Device)); Assert.Equal(cleanDump, xml); } @@ -1044,15 +932,9 @@ public void DumpScreenStringTest() [Fact] public void DumpScreenStringMIUITest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:uiautomator dump /dev/tty" ]; @@ -1063,11 +945,11 @@ public void DumpScreenStringMIUITest() string miuiXml = string.Empty; RunTest( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, miuiStream, - () => miuiXml = TestClient.DumpScreenString(device)); + () => miuiXml = TestClient.DumpScreenString(Device)); Assert.Equal(cleanMIUIDump, miuiXml); } @@ -1078,15 +960,9 @@ public void DumpScreenStringMIUITest() [Fact] public void DumpScreenStringEmptyTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:uiautomator dump /dev/tty" ]; @@ -1095,11 +971,11 @@ public void DumpScreenStringEmptyTest() string emptyXml = string.Empty; RunTest( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, emptyStream, - () => emptyXml = TestClient.DumpScreenString(device)); + () => emptyXml = TestClient.DumpScreenString(Device)); Assert.True(string.IsNullOrEmpty(emptyXml)); } @@ -1110,15 +986,9 @@ public void DumpScreenStringEmptyTest() [Fact] public void DumpScreenStringErrorTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:uiautomator dump /dev/tty" ]; @@ -1128,11 +998,11 @@ public void DumpScreenStringErrorTest() Assert.Throws(() => RunTest( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, errorStream, - () => TestClient.DumpScreenString(device))); + () => TestClient.DumpScreenString(Device))); } /// @@ -1141,15 +1011,9 @@ public void DumpScreenStringErrorTest() [Fact] public void DumpScreenTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:uiautomator dump /dev/tty" ]; @@ -1159,11 +1023,11 @@ public void DumpScreenTest() XmlDocument xml = null; RunTest( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - () => xml = TestClient.DumpScreen(device)); + () => xml = TestClient.DumpScreen(Device)); string cleanDump = File.ReadAllText(@"Assets/dumpscreen_clean.txt"); XmlDocument doc = new(); @@ -1178,15 +1042,9 @@ public void DumpScreenTest() [Fact] public void ClickTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:input tap 100 100" ]; @@ -1216,11 +1074,11 @@ at android.os.Binder.execTransactInternal(Binder.java:1165) JavaException exception = Assert.Throws(() => RunTest( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - () => TestClient.Click(device, 100, 100))); + () => TestClient.Click(Device, 100, 100))); Assert.Equal("SecurityException", exception.JavaName); Assert.Equal("Injecting to another application requires INJECT_EVENTS permission", exception.Message); @@ -1253,15 +1111,9 @@ at android.os.Binder.execTransactInternal(Binder.java:1165) [Fact] public void ClickCordsTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:input tap 100 100" ]; @@ -1270,11 +1122,11 @@ public void ClickCordsTest() _ = Assert.Throws(() => RunTest( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - () => TestClient.Click(device, new Cords(100, 100)))); + () => TestClient.Click(Device, new Cords(100, 100)))); } /// @@ -1283,26 +1135,42 @@ public void ClickCordsTest() [Fact] public void SwipeTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; + string[] requests = + [ + "host:transport:169.254.109.177:5555", + "shell:input swipe 100 200 300 400 500" + ]; + + using MemoryStream shellStream = new(); + + RunTest( + OkResponses(2), + NoResponseMessages, + requests, + shellStream, + () => TestClient.Swipe(Device, 100, 200, 300, 400, 500)); + } + /// + /// Tests the method. + /// + [Fact] + public void SwipeElementTest() + { string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:input swipe 100 200 300 400 500" ]; using MemoryStream shellStream = new(); RunTest( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - () => TestClient.Swipe(device, 100, 200, 300, 400, 500)); + () => TestClient.Swipe(Device, new Element(TestClient, Device, new Area(0, 0, 200, 400)), new Element(TestClient, Device, new Area(0, 0, 600, 800)), 500)); } /// @@ -1317,15 +1185,9 @@ public void SwipeTest() [InlineData("", false)] public void IsAppRunningTest(string response, bool expected) { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:pidof com.google.android.gms" ]; @@ -1334,11 +1196,11 @@ public void IsAppRunningTest(string response, bool expected) bool result = !expected; RunTest( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - () => result = TestClient.IsAppRunning(device, "com.google.android.gms")); + () => result = TestClient.IsAppRunning(Device, "com.google.android.gms")); Assert.Equal(expected, result); } @@ -1352,15 +1214,9 @@ public void IsAppRunningTest(string response, bool expected) [InlineData("com.google.android.gms", false)] public void IsAppInForegroundTest(string packageName, bool expected) { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:dumpsys activity activities | grep mResumedActivity" ]; @@ -1370,11 +1226,11 @@ public void IsAppInForegroundTest(string packageName, bool expected) bool result = !expected; RunTest( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - () => result = TestClient.IsAppInForeground(device, packageName)); + () => result = TestClient.IsAppInForeground(Device, packageName)); Assert.Equal(expected, result); } @@ -1385,15 +1241,9 @@ public void IsAppInForegroundTest(string packageName, bool expected) [Fact] public void FindElementTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:uiautomator dump /dev/tty" ]; @@ -1403,11 +1253,11 @@ public void FindElementTest() Element element = null; RunTest( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - () => element = TestClient.FindElement(device)); + () => element = TestClient.FindElement(Device)); Assert.Equal(144, element.GetChildCount()); element = element[0][0][0][0][0][0][0][0][2][1][0][0]; @@ -1421,15 +1271,9 @@ public void FindElementTest() [Fact] public void FindElementsTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:uiautomator dump /dev/tty" ]; @@ -1437,21 +1281,20 @@ public void FindElementsTest() byte[] streamData = Encoding.UTF8.GetBytes(dump); using MemoryStream shellStream = new(streamData); + List elements = null; RunTest( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - () => - { - List elements = TestClient.FindElements(device).ToList(); - int childCount = elements.Count; - elements.ForEach(x => childCount += x.GetChildCount()); - Assert.Equal(145, childCount); - Element element = elements[0][0][0][0][0][0][0][0][0][2][1][0][0]; - Assert.Equal("where-where", element.Attributes["text"]); - Assert.Equal(Area.FromLTRB(45, 889, 427, 973), element.Area); - }); + () => elements = TestClient.FindElements(Device).ToList()); + + int childCount = elements.Count; + elements.ForEach(x => childCount += x.GetChildCount()); + Assert.Equal(145, childCount); + Element element = elements[0][0][0][0][0][0][0][0][0][2][1][0][0]; + Assert.Equal("where-where", element.Attributes["text"]); + Assert.Equal(Area.FromLTRB(45, 889, 427, 973), element.Area); } /// @@ -1460,26 +1303,20 @@ public void FindElementsTest() [Fact] public void SendKeyEventTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:input keyevent KEYCODE_MOVE_END" ]; using MemoryStream shellStream = new(); RunTest( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - () => TestClient.SendKeyEvent(device, "KEYCODE_MOVE_END")); + () => TestClient.SendKeyEvent(Device, "KEYCODE_MOVE_END")); } /// @@ -1488,26 +1325,20 @@ public void SendKeyEventTest() [Fact] public void SendTextTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:input text Hello, World", ]; using MemoryStream shellStream = new(); RunTest( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - () => TestClient.SendText(device, "Hello, World")); + () => TestClient.SendText(Device, "Hello, World")); } /// @@ -1516,26 +1347,20 @@ public void SendTextTest() [Fact] public void StartAppTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:monkey -p com.android.settings 1", ]; using MemoryStream shellStream = new(); RunTest( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - () => TestClient.StartApp(device, "com.android.settings")); + () => TestClient.StartApp(Device, "com.android.settings")); } /// @@ -1544,26 +1369,20 @@ public void StartAppTest() [Fact] public void StopAppTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:am force-stop com.android.settings", ]; using MemoryStream shellStream = new(); RunTest( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - () => TestClient.StopApp(device, "com.android.settings")); + () => TestClient.StopApp(Device, "com.android.settings")); } /// @@ -1572,26 +1391,20 @@ public void StopAppTest() [Fact] public void BackBtnTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:input keyevent KEYCODE_BACK" ]; using MemoryStream shellStream = new(); RunTest( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - () => TestClient.BackBtn(device)); + () => TestClient.BackBtn(Device)); } /// @@ -1600,26 +1413,20 @@ public void BackBtnTest() [Fact] public void HomeBtnTest() { - DeviceData device = new() - { - Serial = "009d1cd696d5194a", - State = DeviceState.Online - }; - string[] requests = [ - "host:transport:009d1cd696d5194a", + "host:transport:169.254.109.177:5555", "shell:input keyevent KEYCODE_HOME" ]; using MemoryStream shellStream = new(); RunTest( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), NoResponseMessages, requests, shellStream, - () => TestClient.HomeBtn(device)); + () => TestClient.HomeBtn(Device)); } private void RunConnectTest(Action test, string connectString) @@ -1655,7 +1462,7 @@ private void RunCreateReverseTest(Action test, string reverseString) ]; RunTest( - [AdbResponse.OK, AdbResponse.OK, AdbResponse.OK], + OkResponses(3), [null], requests, () => test(Device)); @@ -1666,7 +1473,7 @@ private void RunCreateForwardTest(Action test, string forwardString) string[] requests = [$"host-serial:169.254.109.177:5555:forward:{forwardString}"]; RunTest( - [AdbResponse.OK, AdbResponse.OK], + OkResponses(2), [null], requests, () => test(Device)); diff --git a/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.Async.cs b/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.Async.cs index 723c0936..5f2967a3 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.Async.cs @@ -19,18 +19,15 @@ public async void DeviceDisconnectedAsyncTest() // Start the monitor, detect the initial device. await RunTestAsync( OkResponse, - ResponseMessages("169.254.109.177:5555\tdevice\n"), - Requests("host:track-devices"), - async () => - { - await monitor.StartAsync(); - - Assert.Single(monitor.Devices); - Assert.Single(sink.ConnectedEvents); - Assert.Empty(sink.ChangedEvents); - Assert.Single(sink.NotifiedEvents); - Assert.Empty(sink.DisconnectedEvents); - }); + ["169.254.109.177:5555\tdevice\n"], + ["host:track-devices"], + () => monitor.StartAsync()); + + Assert.Single(monitor.Devices); + Assert.Single(sink.ConnectedEvents); + Assert.Empty(sink.ChangedEvents); + Assert.Single(sink.NotifiedEvents); + Assert.Empty(sink.DisconnectedEvents); Socket.ResponseMessages.Clear(); Socket.Responses.Clear(); @@ -41,19 +38,16 @@ await RunTestAsync( RunTest( NoResponses, - ResponseMessages(""), - Requests(), - () => - { - eventWaiter.WaitOne(1000); - - Assert.Empty(monitor.Devices); - Assert.Single(sink.ConnectedEvents); - Assert.Empty(sink.ChangedEvents); - Assert.Single(sink.NotifiedEvents); - Assert.Single(sink.DisconnectedEvents); - Assert.Equal("169.254.109.177:5555", sink.DisconnectedEvents[0].Device.Serial); - }); + [string.Empty], + NoRequests, + () => _ = eventWaiter.WaitOne(1000)); + + Assert.Empty(monitor.Devices); + Assert.Single(sink.ConnectedEvents); + Assert.Empty(sink.ChangedEvents); + Assert.Single(sink.NotifiedEvents); + Assert.Single(sink.DisconnectedEvents); + Assert.Equal("169.254.109.177:5555", sink.DisconnectedEvents[0].Device.Serial); } [Fact] @@ -69,18 +63,15 @@ public async void DeviceConnectedAsyncTest() // Start the monitor, detect the initial device. await RunTestAsync( OkResponse, - ResponseMessages(""), - Requests("host:track-devices"), - async () => - { - await monitor.StartAsync(); - - Assert.Empty(monitor.Devices); - Assert.Empty(sink.ConnectedEvents); - Assert.Empty(sink.ChangedEvents); - Assert.Empty(sink.NotifiedEvents); - Assert.Empty(sink.DisconnectedEvents); - }); + [string.Empty], + ["host:track-devices"], + () => monitor.StartAsync()); + + Assert.Empty(monitor.Devices); + Assert.Empty(sink.ConnectedEvents); + Assert.Empty(sink.ChangedEvents); + Assert.Empty(sink.NotifiedEvents); + Assert.Empty(sink.DisconnectedEvents); Socket.ResponseMessages.Clear(); Socket.Responses.Clear(); @@ -91,19 +82,16 @@ await RunTestAsync( RunTest( NoResponses, - ResponseMessages("169.254.109.177:5555\tdevice\n"), - Requests(), - () => - { - eventWaiter.WaitOne(1000); - - Assert.Single(monitor.Devices); - Assert.Single(sink.ConnectedEvents); - Assert.Empty(sink.ChangedEvents); - Assert.Single(sink.NotifiedEvents); - Assert.Empty(sink.DisconnectedEvents); - Assert.Equal("169.254.109.177:5555", sink.ConnectedEvents[0].Device.Serial); - }); + ["169.254.109.177:5555\tdevice\n"], + NoRequests, + () => _ = eventWaiter.WaitOne(1000)); + + Assert.Single(monitor.Devices); + Assert.Single(sink.ConnectedEvents); + Assert.Empty(sink.ChangedEvents); + Assert.Single(sink.NotifiedEvents); + Assert.Empty(sink.DisconnectedEvents); + Assert.Equal("169.254.109.177:5555", sink.ConnectedEvents[0].Device.Serial); } [Fact] @@ -118,20 +106,17 @@ public async void StartInitialDeviceListAsyncTest() await RunTestAsync( OkResponse, - ResponseMessages("169.254.109.177:5555\tdevice\n"), - Requests("host:track-devices"), - async () => - { - await monitor.StartAsync(); - - Assert.Single(monitor.Devices); - Assert.Equal("169.254.109.177:5555", monitor.Devices.ElementAt(0).Serial); - Assert.Single(sink.ConnectedEvents); - Assert.Equal("169.254.109.177:5555", sink.ConnectedEvents[0].Device.Serial); - Assert.Empty(sink.ChangedEvents); - Assert.Single(sink.NotifiedEvents); - Assert.Empty(sink.DisconnectedEvents); - }); + ["169.254.109.177:5555\tdevice\n"], + ["host:track-devices"], + () => monitor.StartAsync()); + + Assert.Single(monitor.Devices); + Assert.Equal("169.254.109.177:5555", monitor.Devices.ElementAt(0).Serial); + Assert.Single(sink.ConnectedEvents); + Assert.Equal("169.254.109.177:5555", sink.ConnectedEvents[0].Device.Serial); + Assert.Empty(sink.ChangedEvents); + Assert.Single(sink.NotifiedEvents); + Assert.Empty(sink.DisconnectedEvents); } [Fact] @@ -147,19 +132,16 @@ public async void DeviceChanged_TriggeredWhenStatusChangedAsyncTest() // Start the monitor, detect the initial device. await RunTestAsync( OkResponse, - ResponseMessages("169.254.109.177:5555\toffline\n"), - Requests("host:track-devices"), - async () => - { - await monitor.StartAsync(); - - Assert.Single(monitor.Devices); - Assert.Equal(DeviceState.Offline, monitor.Devices.ElementAt(0).State); - Assert.Single(sink.ConnectedEvents); - Assert.Empty(sink.ChangedEvents); - Assert.Single(sink.NotifiedEvents); - Assert.Empty(sink.DisconnectedEvents); - }); + ["169.254.109.177:5555\toffline\n"], + ["host:track-devices"], + () => monitor.StartAsync()); + + Assert.Single(monitor.Devices); + Assert.Equal(DeviceState.Offline, monitor.Devices.ElementAt(0).State); + Assert.Single(sink.ConnectedEvents); + Assert.Empty(sink.ChangedEvents); + Assert.Single(sink.NotifiedEvents); + Assert.Empty(sink.DisconnectedEvents); Socket.ResponseMessages.Clear(); Socket.Responses.Clear(); @@ -172,20 +154,17 @@ await RunTestAsync( RunTest( NoResponses, - ResponseMessages("169.254.109.177:5555\tdevice\n"), - Requests(), - () => - { - eventWaiter.WaitOne(1000); - - Assert.Single(monitor.Devices); - Assert.Equal(DeviceState.Online, monitor.Devices.ElementAt(0).State); - Assert.Empty(sink.ConnectedEvents); - Assert.Single(sink.ChangedEvents); - Assert.Single(sink.NotifiedEvents); - Assert.Empty(sink.DisconnectedEvents); - Assert.Equal("169.254.109.177:5555", sink.ChangedEvents[0].Device.Serial); - }); + ["169.254.109.177:5555\tdevice\n"], + NoRequests, + () => _ = eventWaiter.WaitOne(1000)); + + Assert.Single(monitor.Devices); + Assert.Equal(DeviceState.Online, monitor.Devices.ElementAt(0).State); + Assert.Empty(sink.ConnectedEvents); + Assert.Single(sink.ChangedEvents); + Assert.Single(sink.NotifiedEvents); + Assert.Empty(sink.DisconnectedEvents); + Assert.Equal("169.254.109.177:5555", sink.ChangedEvents[0].Device.Serial); } [Fact] @@ -201,19 +180,16 @@ public async void DeviceChanged_NoTriggerIfStatusIsSameAsyncTest() // Start the monitor, detect the initial device. await RunTestAsync( OkResponse, - ResponseMessages("169.254.109.177:5555\toffline\n"), - Requests("host:track-devices"), - async () => - { - await monitor.StartAsync(); - - Assert.Single(monitor.Devices); - Assert.Equal(DeviceState.Offline, monitor.Devices.ElementAt(0).State); - Assert.Single(sink.ConnectedEvents); - Assert.Empty(sink.ChangedEvents); - Assert.Single(sink.NotifiedEvents); - Assert.Empty(sink.DisconnectedEvents); - }); + ["169.254.109.177:5555\toffline\n"], + ["host:track-devices"], + () => monitor.StartAsync()); + + Assert.Single(monitor.Devices); + Assert.Equal(DeviceState.Offline, monitor.Devices.ElementAt(0).State); + Assert.Single(sink.ConnectedEvents); + Assert.Empty(sink.ChangedEvents); + Assert.Single(sink.NotifiedEvents); + Assert.Empty(sink.DisconnectedEvents); Socket.ResponseMessages.Clear(); Socket.Responses.Clear(); @@ -226,19 +202,16 @@ await RunTestAsync( RunTest( NoResponses, - ResponseMessages("169.254.109.177:5555\toffline\n"), - Requests(), - () => - { - eventWaiter.WaitOne(1000); - - Assert.Single(monitor.Devices); - Assert.Equal(DeviceState.Offline, monitor.Devices.ElementAt(0).State); - Assert.Empty(sink.ConnectedEvents); - Assert.Empty(sink.ChangedEvents); - Assert.Single(sink.NotifiedEvents); - Assert.Empty(sink.DisconnectedEvents); - }); + ["169.254.109.177:5555\toffline\n"], + NoRequests, + () => _ = eventWaiter.WaitOne(1000)); + + Assert.Single(monitor.Devices); + Assert.Equal(DeviceState.Offline, monitor.Devices.ElementAt(0).State); + Assert.Empty(sink.ConnectedEvents); + Assert.Empty(sink.ChangedEvents); + Assert.Single(sink.NotifiedEvents); + Assert.Empty(sink.DisconnectedEvents); } /// @@ -255,20 +228,13 @@ public async void AdbKilledAsyncTest() await using DeviceMonitor monitor = new(Socket); await RunTestAsync( - new AdbResponse[] { AdbResponse.OK, AdbResponse.OK }, - ResponseMessages( - DummyAdbSocket.ServerDisconnected, - string.Empty), - Requests( - "host:track-devices", - "host:track-devices"), - async () => - { - await monitor.StartAsync(); - - Assert.True(Socket.DidReconnect); - Assert.True(dummyAdbServer.WasRestarted); - }); + OkResponses(2), + [DummyAdbSocket.ServerDisconnected, string.Empty], + ["host:track-devices", "host:track-devices"], + () => monitor.StartAsync()); + + Assert.True(Socket.DidReconnect); + Assert.True(dummyAdbServer.WasRestarted); } } } diff --git a/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs b/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs index 67576303..19abce88 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs @@ -44,18 +44,15 @@ public void DeviceDisconnectedTest() // Start the monitor, detect the initial device. RunTest( OkResponse, - ResponseMessages("169.254.109.177:5555\tdevice\n"), - Requests("host:track-devices"), - () => - { - monitor.Start(); - - Assert.Single(monitor.Devices); - Assert.Single(sink.ConnectedEvents); - Assert.Empty(sink.ChangedEvents); - Assert.Single(sink.NotifiedEvents); - Assert.Empty(sink.DisconnectedEvents); - }); + ["169.254.109.177:5555\tdevice\n"], + ["host:track-devices"], + monitor.Start); + + Assert.Single(monitor.Devices); + Assert.Single(sink.ConnectedEvents); + Assert.Empty(sink.ChangedEvents); + Assert.Single(sink.NotifiedEvents); + Assert.Empty(sink.DisconnectedEvents); Socket.ResponseMessages.Clear(); Socket.Responses.Clear(); @@ -66,19 +63,16 @@ public void DeviceDisconnectedTest() RunTest( NoResponses, - ResponseMessages(""), - Requests(), - () => - { - eventWaiter.WaitOne(1000); - - Assert.Empty(monitor.Devices); - Assert.Single(sink.ConnectedEvents); - Assert.Empty(sink.ChangedEvents); - Assert.Single(sink.NotifiedEvents); - Assert.Single(sink.DisconnectedEvents); - Assert.Equal("169.254.109.177:5555", sink.DisconnectedEvents[0].Device.Serial); - }); + [string.Empty], + NoRequests, + () => _ = eventWaiter.WaitOne(1000)); + + Assert.Empty(monitor.Devices); + Assert.Single(sink.ConnectedEvents); + Assert.Empty(sink.ChangedEvents); + Assert.Single(sink.NotifiedEvents); + Assert.Single(sink.DisconnectedEvents); + Assert.Equal("169.254.109.177:5555", sink.DisconnectedEvents[0].Device.Serial); } [Fact] @@ -94,18 +88,15 @@ public void DeviceConnectedTest() // Start the monitor, detect the initial device. RunTest( OkResponse, - ResponseMessages(""), - Requests("host:track-devices"), - () => - { - monitor.Start(); - - Assert.Empty(monitor.Devices); - Assert.Empty(sink.ConnectedEvents); - Assert.Empty(sink.ChangedEvents); - Assert.Empty(sink.NotifiedEvents); - Assert.Empty(sink.DisconnectedEvents); - }); + [string.Empty], + ["host:track-devices"], + monitor.Start); + + Assert.Empty(monitor.Devices); + Assert.Empty(sink.ConnectedEvents); + Assert.Empty(sink.ChangedEvents); + Assert.Empty(sink.NotifiedEvents); + Assert.Empty(sink.DisconnectedEvents); Socket.ResponseMessages.Clear(); Socket.Responses.Clear(); @@ -116,19 +107,16 @@ public void DeviceConnectedTest() RunTest( NoResponses, - ResponseMessages("169.254.109.177:5555\tdevice\n"), - Requests(), - () => - { - eventWaiter.WaitOne(1000); - - Assert.Single(monitor.Devices); - Assert.Single(sink.ConnectedEvents); - Assert.Empty(sink.ChangedEvents); - Assert.Single(sink.NotifiedEvents); - Assert.Empty(sink.DisconnectedEvents); - Assert.Equal("169.254.109.177:5555", sink.ConnectedEvents[0].Device.Serial); - }); + ["169.254.109.177:5555\tdevice\n"], + NoRequests, + () => _ = eventWaiter.WaitOne(1000)); + + Assert.Single(monitor.Devices); + Assert.Single(sink.ConnectedEvents); + Assert.Empty(sink.ChangedEvents); + Assert.Single(sink.NotifiedEvents); + Assert.Empty(sink.DisconnectedEvents); + Assert.Equal("169.254.109.177:5555", sink.ConnectedEvents[0].Device.Serial); } [Fact] @@ -143,20 +131,17 @@ public void StartInitialDeviceListTest() RunTest( OkResponse, - ResponseMessages("169.254.109.177:5555\tdevice\n"), - Requests("host:track-devices"), - () => - { - monitor.Start(); - - Assert.Single(monitor.Devices); - Assert.Equal("169.254.109.177:5555", monitor.Devices.ElementAt(0).Serial); - Assert.Single(sink.ConnectedEvents); - Assert.Equal("169.254.109.177:5555", sink.ConnectedEvents[0].Device.Serial); - Assert.Empty(sink.ChangedEvents); - Assert.Single(sink.NotifiedEvents); - Assert.Empty(sink.DisconnectedEvents); - }); + ["169.254.109.177:5555\tdevice\n"], + ["host:track-devices"], + monitor.Start); + + Assert.Single(monitor.Devices); + Assert.Equal("169.254.109.177:5555", monitor.Devices[0].Serial); + Assert.Single(sink.ConnectedEvents); + Assert.Equal("169.254.109.177:5555", sink.ConnectedEvents[0].Device.Serial); + Assert.Empty(sink.ChangedEvents); + Assert.Single(sink.NotifiedEvents); + Assert.Empty(sink.DisconnectedEvents); } [Fact] @@ -172,19 +157,16 @@ public void DeviceChanged_TriggeredWhenStatusChangedTest() // Start the monitor, detect the initial device. RunTest( OkResponse, - ResponseMessages("169.254.109.177:5555\toffline\n"), - Requests("host:track-devices"), - () => - { - monitor.Start(); - - Assert.Single(monitor.Devices); - Assert.Equal(DeviceState.Offline, monitor.Devices.ElementAt(0).State); - Assert.Single(sink.ConnectedEvents); - Assert.Empty(sink.ChangedEvents); - Assert.Single(sink.NotifiedEvents); - Assert.Empty(sink.DisconnectedEvents); - }); + ["169.254.109.177:5555\toffline\n"], + ["host:track-devices"], + monitor.Start); + + Assert.Single(monitor.Devices); + Assert.Equal(DeviceState.Offline, monitor.Devices[0].State); + Assert.Single(sink.ConnectedEvents); + Assert.Empty(sink.ChangedEvents); + Assert.Single(sink.NotifiedEvents); + Assert.Empty(sink.DisconnectedEvents); Socket.ResponseMessages.Clear(); Socket.Responses.Clear(); @@ -197,20 +179,17 @@ public void DeviceChanged_TriggeredWhenStatusChangedTest() RunTest( NoResponses, - ResponseMessages("169.254.109.177:5555\tdevice\n"), - Requests(), - () => - { - eventWaiter.WaitOne(1000); - - Assert.Single(monitor.Devices); - Assert.Equal(DeviceState.Online, monitor.Devices.ElementAt(0).State); - Assert.Empty(sink.ConnectedEvents); - Assert.Single(sink.ChangedEvents); - Assert.Single(sink.NotifiedEvents); - Assert.Empty(sink.DisconnectedEvents); - Assert.Equal("169.254.109.177:5555", sink.ChangedEvents[0].Device.Serial); - }); + ["169.254.109.177:5555\tdevice\n"], + NoRequests, + () => _ = eventWaiter.WaitOne(1000)); + + Assert.Single(monitor.Devices); + Assert.Equal(DeviceState.Online, monitor.Devices[0].State); + Assert.Empty(sink.ConnectedEvents); + Assert.Single(sink.ChangedEvents); + Assert.Single(sink.NotifiedEvents); + Assert.Empty(sink.DisconnectedEvents); + Assert.Equal("169.254.109.177:5555", sink.ChangedEvents[0].Device.Serial); } [Fact] @@ -226,19 +205,16 @@ public void DeviceChanged_NoTriggerIfStatusIsSameTest() // Start the monitor, detect the initial device. RunTest( OkResponse, - ResponseMessages("169.254.109.177:5555\toffline\n"), - Requests("host:track-devices"), - () => - { - monitor.Start(); - - Assert.Single(monitor.Devices); - Assert.Equal(DeviceState.Offline, monitor.Devices.ElementAt(0).State); - Assert.Single(sink.ConnectedEvents); - Assert.Empty(sink.ChangedEvents); - Assert.Single(sink.NotifiedEvents); - Assert.Empty(sink.DisconnectedEvents); - }); + ["169.254.109.177:5555\toffline\n"], + ["host:track-devices"], + monitor.Start); + + Assert.Single(monitor.Devices); + Assert.Equal(DeviceState.Offline, monitor.Devices[0].State); + Assert.Single(sink.ConnectedEvents); + Assert.Empty(sink.ChangedEvents); + Assert.Single(sink.NotifiedEvents); + Assert.Empty(sink.DisconnectedEvents); Socket.ResponseMessages.Clear(); Socket.Responses.Clear(); @@ -251,19 +227,16 @@ public void DeviceChanged_NoTriggerIfStatusIsSameTest() RunTest( NoResponses, - ResponseMessages("169.254.109.177:5555\toffline\n"), - Requests(), - () => - { - eventWaiter.WaitOne(1000); - - Assert.Single(monitor.Devices); - Assert.Equal(DeviceState.Offline, monitor.Devices.ElementAt(0).State); - Assert.Empty(sink.ConnectedEvents); - Assert.Empty(sink.ChangedEvents); - Assert.Single(sink.NotifiedEvents); - Assert.Empty(sink.DisconnectedEvents); - }); + ["169.254.109.177:5555\toffline\n"], + NoRequests, + () => _ = eventWaiter.WaitOne(1000)); + + Assert.Single(monitor.Devices); + Assert.Equal(DeviceState.Offline, monitor.Devices[0].State); + Assert.Empty(sink.ConnectedEvents); + Assert.Empty(sink.ChangedEvents); + Assert.Single(sink.NotifiedEvents); + Assert.Empty(sink.DisconnectedEvents); } /// @@ -280,20 +253,13 @@ public void AdbKilledTest() using DeviceMonitor monitor = new(Socket); RunTest( - [AdbResponse.OK, AdbResponse.OK], - ResponseMessages( - DummyAdbSocket.ServerDisconnected, - string.Empty), - Requests( - "host:track-devices", - "host:track-devices"), - () => - { - monitor.Start(); - - Assert.True(Socket.DidReconnect); - Assert.True(dummyAdbServer.WasRestarted); - }); + OkResponses(2), + [DummyAdbSocket.ServerDisconnected, string.Empty], + ["host:track-devices", "host:track-devices"], + monitor.Start); + + Assert.True(Socket.DidReconnect); + Assert.True(dummyAdbServer.WasRestarted); } } } diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs index 0c39933a..bb25536d 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs @@ -9,7 +9,7 @@ namespace AdvancedSharpAdbClient.Tests { - internal class DummyAdbSocket : IAdbSocket, IDummyAdbSocket + internal class DummyAdbSocket : IDummyAdbSocket { /// /// Use this message to cause and to throw @@ -52,7 +52,7 @@ public void Send(byte[] data, int length) public void Send(byte[] data, int offset, int length) { - SyncDataSent.Enqueue(data[offset..(offset + length)]); + SyncDataSent.Enqueue(data.AsSpan(offset, length).ToArray()); } public void Send(ReadOnlySpan data) @@ -72,27 +72,24 @@ public void Send(ReadOnlySpan data) public int Read(byte[] data, int length) { - byte[] actual = SyncDataReceived.Dequeue(); + Span actual = SyncDataReceived.Dequeue(); Assert.True(actual.Length >= length); - Buffer.BlockCopy(actual, 0, data, 0, length); - return Math.Min(actual.Length, length); + Assert.True(actual[..length].TryCopyTo(data)); + return length; } public int Read(byte[] data, int offset, int length) { - byte[] actual = SyncDataReceived.Dequeue(); + Span actual = SyncDataReceived.Dequeue(); Assert.True(actual.Length >= length); - Buffer.BlockCopy(actual, 0, data, offset, length); - return Math.Min(actual.Length, length); + Assert.True(actual[..length].TryCopyTo(data.AsSpan(offset))); + return length; } public int Read(Span data) { - byte[] actual = SyncDataReceived.Dequeue(); - for (int i = 0; i < data.Length && i < actual.Length; i++) - { - data[i] = actual[i]; - } + Span actual = SyncDataReceived.Dequeue(); + Assert.True(actual[..Math.Min(actual.Length, data.Length)].TryCopyTo(data)); return actual.Length; } diff --git a/AdvancedSharpAdbClient.Tests/Dummys/IDummyAdbSocket.cs b/AdvancedSharpAdbClient.Tests/Dummys/IDummyAdbSocket.cs index 94dc2da0..b0257854 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/IDummyAdbSocket.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/IDummyAdbSocket.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; namespace AdvancedSharpAdbClient.Tests diff --git a/AdvancedSharpAdbClient.Tests/Dummys/TracingAdbSocket.cs b/AdvancedSharpAdbClient.Tests/Dummys/TracingAdbSocket.cs index c6c8c0aa..02635447 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/TracingAdbSocket.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/TracingAdbSocket.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; @@ -50,7 +49,35 @@ public override int Read(byte[] data, int length) if (trace != null && trace.GetFrames()[1].GetMethod().DeclaringType != typeof(AdbSocket)) { - SyncDataReceived.Enqueue(data.Take(length).ToArray()); + SyncDataReceived.Enqueue(data[..length]); + } + + return read; + } + + public override int Read(byte[] data, int offset, int length) + { + StackTrace trace = new(); + + int read = base.Read(data, offset, length); + + if (trace != null && trace.GetFrames()[1].GetMethod().DeclaringType != typeof(AdbSocket)) + { + SyncDataReceived.Enqueue(data.AsSpan(offset, length).ToArray()); + } + + return read; + } + + public override int Read(Span data) + { + StackTrace trace = new(); + + int read = base.Read(data); + + if (trace != null && trace.GetFrames()[1].GetMethod().DeclaringType != typeof(AdbSocket)) + { + SyncDataReceived.Enqueue(data.ToArray()); } return read; diff --git a/AdvancedSharpAdbClient.Tests/Extensions/ExceptionExtensionsTests.cs b/AdvancedSharpAdbClient.Tests/Extensions/ExceptionExtensionsTests.cs index 61405f28..8de7a169 100644 --- a/AdvancedSharpAdbClient.Tests/Extensions/ExceptionExtensionsTests.cs +++ b/AdvancedSharpAdbClient.Tests/Extensions/ExceptionExtensionsTests.cs @@ -14,7 +14,7 @@ public class ExceptionExtensionsTests [InlineData("name")] public void ThrowIfNullTest(string paramName) { - foreach (object o in new[] { new object(), "", "argument" }) + foreach (object o in new[] { new object(), string.Empty, "argument" }) { ExceptionExtensions.ThrowIfNull(o); ExceptionExtensions.ThrowIfNull(o, nameof(paramName)); diff --git a/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs b/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs index dcac2be8..515af61e 100644 --- a/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs +++ b/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs @@ -51,6 +51,9 @@ protected SocketBasedTests(bool integrationTest, bool doDispose) protected static AdbResponse[] NoResponses { get; } = []; protected static AdbResponse[] OkResponse { get; } = [AdbResponse.OK]; protected static string[] NoResponseMessages { get; } = []; + protected static string[] NoRequests { get; } = []; + protected static (SyncCommand, string)[] NoSyncRequests { get; } = []; + protected static SyncCommand[] NoSyncResponses { get; } = []; protected static DeviceData Device { get; } = new() { Serial = "169.254.109.177:5555", @@ -259,11 +262,11 @@ protected void RunTest( Assert.Empty(Socket.SyncDataReceived); // Make sure a request was sent - Assert.Equal(requests.ToList(), Socket.Requests); + Assert.Equal(requests.ToArray(), Socket.Requests); if (syncRequests != null) { - Assert.Equal(syncRequests.ToList(), Socket.SyncRequests); + Assert.Equal(syncRequests.ToArray(), Socket.SyncRequests); } else { @@ -272,7 +275,7 @@ protected void RunTest( if (syncDataSent != null) { - AssertEqual(syncDataSent.ToList(), Socket.SyncDataSent.ToList()); + AssertEqual(syncDataSent.ToArray(), Socket.SyncDataSent.ToArray()); } else { @@ -283,23 +286,23 @@ protected void RunTest( { // Make sure the traffic sent on the wire matches the traffic // we have defined in our unit test. - Assert.Equal(requests.ToList(), Socket.Requests); + Assert.Equal(requests.ToArray(), Socket.Requests); if (syncRequests != null) { - Assert.Equal(syncRequests.ToList(), Socket.SyncRequests); + Assert.Equal(syncRequests.ToArray(), Socket.SyncRequests); } else { Assert.Empty(Socket.SyncRequests); } - Assert.Equal(responses.ToList(), Socket.Responses); - Assert.Equal(responseMessages.ToList(), Socket.ResponseMessages); + Assert.Equal(responses.ToArray(), Socket.Responses); + Assert.Equal(responseMessages.ToArray(), Socket.ResponseMessages); if (syncResponses != null) { - Assert.Equal(syncResponses.ToList(), Socket.SyncResponses); + Assert.Equal(syncResponses.ToArray(), Socket.SyncResponses); } else { @@ -308,7 +311,7 @@ protected void RunTest( if (syncDataReceived != null) { - AssertEqual(syncDataReceived.ToList(), Socket.SyncDataReceived.ToList()); + AssertEqual(syncDataReceived.ToArray(), Socket.SyncDataReceived.ToArray()); } else { @@ -317,7 +320,7 @@ protected void RunTest( if (syncDataSent != null) { - AssertEqual(syncDataSent.ToList(), Socket.SyncDataSent.ToList()); + AssertEqual(syncDataSent.ToArray(), Socket.SyncDataSent.ToArray()); } else { @@ -535,11 +538,11 @@ protected async Task RunTestAsync( Assert.Empty(Socket.SyncDataReceived); // Make sure a request was sent - Assert.Equal(requests.ToList(), Socket.Requests); + Assert.Equal(requests.ToArray(), Socket.Requests); if (syncRequests != null) { - Assert.Equal(syncRequests.ToList(), Socket.SyncRequests); + Assert.Equal(syncRequests.ToArray(), Socket.SyncRequests); } else { @@ -548,7 +551,7 @@ protected async Task RunTestAsync( if (syncDataSent != null) { - AssertEqual(syncDataSent.ToList(), Socket.SyncDataSent.ToList()); + AssertEqual(syncDataSent.ToArray(), Socket.SyncDataSent.ToArray()); } else { @@ -559,23 +562,23 @@ protected async Task RunTestAsync( { // Make sure the traffic sent on the wire matches the traffic // we have defined in our unit test. - Assert.Equal(requests.ToList(), Socket.Requests); + Assert.Equal(requests.ToArray(), Socket.Requests); if (syncRequests != null) { - Assert.Equal(syncRequests.ToList(), Socket.SyncRequests); + Assert.Equal(syncRequests.ToArray(), Socket.SyncRequests); } else { Assert.Empty(Socket.SyncRequests); } - Assert.Equal(responses.ToList(), Socket.Responses); - Assert.Equal(responseMessages.ToList(), Socket.ResponseMessages); + Assert.Equal(responses.ToArray(), Socket.Responses); + Assert.Equal(responseMessages.ToArray(), Socket.ResponseMessages); if (syncResponses != null) { - Assert.Equal(syncResponses.ToList(), Socket.SyncResponses); + Assert.Equal(syncResponses.ToArray(), Socket.SyncResponses); } else { @@ -584,7 +587,7 @@ protected async Task RunTestAsync( if (syncDataReceived != null) { - AssertEqual(syncDataReceived.ToList(), Socket.SyncDataReceived.ToList()); + AssertEqual(syncDataReceived.ToArray(), Socket.SyncDataReceived.ToArray()); } else { @@ -593,7 +596,7 @@ protected async Task RunTestAsync( if (syncDataSent != null) { - AssertEqual(syncDataSent.ToList(), Socket.SyncDataSent.ToList()); + AssertEqual(syncDataSent.ToArray(), Socket.SyncDataSent.ToArray()); } else { @@ -607,35 +610,7 @@ protected async Task RunTestAsync( } } - protected static IEnumerable Requests(params string[] requests) => requests; - - protected static IEnumerable ResponseMessages(params string[] requests) => requests; - - protected static IEnumerable<(SyncCommand, string)> SyncRequests(SyncCommand command, string path) - { - yield return (command, path); - } - - protected static IEnumerable<(SyncCommand, string)> SyncRequests(SyncCommand command, string path, SyncCommand command2, string path2) - { - yield return (command, path); - yield return (command2, path2); - } - - protected static IEnumerable<(SyncCommand, string)> SyncRequests(SyncCommand command, string path, SyncCommand command2, string path2, SyncCommand command3, string path3) - { - yield return (command, path); - yield return (command2, path2); - yield return (command3, path3); - } - - protected static IEnumerable OkResponses(int count) - { - for (int i = 0; i < count; i++) - { - yield return AdbResponse.OK; - } - } + protected static IEnumerable OkResponses(int count) => Enumerable.Repeat(AdbResponse.OK, count); private static void AssertEqual(IList expected, IList actual) { diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs index 85064140..ce1de291 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs @@ -12,25 +12,19 @@ public partial class SyncServiceTests [Fact] public async void StatAsyncTest() { - DeviceData device = new() - { - Serial = "169.254.109.177:5555", - State = DeviceState.Online - }; - FileStatistics value = null; await RunTestAsync( OkResponses(2), NoResponseMessages, - Requests("host:transport:169.254.109.177:5555", "sync:"), - SyncRequests(SyncCommand.STAT, "/fstab.donatello"), + ["host:transport:169.254.109.177:5555", "sync:"], + [(SyncCommand.STAT, "/fstab.donatello")], [SyncCommand.STAT], [[160, 129, 0, 0, 85, 2, 0, 0, 0, 0, 0, 0]], null, async () => { - using SyncService service = new(Socket, device); + using SyncService service = new(Socket, Device); value = await service.StatAsync("/fstab.donatello"); }); @@ -43,19 +37,13 @@ await RunTestAsync( [Fact] public async void GetListingAsyncTest() { - DeviceData device = new() - { - Serial = "169.254.109.177:5555", - State = DeviceState.Online - }; - List value = null; await RunTestAsync( OkResponses(2), - ResponseMessages(".", "..", "sdcard0", "emulated"), - Requests("host:transport:169.254.109.177:5555", "sync:"), - SyncRequests(SyncCommand.LIST, "/storage"), + [".", "..", "sdcard0", "emulated"], + ["host:transport:169.254.109.177:5555", "sync:"], + [(SyncCommand.LIST, "/storage")], [SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DONE], [ [233, 65, 0, 0, 0, 0, 0, 0, 152, 130, 56, 86], @@ -66,7 +54,7 @@ await RunTestAsync( null, async () => { - using SyncService service = new(Socket, device); + using SyncService service = new(Socket, Device); value = (await service.GetDirectoryListingAsync("/storage")).ToList(); }); @@ -102,19 +90,13 @@ await RunTestAsync( [Fact] public async void GetAsyncListingTest() { - DeviceData device = new() - { - Serial = "169.254.109.177:5555", - State = DeviceState.Online - }; - List value = null; await RunTestAsync( OkResponses(2), - ResponseMessages(".", "..", "sdcard0", "emulated"), - Requests("host:transport:169.254.109.177:5555", "sync:"), - SyncRequests(SyncCommand.LIST, "/storage"), + [".", "..", "sdcard0", "emulated"], + ["host:transport:169.254.109.177:5555", "sync:"], + [(SyncCommand.LIST, "/storage")], [SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DONE], [ [233, 65, 0, 0, 0, 0, 0, 0, 152, 130, 56, 86], @@ -125,7 +107,7 @@ await RunTestAsync( null, async () => { - using SyncService service = new(Socket, device); + using SyncService service = new(Socket, Device); value = []; await foreach (FileStatistics statistics in service.GetDirectoryAsyncListing("/storage")) { @@ -165,21 +147,18 @@ await RunTestAsync( [Fact] public async void PullAsyncTest() { - DeviceData device = new() - { - Serial = "169.254.109.177:5555", - State = DeviceState.Online - }; - await using MemoryStream stream = new(); byte[] content = File.ReadAllBytes("Assets/fstab.bin"); byte[] contentLength = BitConverter.GetBytes(content.Length); await RunTestAsync( OkResponses(2), - ResponseMessages(), - Requests("host:transport:169.254.109.177:5555", "sync:"), - SyncRequests(SyncCommand.STAT, "/fstab.donatello").Union(SyncRequests(SyncCommand.RECV, "/fstab.donatello")), + NoResponseMessages, + ["host:transport:169.254.109.177:5555", "sync:"], + [ + (SyncCommand.STAT, "/fstab.donatello"), + (SyncCommand.RECV, "/fstab.donatello") + ], [SyncCommand.STAT, SyncCommand.DATA, SyncCommand.DONE], [ [160, 129, 0, 0, 85, 2, 0, 0, 0, 0, 0, 0], @@ -189,7 +168,7 @@ await RunTestAsync( null, async () => { - using SyncService service = new(Socket, device); + using SyncService service = new(Socket, Device); await service.PullAsync("/fstab.donatello", stream, null, CancellationToken.None); }); @@ -200,15 +179,9 @@ await RunTestAsync( [Fact] public async void PushAsyncTest() { - DeviceData device = new() - { - Serial = "169.254.109.177:5555", - State = DeviceState.Online - }; - FileStream stream = File.OpenRead("Assets/fstab.bin"); byte[] content = File.ReadAllBytes("Assets/fstab.bin"); - List contentMessage = + byte[] contentMessage = [ .. SyncCommandConverter.GetBytes(SyncCommand.DATA), .. BitConverter.GetBytes(content.Length), @@ -217,17 +190,18 @@ .. BitConverter.GetBytes(content.Length), await RunTestAsync( OkResponses(2), - ResponseMessages(), - Requests("host:transport:169.254.109.177:5555", "sync:"), - SyncRequests( - SyncCommand.SEND, "/sdcard/test,644", - SyncCommand.DONE, "1446505200"), + NoResponseMessages, + ["host:transport:169.254.109.177:5555", "sync:"], + [ + (SyncCommand.SEND, "/sdcard/test,644"), + (SyncCommand.DONE, "1446505200") + ], [SyncCommand.OKAY], null, - [[.. contentMessage]], + [contentMessage], async () => { - using SyncService service = new(Socket, device); + using SyncService service = new(Socket, Device); await service.PushAsync(stream, "/sdcard/test", 0644, new DateTime(2015, 11, 2, 23, 0, 0, DateTimeKind.Utc), null, CancellationToken.None); }); } diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs index 0bcc2a00..17b10fe6 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs @@ -22,25 +22,19 @@ public SyncServiceTests() : base(integrationTest: false, doDispose: false) [Fact] public void StatTest() { - DeviceData device = new() - { - Serial = "169.254.109.177:5555", - State = DeviceState.Online - }; - FileStatistics value = null; RunTest( OkResponses(2), NoResponseMessages, - Requests("host:transport:169.254.109.177:5555", "sync:"), - SyncRequests(SyncCommand.STAT, "/fstab.donatello"), + ["host:transport:169.254.109.177:5555", "sync:"], + [(SyncCommand.STAT, "/fstab.donatello")], [SyncCommand.STAT], [[160, 129, 0, 0, 85, 2, 0, 0, 0, 0, 0, 0]], null, () => { - using SyncService service = new(Socket, device); + using SyncService service = new(Socket, Device); value = service.Stat("/fstab.donatello"); }); @@ -53,19 +47,13 @@ public void StatTest() [Fact] public void GetListingTest() { - DeviceData device = new() - { - Serial = "169.254.109.177:5555", - State = DeviceState.Online - }; - List value = null; RunTest( OkResponses(2), - ResponseMessages(".", "..", "sdcard0", "emulated"), - Requests("host:transport:169.254.109.177:5555", "sync:"), - SyncRequests(SyncCommand.LIST, "/storage"), + [".", "..", "sdcard0", "emulated"], + ["host:transport:169.254.109.177:5555", "sync:"], + [(SyncCommand.LIST, "/storage")], [SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DENT, SyncCommand.DONE], [ [233, 65, 0, 0, 0, 0, 0, 0, 152, 130, 56, 86], @@ -76,7 +64,7 @@ public void GetListingTest() null, () => { - using SyncService service = new(Socket, device); + using SyncService service = new(Socket, Device); value = service.GetDirectoryListing("/storage").ToList(); }); @@ -112,21 +100,18 @@ public void GetListingTest() [Fact] public void PullTest() { - DeviceData device = new() - { - Serial = "169.254.109.177:5555", - State = DeviceState.Online - }; - using MemoryStream stream = new(); byte[] content = File.ReadAllBytes("Assets/fstab.bin"); byte[] contentLength = BitConverter.GetBytes(content.Length); RunTest( OkResponses(2), - ResponseMessages(), - Requests("host:transport:169.254.109.177:5555", "sync:"), - SyncRequests(SyncCommand.STAT, "/fstab.donatello").Union(SyncRequests(SyncCommand.RECV, "/fstab.donatello")), + NoResponseMessages, + ["host:transport:169.254.109.177:5555", "sync:"], + [ + (SyncCommand.STAT, "/fstab.donatello"), + (SyncCommand.RECV, "/fstab.donatello") + ], [SyncCommand.STAT, SyncCommand.DATA, SyncCommand.DONE], [ [160, 129, 0, 0, 85, 2, 0, 0, 0, 0, 0, 0], @@ -136,7 +121,7 @@ public void PullTest() null, () => { - using SyncService service = new(Socket, device); + using SyncService service = new(Socket, Device); service.Pull("/fstab.donatello", stream); }); @@ -147,15 +132,9 @@ public void PullTest() [Fact] public void PushTest() { - DeviceData device = new() - { - Serial = "169.254.109.177:5555", - State = DeviceState.Online - }; - FileStream stream = File.OpenRead("Assets/fstab.bin"); byte[] content = File.ReadAllBytes("Assets/fstab.bin"); - List contentMessage = + byte[] contentMessage = [ .. SyncCommandConverter.GetBytes(SyncCommand.DATA), .. BitConverter.GetBytes(content.Length), @@ -164,17 +143,18 @@ .. BitConverter.GetBytes(content.Length), RunTest( OkResponses(2), - ResponseMessages(), - Requests("host:transport:169.254.109.177:5555", "sync:"), - SyncRequests( - SyncCommand.SEND, "/sdcard/test,644", - SyncCommand.DONE, "1446505200"), + NoResponseMessages, + ["host:transport:169.254.109.177:5555", "sync:"], + [ + (SyncCommand.SEND, "/sdcard/test,644"), + (SyncCommand.DONE, "1446505200") + ], [SyncCommand.OKAY], null, - [[.. contentMessage]], + [contentMessage], () => { - using SyncService service = new(Socket, device); + using SyncService service = new(Socket, Device); service.Push(stream, "/sdcard/test", 0644, new DateTime(2015, 11, 2, 23, 0, 0, DateTimeKind.Utc)); }); } diff --git a/AdvancedSharpAdbClient/Models/Element.cs b/AdvancedSharpAdbClient/Models/Element.cs index 72c9271d..103d00db 100644 --- a/AdvancedSharpAdbClient/Models/Element.cs +++ b/AdvancedSharpAdbClient/Models/Element.cs @@ -14,48 +14,6 @@ namespace AdvancedSharpAdbClient /// public class Element { - /// - /// Gets or sets the current ADB client that manages the connection. - /// - protected IAdbClient Client { get; set; } - - /// - /// Gets the current device containing the element. - /// - protected DeviceData Device { get; } - - /// - /// Gets the coordinates and size of the element. - /// - public Area Area { get; } - - /// - /// Gets or sets the coordinates of the element to click. Default is the center of area. - /// - public Cords Cords { get; set; } - - /// - /// Gets the children of this element. - /// - public List Children { get; } - - /// - /// Gets the element attributes. - /// - public Dictionary Attributes { get; } - - /// - /// Gets the of this element. - /// - public XmlNode Node { get; } - - /// - /// Gets or sets the element at the specified index. - /// - /// The zero-based index of the element to get or set. - /// The element at the specified index. - public Element this[int index] => Children[index]; - /// /// Initializes a new instance of the class. /// @@ -63,7 +21,7 @@ public class Element /// The current device containing the element. /// The coordinates of the element to click. /// Gets or sets element attributes. - public Element(IAdbClient client, DeviceData device, Cords cords, Dictionary attributes) + public Element(IAdbClient client, DeviceData device, Cords cords, Dictionary attributes = null) { Client = client; Device = device; @@ -78,7 +36,7 @@ public Element(IAdbClient client, DeviceData device, Cords cords, DictionaryThe current device containing the element. /// The coordinates and size of the element. /// Gets or sets element attributes. - public Element(IAdbClient client, DeviceData device, Area area, Dictionary attributes) + public Element(IAdbClient client, DeviceData device, Area area, Dictionary attributes = null) { Client = client; Device = device; @@ -96,7 +54,7 @@ public Element(IAdbClient client, DeviceData device, Area area, DictionaryThe children of the element. /// The coordinates and size of the element. /// Gets or sets element attributes. - public Element(IAdbClient client, DeviceData device, XmlNode node, List children, Area area, Dictionary attributes) + public Element(IAdbClient client, DeviceData device, XmlNode node, List children, Area area, Dictionary attributes = null) { Client = client; Device = device; @@ -117,7 +75,7 @@ public Element(IAdbClient client, DeviceData device, XmlNode node, List /// The children of the element. /// The coordinates and size of the element. /// Gets or sets element attributes. - public Element(IAdbClient client, DeviceData device, Windows.Data.Xml.Dom.IXmlNode node, List children, Area area, Dictionary attributes) + public Element(IAdbClient client, DeviceData device, Windows.Data.Xml.Dom.IXmlNode node, List children, Area area, Dictionary attributes = null) { XmlDocument doc = new(); doc.LoadXml(node.GetXml()); @@ -132,6 +90,48 @@ public Element(IAdbClient client, DeviceData device, Windows.Data.Xml.Dom.IXmlNo } #endif + /// + /// Gets or sets the current ADB client that manages the connection. + /// + protected IAdbClient Client { get; set; } + + /// + /// Gets the current device containing the element. + /// + protected DeviceData Device { get; } + + /// + /// Gets the coordinates and size of the element. + /// + public Area Area { get; } + + /// + /// Gets or sets the coordinates of the element to click. Default is the center of area. + /// + public Cords Cords { get; set; } + + /// + /// Gets the children of this element. + /// + public List Children { get; } + + /// + /// Gets the element attributes. + /// + public Dictionary Attributes { get; } + + /// + /// Gets the of this element. + /// + public XmlNode Node { get; } + + /// + /// Gets or sets the element at the specified index. + /// + /// The zero-based index of the element to get or set. + /// The element at the specified index. + public Element this[int index] => Children[index]; + /// /// Creates a new with the specified . /// From 213cf214a2d1c126184ecddb38c72b328baa21a9 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Tue, 3 Oct 2023 20:10:57 +0800 Subject: [PATCH 25/66] SocketBasedTests can test multi ShellStream now --- .../AdbClientTests.Async.cs | 75 ++++++++++++------- .../AdbClientTests.cs | 74 +++++++++++------- .../DeviceMonitorTests.cs | 1 - .../Dummys/DummyAdbSocket.cs | 8 +- .../Dummys/IDummyAdbSocket.cs | 7 +- .../Dummys/TracingAdbSocket.cs | 18 ++++- .../SocketBasedTests.cs | 60 +++++++++++---- AdvancedSharpAdbClient/AdbClient.Async.cs | 2 +- 8 files changed, 163 insertions(+), 82 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs index e309d720..13475608 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs @@ -274,7 +274,7 @@ await RunTestAsync( OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], () => TestClient.ExecuteRemoteCommandAsync("echo Hello, World", Device, receiver)); Assert.Equal("Hello, World\r\n", receiver.ToString(), ignoreLineEndingDifferences: true); @@ -392,7 +392,7 @@ await RunTestAsync( OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], () => TestClient.RunLogServiceAsync(Device, sink, CancellationToken.None, LogId.System)); Assert.Equal(3, logs.Count); @@ -689,7 +689,7 @@ await RunTestAsync( OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], async () => session = await TestClient.InstallCreateAsync(Device, "com.google.android.gms")); Assert.Equal("936013062", session); @@ -764,7 +764,7 @@ await RunTestAsync( OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], () => TestClient.InstallCommitAsync(Device, "936013062")); } @@ -811,7 +811,7 @@ await RunTestAsync( OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], async () => xml = await TestClient.DumpScreenStringAsync(Device)); Assert.Equal(cleanDump, xml); @@ -839,7 +839,7 @@ await RunTestAsync( OkResponses(2), NoResponseMessages, requests, - miuiStream, + [miuiStream], async () => miuiXml = await TestClient.DumpScreenStringAsync(Device)); Assert.Equal(cleanMIUIDump, miuiXml); @@ -864,7 +864,7 @@ await RunTestAsync( OkResponses(2), NoResponseMessages, requests, - emptyStream, + [emptyStream], async () => emptyXml = await TestClient.DumpScreenStringAsync(Device)); Assert.True(string.IsNullOrEmpty(emptyXml)); @@ -891,7 +891,7 @@ await Assert.ThrowsAsync(() => OkResponses(2), NoResponseMessages, requests, - errorStream, + [errorStream], () => TestClient.DumpScreenStringAsync(Device))); } @@ -916,7 +916,7 @@ await RunTestAsync( OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], async () => xml = await TestClient.DumpScreenAsync(Device)); string cleanDump = File.ReadAllText(@"Assets/dumpscreen_clean.txt"); @@ -967,7 +967,7 @@ at android.os.Binder.execTransactInternal(Binder.java:1165) OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], () => TestClient.ClickAsync(Device, 100, 100))); Assert.Equal("SecurityException", exception.JavaName); @@ -1015,7 +1015,7 @@ public async void ClickCordsAsyncTest() OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], () => TestClient.ClickAsync(Device, new Cords(100, 100)))); } @@ -1037,7 +1037,7 @@ await RunTestAsync( OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], () => TestClient.SwipeAsync(Device, 100, 200, 300, 400, 500)); } @@ -1059,7 +1059,7 @@ await RunTestAsync( OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], () => TestClient.SwipeAsync(Device, new Element(TestClient, Device, new Area(0, 0, 200, 400)), new Element(TestClient, Device, new Area(0, 0, 600, 800)), 500)); } @@ -1089,7 +1089,7 @@ await RunTestAsync( OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], async () => result = await TestClient.IsAppRunningAsync(Device, "com.google.android.gms")); Assert.Equal(expected, result); @@ -1119,7 +1119,7 @@ await RunTestAsync( OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], async () => result = await TestClient.IsAppInForegroundAsync(Device, packageName)); Assert.Equal(expected, result); @@ -1146,7 +1146,7 @@ await RunTestAsync( OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], async () => element = await TestClient.FindElementAsync(Device)); Assert.Equal(144, element.GetChildCount()); @@ -1176,7 +1176,7 @@ await RunTestAsync( OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], async () => elements = await TestClient.FindElementsAsync(Device)); int childCount = elements.Count; @@ -1208,7 +1208,7 @@ await RunTestAsync( OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], async () => { elements = []; @@ -1244,7 +1244,7 @@ await RunTestAsync( OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], () => TestClient.SendKeyEventAsync(Device, "KEYCODE_MOVE_END")); } @@ -1266,10 +1266,35 @@ await RunTestAsync( OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], () => TestClient.SendTextAsync(Device, "Hello, World")); } + /// + /// Tests the method. + /// + [Fact] + public async void ClearInputAsyncTest() + { + string[] requests = + [ + "host:transport:169.254.109.177:5555", + "shell:input keyevent KEYCODE_MOVE_END", + "host:transport:169.254.109.177:5555", + "shell:input keyevent KEYCODE_DEL KEYCODE_DEL KEYCODE_DEL" + ]; + + await using MemoryStream firstShellStream = new(); + await using MemoryStream secondShellStream = new(); + + await RunTestAsync( + OkResponses(4), + NoResponseMessages, + requests, + [firstShellStream, secondShellStream], + () => TestClient.ClearInputAsync(Device, 3)); + } + /// /// Tests the method. /// @@ -1282,13 +1307,10 @@ public async void StartAppAsyncTest() "shell:monkey -p com.android.settings 1", ]; - await using MemoryStream shellStream = new(); - await RunTestAsync( OkResponses(2), NoResponseMessages, requests, - shellStream, () => TestClient.StartAppAsync(Device, "com.android.settings")); } @@ -1304,13 +1326,10 @@ public async void StopAppAsyncTest() "shell:am force-stop com.android.settings", ]; - await using MemoryStream shellStream = new(); - await RunTestAsync( OkResponses(2), NoResponseMessages, requests, - shellStream, () => TestClient.StopAppAsync(Device, "com.android.settings")); } @@ -1332,7 +1351,7 @@ await RunTestAsync( OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], () => TestClient.BackBtnAsync(Device)); } @@ -1354,7 +1373,7 @@ await RunTestAsync( OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], () => TestClient.HomeBtnAsync(Device)); } diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs index 27e6c49f..1c73ca32 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs @@ -10,7 +10,6 @@ using System.Runtime.InteropServices; using System.Text; using System.Xml; -using System.Xml.Linq; using Xunit; namespace AdvancedSharpAdbClient.Tests @@ -381,7 +380,7 @@ public void ExecuteRemoteCommandTest() OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], () => TestClient.ExecuteRemoteCommand("echo Hello, World", Device, receiver)); Assert.Equal("Hello, World\r\n", receiver.ToString(), ignoreLineEndingDifferences: true); @@ -501,7 +500,7 @@ public void RunLogServiceTest() OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], () => TestClient.RunLogService(Device, sink, LogId.System)); Assert.Equal(3, logs.Count); @@ -798,7 +797,7 @@ public void InstallCreateTest() OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], () => session = TestClient.InstallCreate(Device, "com.google.android.gms")); Assert.Equal("936013062", session); @@ -873,7 +872,7 @@ public void InstallCommitTest() OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], () => TestClient.InstallCommit(Device, "936013062")); } @@ -920,7 +919,7 @@ public void DumpScreenStringTest() OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], () => xml = TestClient.DumpScreenString(Device)); Assert.Equal(cleanDump, xml); @@ -948,7 +947,7 @@ public void DumpScreenStringMIUITest() OkResponses(2), NoResponseMessages, requests, - miuiStream, + [miuiStream], () => miuiXml = TestClient.DumpScreenString(Device)); Assert.Equal(cleanMIUIDump, miuiXml); @@ -974,7 +973,7 @@ public void DumpScreenStringEmptyTest() OkResponses(2), NoResponseMessages, requests, - emptyStream, + [emptyStream], () => emptyXml = TestClient.DumpScreenString(Device)); Assert.True(string.IsNullOrEmpty(emptyXml)); @@ -1001,7 +1000,7 @@ public void DumpScreenStringErrorTest() OkResponses(2), NoResponseMessages, requests, - errorStream, + [errorStream], () => TestClient.DumpScreenString(Device))); } @@ -1026,7 +1025,7 @@ public void DumpScreenTest() OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], () => xml = TestClient.DumpScreen(Device)); string cleanDump = File.ReadAllText(@"Assets/dumpscreen_clean.txt"); @@ -1077,7 +1076,7 @@ at android.os.Binder.execTransactInternal(Binder.java:1165) OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], () => TestClient.Click(Device, 100, 100))); Assert.Equal("SecurityException", exception.JavaName); @@ -1125,7 +1124,7 @@ public void ClickCordsTest() OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], () => TestClient.Click(Device, new Cords(100, 100)))); } @@ -1147,7 +1146,7 @@ public void SwipeTest() OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], () => TestClient.Swipe(Device, 100, 200, 300, 400, 500)); } @@ -1169,7 +1168,7 @@ public void SwipeElementTest() OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], () => TestClient.Swipe(Device, new Element(TestClient, Device, new Area(0, 0, 200, 400)), new Element(TestClient, Device, new Area(0, 0, 600, 800)), 500)); } @@ -1199,7 +1198,7 @@ public void IsAppRunningTest(string response, bool expected) OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], () => result = TestClient.IsAppRunning(Device, "com.google.android.gms")); Assert.Equal(expected, result); @@ -1229,7 +1228,7 @@ public void IsAppInForegroundTest(string packageName, bool expected) OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], () => result = TestClient.IsAppInForeground(Device, packageName)); Assert.Equal(expected, result); @@ -1256,7 +1255,7 @@ public void FindElementTest() OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], () => element = TestClient.FindElement(Device)); Assert.Equal(144, element.GetChildCount()); @@ -1286,7 +1285,7 @@ public void FindElementsTest() OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], () => elements = TestClient.FindElements(Device).ToList()); int childCount = elements.Count; @@ -1315,7 +1314,7 @@ public void SendKeyEventTest() OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], () => TestClient.SendKeyEvent(Device, "KEYCODE_MOVE_END")); } @@ -1337,10 +1336,35 @@ public void SendTextTest() OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], () => TestClient.SendText(Device, "Hello, World")); } + /// + /// Tests the method. + /// + [Fact] + public void ClearInputTest() + { + string[] requests = + [ + "host:transport:169.254.109.177:5555", + "shell:input keyevent KEYCODE_MOVE_END", + "host:transport:169.254.109.177:5555", + "shell:input keyevent KEYCODE_DEL KEYCODE_DEL KEYCODE_DEL" + ]; + + using MemoryStream firstShellStream = new(); + using MemoryStream secondShellStream = new(); + + RunTest( + OkResponses(4), + NoResponseMessages, + requests, + [firstShellStream, secondShellStream], + () => TestClient.ClearInput(Device, 3)); + } + /// /// Tests the method. /// @@ -1353,13 +1377,10 @@ public void StartAppTest() "shell:monkey -p com.android.settings 1", ]; - using MemoryStream shellStream = new(); - RunTest( OkResponses(2), NoResponseMessages, requests, - shellStream, () => TestClient.StartApp(Device, "com.android.settings")); } @@ -1375,13 +1396,10 @@ public void StopAppTest() "shell:am force-stop com.android.settings", ]; - using MemoryStream shellStream = new(); - RunTest( OkResponses(2), NoResponseMessages, requests, - shellStream, () => TestClient.StopApp(Device, "com.android.settings")); } @@ -1403,7 +1421,7 @@ public void BackBtnTest() OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], () => TestClient.BackBtn(Device)); } @@ -1425,7 +1443,7 @@ public void HomeBtnTest() OkResponses(2), NoResponseMessages, requests, - shellStream, + [shellStream], () => TestClient.HomeBtn(Device)); } diff --git a/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs b/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs index 19abce88..26096a04 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using System.Threading; using Xunit; diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs index bb25536d..8fa93971 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs @@ -19,8 +19,6 @@ internal class DummyAdbSocket : IDummyAdbSocket public DummyAdbSocket() => IsConnected = true; - public Stream ShellStream { get; set; } - public Queue Responses { get; } = new Queue(); public Queue SyncResponses { get; } = new Queue(); @@ -35,6 +33,8 @@ internal class DummyAdbSocket : IDummyAdbSocket public List<(SyncCommand, string)> SyncRequests { get; } = new List<(SyncCommand, string)>(); + public Queue ShellStreams { get; } = new Queue(); + public bool IsConnected { get; set; } public bool WaitForNewData { get; set; } @@ -106,9 +106,9 @@ public AdbResponse ReadAdbResponse() public Stream GetShellStream() { - if (ShellStream != null) + if (ShellStreams.Dequeue() is Stream actual) { - return ShellStream; + return actual; } else { diff --git a/AdvancedSharpAdbClient.Tests/Dummys/IDummyAdbSocket.cs b/AdvancedSharpAdbClient.Tests/Dummys/IDummyAdbSocket.cs index b0257854..febcff1d 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/IDummyAdbSocket.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/IDummyAdbSocket.cs @@ -1,13 +1,10 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; namespace AdvancedSharpAdbClient.Tests { public interface IDummyAdbSocket : IAdbSocket { - Stream ShellStream { get; set; } - Queue Responses { get; } Queue ResponseMessages { get; } @@ -22,6 +19,8 @@ public interface IDummyAdbSocket : IAdbSocket List<(SyncCommand, string)> SyncRequests { get; } + Queue ShellStreams { get; } + /// /// Gets a value indicating whether the socket reconnected. /// diff --git a/AdvancedSharpAdbClient.Tests/Dummys/TracingAdbSocket.cs b/AdvancedSharpAdbClient.Tests/Dummys/TracingAdbSocket.cs index 02635447..0146fa26 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/TracingAdbSocket.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/TracingAdbSocket.cs @@ -11,8 +11,6 @@ namespace AdvancedSharpAdbClient.Tests { internal class TracingAdbSocket(EndPoint endPoint) : AdbSocket(endPoint), IDummyAdbSocket { - public Stream ShellStream { get; set; } - public bool DoDispose { get; set; } public Queue Responses { get; } = new Queue(); @@ -29,6 +27,8 @@ internal class TracingAdbSocket(EndPoint endPoint) : AdbSocket(endPoint), IDummy public List<(SyncCommand, string)> SyncRequests { get; } = new List<(SyncCommand, string)>(); + public Queue ShellStreams { get; } = new Queue(); + public bool DidReconnect { get; private set; } public bool WaitForNewData { get; set; } @@ -103,6 +103,20 @@ public override AdbResponse ReadAdbResponse() return exception != null ? throw exception : response; } + public override Stream GetShellStream() + { + StackTrace trace = new(); + + Stream stream = base.GetShellStream(); + + if (trace != null && trace.GetFrames()[1].GetMethod().DeclaringType != typeof(AdbSocket)) + { + ShellStreams.Enqueue(stream); + } + + return stream; + } + public override string ReadString() { string value = base.ReadString(); diff --git a/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs b/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs index 515af61e..d2b270b0 100644 --- a/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs +++ b/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs @@ -114,15 +114,15 @@ protected void RunTest( /// The messages that the ADB sever should send. /// The messages that should follow the . /// The requests the client should send. - /// The of . + /// The of which the should use. /// The test to run. protected void RunTest( IEnumerable responses, IEnumerable responseMessages, IEnumerable requests, - Stream shellStream, + IEnumerable shellStreams, Action test) => - RunTest(responses, responseMessages, requests, null, null, null, null, shellStream, test); + RunTest(responses, responseMessages, requests, null, null, null, null, shellStreams, test); /// /// @@ -193,7 +193,7 @@ protected void RunTest( /// The messages that the ADB sever should send. /// The of data which the ADB sever should send. /// The of data which the client should send. - /// The of . + /// The of which the should use. /// The test to run. protected void RunTest( IEnumerable responses, @@ -203,15 +203,13 @@ protected void RunTest( IEnumerable syncResponses, IEnumerable syncDataReceived, IEnumerable syncDataSent, - Stream shellStream, + IEnumerable shellStreams, Action test) { // If we are running unit tests, we need to mock all the responses // that are sent by the device. Do that now. if (!IntegrationTest) { - Socket.ShellStream = shellStream; - foreach (AdbResponse response in responses) { Socket.Responses.Enqueue(response); @@ -237,6 +235,14 @@ protected void RunTest( Socket.SyncDataReceived.Enqueue(syncDatum); } } + + if (shellStreams != null) + { + foreach (Stream shellStream in shellStreams) + { + Socket.ShellStreams.Enqueue(shellStream); + } + } } Exception exception = null; @@ -260,6 +266,7 @@ protected void RunTest( Assert.Empty(Socket.Responses); Assert.Empty(Socket.SyncResponses); Assert.Empty(Socket.SyncDataReceived); + Assert.Empty(Socket.ShellStreams); // Make sure a request was sent Assert.Equal(requests.ToArray(), Socket.Requests); @@ -326,6 +333,15 @@ protected void RunTest( { Assert.Empty(Socket.SyncDataSent); } + + if (shellStreams != null) + { + Assert.Equal(shellStreams.ToArray(), [.. Socket.ShellStreams]); + } + else + { + Assert.Empty(Socket.ShellStreams); + } } if (exception != null) @@ -383,16 +399,16 @@ protected Task RunTestAsync( /// The messages that the ADB sever should send. /// The messages that should follow the . /// The requests the client should send. - /// The of . + /// The of which the should use. /// The test to run. /// A which represents the asynchronous operation. protected Task RunTestAsync( IEnumerable responses, IEnumerable responseMessages, IEnumerable requests, - Stream shellStream, + IEnumerable shellStreams, Func test) => - RunTestAsync(responses, responseMessages, requests, null, null, null, null, shellStream, test); + RunTestAsync(responses, responseMessages, requests, null, null, null, null, shellStreams, test); /// /// @@ -464,7 +480,7 @@ protected Task RunTestAsync( /// The messages that the ADB sever should send. /// The of data which the ADB sever should send. /// The of data which the client should send. - /// The of . + /// The of which the should use. /// The test to run. /// A which represents the asynchronous operation. protected async Task RunTestAsync( @@ -475,15 +491,13 @@ protected async Task RunTestAsync( IEnumerable syncResponses, IEnumerable syncDataReceived, IEnumerable syncDataSent, - Stream shellStream, + IEnumerable shellStreams, Func test) { // If we are running unit tests, we need to mock all the responses // that are sent by the device. Do that now. if (!IntegrationTest) { - Socket.ShellStream = shellStream; - foreach (AdbResponse response in responses) { Socket.Responses.Enqueue(response); @@ -509,6 +523,14 @@ protected async Task RunTestAsync( Socket.SyncDataReceived.Enqueue(syncDatum); } } + + if (shellStreams != null) + { + foreach (Stream shellStream in shellStreams) + { + Socket.ShellStreams.Enqueue(shellStream); + } + } } Exception exception = null; @@ -536,6 +558,7 @@ protected async Task RunTestAsync( Assert.Empty(Socket.Responses); Assert.Empty(Socket.SyncResponses); Assert.Empty(Socket.SyncDataReceived); + Assert.Empty(Socket.ShellStreams); // Make sure a request was sent Assert.Equal(requests.ToArray(), Socket.Requests); @@ -602,6 +625,15 @@ protected async Task RunTestAsync( { Assert.Empty(Socket.SyncDataSent); } + + if (shellStreams != null) + { + Assert.Equal(shellStreams.ToArray(), [.. Socket.ShellStreams]); + } + else + { + Assert.Empty(Socket.ShellStreams); + } } if (exception != null) diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index 789bba32..ec960d20 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -1010,7 +1010,7 @@ public async Task SendTextAsync(DeviceData device, string text, CancellationToke public async Task ClearInputAsync(DeviceData device, int charCount, CancellationToken cancellationToken = default) { await SendKeyEventAsync(device, "KEYCODE_MOVE_END", cancellationToken).ConfigureAwait(false); - await SendKeyEventAsync(device, StringExtensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL ", charCount))).ConfigureAwait(false); + await SendKeyEventAsync(device, StringExtensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL", charCount))).ConfigureAwait(false); } /// From e1ef9fbe21425f006322efb8012a465952e5d1e2 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Tue, 3 Oct 2023 20:39:37 +0800 Subject: [PATCH 26/66] Add GetAppStatus test --- .../AdbClientTests.Async.cs | 62 ++++++++++++++++ .../AdbClientTests.cs | 71 +++++++++++++++++-- .../SyncServiceTests.Async.cs | 6 +- .../SyncServiceTests.cs | 6 +- AdvancedSharpAdbClient/AdbClient.cs | 6 +- 5 files changed, 138 insertions(+), 13 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs index 13475608..63da2992 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs @@ -1125,6 +1125,68 @@ await RunTestAsync( Assert.Equal(expected, result); } + /// + /// Tests the method. + /// + [Theory] + [InlineData("com.google.android.gms", "21216 27761\r\n", AppStatus.Background)] + [InlineData("com.android.gallery3d", "\r\n", AppStatus.Stopped)] + public async void GetAppStatusAsyncTest(string packageName, string response, AppStatus expected) + { + string[] requests = + [ + "host:transport:169.254.109.177:5555", + "shell:dumpsys activity activities | grep mResumedActivity", + "host:transport:169.254.109.177:5555", + $"shell:pidof {packageName}" + ]; + + byte[] activityData = @" mResumedActivity: ActivityRecord{1f5309a u0 com.android.settings/.homepage.SettingsHomepageActivity t61029} + mResumedActivity: ActivityRecord{896cc3 u0 app.lawnchair/.LawnchairLauncher t5}"u8.ToArray(); + await using MemoryStream activityStream = new(activityData); + byte[] pidData = Encoding.UTF8.GetBytes(response); + await using MemoryStream pidStream = new(pidData); + + AppStatus result = AppStatus.Foreground; + await RunTestAsync( + OkResponses(4), + NoResponseMessages, + requests, + [activityStream, pidStream], + async () => result = await TestClient.GetAppStatusAsync(Device, packageName)); + + Assert.Equal(expected, result); + } + + /// + /// Tests the method. + /// + [Theory] + [InlineData("app.lawnchair", AppStatus.Foreground)] + [InlineData("com.android.settings", AppStatus.Foreground)] + public async void GetAppStatusAsyncForegroundTest(string packageName, AppStatus expected) + { + string[] requests = + [ + "host:transport:169.254.109.177:5555", + "shell:dumpsys activity activities | grep mResumedActivity" + ]; + + byte[] streamData = @" mResumedActivity: ActivityRecord{1f5309a u0 com.android.settings/.homepage.SettingsHomepageActivity t61029} + mResumedActivity: ActivityRecord{896cc3 u0 app.lawnchair/.LawnchairLauncher t5}"u8.ToArray(); + await using MemoryStream shellStream = new(streamData); + + AppStatus result = default; + await RunTestAsync( + OkResponses(2), + NoResponseMessages, + requests, + [shellStream], + async () => result = await TestClient.GetAppStatusAsync(Device, packageName)); + + Assert.Equal(expected, result); + } + /// /// Tests the method. /// diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs index 1c73ca32..48962f44 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs @@ -1178,6 +1178,7 @@ public void SwipeElementTest() [Theory] [InlineData("21216 27761\r\n", true)] [InlineData(" 21216 27761\r\n", true)] + [InlineData("12836\r\n", true)] [InlineData(" \r\n", false)] [InlineData("\r\n", false)] [InlineData(" ", false)] @@ -1234,6 +1235,68 @@ public void IsAppInForegroundTest(string packageName, bool expected) Assert.Equal(expected, result); } + /// + /// Tests the method. + /// + [Theory] + [InlineData("com.google.android.gms", "21216 27761\r\n", AppStatus.Background)] + [InlineData("com.android.gallery3d", "\r\n", AppStatus.Stopped)] + public void GetAppStatusTest(string packageName, string response, AppStatus expected) + { + string[] requests = + [ + "host:transport:169.254.109.177:5555", + "shell:dumpsys activity activities | grep mResumedActivity", + "host:transport:169.254.109.177:5555", + $"shell:pidof {packageName}" + ]; + + byte[] activityData = @" mResumedActivity: ActivityRecord{1f5309a u0 com.android.settings/.homepage.SettingsHomepageActivity t61029} + mResumedActivity: ActivityRecord{896cc3 u0 app.lawnchair/.LawnchairLauncher t5}"u8.ToArray(); + using MemoryStream activityStream = new(activityData); + byte[] pidData = Encoding.UTF8.GetBytes(response); + using MemoryStream pidStream = new(pidData); + + AppStatus result = AppStatus.Foreground; + RunTest( + OkResponses(4), + NoResponseMessages, + requests, + [activityStream, pidStream], + () => result = TestClient.GetAppStatus(Device, packageName)); + + Assert.Equal(expected, result); + } + + /// + /// Tests the method. + /// + [Theory] + [InlineData("app.lawnchair", AppStatus.Foreground)] + [InlineData("com.android.settings", AppStatus.Foreground)] + public void GetAppStatusForegroundTest(string packageName, AppStatus expected) + { + string[] requests = + [ + "host:transport:169.254.109.177:5555", + "shell:dumpsys activity activities | grep mResumedActivity" + ]; + + byte[] streamData = @" mResumedActivity: ActivityRecord{1f5309a u0 com.android.settings/.homepage.SettingsHomepageActivity t61029} + mResumedActivity: ActivityRecord{896cc3 u0 app.lawnchair/.LawnchairLauncher t5}"u8.ToArray(); + using MemoryStream shellStream = new(streamData); + + AppStatus result = default; + RunTest( + OkResponses(2), + NoResponseMessages, + requests, + [shellStream], + () => result = TestClient.GetAppStatus(Device, packageName)); + + Assert.Equal(expected, result); + } + /// /// Tests the method. /// @@ -1280,16 +1343,16 @@ public void FindElementsTest() byte[] streamData = Encoding.UTF8.GetBytes(dump); using MemoryStream shellStream = new(streamData); - List elements = null; + Element[] elements = null; RunTest( OkResponses(2), NoResponseMessages, requests, [shellStream], - () => elements = TestClient.FindElements(Device).ToList()); + () => elements = TestClient.FindElements(Device).ToArray()); - int childCount = elements.Count; - elements.ForEach(x => childCount += x.GetChildCount()); + int childCount = elements.Length; + Array.ForEach(elements, x => childCount += x.GetChildCount()); Assert.Equal(145, childCount); Element element = elements[0][0][0][0][0][0][0][0][0][2][1][0][0]; Assert.Equal("where-where", element.Attributes["text"]); diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs index ce1de291..9abc4f18 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs @@ -37,7 +37,7 @@ await RunTestAsync( [Fact] public async void GetListingAsyncTest() { - List value = null; + FileStatistics[] value = null; await RunTestAsync( OkResponses(2), @@ -55,10 +55,10 @@ await RunTestAsync( async () => { using SyncService service = new(Socket, Device); - value = (await service.GetDirectoryListingAsync("/storage")).ToList(); + value = (await service.GetDirectoryListingAsync("/storage")).ToArray(); }); - Assert.Equal(4, value.Count); + Assert.Equal(4, value.Length); DateTime time = new DateTime(2015, 11, 3, 9, 47, 4, DateTimeKind.Utc).ToLocalTime(); diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs index 17b10fe6..0194cc1c 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs @@ -47,7 +47,7 @@ public void StatTest() [Fact] public void GetListingTest() { - List value = null; + FileStatistics[] value = null; RunTest( OkResponses(2), @@ -65,10 +65,10 @@ public void GetListingTest() () => { using SyncService service = new(Socket, Device); - value = service.GetDirectoryListing("/storage").ToList(); + value = service.GetDirectoryListing("/storage").ToArray(); }); - Assert.Equal(4, value.Count); + Assert.Equal(4, value.Length); DateTime time = new DateTime(2015, 11, 3, 9, 47, 4, DateTimeKind.Utc).ToLocalTime(); diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index 884ad592..cf6aa841 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -120,7 +120,7 @@ public AdbClient(EndPoint endPoint, Func adbSocketFactory) } /// - /// Get or set default encoding + /// Get or set default encoding. /// public static Encoding Encoding { get; set; } = Encoding.UTF8; @@ -1120,9 +1120,9 @@ public void StopApp(DeviceData device, string packageName) public void HomeBtn(DeviceData device) => SendKeyEvent(device, "KEYCODE_HOME"); /// - /// Sets default encoding (default - UTF8) + /// Sets default encoding (default - UTF8). /// - /// + /// The to set. public static void SetEncoding(Encoding encoding) => Encoding = encoding; /// From 0b26b41a2ac0bdca8b931f2de60e33f423dd32c7 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Wed, 4 Oct 2023 16:28:39 +0800 Subject: [PATCH 27/66] Use TryParse in SyncCommandConverter --- .../Extensions/SyncCommandConverterTests.cs | 12 +++++++ .../Extensions/SyncCommandConverter.cs | 34 ++++++------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/Extensions/SyncCommandConverterTests.cs b/AdvancedSharpAdbClient.Tests/Extensions/SyncCommandConverterTests.cs index 88ea2592..bc4511e0 100644 --- a/AdvancedSharpAdbClient.Tests/Extensions/SyncCommandConverterTests.cs +++ b/AdvancedSharpAdbClient.Tests/Extensions/SyncCommandConverterTests.cs @@ -23,5 +23,17 @@ public void GetCommandInvalidCommandTest() => [Fact] public void GetBytesInvalidCommandTest() => _ = Assert.Throws(() => SyncCommandConverter.GetBytes((SyncCommand)99)); + + [Fact] + public void SyncCommandConverterTest() + { + SyncCommand[] commands = Enum.GetValues(); + foreach (SyncCommand command in commands) + { + byte[] bytes = SyncCommandConverter.GetBytes(command); + Assert.Equal(4, bytes.Length); + Assert.Equal(command, SyncCommandConverter.GetCommand(bytes)); + } + } } } diff --git a/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs b/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs index a83b52d0..73d8b250 100644 --- a/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs +++ b/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs @@ -3,8 +3,6 @@ // using System; -using System.Collections.Generic; -using System.Linq; namespace AdvancedSharpAdbClient { @@ -13,22 +11,6 @@ namespace AdvancedSharpAdbClient /// public static class SyncCommandConverter { - /// - /// Maps the values to their string representations. - /// - private static readonly Dictionary Values = new(9) - { - { SyncCommand.DATA, "DATA" }, - { SyncCommand.DENT, "DENT" }, - { SyncCommand.DONE, "DONE" }, - { SyncCommand.FAIL, "FAIL" }, - { SyncCommand.LIST, "LIST" }, - { SyncCommand.OKAY, "OKAY" }, - { SyncCommand.RECV, "RECV" }, - { SyncCommand.SEND, "SEND" }, - { SyncCommand.STAT, "STAT" } - }; - /// /// Gets the byte array that represents the . /// @@ -36,12 +18,20 @@ public static class SyncCommandConverter /// A byte array that represents the . public static byte[] GetBytes(SyncCommand command) { - if (!Values.TryGetValue(command, out string value)) + if (command is not (SyncCommand.LIST + or SyncCommand.RECV + or SyncCommand.SEND + or SyncCommand.STAT + or SyncCommand.DENT + or SyncCommand.FAIL + or SyncCommand.DATA + or SyncCommand.OKAY + or SyncCommand.DONE)) { throw new ArgumentOutOfRangeException(nameof(command), $"{command} is not a valid sync command"); } - string commandText = value; + string commandText = command.ToString(); byte[] commandBytes = AdbClient.Encoding.GetBytes(commandText); return commandBytes; @@ -69,9 +59,7 @@ public static SyncCommand GetCommand(byte[] value) string commandText = AdbClient.Encoding.GetString(value); - SyncCommand? key = Values.Where(d => string.Equals(d.Value, commandText, StringComparison.OrdinalIgnoreCase)).Select(d => new SyncCommand?(d.Key)).SingleOrDefault(); - - return key == null ? throw new ArgumentOutOfRangeException(nameof(value), $"{commandText} is not a valid sync command") : key.Value; + return Extensions.TryParse(commandText, true, out SyncCommand command) ? command : throw new ArgumentOutOfRangeException(nameof(value), $"{commandText} is not a valid sync command"); } } } From 4b58e78c3c0e63bec2941642f76b9e2ff17d6ee7 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Wed, 4 Oct 2023 19:20:04 +0800 Subject: [PATCH 28/66] Add ExecuteServerCommand test Return IEnumerable in FindElementsAsync Return List in GetDirectoryListingAsync --- .../AdbClientTests.Async.cs | 34 +++++++++++-- .../AdbClientTests.cs | 26 ++++++++++ .../Dummys/DummyAdbClient.cs | 2 +- .../Dummys/DummySyncService.cs | 2 +- .../SyncServiceTests.Async.cs | 1 - .../SyncServiceTests.cs | 1 - AdvancedSharpAdbClient/AdbClient.Async.cs | 48 +++++++++++-------- AdvancedSharpAdbClient/AdbClient.cs | 12 +---- AdvancedSharpAdbClient/AdbSocket.Async.cs | 2 +- .../AdvancedSharpAdbClient.csproj | 4 +- .../Extensions/AdbClientExtensions.Async.cs | 2 +- .../Extensions/DateTimeExtensions.cs | 20 ++++---- .../Extensions/SocketExtensions.cs | 4 +- .../Extensions/SteamExtensions.cs | 2 +- .../Interfaces/IAdbClient.Async.cs | 2 +- .../Interfaces/IAdbSocket.Async.cs | 2 +- .../Interfaces/ISyncService.Async.cs | 2 +- .../Interfaces/ITcpSocket.Async.cs | 2 +- .../Interfaces/ITcpSocket.cs | 2 +- AdvancedSharpAdbClient/SyncService.Async.cs | 2 +- AdvancedSharpAdbClient/TcpSocket.cs | 2 +- 21 files changed, 113 insertions(+), 61 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs index 63da2992..fb8c8f53 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; +using System.Globalization; using System.IO; using System.Linq; using System.Net; @@ -253,6 +254,31 @@ await RunTestAsync( Assert.Equal("tcp:100", forwards[0].Remote); } + /// + /// Tests the method. + /// + [Fact] + public async void ExecuteServerCommandAsyncTest() + { + string[] requests = ["host:version"]; + + byte[] streamData = Encoding.ASCII.GetBytes("0020"); + await using MemoryStream shellStream = new(streamData); + + ConsoleOutputReceiver receiver = new(); + + await RunTestAsync( + OkResponse, + NoResponseMessages, + requests, + [shellStream], + () => TestClient.ExecuteServerCommandAsync("host", "version", receiver)); + + string version = receiver.ToString(); + Assert.Equal("0020\r\n", receiver.ToString(), ignoreLineEndingDifferences: true); + Assert.Equal(32, int.Parse(version, NumberStyles.HexNumber)); + } + /// /// Tests the method. /// @@ -1233,16 +1259,16 @@ public async void FindElementsAsyncTest() byte[] streamData = Encoding.UTF8.GetBytes(dump); await using MemoryStream shellStream = new(streamData); - List elements = null; + Element[] elements = null; await RunTestAsync( OkResponses(2), NoResponseMessages, requests, [shellStream], - async () => elements = await TestClient.FindElementsAsync(Device)); + async () => elements = (await TestClient.FindElementsAsync(Device)).ToArray()); - int childCount = elements.Count; - elements.ForEach(x => childCount += x.GetChildCount()); + int childCount = elements.Length; + Array.ForEach(elements, x => childCount += x.GetChildCount()); Assert.Equal(145, childCount); Element element = elements[0][0][0][0][0][0][0][0][0][2][1][0][0]; Assert.Equal("where-where", element.Attributes["text"]); diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs index 48962f44..90a79b97 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; +using System.Globalization; using System.IO; using System.Linq; using System.Net; @@ -359,6 +360,31 @@ public void ListReverseForwardTest() Assert.Equal("tcp:100", forwards[0].Remote); } + /// + /// Tests the method. + /// + [Fact] + public void ExecuteServerCommandTest() + { + string[] requests = ["host:version"]; + + byte[] streamData = Encoding.ASCII.GetBytes("0020"); + using MemoryStream shellStream = new(streamData); + + ConsoleOutputReceiver receiver = new(); + + RunTest( + OkResponse, + NoResponseMessages, + requests, + [shellStream], + () => TestClient.ExecuteServerCommand("host", "version", receiver)); + + string version = receiver.ToString(); + Assert.Equal("0020\r\n", receiver.ToString(), ignoreLineEndingDifferences: true); + Assert.Equal(32, int.Parse(version, NumberStyles.HexNumber)); + } + /// /// Tests the method. /// diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs index 07e3da4e..c2686c26 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs @@ -150,7 +150,7 @@ public Task ExecuteServerCommandAsync(string target, string command, IAdbSocket public IEnumerable FindElements(DeviceData device, string xpath, TimeSpan timeout = default) => throw new NotImplementedException(); - public Task> FindElementsAsync(DeviceData device, string xpath, CancellationToken cancellationToken) => throw new NotImplementedException(); + public Task> FindElementsAsync(DeviceData device, string xpath, CancellationToken cancellationToken) => throw new NotImplementedException(); public int GetAdbVersion() => throw new NotImplementedException(); diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs index 1640d984..c69db69f 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs @@ -20,7 +20,7 @@ internal class DummySyncService : ISyncService public IEnumerable GetDirectoryListing(string remotePath) => throw new NotImplementedException(); - public Task> GetDirectoryListingAsync(string remotePath, CancellationToken cancellationToken) => throw new NotImplementedException(); + public Task> GetDirectoryListingAsync(string remotePath, CancellationToken cancellationToken) => throw new NotImplementedException(); public void Open() => IsOpen = true; diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs index 9abc4f18..4e8652c8 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Threading; using Xunit; diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs index 0194cc1c..9fb406ff 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.IO; using System.Linq; using Xunit; diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index ec960d20..ddb5c172 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -171,7 +171,7 @@ public async Task> ListReverseForwardAsync(DeviceData d return parts.Select(x => new ForwardData(x)); } - + /// public async Task ExecuteServerCommandAsync(string target, string command, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken = default) { @@ -885,7 +885,7 @@ public async Task FindElementAsync(DeviceData device, string xpath = "h } /// - public async Task> FindElementsAsync(DeviceData device, string xpath = "hierarchy/node", CancellationToken cancellationToken = default) + public async Task> FindElementsAsync(DeviceData device, string xpath = "hierarchy/node", CancellationToken cancellationToken = default) { try { @@ -897,22 +897,21 @@ public async Task> FindElementsAsync(DeviceData device, string xpa XmlNodeList xmlNodes = doc.SelectNodes(xpath); if (xmlNodes != null) { - List elements = new(xmlNodes.Count); - for (int i = 0; i < xmlNodes.Count; i++) + IEnumerable FindElements() { - Element element = Element.FromXmlNode(this, device, xmlNodes[i]); - if (element != null) + for (int i = 0; i < xmlNodes.Count; i++) { - elements.Add(element); + Element element = Element.FromXmlNode(this, device, xmlNodes[i]); + if (element != null) + { + yield return element; + } } } - return elements.Count == 0 ? null : elements; + return FindElements(); } } - if (cancellationToken == default) - { - break; - } + if (cancellationToken == default) { break; } } } catch (Exception e) @@ -934,26 +933,37 @@ public async IAsyncEnumerable FindAsyncElements(DeviceData device, stri { while (!cancellationToken.IsCancellationRequested) { - XmlDocument doc = await DumpScreenAsync(device, cancellationToken).ConfigureAwait(false); + XmlDocument doc = null; + + try + { + doc = await DumpScreenAsync(device, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) + { + // If a cancellation was requested, this main loop is interrupted with an exception + // because the socket is closed. In that case, we don't need to throw a ShellCommandUnresponsiveException. + // In all other cases, something went wrong, and we want to report it to the user. + if (!cancellationToken.IsCancellationRequested) + { + throw new ShellCommandUnresponsiveException(e); + } + } + if (doc != null) { XmlNodeList xmlNodes = doc.SelectNodes(xpath); if (xmlNodes != null) { - bool isBreak = false; for (int i = 0; i < xmlNodes.Count; i++) { Element element = Element.FromXmlNode(this, device, xmlNodes[i]); if (element != null) { - isBreak = true; yield return element; } } - if (isBreak) - { - break; - } + break; } } if (cancellationToken == default) diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index cf6aa841..5c8b12b3 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -1019,26 +1019,18 @@ public IEnumerable FindElements(DeviceData device, string xpath = "hier XmlNodeList xmlNodes = doc.SelectNodes(xpath); if (xmlNodes != null) { - bool isBreak = false; for (int i = 0; i < xmlNodes.Count; i++) { Element element = Element.FromXmlNode(this, device, xmlNodes[i]); if (element != null) { - isBreak = true; yield return element; } } - if (isBreak) - { - break; - } + break; } } - if (timeout == TimeSpan.Zero) - { - break; - } + if (timeout == TimeSpan.Zero) { break; } } } diff --git a/AdvancedSharpAdbClient/AdbSocket.Async.cs b/AdvancedSharpAdbClient/AdbSocket.Async.cs index ebffbaa2..59746414 100644 --- a/AdvancedSharpAdbClient/AdbSocket.Async.cs +++ b/AdvancedSharpAdbClient/AdbSocket.Async.cs @@ -336,7 +336,7 @@ public virtual async Task SendAsync(byte[] data, CancellationToken cancellationT throw; } } - + /// public virtual Task ReadAsync(byte[] data, CancellationToken cancellationToken = default) => ReadAsync(data, 0, data.Length, cancellationToken); diff --git a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj index 879b022e..d0f8b1b3 100644 --- a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj +++ b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj @@ -16,8 +16,8 @@ - net6.0;net8.0;netstandard1.3;netstandard2.0;netstandard2.1 - $(TargetFrameworks);netcoreapp3.1 + net8.0;netstandard1.3;netstandard2.0;netstandard2.1 + $(TargetFrameworks);net6.0;netcoreapp3.1 $(TargetFrameworks);net2.0-client;net3.5-client;net4.5.2;net4.8.1 diff --git a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs index 0c7d15e8..ba3bb52d 100644 --- a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs +++ b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs @@ -23,7 +23,7 @@ public static partial class AdbClientExtensions /// Optionally, a that processes the command output. /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. - public static Task ExecuteServerCommandAsync(this IAdbClient client, string target, string command, IShellOutputReceiver receiver, CancellationToken cancellationToken=default)=> + public static Task ExecuteServerCommandAsync(this IAdbClient client, string target, string command, IShellOutputReceiver receiver, CancellationToken cancellationToken = default) => client.ExecuteServerCommandAsync(target, command, receiver, AdbClient.Encoding, cancellationToken); /// diff --git a/AdvancedSharpAdbClient/Extensions/DateTimeExtensions.cs b/AdvancedSharpAdbClient/Extensions/DateTimeExtensions.cs index 3da6d28c..c38fecab 100644 --- a/AdvancedSharpAdbClient/Extensions/DateTimeExtensions.cs +++ b/AdvancedSharpAdbClient/Extensions/DateTimeExtensions.cs @@ -26,26 +26,26 @@ public static class DateTimeExtensions // Number of days in a non-leap year private const int DaysPerYear = 365; // Number of days in 4 years - private const int DaysPer4Years = DaysPerYear * 4 + 1; // 1461 + private const int DaysPer4Years = (DaysPerYear * 4) + 1; // 1461 // Number of days in 100 years - private const int DaysPer100Years = DaysPer4Years * 25 - 1; // 36524 + private const int DaysPer100Years = (DaysPer4Years * 25) - 1; // 36524 // Number of days in 400 years - private const int DaysPer400Years = DaysPer100Years * 4 + 1; // 146097 + private const int DaysPer400Years = (DaysPer100Years * 4) + 1; // 146097 // Number of days from 1/1/0001 to 12/31/1969 - internal const int DaysTo1970 = DaysPer400Years * 4 + DaysPer100Years * 3 + DaysPer4Years * 17 + DaysPerYear; // 719,162 + internal const int DaysTo1970 = (DaysPer400Years * 4) + (DaysPer100Years * 3) + (DaysPer4Years * 17) + DaysPerYear; // 719,162 // Number of days from 1/1/0001 to 12/31/9999 - private const int DaysTo10000 = DaysPer400Years * 25 - 366; // 3652059 + private const int DaysTo10000 = (DaysPer400Years * 25) - 366; // 3652059 internal const long MinTicks = 0; - internal const long MaxTicks = DaysTo10000 * TicksPerDay - 1; + internal const long MaxTicks = (DaysTo10000 * TicksPerDay) - 1; internal const long UnixEpochTicks = DaysTo1970 * TicksPerDay; private const long UnixEpochSeconds = UnixEpochTicks / TimeSpan.TicksPerSecond; // 62,135,596,800 - internal const long UnixMinSeconds = MinTicks / TimeSpan.TicksPerSecond - UnixEpochSeconds; - internal const long UnixMaxSeconds = MaxTicks / TimeSpan.TicksPerSecond - UnixEpochSeconds; + internal const long UnixMinSeconds = (MinTicks / TimeSpan.TicksPerSecond) - UnixEpochSeconds; + internal const long UnixMaxSeconds = (MaxTicks / TimeSpan.TicksPerSecond) - UnixEpochSeconds; #endif /// @@ -72,13 +72,13 @@ public static class DateTimeExtensions public static DateTimeOffset FromUnixTimeSeconds(long seconds) { #if NETFRAMEWORK && !NET46_OR_GREATER - if (seconds < UnixMinSeconds || seconds > UnixMaxSeconds) + if (seconds is < UnixMinSeconds or > UnixMaxSeconds) { throw new ArgumentOutOfRangeException(nameof(seconds), string.Format("Valid values are between {0} and {1}, inclusive.", UnixMinSeconds, UnixMaxSeconds)); } - long ticks = seconds * TimeSpan.TicksPerSecond + UnixEpochTicks; + long ticks = (seconds * TimeSpan.TicksPerSecond) + UnixEpochTicks; return new DateTimeOffset(ticks, TimeSpan.Zero); #else return DateTimeOffset.FromUnixTimeSeconds(seconds); diff --git a/AdvancedSharpAdbClient/Extensions/SocketExtensions.cs b/AdvancedSharpAdbClient/Extensions/SocketExtensions.cs index 335fbbd5..b53f0ee4 100644 --- a/AdvancedSharpAdbClient/Extensions/SocketExtensions.cs +++ b/AdvancedSharpAdbClient/Extensions/SocketExtensions.cs @@ -93,7 +93,7 @@ public static Task ReceiveAsync(this Socket socket, byte[] buffer, int offs cancellationTokenRegistration.Dispose(); } }, taskCompletionSource); - + return taskCompletionSource.Task; #else return Extensions.Run(() => socket.Receive(buffer, offset, size, socketFlags), cancellationToken); @@ -126,7 +126,7 @@ public static Task SendAsync(this Socket socket, byte[] buffer, SocketFlags /// The number of bytes received. public static Task SendAsync(this Socket socket, byte[] buffer, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) #if HAS_BUFFERS - => socket.SendAsync(buffer.AsMemory( 0, size), socketFlags, cancellationToken).AsTask(); + => socket.SendAsync(buffer.AsMemory(0, size), socketFlags, cancellationToken).AsTask(); #else => socket.SendAsync(buffer, 0, size, socketFlags, cancellationToken); #endif diff --git a/AdvancedSharpAdbClient/Extensions/SteamExtensions.cs b/AdvancedSharpAdbClient/Extensions/SteamExtensions.cs index 43bff95d..3e5ed553 100644 --- a/AdvancedSharpAdbClient/Extensions/SteamExtensions.cs +++ b/AdvancedSharpAdbClient/Extensions/SteamExtensions.cs @@ -158,7 +158,7 @@ public static Task ReadAsync(this Stream stream, byte[] buffer, int offset, cancellationTokenRegistration.Dispose(); } }, taskCompletionSource); - + return taskCompletionSource.Task; } diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs b/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs index ae0a6f7c..df456064 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs @@ -542,7 +542,7 @@ public partial interface IAdbClient /// A which can be used to cancel the asynchronous operation. /// Only check once if . Or it will continue check until is . /// A which return the of has got. - Task> FindElementsAsync(DeviceData device, string xpath, CancellationToken cancellationToken); + Task> FindElementsAsync(DeviceData device, string xpath, CancellationToken cancellationToken); #if NETCOREAPP3_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER /// diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbSocket.Async.cs b/AdvancedSharpAdbClient/Interfaces/IAdbSocket.Async.cs index 27481e6c..ad667dee 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbSocket.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbSocket.Async.cs @@ -143,7 +143,7 @@ public partial interface IAdbSocket /// A that can be used to cancel the task. /// A that represents the asynchronous operation. Task SendAsync(byte[] data, CancellationToken cancellationToken); - + /// /// Reads a from an instance when /// the connection is in sync mode. diff --git a/AdvancedSharpAdbClient/Interfaces/ISyncService.Async.cs b/AdvancedSharpAdbClient/Interfaces/ISyncService.Async.cs index f6f8c18e..0c6c81a3 100644 --- a/AdvancedSharpAdbClient/Interfaces/ISyncService.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/ISyncService.Async.cs @@ -48,7 +48,7 @@ public partial interface ISyncService /// The path to the directory on the device. /// A that can be used to cancel the task. /// A which return for each child item of the directory, a object with information of the item. - Task> GetDirectoryListingAsync(string remotePath, CancellationToken cancellationToken); + Task> GetDirectoryListingAsync(string remotePath, CancellationToken cancellationToken); #if NETCOREAPP3_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER /// diff --git a/AdvancedSharpAdbClient/Interfaces/ITcpSocket.Async.cs b/AdvancedSharpAdbClient/Interfaces/ITcpSocket.Async.cs index b948d5c9..dd7dbc37 100644 --- a/AdvancedSharpAdbClient/Interfaces/ITcpSocket.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/ITcpSocket.Async.cs @@ -110,7 +110,7 @@ public partial interface ITcpSocket /// A which can be used to cancel the asynchronous task. /// The number of bytes sent to the Socket. Task SendAsync(byte[] buffer, SocketFlags socketFlags, CancellationToken cancellationToken); - + /// /// Receives the specified number of bytes from a bound /// using the specified SocketFlags. diff --git a/AdvancedSharpAdbClient/Interfaces/ITcpSocket.cs b/AdvancedSharpAdbClient/Interfaces/ITcpSocket.cs index 6e0567e3..6ed4ae35 100644 --- a/AdvancedSharpAdbClient/Interfaces/ITcpSocket.cs +++ b/AdvancedSharpAdbClient/Interfaces/ITcpSocket.cs @@ -109,7 +109,7 @@ public partial interface ITcpSocket : IDisposable /// A bitwise combination of the SocketFlags values. /// The number of bytes sent to the Socket. int Send(byte[] buffer, SocketFlags socketFlags); - + /// /// Receives the specified number of bytes from a bound /// using the specified SocketFlags. diff --git a/AdvancedSharpAdbClient/SyncService.Async.cs b/AdvancedSharpAdbClient/SyncService.Async.cs index 6b94bf88..f826f6b8 100644 --- a/AdvancedSharpAdbClient/SyncService.Async.cs +++ b/AdvancedSharpAdbClient/SyncService.Async.cs @@ -245,7 +245,7 @@ public virtual async Task StatAsync(string remotePath, Cancellat } /// - public virtual async Task> GetDirectoryListingAsync(string remotePath, CancellationToken cancellationToken = default) + public virtual async Task> GetDirectoryListingAsync(string remotePath, CancellationToken cancellationToken = default) { List value = []; diff --git a/AdvancedSharpAdbClient/TcpSocket.cs b/AdvancedSharpAdbClient/TcpSocket.cs index 8a953ab3..2beb4bc2 100644 --- a/AdvancedSharpAdbClient/TcpSocket.cs +++ b/AdvancedSharpAdbClient/TcpSocket.cs @@ -131,7 +131,7 @@ public virtual int Receive(Span buffer, SocketFlags socketFlags) => /// public virtual int Send(byte[] buffer, SocketFlags socketFlags) => socket.Send(buffer, socketFlags); - + /// public virtual int Receive(byte[] buffer, SocketFlags socketFlags) => socket.Receive(buffer, socketFlags); From 46d20563db1d4551e0efc531cb7cdb8039ee0098 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Sat, 7 Oct 2023 16:32:52 +0800 Subject: [PATCH 29/66] Add returnable test for SocketBasedTests --- .../AdbClientTests.Async.cs | 94 ++- .../AdbClientTests.cs | 88 +-- .../DeviceMonitorTests.Async.cs | 12 +- .../DeviceMonitorTests.cs | 16 +- .../SocketBasedTests.cs | 650 +++++++++++++++++- .../SyncServiceTests.Async.cs | 19 +- .../SyncServiceTests.cs | 12 +- 7 files changed, 721 insertions(+), 170 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs index fb8c8f53..28ef0dbc 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs @@ -28,12 +28,11 @@ public async void GetAdbVersionAsyncTest() string[] responseMessages = ["0020"]; string[] requests = ["host:version"]; - int version = 0; - await RunTestAsync( + int version = await RunTestAsync( OkResponse, responseMessages, requests, - async () => version = await TestClient.GetAdbVersionAsync()); + () => TestClient.GetAdbVersionAsync()); // Make sure and the correct value is returned. Assert.Equal(32, version); @@ -63,18 +62,17 @@ public async void GetDevicesAsyncTest() string[] responseMessages = ["169.254.109.177:5555 device product:VS Emulator 5\" KitKat (4.4) XXHDPI Phone model:5__KitKat__4_4__XXHDPI_Phone device:donatello\n"]; string[] requests = ["host:devices-l"]; - IEnumerable devices = null; - await RunTestAsync( + DeviceData[] devices = await RunTestAsync( OkResponse, responseMessages, requests, - async () => devices = await TestClient.GetDevicesAsync()); + async () => (await TestClient.GetDevicesAsync()).ToArray()); // Make sure and the correct value is returned. Assert.NotNull(devices); Assert.Single(devices); - DeviceData device = devices.Single(); + DeviceData device = devices.SingleOrDefault(); Assert.Equal("169.254.109.177:5555", device.Serial); Assert.Equal(DeviceState.Online, device.State); @@ -212,12 +210,11 @@ public async void ListForwardAsyncTest() string[] responseMessages = ["169.254.109.177:5555 tcp:1 tcp:2\n169.254.109.177:5555 tcp:3 tcp:4\n169.254.109.177:5555 tcp:5 local:/socket/1\n"]; string[] requests = ["host-serial:169.254.109.177:5555:list-forward"]; - ForwardData[] forwards = null; - await RunTestAsync( + ForwardData[] forwards = await RunTestAsync( OkResponse, responseMessages, requests, - async () => forwards = (await TestClient.ListForwardAsync(Device)).ToArray()); + async () => (await TestClient.ListForwardAsync(Device)).ToArray()); Assert.NotNull(forwards); Assert.Equal(3, forwards.Length); @@ -240,12 +237,11 @@ public async void ListReverseForwardAsyncTest() "reverse:list-forward" ]; - ForwardData[] forwards = null; - await RunTestAsync( + ForwardData[] forwards = await RunTestAsync( OkResponses(2), responseMessages, requests, - async () => forwards = (await TestClient.ListReverseForwardAsync(Device)).ToArray()); + async () => (await TestClient.ListReverseForwardAsync(Device)).ToArray()); Assert.NotNull(forwards); Assert.Equal(3, forwards.Length); @@ -710,13 +706,12 @@ public async void InstallCreateAsyncTest() byte[] streamData = Encoding.ASCII.GetBytes("Success: created install session [936013062]\r\n"); await using MemoryStream shellStream = new(streamData); - string session = string.Empty; - await RunTestAsync( + string session = await RunTestAsync( OkResponses(2), NoResponseMessages, requests, [shellStream], - async () => session = await TestClient.InstallCreateAsync(Device, "com.google.android.gms")); + () => TestClient.InstallCreateAsync(Device, "com.google.android.gms")); Assert.Equal("936013062", session); } @@ -803,16 +798,15 @@ public async void GetFeatureSetAsyncTest() string[] requests = ["host-serial:169.254.109.177:5555:features"]; string[] responses = ["sendrecv_v2_brotli,remount_shell,sendrecv_v2,abb_exec,fixed_push_mkdir,fixed_push_symlink_timestamp,abb,shell_v2,cmd,ls_v2,apex,stat_v2\r\n"]; - IEnumerable features = null; - await RunTestAsync( + string[] features = await RunTestAsync( OkResponse, responses, requests, - async () => features = await TestClient.GetFeatureSetAsync(Device)); + async () => (await TestClient.GetFeatureSetAsync(Device)).ToArray()); - Assert.Equal(12, features.Count()); - Assert.Equal("sendrecv_v2_brotli", features.First()); - Assert.Equal("stat_v2", features.Last()); + Assert.Equal(12, features.Length); + Assert.Equal("sendrecv_v2_brotli", features.FirstOrDefault()); + Assert.Equal("stat_v2", features.LastOrDefault()); } /// @@ -832,13 +826,12 @@ public async void DumpScreenStringAsyncTest() byte[] streamData = Encoding.UTF8.GetBytes(dump); await using MemoryStream shellStream = new(streamData); - string xml = string.Empty; - await RunTestAsync( + string xml = await RunTestAsync( OkResponses(2), NoResponseMessages, requests, [shellStream], - async () => xml = await TestClient.DumpScreenStringAsync(Device)); + () => TestClient.DumpScreenStringAsync(Device)); Assert.Equal(cleanDump, xml); } @@ -860,13 +853,12 @@ public async void DumpScreenStringAsyncMIUITest() byte[] miuiStreamData = Encoding.UTF8.GetBytes(miuiDump); await using MemoryStream miuiStream = new(miuiStreamData); - string miuiXml = string.Empty; - await RunTestAsync( + string miuiXml = await RunTestAsync( OkResponses(2), NoResponseMessages, requests, [miuiStream], - async () => miuiXml = await TestClient.DumpScreenStringAsync(Device)); + () => TestClient.DumpScreenStringAsync(Device)); Assert.Equal(cleanMIUIDump, miuiXml); } @@ -885,13 +877,12 @@ public async void DumpScreenStringAsyncEmptyTest() await using MemoryStream emptyStream = new(); - string emptyXml = string.Empty; - await RunTestAsync( + string emptyXml = await RunTestAsync( OkResponses(2), NoResponseMessages, requests, [emptyStream], - async () => emptyXml = await TestClient.DumpScreenStringAsync(Device)); + () => TestClient.DumpScreenStringAsync(Device)); Assert.True(string.IsNullOrEmpty(emptyXml)); } @@ -937,13 +928,12 @@ public async void DumpScreenAsyncTest() byte[] streamData = Encoding.UTF8.GetBytes(dump); await using MemoryStream shellStream = new(streamData); - XmlDocument xml = null; - await RunTestAsync( + XmlDocument xml = await RunTestAsync( OkResponses(2), NoResponseMessages, requests, [shellStream], - async () => xml = await TestClient.DumpScreenAsync(Device)); + () => TestClient.DumpScreenAsync(Device)); string cleanDump = File.ReadAllText(@"Assets/dumpscreen_clean.txt"); XmlDocument doc = new(); @@ -1110,13 +1100,12 @@ public async void IsAppRunningAsyncTest(string response, bool expected) byte[] streamData = Encoding.UTF8.GetBytes(response); await using MemoryStream shellStream = new(streamData); - bool result = !expected; - await RunTestAsync( + bool result = await RunTestAsync( OkResponses(2), NoResponseMessages, requests, [shellStream], - async () => result = await TestClient.IsAppRunningAsync(Device, "com.google.android.gms")); + () => TestClient.IsAppRunningAsync(Device, "com.google.android.gms")); Assert.Equal(expected, result); } @@ -1140,13 +1129,12 @@ public async void IsAppInForegroundAsyncTest(string packageName, bool expected) mResumedActivity: ActivityRecord{896cc3 u0 app.lawnchair/.LawnchairLauncher t5}"u8.ToArray(); await using MemoryStream shellStream = new(streamData); - bool result = !expected; - await RunTestAsync( + bool result = await RunTestAsync( OkResponses(2), NoResponseMessages, requests, [shellStream], - async () => result = await TestClient.IsAppInForegroundAsync(Device, packageName)); + () => TestClient.IsAppInForegroundAsync(Device, packageName)); Assert.Equal(expected, result); } @@ -1173,13 +1161,12 @@ public async void GetAppStatusAsyncTest(string packageName, string response, App byte[] pidData = Encoding.UTF8.GetBytes(response); await using MemoryStream pidStream = new(pidData); - AppStatus result = AppStatus.Foreground; - await RunTestAsync( + AppStatus result = await RunTestAsync( OkResponses(4), NoResponseMessages, requests, [activityStream, pidStream], - async () => result = await TestClient.GetAppStatusAsync(Device, packageName)); + () => TestClient.GetAppStatusAsync(Device, packageName)); Assert.Equal(expected, result); } @@ -1202,13 +1189,12 @@ public async void GetAppStatusAsyncForegroundTest(string packageName, AppStatus mResumedActivity: ActivityRecord{896cc3 u0 app.lawnchair/.LawnchairLauncher t5}"u8.ToArray(); await using MemoryStream shellStream = new(streamData); - AppStatus result = default; - await RunTestAsync( + AppStatus result = await RunTestAsync( OkResponses(2), NoResponseMessages, requests, [shellStream], - async () => result = await TestClient.GetAppStatusAsync(Device, packageName)); + () => TestClient.GetAppStatusAsync(Device, packageName)); Assert.Equal(expected, result); } @@ -1229,13 +1215,12 @@ public async void FindElementAsyncTest() byte[] streamData = Encoding.UTF8.GetBytes(dump); await using MemoryStream shellStream = new(streamData); - Element element = null; - await RunTestAsync( + Element element = await RunTestAsync( OkResponses(2), NoResponseMessages, requests, [shellStream], - async () => element = await TestClient.FindElementAsync(Device)); + () => TestClient.FindElementAsync(Device)); Assert.Equal(144, element.GetChildCount()); element = element[0][0][0][0][0][0][0][0][2][1][0][0]; @@ -1259,13 +1244,12 @@ public async void FindElementsAsyncTest() byte[] streamData = Encoding.UTF8.GetBytes(dump); await using MemoryStream shellStream = new(streamData); - Element[] elements = null; - await RunTestAsync( + Element[] elements = await RunTestAsync( OkResponses(2), NoResponseMessages, requests, [shellStream], - async () => elements = (await TestClient.FindElementsAsync(Device)).ToArray()); + async () => (await TestClient.FindElementsAsync(Device)).ToArray()); int childCount = elements.Length; Array.ForEach(elements, x => childCount += x.GetChildCount()); @@ -1291,19 +1275,19 @@ public async void FindAsyncElementsTest() byte[] streamData = Encoding.UTF8.GetBytes(dump); await using MemoryStream shellStream = new(streamData); - List elements = null; - await RunTestAsync( + List elements = await RunTestAsync( OkResponses(2), NoResponseMessages, requests, [shellStream], async () => { - elements = []; + List elements = []; await foreach (Element element in TestClient.FindAsyncElements(Device)) { elements.Add(element); } + return elements; }); int childCount = elements.Count; diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs index 90a79b97..e1dc796a 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs @@ -87,12 +87,11 @@ public void GetAdbVersionTest() string[] responseMessages = ["0020"]; string[] requests = ["host:version"]; - int version = 0; - RunTest( + int version = RunTest( OkResponse, responseMessages, requests, - () => version = TestClient.GetAdbVersion()); + TestClient.GetAdbVersion); // Make sure and the correct value is returned. Assert.Equal(32, version); @@ -122,18 +121,17 @@ public void GetDevicesTest() string[] responseMessages = ["169.254.109.177:5555 device product:VS Emulator 5\" KitKat (4.4) XXHDPI Phone model:5__KitKat__4_4__XXHDPI_Phone device:donatello\n"]; string[] requests = ["host:devices-l"]; - IEnumerable devices = null; - RunTest( + DeviceData[] devices = RunTest( OkResponse, responseMessages, requests, - () => devices = TestClient.GetDevices()); + () => TestClient.GetDevices().ToArray()); // Make sure and the correct value is returned. Assert.NotNull(devices); Assert.Single(devices); - DeviceData device = devices.Single(); + DeviceData device = devices.SingleOrDefault(); Assert.Equal("169.254.109.177:5555", device.Serial); Assert.Equal(DeviceState.Online, device.State); @@ -318,12 +316,11 @@ public void ListForwardTest() string[] responseMessages = ["169.254.109.177:5555 tcp:1 tcp:2\n169.254.109.177:5555 tcp:3 tcp:4\n169.254.109.177:5555 tcp:5 local:/socket/1\n"]; string[] requests = ["host-serial:169.254.109.177:5555:list-forward"]; - ForwardData[] forwards = null; - RunTest( + ForwardData[] forwards = RunTest( OkResponse, responseMessages, requests, - () => forwards = TestClient.ListForward(Device).ToArray()); + () => TestClient.ListForward(Device).ToArray()); Assert.NotNull(forwards); Assert.Equal(3, forwards.Length); @@ -346,12 +343,11 @@ public void ListReverseForwardTest() "reverse:list-forward" ]; - ForwardData[] forwards = null; - RunTest( + ForwardData[] forwards = RunTest( OkResponses(2), responseMessages, requests, - () => forwards = TestClient.ListReverseForward(Device).ToArray()); + () => TestClient.ListReverseForward(Device).ToArray()); Assert.NotNull(forwards); Assert.Equal(3, forwards.Length); @@ -818,13 +814,12 @@ public void InstallCreateTest() byte[] streamData = Encoding.ASCII.GetBytes("Success: created install session [936013062]\r\n"); using MemoryStream shellStream = new(streamData); - string session = string.Empty; - RunTest( + string session = RunTest( OkResponses(2), NoResponseMessages, requests, [shellStream], - () => session = TestClient.InstallCreate(Device, "com.google.android.gms")); + () => TestClient.InstallCreate(Device, "com.google.android.gms")); Assert.Equal("936013062", session); } @@ -911,16 +906,15 @@ public void GetFeatureSetTest() string[] requests = ["host-serial:169.254.109.177:5555:features"]; string[] responses = ["sendrecv_v2_brotli,remount_shell,sendrecv_v2,abb_exec,fixed_push_mkdir,fixed_push_symlink_timestamp,abb,shell_v2,cmd,ls_v2,apex,stat_v2\r\n"]; - IEnumerable features = null; - RunTest( + string[] features = RunTest( OkResponse, responses, requests, - () => features = TestClient.GetFeatureSet(Device)); + () => TestClient.GetFeatureSet(Device).ToArray()); - Assert.Equal(12, features.Count()); - Assert.Equal("sendrecv_v2_brotli", features.First()); - Assert.Equal("stat_v2", features.Last()); + Assert.Equal(12, features.Length); + Assert.Equal("sendrecv_v2_brotli", features.FirstOrDefault()); + Assert.Equal("stat_v2", features.LastOrDefault()); } /// @@ -940,13 +934,12 @@ public void DumpScreenStringTest() byte[] streamData = Encoding.UTF8.GetBytes(dump); using MemoryStream shellStream = new(streamData); - string xml = string.Empty; - RunTest( + string xml = RunTest( OkResponses(2), NoResponseMessages, requests, [shellStream], - () => xml = TestClient.DumpScreenString(Device)); + () => TestClient.DumpScreenString(Device)); Assert.Equal(cleanDump, xml); } @@ -968,13 +961,12 @@ public void DumpScreenStringMIUITest() byte[] miuiStreamData = Encoding.UTF8.GetBytes(miuidump); using MemoryStream miuiStream = new(miuiStreamData); - string miuiXml = string.Empty; - RunTest( + string miuiXml = RunTest( OkResponses(2), NoResponseMessages, requests, [miuiStream], - () => miuiXml = TestClient.DumpScreenString(Device)); + () => TestClient.DumpScreenString(Device)); Assert.Equal(cleanMIUIDump, miuiXml); } @@ -994,13 +986,12 @@ public void DumpScreenStringEmptyTest() byte[] emptyStreamData = Encoding.UTF8.GetBytes(string.Empty); using MemoryStream emptyStream = new(emptyStreamData); - string emptyXml = string.Empty; - RunTest( + string emptyXml = RunTest( OkResponses(2), NoResponseMessages, requests, [emptyStream], - () => emptyXml = TestClient.DumpScreenString(Device)); + () => TestClient.DumpScreenString(Device)); Assert.True(string.IsNullOrEmpty(emptyXml)); } @@ -1046,13 +1037,12 @@ public void DumpScreenTest() byte[] streamData = Encoding.UTF8.GetBytes(dump); using MemoryStream shellStream = new(streamData); - XmlDocument xml = null; - RunTest( + XmlDocument xml = RunTest( OkResponses(2), NoResponseMessages, requests, [shellStream], - () => xml = TestClient.DumpScreen(Device)); + () => TestClient.DumpScreen(Device)); string cleanDump = File.ReadAllText(@"Assets/dumpscreen_clean.txt"); XmlDocument doc = new(); @@ -1220,13 +1210,12 @@ public void IsAppRunningTest(string response, bool expected) byte[] streamData = Encoding.UTF8.GetBytes(response); using MemoryStream shellStream = new(streamData); - bool result = !expected; - RunTest( + bool result = RunTest( OkResponses(2), NoResponseMessages, requests, [shellStream], - () => result = TestClient.IsAppRunning(Device, "com.google.android.gms")); + () => TestClient.IsAppRunning(Device, "com.google.android.gms")); Assert.Equal(expected, result); } @@ -1250,13 +1239,12 @@ public void IsAppInForegroundTest(string packageName, bool expected) mResumedActivity: ActivityRecord{896cc3 u0 app.lawnchair/.LawnchairLauncher t5}"u8.ToArray(); using MemoryStream shellStream = new(streamData); - bool result = !expected; - RunTest( + bool result = RunTest( OkResponses(2), NoResponseMessages, requests, [shellStream], - () => result = TestClient.IsAppInForeground(Device, packageName)); + () => TestClient.IsAppInForeground(Device, packageName)); Assert.Equal(expected, result); } @@ -1283,13 +1271,12 @@ public void GetAppStatusTest(string packageName, string response, AppStatus expe byte[] pidData = Encoding.UTF8.GetBytes(response); using MemoryStream pidStream = new(pidData); - AppStatus result = AppStatus.Foreground; - RunTest( + AppStatus result = RunTest( OkResponses(4), NoResponseMessages, requests, [activityStream, pidStream], - () => result = TestClient.GetAppStatus(Device, packageName)); + () => TestClient.GetAppStatus(Device, packageName)); Assert.Equal(expected, result); } @@ -1312,13 +1299,12 @@ public void GetAppStatusForegroundTest(string packageName, AppStatus expected) mResumedActivity: ActivityRecord{896cc3 u0 app.lawnchair/.LawnchairLauncher t5}"u8.ToArray(); using MemoryStream shellStream = new(streamData); - AppStatus result = default; - RunTest( + AppStatus result = RunTest( OkResponses(2), NoResponseMessages, requests, [shellStream], - () => result = TestClient.GetAppStatus(Device, packageName)); + () => TestClient.GetAppStatus(Device, packageName)); Assert.Equal(expected, result); } @@ -1339,13 +1325,12 @@ public void FindElementTest() byte[] streamData = Encoding.UTF8.GetBytes(dump); using MemoryStream shellStream = new(streamData); - Element element = null; - RunTest( + Element element = RunTest( OkResponses(2), NoResponseMessages, requests, [shellStream], - () => element = TestClient.FindElement(Device)); + () => TestClient.FindElement(Device)); Assert.Equal(144, element.GetChildCount()); element = element[0][0][0][0][0][0][0][0][2][1][0][0]; @@ -1369,13 +1354,12 @@ public void FindElementsTest() byte[] streamData = Encoding.UTF8.GetBytes(dump); using MemoryStream shellStream = new(streamData); - Element[] elements = null; - RunTest( + Element[] elements = RunTest( OkResponses(2), NoResponseMessages, requests, [shellStream], - () => elements = TestClient.FindElements(Device).ToArray()); + () => TestClient.FindElements(Device).ToArray()); int childCount = elements.Length; Array.ForEach(elements, x => childCount += x.GetChildCount()); diff --git a/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.Async.cs b/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.Async.cs index 5f2967a3..fa429c0d 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.Async.cs @@ -80,11 +80,11 @@ await RunTestAsync( // Device disconnects ManualResetEvent eventWaiter = sink.CreateEventSignal(); - RunTest( + _ = RunTest( NoResponses, ["169.254.109.177:5555\tdevice\n"], NoRequests, - () => _ = eventWaiter.WaitOne(1000)); + () => eventWaiter.WaitOne(1000)); Assert.Single(monitor.Devices); Assert.Single(sink.ConnectedEvents); @@ -152,11 +152,11 @@ await RunTestAsync( // Device disconnects ManualResetEvent eventWaiter = sink.CreateEventSignal(); - RunTest( + _ = RunTest( NoResponses, ["169.254.109.177:5555\tdevice\n"], NoRequests, - () => _ = eventWaiter.WaitOne(1000)); + () => eventWaiter.WaitOne(1000)); Assert.Single(monitor.Devices); Assert.Equal(DeviceState.Online, monitor.Devices.ElementAt(0).State); @@ -200,11 +200,11 @@ await RunTestAsync( // Something happens but device does not change ManualResetEvent eventWaiter = sink.CreateEventSignal(); - RunTest( + _ = RunTest( NoResponses, ["169.254.109.177:5555\toffline\n"], NoRequests, - () => _ = eventWaiter.WaitOne(1000)); + () => eventWaiter.WaitOne(1000)); Assert.Single(monitor.Devices); Assert.Equal(DeviceState.Offline, monitor.Devices.ElementAt(0).State); diff --git a/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs b/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs index 26096a04..dbf46fa4 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs @@ -60,11 +60,11 @@ public void DeviceDisconnectedTest() // Device disconnects ManualResetEvent eventWaiter = sink.CreateEventSignal(); - RunTest( + _ = RunTest( NoResponses, [string.Empty], NoRequests, - () => _ = eventWaiter.WaitOne(1000)); + () => eventWaiter.WaitOne(1000)); Assert.Empty(monitor.Devices); Assert.Single(sink.ConnectedEvents); @@ -104,11 +104,11 @@ public void DeviceConnectedTest() // Device disconnects ManualResetEvent eventWaiter = sink.CreateEventSignal(); - RunTest( + _ = RunTest( NoResponses, ["169.254.109.177:5555\tdevice\n"], NoRequests, - () => _ = eventWaiter.WaitOne(1000)); + () => eventWaiter.WaitOne(1000)); Assert.Single(monitor.Devices); Assert.Single(sink.ConnectedEvents); @@ -176,11 +176,11 @@ public void DeviceChanged_TriggeredWhenStatusChangedTest() // Device disconnects ManualResetEvent eventWaiter = sink.CreateEventSignal(); - RunTest( + _ = RunTest( NoResponses, ["169.254.109.177:5555\tdevice\n"], NoRequests, - () => _ = eventWaiter.WaitOne(1000)); + () => eventWaiter.WaitOne(1000)); Assert.Single(monitor.Devices); Assert.Equal(DeviceState.Online, monitor.Devices[0].State); @@ -224,11 +224,11 @@ public void DeviceChanged_NoTriggerIfStatusIsSameTest() // Something happens but device does not change ManualResetEvent eventWaiter = sink.CreateEventSignal(); - RunTest( + _ = RunTest( NoResponses, ["169.254.109.177:5555\toffline\n"], NoRequests, - () => _ = eventWaiter.WaitOne(1000)); + () => eventWaiter.WaitOne(1000)); Assert.Single(monitor.Devices); Assert.Equal(DeviceState.Offline, monitor.Devices[0].State); diff --git a/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs b/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs index d2b270b0..3f8d45af 100644 --- a/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs +++ b/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs @@ -11,8 +11,6 @@ namespace AdvancedSharpAdbClient { public class SocketBasedTests { - protected AdbClient TestClient { get; private set; } - protected SocketBasedTests(bool integrationTest, bool doDispose) { using FactoriesLocker locker = FactoriesLocker.Wait(); @@ -60,12 +58,16 @@ protected SocketBasedTests(bool integrationTest, bool doDispose) State = DeviceState.Online }; + protected AdbClient TestClient { get; private set; } + protected IDummyAdbSocket Socket { get; set; } public EndPoint EndPoint { get; set; } public bool IntegrationTest { get; set; } + #region Action + /// /// /// Runs an ADB helper test, either as a unit test or as an integration test. @@ -350,9 +352,13 @@ protected void RunTest( } } + #endregion + + #region Function + /// /// - /// Runs an async ADB helper test, either as a unit test or as an integration test. + /// Runs an ADB helper test, either as a unit test or as an integration test. /// /// /// When running as a unit test, the and @@ -371,17 +377,17 @@ protected void RunTest( /// The messages that should follow the . /// The requests the client should send. /// The test to run. - /// A which represents the asynchronous operation. - protected Task RunTestAsync( + /// The result of . + protected TResult RunTest( IEnumerable responses, IEnumerable responseMessages, IEnumerable requests, - Func test) => - RunTestAsync(responses, responseMessages, requests, null, null, null, null, null, test); + Func test) => + RunTest(responses, responseMessages, requests, null, null, null, null, null, test); /// /// - /// Runs an async ADB helper test, either as a unit test or as an integration test. + /// Runs an ADB helper test, either as a unit test or as an integration test. /// /// /// When running as a unit test, the and @@ -401,18 +407,18 @@ protected Task RunTestAsync( /// The requests the client should send. /// The of which the should use. /// The test to run. - /// A which represents the asynchronous operation. - protected Task RunTestAsync( + /// The result of . + protected TResult RunTest( IEnumerable responses, IEnumerable responseMessages, IEnumerable requests, IEnumerable shellStreams, - Func test) => - RunTestAsync(responses, responseMessages, requests, null, null, null, null, shellStreams, test); + Func test) => + RunTest(responses, responseMessages, requests, null, null, null, null, shellStreams, test); /// /// - /// Runs an async ADB helper test, either as a unit test or as an integration test. + /// Runs an ADB helper test, either as a unit test or as an integration test. /// /// /// When running as a unit test, the and @@ -435,8 +441,8 @@ protected Task RunTestAsync( /// The of data which the ADB sever should send. /// The of data which the client should send. /// The test to run. - /// A which represents the asynchronous operation. - protected Task RunTestAsync( + /// The result of . + protected TResult RunTest( IEnumerable responses, IEnumerable responseMessages, IEnumerable requests, @@ -444,8 +450,8 @@ protected Task RunTestAsync( IEnumerable syncResponses, IEnumerable syncDataReceived, IEnumerable syncDataSent, - Func test) => - RunTestAsync( + Func test) => + RunTest( responses, responseMessages, requests, @@ -458,7 +464,7 @@ protected Task RunTestAsync( /// /// - /// Runs an async ADB helper test, either as a unit test or as an integration test. + /// Runs an ADB helper test, either as a unit test or as an integration test. /// /// /// When running as a unit test, the and @@ -482,8 +488,8 @@ protected Task RunTestAsync( /// The of data which the client should send. /// The of which the should use. /// The test to run. - /// A which represents the asynchronous operation. - protected async Task RunTestAsync( + /// The result of . + protected TResult RunTest( IEnumerable responses, IEnumerable responseMessages, IEnumerable requests, @@ -492,7 +498,7 @@ protected async Task RunTestAsync( IEnumerable syncDataReceived, IEnumerable syncDataSent, IEnumerable shellStreams, - Func test) + Func test) { // If we are running unit tests, we need to mock all the responses // that are sent by the device. Do that now. @@ -533,15 +539,12 @@ protected async Task RunTestAsync( } } + TResult result = default; Exception exception = null; try { - await test(); - } - catch (AggregateException ex) - { - exception = ex.InnerExceptions.Count == 1 ? ex.InnerException : ex; + result = test(); } catch (Exception ex) { @@ -636,11 +639,600 @@ protected async Task RunTestAsync( } } - if (exception != null) + return exception != null ? throw exception : result; + } + + #endregion + + #region Task + + /// + /// + /// Runs an async ADB helper test, either as a unit test or as an integration test. + /// + /// + /// When running as a unit test, the and + /// are used by the to mock the responses an actual device + /// would send; and the parameter is used to ensure the code + /// did send the correct requests to the device. + /// + /// + /// When running as an integration test, all three parameters, , + /// and are used to validate + /// that the traffic we simulate in the unit tests matches the traffic that is actually sent + /// over the wire. + /// + /// + /// The messages that the ADB sever should send. + /// The messages that should follow the . + /// The requests the client should send. + /// The test to run. + /// A which represents the asynchronous operation. + protected Task RunTestAsync( + IEnumerable responses, + IEnumerable responseMessages, + IEnumerable requests, + Func test) => + RunTestAsync(responses, responseMessages, requests, null, null, null, null, null, test); + + /// + /// + /// Runs an async ADB helper test, either as a unit test or as an integration test. + /// + /// + /// When running as a unit test, the and + /// are used by the to mock the responses an actual device + /// would send; and the parameter is used to ensure the code + /// did send the correct requests to the device. + /// + /// + /// When running as an integration test, all three parameters, , + /// and are used to validate + /// that the traffic we simulate in the unit tests matches the traffic that is actually sent + /// over the wire. + /// + /// + /// The messages that the ADB sever should send. + /// The messages that should follow the . + /// The requests the client should send. + /// The of which the should use. + /// The test to run. + /// A which represents the asynchronous operation. + protected Task RunTestAsync( + IEnumerable responses, + IEnumerable responseMessages, + IEnumerable requests, + IEnumerable shellStreams, + Func test) => + RunTestAsync(responses, responseMessages, requests, null, null, null, null, shellStreams, test); + + /// + /// + /// Runs an async ADB helper test, either as a unit test or as an integration test. + /// + /// + /// When running as a unit test, the and + /// are used by the to mock the responses an actual device + /// would send; and the parameter is used to ensure the code + /// did send the correct requests to the device. + /// + /// + /// When running as an integration test, all three parameters, , + /// and are used to validate + /// that the traffic we simulate in the unit tests matches the traffic that is actually sent + /// over the wire. + /// + /// + /// The messages that the ADB sever should send. + /// The messages that should follow the . + /// The requests the client should send. + /// The requests the client should send. + /// The messages that the ADB sever should send. + /// The of data which the ADB sever should send. + /// The of data which the client should send. + /// The test to run. + /// A which represents the asynchronous operation. + protected Task RunTestAsync( + IEnumerable responses, + IEnumerable responseMessages, + IEnumerable requests, + IEnumerable<(SyncCommand, string)> syncRequests, + IEnumerable syncResponses, + IEnumerable syncDataReceived, + IEnumerable syncDataSent, + Func test) => + RunTestAsync( + responses, + responseMessages, + requests, + syncRequests, + syncResponses, + syncDataReceived, + syncDataSent, + null, + test); + + /// + /// + /// Runs an async ADB helper test, either as a unit test or as an integration test. + /// + /// + /// When running as a unit test, the and + /// are used by the to mock the responses an actual device + /// would send; and the parameter is used to ensure the code + /// did send the correct requests to the device. + /// + /// + /// When running as an integration test, all three parameters, , + /// and are used to validate + /// that the traffic we simulate in the unit tests matches the traffic that is actually sent + /// over the wire. + /// + /// + /// The messages that the ADB sever should send. + /// The messages that should follow the . + /// The requests the client should send. + /// The requests the client should send. + /// The messages that the ADB sever should send. + /// The of data which the ADB sever should send. + /// The of data which the client should send. + /// The of which the should use. + /// The test to run. + /// A which represents the asynchronous operation. + protected async Task RunTestAsync( + IEnumerable responses, + IEnumerable responseMessages, + IEnumerable requests, + IEnumerable<(SyncCommand, string)> syncRequests, + IEnumerable syncResponses, + IEnumerable syncDataReceived, + IEnumerable syncDataSent, + IEnumerable shellStreams, + Func test) + { + // If we are running unit tests, we need to mock all the responses + // that are sent by the device. Do that now. + if (!IntegrationTest) { - throw exception; + foreach (AdbResponse response in responses) + { + Socket.Responses.Enqueue(response); + } + + foreach (string responseMessage in responseMessages) + { + Socket.ResponseMessages.Enqueue(responseMessage); + } + + if (syncResponses != null) + { + foreach (SyncCommand syncResponse in syncResponses) + { + Socket.SyncResponses.Enqueue(syncResponse); + } + } + + if (syncDataReceived != null) + { + foreach (byte[] syncDatum in syncDataReceived) + { + Socket.SyncDataReceived.Enqueue(syncDatum); + } + } + + if (shellStreams != null) + { + foreach (Stream shellStream in shellStreams) + { + Socket.ShellStreams.Enqueue(shellStream); + } + } } - } + + Exception exception = null; + + try + { + await test(); + } + catch (AggregateException ex) + { + exception = ex.InnerExceptions.Count == 1 ? ex.InnerException : ex; + } + catch (Exception ex) + { + exception = ex; + } + + if (!IntegrationTest) + { + // If we are running unit tests, we need to make sure all messages + // were read, and the correct request was sent. + + // Make sure the messages were read + Assert.Empty(Socket.ResponseMessages); + Assert.Empty(Socket.Responses); + Assert.Empty(Socket.SyncResponses); + Assert.Empty(Socket.SyncDataReceived); + Assert.Empty(Socket.ShellStreams); + + // Make sure a request was sent + Assert.Equal(requests.ToArray(), Socket.Requests); + + if (syncRequests != null) + { + Assert.Equal(syncRequests.ToArray(), Socket.SyncRequests); + } + else + { + Assert.Empty(Socket.SyncRequests); + } + + if (syncDataSent != null) + { + AssertEqual(syncDataSent.ToArray(), Socket.SyncDataSent.ToArray()); + } + else + { + Assert.Empty(Socket.SyncDataSent); + } + } + else + { + // Make sure the traffic sent on the wire matches the traffic + // we have defined in our unit test. + Assert.Equal(requests.ToArray(), Socket.Requests); + + if (syncRequests != null) + { + Assert.Equal(syncRequests.ToArray(), Socket.SyncRequests); + } + else + { + Assert.Empty(Socket.SyncRequests); + } + + Assert.Equal(responses.ToArray(), Socket.Responses); + Assert.Equal(responseMessages.ToArray(), Socket.ResponseMessages); + + if (syncResponses != null) + { + Assert.Equal(syncResponses.ToArray(), Socket.SyncResponses); + } + else + { + Assert.Empty(Socket.SyncResponses); + } + + if (syncDataReceived != null) + { + AssertEqual(syncDataReceived.ToArray(), Socket.SyncDataReceived.ToArray()); + } + else + { + Assert.Empty(Socket.SyncDataReceived); + } + + if (syncDataSent != null) + { + AssertEqual(syncDataSent.ToArray(), Socket.SyncDataSent.ToArray()); + } + else + { + Assert.Empty(Socket.SyncDataSent); + } + + if (shellStreams != null) + { + Assert.Equal(shellStreams.ToArray(), [.. Socket.ShellStreams]); + } + else + { + Assert.Empty(Socket.ShellStreams); + } + } + + if (exception != null) + { + throw exception; + } + } + + #endregion + + #region Task + + /// + /// + /// Runs an async ADB helper test, either as a unit test or as an integration test. + /// + /// + /// When running as a unit test, the and + /// are used by the to mock the responses an actual device + /// would send; and the parameter is used to ensure the code + /// did send the correct requests to the device. + /// + /// + /// When running as an integration test, all three parameters, , + /// and are used to validate + /// that the traffic we simulate in the unit tests matches the traffic that is actually sent + /// over the wire. + /// + /// + /// The messages that the ADB sever should send. + /// The messages that should follow the . + /// The requests the client should send. + /// The test to run. + /// A which return the result of . + protected Task RunTestAsync( + IEnumerable responses, + IEnumerable responseMessages, + IEnumerable requests, + Func> test) => + RunTestAsync(responses, responseMessages, requests, null, null, null, null, null, test); + + /// + /// + /// Runs an async ADB helper test, either as a unit test or as an integration test. + /// + /// + /// When running as a unit test, the and + /// are used by the to mock the responses an actual device + /// would send; and the parameter is used to ensure the code + /// did send the correct requests to the device. + /// + /// + /// When running as an integration test, all three parameters, , + /// and are used to validate + /// that the traffic we simulate in the unit tests matches the traffic that is actually sent + /// over the wire. + /// + /// + /// The messages that the ADB sever should send. + /// The messages that should follow the . + /// The requests the client should send. + /// The of which the should use. + /// The test to run. + /// A which return the result of . + protected Task RunTestAsync( + IEnumerable responses, + IEnumerable responseMessages, + IEnumerable requests, + IEnumerable shellStreams, + Func> test) => + RunTestAsync(responses, responseMessages, requests, null, null, null, null, shellStreams, test); + + /// + /// + /// Runs an async ADB helper test, either as a unit test or as an integration test. + /// + /// + /// When running as a unit test, the and + /// are used by the to mock the responses an actual device + /// would send; and the parameter is used to ensure the code + /// did send the correct requests to the device. + /// + /// + /// When running as an integration test, all three parameters, , + /// and are used to validate + /// that the traffic we simulate in the unit tests matches the traffic that is actually sent + /// over the wire. + /// + /// + /// The messages that the ADB sever should send. + /// The messages that should follow the . + /// The requests the client should send. + /// The requests the client should send. + /// The messages that the ADB sever should send. + /// The of data which the ADB sever should send. + /// The of data which the client should send. + /// The test to run. + /// A which return the result of . + protected Task RunTestAsync( + IEnumerable responses, + IEnumerable responseMessages, + IEnumerable requests, + IEnumerable<(SyncCommand, string)> syncRequests, + IEnumerable syncResponses, + IEnumerable syncDataReceived, + IEnumerable syncDataSent, + Func> test) => + RunTestAsync( + responses, + responseMessages, + requests, + syncRequests, + syncResponses, + syncDataReceived, + syncDataSent, + null, + test); + + /// + /// + /// Runs an async ADB helper test, either as a unit test or as an integration test. + /// + /// + /// When running as a unit test, the and + /// are used by the to mock the responses an actual device + /// would send; and the parameter is used to ensure the code + /// did send the correct requests to the device. + /// + /// + /// When running as an integration test, all three parameters, , + /// and are used to validate + /// that the traffic we simulate in the unit tests matches the traffic that is actually sent + /// over the wire. + /// + /// + /// The messages that the ADB sever should send. + /// The messages that should follow the . + /// The requests the client should send. + /// The requests the client should send. + /// The messages that the ADB sever should send. + /// The of data which the ADB sever should send. + /// The of data which the client should send. + /// The of which the should use. + /// The test to run. + /// A which return the result of . + protected async Task RunTestAsync( + IEnumerable responses, + IEnumerable responseMessages, + IEnumerable requests, + IEnumerable<(SyncCommand, string)> syncRequests, + IEnumerable syncResponses, + IEnumerable syncDataReceived, + IEnumerable syncDataSent, + IEnumerable shellStreams, + Func> test) + { + // If we are running unit tests, we need to mock all the responses + // that are sent by the device. Do that now. + if (!IntegrationTest) + { + foreach (AdbResponse response in responses) + { + Socket.Responses.Enqueue(response); + } + + foreach (string responseMessage in responseMessages) + { + Socket.ResponseMessages.Enqueue(responseMessage); + } + + if (syncResponses != null) + { + foreach (SyncCommand syncResponse in syncResponses) + { + Socket.SyncResponses.Enqueue(syncResponse); + } + } + + if (syncDataReceived != null) + { + foreach (byte[] syncDatum in syncDataReceived) + { + Socket.SyncDataReceived.Enqueue(syncDatum); + } + } + + if (shellStreams != null) + { + foreach (Stream shellStream in shellStreams) + { + Socket.ShellStreams.Enqueue(shellStream); + } + } + } + + TResult result = default; + Exception exception = null; + + try + { + result = await test(); + } + catch (AggregateException ex) + { + exception = ex.InnerExceptions.Count == 1 ? ex.InnerException : ex; + } + catch (Exception ex) + { + exception = ex; + } + + if (!IntegrationTest) + { + // If we are running unit tests, we need to make sure all messages + // were read, and the correct request was sent. + + // Make sure the messages were read + Assert.Empty(Socket.ResponseMessages); + Assert.Empty(Socket.Responses); + Assert.Empty(Socket.SyncResponses); + Assert.Empty(Socket.SyncDataReceived); + Assert.Empty(Socket.ShellStreams); + + // Make sure a request was sent + Assert.Equal(requests.ToArray(), Socket.Requests); + + if (syncRequests != null) + { + Assert.Equal(syncRequests.ToArray(), Socket.SyncRequests); + } + else + { + Assert.Empty(Socket.SyncRequests); + } + + if (syncDataSent != null) + { + AssertEqual(syncDataSent.ToArray(), Socket.SyncDataSent.ToArray()); + } + else + { + Assert.Empty(Socket.SyncDataSent); + } + } + else + { + // Make sure the traffic sent on the wire matches the traffic + // we have defined in our unit test. + Assert.Equal(requests.ToArray(), Socket.Requests); + + if (syncRequests != null) + { + Assert.Equal(syncRequests.ToArray(), Socket.SyncRequests); + } + else + { + Assert.Empty(Socket.SyncRequests); + } + + Assert.Equal(responses.ToArray(), Socket.Responses); + Assert.Equal(responseMessages.ToArray(), Socket.ResponseMessages); + + if (syncResponses != null) + { + Assert.Equal(syncResponses.ToArray(), Socket.SyncResponses); + } + else + { + Assert.Empty(Socket.SyncResponses); + } + + if (syncDataReceived != null) + { + AssertEqual(syncDataReceived.ToArray(), Socket.SyncDataReceived.ToArray()); + } + else + { + Assert.Empty(Socket.SyncDataReceived); + } + + if (syncDataSent != null) + { + AssertEqual(syncDataSent.ToArray(), Socket.SyncDataSent.ToArray()); + } + else + { + Assert.Empty(Socket.SyncDataSent); + } + + if (shellStreams != null) + { + Assert.Equal(shellStreams.ToArray(), [.. Socket.ShellStreams]); + } + else + { + Assert.Empty(Socket.ShellStreams); + } + } + + return exception != null ? throw exception : result; + } + + #endregion protected static IEnumerable OkResponses(int count) => Enumerable.Repeat(AdbResponse.OK, count); diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs index 4e8652c8..57617d65 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs @@ -11,9 +11,7 @@ public partial class SyncServiceTests [Fact] public async void StatAsyncTest() { - FileStatistics value = null; - - await RunTestAsync( + FileStatistics value = await RunTestAsync( OkResponses(2), NoResponseMessages, ["host:transport:169.254.109.177:5555", "sync:"], @@ -24,7 +22,7 @@ await RunTestAsync( async () => { using SyncService service = new(Socket, Device); - value = await service.StatAsync("/fstab.donatello"); + return await service.StatAsync("/fstab.donatello"); }); Assert.NotNull(value); @@ -36,9 +34,7 @@ await RunTestAsync( [Fact] public async void GetListingAsyncTest() { - FileStatistics[] value = null; - - await RunTestAsync( + FileStatistics[] value = await RunTestAsync( OkResponses(2), [".", "..", "sdcard0", "emulated"], ["host:transport:169.254.109.177:5555", "sync:"], @@ -54,7 +50,7 @@ await RunTestAsync( async () => { using SyncService service = new(Socket, Device); - value = (await service.GetDirectoryListingAsync("/storage")).ToArray(); + return (await service.GetDirectoryListingAsync("/storage")).ToArray(); }); Assert.Equal(4, value.Length); @@ -89,9 +85,7 @@ await RunTestAsync( [Fact] public async void GetAsyncListingTest() { - List value = null; - - await RunTestAsync( + List value = await RunTestAsync( OkResponses(2), [".", "..", "sdcard0", "emulated"], ["host:transport:169.254.109.177:5555", "sync:"], @@ -106,12 +100,13 @@ await RunTestAsync( null, async () => { + List value = []; using SyncService service = new(Socket, Device); - value = []; await foreach (FileStatistics statistics in service.GetDirectoryAsyncListing("/storage")) { value.Add(statistics); } + return value; }); Assert.Equal(4, value.Count); diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs index 9fb406ff..78b4f6c8 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs @@ -21,9 +21,7 @@ public SyncServiceTests() : base(integrationTest: false, doDispose: false) [Fact] public void StatTest() { - FileStatistics value = null; - - RunTest( + FileStatistics value = RunTest( OkResponses(2), NoResponseMessages, ["host:transport:169.254.109.177:5555", "sync:"], @@ -34,7 +32,7 @@ public void StatTest() () => { using SyncService service = new(Socket, Device); - value = service.Stat("/fstab.donatello"); + return service.Stat("/fstab.donatello"); }); Assert.NotNull(value); @@ -46,9 +44,7 @@ public void StatTest() [Fact] public void GetListingTest() { - FileStatistics[] value = null; - - RunTest( + FileStatistics[] value = RunTest( OkResponses(2), [".", "..", "sdcard0", "emulated"], ["host:transport:169.254.109.177:5555", "sync:"], @@ -64,7 +60,7 @@ public void GetListingTest() () => { using SyncService service = new(Socket, Device); - value = service.GetDirectoryListing("/storage").ToArray(); + return service.GetDirectoryListing("/storage").ToArray(); }); Assert.Equal(4, value.Length); From d0c8973af11bc8264fc74969b86640d7fd7e7fc3 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Sat, 7 Oct 2023 17:23:18 +0800 Subject: [PATCH 30/66] Use ContinueWith to continue Task --- .../AdbClientTests.Async.cs | 16 +++--- .../AdbCommandLineClientTests.Async.cs | 12 +++++ .../AdbCommandLineClientTests.cs | 12 +++++ .../AdbServerTests.Async.cs | 36 ++++++++++++++ .../AdbServerTests.cs | 39 +++++++++++++++ .../AdbSocketTests.Async.cs | 47 +++++++++++++++--- .../AdbSocketTests.cs | 49 ++++++++++++++++++- .../DeviceExtensionsTests.Async.cs | 2 +- .../DeviceMonitorTests.Async.cs | 3 ++ .../DeviceMonitorTests.cs | 12 ++++- .../Dummys/DummyAdbSocket.cs | 23 ++++++++- AdvancedSharpAdbClient.Tests/Logs/LogTests.cs | 12 ++--- .../SyncServiceTests.Async.cs | 25 ++++++++-- .../SyncServiceTests.cs | 12 +++++ .../TcpSocketTests.Async.cs | 14 ++++++ .../TcpSocketTests.cs | 9 ++++ AdvancedSharpAdbClient/AdbClient.Async.cs | 16 +++--- AdvancedSharpAdbClient/AdbSocket.Async.cs | 2 +- .../Extensions/Extensions.cs | 10 ++++ AdvancedSharpAdbClient/SyncService.Async.cs | 2 +- 20 files changed, 312 insertions(+), 41 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs index 28ef0dbc..84135590 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs @@ -66,7 +66,7 @@ public async void GetDevicesAsyncTest() OkResponse, responseMessages, requests, - async () => (await TestClient.GetDevicesAsync()).ToArray()); + async () => await TestClient.GetDevicesAsync().ToArray()); // Make sure and the correct value is returned. Assert.NotNull(devices); @@ -214,7 +214,7 @@ public async void ListForwardAsyncTest() OkResponse, responseMessages, requests, - async () => (await TestClient.ListForwardAsync(Device)).ToArray()); + async () => await TestClient.ListForwardAsync(Device).ToArray()); Assert.NotNull(forwards); Assert.Equal(3, forwards.Length); @@ -241,7 +241,7 @@ public async void ListReverseForwardAsyncTest() OkResponses(2), responseMessages, requests, - async () => (await TestClient.ListReverseForwardAsync(Device)).ToArray()); + async () => await TestClient.ListReverseForwardAsync(Device).ToArray()); Assert.NotNull(forwards); Assert.Equal(3, forwards.Length); @@ -322,7 +322,7 @@ public async void ExecuteRemoteCommandAsyncUnresponsiveTest() NoResponseMessages, requests, null, - () => TestClient.ExecuteRemoteCommandAsync("echo Hello, World", Device, receiver, CancellationToken.None))); + () => TestClient.ExecuteRemoteCommandAsync("echo Hello, World", Device, receiver))); } /// @@ -392,7 +392,7 @@ public async void GetFrameBufferAsyncTest() } /// - /// Tests the method. + /// Tests the method. /// [Fact] public async void RunLogServiceAsyncTest() @@ -415,7 +415,7 @@ await RunTestAsync( NoResponseMessages, requests, [shellStream], - () => TestClient.RunLogServiceAsync(Device, sink, CancellationToken.None, LogId.System)); + () => TestClient.RunLogServiceAsync(Device, sink, LogId.System)); Assert.Equal(3, logs.Count); } @@ -802,7 +802,7 @@ public async void GetFeatureSetAsyncTest() OkResponse, responses, requests, - async () => (await TestClient.GetFeatureSetAsync(Device)).ToArray()); + async () => await TestClient.GetFeatureSetAsync(Device).ToArray()); Assert.Equal(12, features.Length); Assert.Equal("sendrecv_v2_brotli", features.FirstOrDefault()); @@ -1249,7 +1249,7 @@ public async void FindElementsAsyncTest() NoResponseMessages, requests, [shellStream], - async () => (await TestClient.FindElementsAsync(Device)).ToArray()); + async () => await TestClient.FindElementsAsync(Device).ToArray()); int childCount = elements.Length; Array.ForEach(elements, x => childCount += x.GetChildCount()); diff --git a/AdvancedSharpAdbClient.Tests/AdbCommandLineClientTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbCommandLineClientTests.Async.cs index ff6a965c..29a55dec 100644 --- a/AdvancedSharpAdbClient.Tests/AdbCommandLineClientTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbCommandLineClientTests.Async.cs @@ -6,6 +6,9 @@ namespace AdvancedSharpAdbClient.Tests { public partial class AdbCommandLineClientTests { + /// + /// Tests the method. + /// [Fact] public async void GetVersionAsyncTest() { @@ -16,6 +19,9 @@ public async void GetVersionAsyncTest() Assert.Equal(new Version(1, 0, 32), await commandLine.GetVersionAsync()); } + /// + /// Tests the method. + /// [Fact] public async void GetVersionAsyncNullTest() { @@ -26,6 +32,9 @@ public async void GetVersionAsyncNullTest() _ = await Assert.ThrowsAsync(() => commandLine.GetVersionAsync()); } + /// + /// Tests the method. + /// [Fact] public async void GetOutdatedVersionAsyncTest() { @@ -36,6 +45,9 @@ public async void GetOutdatedVersionAsyncTest() _ = await Assert.ThrowsAsync(() => commandLine.GetVersionAsync()); } + /// + /// Tests the method. + /// [Fact] public async void StartServerAsyncTest() { diff --git a/AdvancedSharpAdbClient.Tests/AdbCommandLineClientTests.cs b/AdvancedSharpAdbClient.Tests/AdbCommandLineClientTests.cs index 103592f8..532f06ea 100644 --- a/AdvancedSharpAdbClient.Tests/AdbCommandLineClientTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbCommandLineClientTests.cs @@ -9,6 +9,9 @@ namespace AdvancedSharpAdbClient.Tests /// public partial class AdbCommandLineClientTests { + /// + /// Tests the method. + /// [Fact] public void GetVersionTest() { @@ -19,6 +22,9 @@ public void GetVersionTest() Assert.Equal(new Version(1, 0, 32), commandLine.GetVersion()); } + /// + /// Tests the method. + /// [Fact] public void GetVersionNullTest() { @@ -29,6 +35,9 @@ public void GetVersionNullTest() _ = Assert.Throws(commandLine.GetVersion); } + /// + /// Tests the method. + /// [Fact] public void GetOutdatedVersionTest() { @@ -39,6 +48,9 @@ public void GetOutdatedVersionTest() _ = Assert.Throws(commandLine.GetVersion); } + /// + /// Tests the method. + /// [Fact] public void StartServerTest() { diff --git a/AdvancedSharpAdbClient.Tests/AdbServerTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbServerTests.Async.cs index a844c0dc..ee4b9c26 100644 --- a/AdvancedSharpAdbClient.Tests/AdbServerTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbServerTests.Async.cs @@ -10,6 +10,9 @@ namespace AdvancedSharpAdbClient.Tests { public partial class AdbServerTests { + /// + /// Tests the method. + /// [Fact] public async void GetStatusAsyncNotRunningTest() { @@ -23,6 +26,9 @@ public async void GetStatusAsyncNotRunningTest() Assert.Null(status.Version); } + /// + /// Tests the method. + /// [Fact] public async void GetStatusAsyncRunningTest() { @@ -40,6 +46,9 @@ public async void GetStatusAsyncRunningTest() Assert.Equal(new Version(1, 0, 32), status.Version); } + /// + /// Tests the method. + /// [Fact] public async void GetStatusAsyncOtherSocketExceptionTest() { @@ -51,6 +60,9 @@ public async void GetStatusAsyncOtherSocketExceptionTest() _ = await Assert.ThrowsAsync(async () => await adbServer.GetStatusAsync()); } + /// + /// Tests the method. + /// [Fact] public async void GetStatusAsyncOtherExceptionTest() { @@ -62,6 +74,9 @@ public async void GetStatusAsyncOtherExceptionTest() _ = await Assert.ThrowsAsync(async () => await adbServer.GetStatusAsync()); } + /// + /// Tests the method. + /// [Fact] public async void StartServerAsyncAlreadyRunningTest() { @@ -77,6 +92,9 @@ public async void StartServerAsyncAlreadyRunningTest() Assert.Equal("host:version", socket.Requests[0]); } + /// + /// Tests the method. + /// [Fact] public async void StartServerAsyncOutdatedRunningNoExecutableTest() { @@ -86,6 +104,9 @@ public async void StartServerAsyncOutdatedRunningNoExecutableTest() _ = await Assert.ThrowsAsync(async () => await adbServer.StartServerAsync(null, false)); } + /// + /// Tests the method. + /// [Fact] public async void StartServerAsyncNotRunningNoExecutableTest() { @@ -97,6 +118,9 @@ public async void StartServerAsyncNotRunningNoExecutableTest() _ = await Assert.ThrowsAsync(async () => await adbServer.StartServerAsync(null, false)); } + /// + /// Tests the method. + /// [Fact] public async void StartServerAsyncOutdatedRunningTest() { @@ -115,6 +139,9 @@ public async void StartServerAsyncOutdatedRunningTest() Assert.Equal("host:kill", socket.Requests[1]); } + /// + /// Tests the method. + /// [Fact] public async void StartServerAsyncNotRunningTest() { @@ -132,6 +159,9 @@ public async void StartServerAsyncNotRunningTest() Assert.True(commandLineClient.ServerStarted); } + /// + /// Tests the method. + /// [Fact] public async void StartServerAsyncIntermediateRestartRequestedRunningTest() { @@ -150,6 +180,9 @@ public async void StartServerAsyncIntermediateRestartRequestedRunningTest() Assert.Equal("host:kill", socket.Requests[1]); } + /// + /// Tests the method. + /// [Fact] public async void StartServerAsyncIntermediateRestartNotRequestedRunningTest() { @@ -167,6 +200,9 @@ public async void StartServerAsyncIntermediateRestartNotRequestedRunningTest() Assert.Equal("host:version", socket.Requests[0]); } + /// + /// Tests the method. + /// [Fact] public async void RestartServerAsyncTest() { diff --git a/AdvancedSharpAdbClient.Tests/AdbServerTests.cs b/AdvancedSharpAdbClient.Tests/AdbServerTests.cs index 54ec6738..29084960 100644 --- a/AdvancedSharpAdbClient.Tests/AdbServerTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbServerTests.cs @@ -33,6 +33,9 @@ public AdbServerTests() adbServer = new AdbServer(adbClient, adbCommandLineClientFactory); } + /// + /// Tests the method. + /// [Fact] public void GetStatusNotRunningTest() { @@ -46,6 +49,9 @@ public void GetStatusNotRunningTest() Assert.Null(status.Version); } + /// + /// Tests the method. + /// [Fact] public void GetStatusRunningTest() { @@ -63,6 +69,9 @@ public void GetStatusRunningTest() Assert.Equal(new Version(1, 0, 32), status.Version); } + /// + /// Tests the method. + /// [Fact] public void GetStatusOtherSocketExceptionTest() { @@ -74,6 +83,9 @@ public void GetStatusOtherSocketExceptionTest() _ = Assert.Throws(() => adbServer.GetStatus()); } + /// + /// Tests the method. + /// [Fact] public void GetStatusOtherExceptionTest() { @@ -85,6 +97,9 @@ public void GetStatusOtherExceptionTest() _ = Assert.Throws(() => adbServer.GetStatus()); } + /// + /// Tests the method. + /// [Fact] public void StartServerAlreadyRunningTest() { @@ -100,6 +115,9 @@ public void StartServerAlreadyRunningTest() Assert.Equal("host:version", socket.Requests[0]); } + /// + /// Tests the method. + /// [Fact] public void StartServerOutdatedRunningNoExecutableTest() { @@ -109,6 +127,9 @@ public void StartServerOutdatedRunningNoExecutableTest() _ = Assert.Throws(() => adbServer.StartServer(null, false)); } + /// + /// Tests the method. + /// [Fact] public void StartServerNotRunningNoExecutableTest() { @@ -120,6 +141,9 @@ public void StartServerNotRunningNoExecutableTest() _ = Assert.Throws(() => adbServer.StartServer(null, false)); } + /// + /// Tests the method. + /// [Fact] public void StartServerOutdatedRunningTest() { @@ -138,6 +162,9 @@ public void StartServerOutdatedRunningTest() Assert.Equal("host:kill", socket.Requests[1]); } + /// + /// Tests the method. + /// [Fact] public void StartServerNotRunningTest() { @@ -155,6 +182,9 @@ public void StartServerNotRunningTest() Assert.True(commandLineClient.ServerStarted); } + /// + /// Tests the method. + /// [Fact] public void StartServerIntermediateRestartRequestedRunningTest() { @@ -173,6 +203,9 @@ public void StartServerIntermediateRestartRequestedRunningTest() Assert.Equal("host:kill", socket.Requests[1]); } + /// + /// Tests the method. + /// [Fact] public void StartServerIntermediateRestartNotRequestedRunningTest() { @@ -190,6 +223,9 @@ public void StartServerIntermediateRestartNotRequestedRunningTest() Assert.Equal("host:version", socket.Requests[0]); } + /// + /// Tests the method. + /// [Fact] public void RestartServerTest() { @@ -207,6 +243,9 @@ public void RestartServerTest() Assert.Equal("host:version", socket.Requests[0]); } + /// + /// Tests the method. + /// [Fact] public void ConstructorAdbClientNullTest() => _ = Assert.Throws(() => new AdbServer(null, adbCommandLineClientFactory)); diff --git a/AdvancedSharpAdbClient.Tests/AdbSocketTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbSocketTests.Async.cs index 0db0287d..7c27e58d 100644 --- a/AdvancedSharpAdbClient.Tests/AdbSocketTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbSocketTests.Async.cs @@ -10,28 +10,43 @@ namespace AdvancedSharpAdbClient.Tests { public partial class AdbSocketTests { + /// + /// Tests the method. + /// [Fact] public async void SendSyncDATARequestAsyncTest() => await RunTestAsync( - (socket) => socket.SendSyncRequestAsync(SyncCommand.DATA, 2, CancellationToken.None), + (socket) => socket.SendSyncRequestAsync(SyncCommand.DATA, 2, default), [(byte)'D', (byte)'A', (byte)'T', (byte)'A', 2, 0, 0, 0]); + /// + /// Tests the method. + /// [Fact] public async void SendSyncSENDRequestAsyncTest() => await RunTestAsync( - (socket) => socket.SendSyncRequestAsync(SyncCommand.SEND, "/test", CancellationToken.None), + (socket) => socket.SendSyncRequestAsync(SyncCommand.SEND, "/test", default), [(byte)'S', (byte)'E', (byte)'N', (byte)'D', 5, 0, 0, 0, (byte)'/', (byte)'t', (byte)'e', (byte)'s', (byte)'t']); + /// + /// Tests the method. + /// [Fact] public async void SendSyncDENTRequestAsyncTest() => await RunTestAsync( - (socket) => socket.SendSyncRequestAsync(SyncCommand.DENT, "/data", 633, CancellationToken.None), + (socket) => socket.SendSyncRequestAsync(SyncCommand.DENT, "/data", 633, default), [(byte)'D', (byte)'E', (byte)'N', (byte)'T', 9, 0, 0, 0, (byte)'/', (byte)'d', (byte)'a', (byte)'t', (byte)'a', (byte)',', (byte)'6', (byte)'3', (byte)'3']); + /// + /// Tests the method. + /// [Fact] public async void SendSyncNullRequestAsyncTest() => - _ = await Assert.ThrowsAsync(() => RunTestAsync((socket) => socket.SendSyncRequestAsync(SyncCommand.DATA, null, CancellationToken.None), [])); + _ = await Assert.ThrowsAsync(() => RunTestAsync((socket) => socket.SendSyncRequestAsync(SyncCommand.DATA, null, default), [])); + /// + /// Tests the method. + /// [Fact] public async void ReadSyncResponseAsync() { @@ -48,6 +63,9 @@ public async void ReadSyncResponseAsync() Assert.Equal(SyncCommand.DENT, await socket.ReadSyncResponseAsync()); } + /// + /// Tests the method. + /// [Fact] public async void ReadStringAsyncTest() { @@ -66,6 +84,9 @@ public async void ReadStringAsyncTest() Assert.Equal("Hello", await socket.ReadStringAsync()); } + /// + /// Tests the method. + /// [Fact] public async void ReadAdbOkayResponseAsyncTest() { @@ -86,6 +107,9 @@ public async void ReadAdbOkayResponseAsyncTest() Assert.False(response.Timeout); } + /// + /// Tests the method. + /// [Fact] public async void ReadAdbFailResponseAsyncTest() { @@ -104,6 +128,9 @@ public async void ReadAdbFailResponseAsyncTest() _ = await Assert.ThrowsAsync(() => socket.ReadAdbResponseAsync()); } + /// + /// Tests the method. + /// [Fact] public async void ReadAsyncTest() { @@ -123,7 +150,7 @@ public async void ReadAsyncTest() // Buffer has a capacity of 101, but we'll only want to read 100 bytes byte[] received = new byte[101]; - await socket.ReadAsync(received, 100, CancellationToken.None); + await socket.ReadAsync(received, 100); for (int i = 0; i < 100; i++) { @@ -133,6 +160,9 @@ public async void ReadAsyncTest() Assert.Equal(0, received[100]); } + /// + /// Tests the method. + /// [Fact] public async void ReadAsyncMemoryTest() { @@ -152,7 +182,7 @@ public async void ReadAsyncMemoryTest() // Buffer has a capacity of 101, but we'll only want to read 100 bytes byte[] received = new byte[101]; - await socket.ReadAsync(received.AsMemory(0, 100), CancellationToken.None); + await socket.ReadAsync(received.AsMemory(0, 100)); for (int i = 0; i < 100; i++) { @@ -162,10 +192,13 @@ public async void ReadAsyncMemoryTest() Assert.Equal(0, received[100]); } + /// + /// Tests the method. + /// [Fact] public async void SendAdbRequestAsyncTest() => await RunTestAsync( - (socket) => socket.SendAdbRequestAsync("Test", CancellationToken.None), + (socket) => socket.SendAdbRequestAsync("Test", default), Encoding.ASCII.GetBytes("0004Test")); private static async Task RunTestAsync(Func test, byte[] expectedDataSent) diff --git a/AdvancedSharpAdbClient.Tests/AdbSocketTests.cs b/AdvancedSharpAdbClient.Tests/AdbSocketTests.cs index db376c07..0f660e04 100644 --- a/AdvancedSharpAdbClient.Tests/AdbSocketTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbSocketTests.cs @@ -11,6 +11,9 @@ namespace AdvancedSharpAdbClient.Tests /// public partial class AdbSocketTests { + /// + /// Tests the method. + /// [Fact] public void CloseTest() { @@ -23,6 +26,9 @@ public void CloseTest() Assert.False(socket.Connected); } + /// + /// Tests the method. + /// [Fact] public void DisposeTest() { @@ -35,6 +41,9 @@ public void DisposeTest() Assert.False(socket.Connected); } + /// + /// Tests the method. + /// [Fact] public void IsOkayTest() { @@ -45,30 +54,45 @@ public void IsOkayTest() Assert.False(AdbSocket.IsOkay(fail)); } + /// + /// Tests the method. + /// [Fact] public void SendSyncDATARequestTest() => RunTest( (socket) => socket.SendSyncRequest(SyncCommand.DATA, 2), [(byte)'D', (byte)'A', (byte)'T', (byte)'A', 2, 0, 0, 0]); + /// + /// Tests the method. + /// [Fact] public void SendSyncSENDRequestTest() => RunTest( (socket) => socket.SendSyncRequest(SyncCommand.SEND, "/test"), [(byte)'S', (byte)'E', (byte)'N', (byte)'D', 5, 0, 0, 0, (byte)'/', (byte)'t', (byte)'e', (byte)'s', (byte)'t']); + /// + /// Tests the method. + /// [Fact] public void SendSyncDENTRequestTest() => RunTest( (socket) => socket.SendSyncRequest(SyncCommand.DENT, "/data", 633), [(byte)'D', (byte)'E', (byte)'N', (byte)'T', 9, 0, 0, 0, (byte)'/', (byte)'d', (byte)'a', (byte)'t', (byte)'a', (byte)',', (byte)'6', (byte)'3', (byte)'3']); + /// + /// Tests the method. + /// [Fact] public void SendSyncNullRequestTest() => _ = Assert.Throws(() => RunTest((socket) => socket.SendSyncRequest(SyncCommand.DATA, null), [])); + /// + /// Tests the method. + /// [Fact] - public void ReadSyncResponse() + public void ReadSyncResponseTest() { using DummyTcpSocket tcpSocket = new(); using AdbSocket socket = new(tcpSocket); @@ -83,8 +107,11 @@ public void ReadSyncResponse() Assert.Equal(SyncCommand.DENT, socket.ReadSyncResponse()); } + /// + /// Tests the method. + /// [Fact] - public void ReadSyncString() + public void ReadSyncStringTest() { using DummyTcpSocket tcpSocket = new(); using AdbSocket socket = new(tcpSocket); @@ -101,6 +128,9 @@ public void ReadSyncString() Assert.Equal("Hello", socket.ReadSyncString()); } + /// + /// Tests the method. + /// [Fact] public void ReadAdbOkayResponseTest() { @@ -121,6 +151,9 @@ public void ReadAdbOkayResponseTest() Assert.False(response.Timeout); } + /// + /// Tests the method. + /// [Fact] public void ReadAdbFailResponseTest() { @@ -139,6 +172,9 @@ public void ReadAdbFailResponseTest() _ = Assert.Throws(socket.ReadAdbResponse); } + /// + /// Tests the method. + /// [Fact] public void ReadTest() { @@ -168,6 +204,9 @@ public void ReadTest() Assert.Equal(0, received[100]); } + /// + /// Tests the method. + /// [Fact] public void ReadSpanTest() { @@ -197,12 +236,18 @@ public void ReadSpanTest() Assert.Equal(0, received[100]); } + /// + /// Tests the method. + /// [Fact] public void SendAdbRequestTest() => RunTest( (socket) => socket.SendAdbRequest("Test"), Encoding.ASCII.GetBytes("0004Test")); + /// + /// Tests the method. + /// [Fact] public void GetShellStreamTest() { diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs index ac6550ba..780cb125 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs @@ -344,7 +344,7 @@ public async void ListProcessesAsyncTest() 3 (ksoftirqd/0) S 2 0 0 0 -1 69238848 0 0 0 0 0 23 0 0 20 0 1 0 7 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 18446744071579284070 0 0 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0"; DeviceData device = new(); - AndroidProcess[] processes = (await adbClient.ListProcessesAsync(device)).ToArray(); + AndroidProcess[] processes = await adbClient.ListProcessesAsync(device).ToArray(); Assert.Equal(3, processes.Length); Assert.Equal("init", processes[0].Name); diff --git a/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.Async.cs b/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.Async.cs index fa429c0d..ae5aa157 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.Async.cs @@ -94,6 +94,9 @@ await RunTestAsync( Assert.Equal("169.254.109.177:5555", sink.ConnectedEvents[0].Device.Serial); } + /// + /// Tests the method. + /// [Fact] public async void StartInitialDeviceListAsyncTest() { diff --git a/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs b/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs index dbf46fa4..f19882cc 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs @@ -1,4 +1,5 @@ -using System; +using AdvancedSharpAdbClient.Logs; +using System; using System.Threading; using Xunit; @@ -17,6 +18,9 @@ public DeviceMonitorTests() : base(integrationTest: false, doDispose: true) { } + /// + /// Tests the method. + /// [Fact] public void ConstructorTest() { @@ -27,6 +31,9 @@ public void ConstructorTest() Assert.False(monitor.IsRunning); } + /// + /// Tests the method. + /// [Fact] public void ConstructorNullTest() => _ = Assert.Throws(() => new DeviceMonitor(null)); @@ -118,6 +125,9 @@ public void DeviceConnectedTest() Assert.Equal("169.254.109.177:5555", sink.ConnectedEvents[0].Device.Serial); } + /// + /// Tests the method. + /// [Fact] public void StartInitialDeviceListTest() { diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs index 8fa93971..4ee8d2fe 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs @@ -93,7 +93,28 @@ public int Read(Span data) return actual.Length; } - public string ReadString() => ReadStringAsync(CancellationToken.None).Result; + public string ReadString() + { + if (WaitForNewData) + { + while (ResponseMessages.Count == 0) + { + Thread.Sleep(1000); + } + } + + string message = ResponseMessages.Dequeue(); + + if (message == ServerDisconnected) + { + SocketException socketException = new(AdbServer.ConnectionReset); + throw new AdbException(socketException.Message, socketException); + } + else + { + return message; + } + } public string ReadSyncString() => ResponseMessages.Dequeue(); diff --git a/AdvancedSharpAdbClient.Tests/Logs/LogTests.cs b/AdvancedSharpAdbClient.Tests/Logs/LogTests.cs index 4802c3c7..6faaa446 100644 --- a/AdvancedSharpAdbClient.Tests/Logs/LogTests.cs +++ b/AdvancedSharpAdbClient.Tests/Logs/LogTests.cs @@ -46,7 +46,7 @@ public async void ReadLogAsyncTest() // This stream contains 3 log entries. Read & validate the first one, // read the next two ones (to be sure all data is read correctly). - LogEntry log = await reader.ReadEntryAsync(CancellationToken.None); + LogEntry log = await reader.ReadEntryAsync(); Assert.IsType(log); @@ -62,8 +62,8 @@ public async void ReadLogAsyncTest() Assert.Equal("ActivityManager", androidLog.Tag); Assert.Equal("Start proc com.google.android.gm for broadcast com.google.android.gm/.widget.GmailWidgetProvider: pid=7026 uid=10066 gids={50066, 9997, 3003, 1028, 1015} abi=x86", androidLog.Message); - Assert.NotNull(await reader.ReadEntryAsync(CancellationToken.None)); - Assert.NotNull(await reader.ReadEntryAsync(CancellationToken.None)); + Assert.NotNull(await reader.ReadEntryAsync()); + Assert.NotNull(await reader.ReadEntryAsync()); } [Fact] @@ -107,7 +107,7 @@ public async void ReadEventLogAsyncTest() // has already taken place. await using FileStream stream = File.OpenRead(@"Assets/logcatevents.bin"); LogReader reader = new(stream); - LogEntry entry = await reader.ReadEntryAsync(CancellationToken.None); + LogEntry entry = await reader.ReadEntryAsync(); Assert.IsType(entry); Assert.Equal(707, entry.ProcessId); @@ -130,8 +130,8 @@ public async void ReadEventLogAsyncTest() Assert.Equal(19512, list[1]); Assert.Equal("com.amazon.kindle", list[2]); - entry = await reader.ReadEntryAsync(CancellationToken.None); - entry = await reader.ReadEntryAsync(CancellationToken.None); + entry = await reader.ReadEntryAsync(); + entry = await reader.ReadEntryAsync(); } } } diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs index 57617d65..c358501d 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs @@ -8,6 +8,9 @@ namespace AdvancedSharpAdbClient.Tests { public partial class SyncServiceTests { + /// + /// Tests the method. + /// [Fact] public async void StatAsyncTest() { @@ -31,10 +34,13 @@ public async void StatAsyncTest() Assert.Equal(DateTimeExtensions.Epoch.ToLocalTime(), value.Time); } + /// + /// Tests the method. + /// [Fact] public async void GetListingAsyncTest() { - FileStatistics[] value = await RunTestAsync( + List value = await RunTestAsync( OkResponses(2), [".", "..", "sdcard0", "emulated"], ["host:transport:169.254.109.177:5555", "sync:"], @@ -50,10 +56,10 @@ public async void GetListingAsyncTest() async () => { using SyncService service = new(Socket, Device); - return (await service.GetDirectoryListingAsync("/storage")).ToArray(); + return await service.GetDirectoryListingAsync("/storage"); }); - Assert.Equal(4, value.Length); + Assert.Equal(4, value.Count); DateTime time = new DateTime(2015, 11, 3, 9, 47, 4, DateTimeKind.Utc).ToLocalTime(); @@ -82,6 +88,9 @@ public async void GetListingAsyncTest() Assert.Equal(time, emulated.Time); } + /// + /// Tests the method. + /// [Fact] public async void GetAsyncListingTest() { @@ -138,6 +147,9 @@ public async void GetAsyncListingTest() Assert.Equal(time, emulated.Time); } + /// + /// Tests the method. + /// [Fact] public async void PullAsyncTest() { @@ -163,13 +175,16 @@ await RunTestAsync( async () => { using SyncService service = new(Socket, Device); - await service.PullAsync("/fstab.donatello", stream, null, CancellationToken.None); + await service.PullAsync("/fstab.donatello", stream, null); }); // Make sure the data that has been sent to the stream is the expected data Assert.Equal(content, stream.ToArray()); } + /// + /// Tests the method. + /// [Fact] public async void PushAsyncTest() { @@ -196,7 +211,7 @@ await RunTestAsync( async () => { using SyncService service = new(Socket, Device); - await service.PushAsync(stream, "/sdcard/test", 0644, new DateTime(2015, 11, 2, 23, 0, 0, DateTimeKind.Utc), null, CancellationToken.None); + await service.PushAsync(stream, "/sdcard/test", 0644, new DateTime(2015, 11, 2, 23, 0, 0, DateTimeKind.Utc), null); }); } } diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs index 78b4f6c8..8c6b98f5 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs @@ -18,6 +18,9 @@ public SyncServiceTests() : base(integrationTest: false, doDispose: false) { } + /// + /// Tests the method. + /// [Fact] public void StatTest() { @@ -41,6 +44,9 @@ public void StatTest() Assert.Equal(DateTimeExtensions.Epoch.ToLocalTime(), value.Time); } + /// + /// Tests the method. + /// [Fact] public void GetListingTest() { @@ -92,6 +98,9 @@ public void GetListingTest() Assert.Equal(time, emulated.Time); } + /// + /// Tests the method. + /// [Fact] public void PullTest() { @@ -124,6 +133,9 @@ public void PullTest() Assert.Equal(content, stream.ToArray()); } + /// + /// Tests the method. + /// [Fact] public void PushTest() { diff --git a/AdvancedSharpAdbClient.Tests/TcpSocketTests.Async.cs b/AdvancedSharpAdbClient.Tests/TcpSocketTests.Async.cs index e8faeb58..2b91f159 100644 --- a/AdvancedSharpAdbClient.Tests/TcpSocketTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/TcpSocketTests.Async.cs @@ -2,6 +2,7 @@ using System.Net; using System.Net.Sockets; using System.Text; +using System.Threading; using Xunit; namespace AdvancedSharpAdbClient.Tests @@ -44,6 +45,9 @@ public async void LifecycleAsyncMemoryTest() _ = Encoding.ASCII.GetString(responseData); } + /// + /// Tests the method. + /// [Fact] public async void ReconnectAsyncTest() { @@ -59,5 +63,15 @@ public async void ReconnectAsyncTest() await socket.ReconnectAsync(); Assert.True(socket.Connected); } + + /// + /// Tests the method. + /// + [Fact] + public async void CreateUnsupportedSocketAsyncTest() + { + using TcpSocket socket = new(); + _ = await Assert.ThrowsAsync(() => socket.ConnectAsync(new CustomEndPoint()).AsTask()); + } } } diff --git a/AdvancedSharpAdbClient.Tests/TcpSocketTests.cs b/AdvancedSharpAdbClient.Tests/TcpSocketTests.cs index 8e540bac..4c6a77de 100644 --- a/AdvancedSharpAdbClient.Tests/TcpSocketTests.cs +++ b/AdvancedSharpAdbClient.Tests/TcpSocketTests.cs @@ -48,6 +48,9 @@ public void LifecycleSpanTest() _ = Encoding.ASCII.GetString(responseData); } + /// + /// Tests the method. + /// [Fact] public void ReconnectTest() { @@ -64,6 +67,9 @@ public void ReconnectTest() Assert.True(socket.Connected); } + /// + /// Tests the property. + /// [Fact] public void BufferSizeTest() { @@ -75,6 +81,9 @@ public void BufferSizeTest() Assert.Equal(RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? 2304 : 1024, socket.ReceiveBufferSize); } + /// + /// Tests the method. + /// [Fact] public void CreateUnsupportedSocketTest() { diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index ddb5c172..151f22ab 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -727,7 +727,7 @@ public async Task ClickAsync(DeviceData device, Cords cords, CancellationToken c await socket.SendAdbRequestAsync($"shell:input tap {cords.X} {cords.Y}", cancellationToken).ConfigureAwait(false); AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); using StreamReader reader = new(socket.GetShellStream(), Encoding); - string result = (await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false)).TrimStart(); + string result = await reader.ReadToEndAsync(cancellationToken).ContinueWith(x => x.Result.TrimStart()).ConfigureAwait(false); if (result.StartsWith("java.lang.")) { throw JavaException.Parse(result); @@ -748,7 +748,7 @@ public async Task ClickAsync(DeviceData device, int x, int y, CancellationToken await socket.SendAdbRequestAsync($"shell:input tap {x} {y}", cancellationToken).ConfigureAwait(false); AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); using StreamReader reader = new(socket.GetShellStream(), Encoding); - string result = (await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false)).TrimStart(); + string result = await reader.ReadToEndAsync(cancellationToken).ContinueWith(x => x.Result.TrimStart()).ConfigureAwait(false); if (result.StartsWith("java.lang.")) { throw JavaException.Parse(result); @@ -769,7 +769,7 @@ public async Task SwipeAsync(DeviceData device, Element first, Element second, l await socket.SendAdbRequestAsync($"shell:input swipe {first.Cords.X} {first.Cords.Y} {second.Cords.X} {second.Cords.Y} {speed}", cancellationToken).ConfigureAwait(false); AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); using StreamReader reader = new(socket.GetShellStream(), Encoding); - string result = (await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false)).TrimStart(); + string result = await reader.ReadToEndAsync(cancellationToken).ContinueWith(x => x.Result.TrimStart()).ConfigureAwait(false); if (result.StartsWith("java.lang.")) { throw JavaException.Parse(result); @@ -790,7 +790,7 @@ public async Task SwipeAsync(DeviceData device, int x1, int y1, int x2, int y2, await socket.SendAdbRequestAsync($"shell:input swipe {x1} {y1} {x2} {y2} {speed}", cancellationToken).ConfigureAwait(false); AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); using StreamReader reader = new(socket.GetShellStream(), Encoding); - string result = (await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false)).TrimStart(); + string result = await reader.ReadToEndAsync(cancellationToken).ContinueWith(x => x.Result.TrimStart()).ConfigureAwait(false); if (result.StartsWith("java.lang.")) { throw JavaException.Parse(result); @@ -811,7 +811,7 @@ public async Task IsAppRunningAsync(DeviceData device, string packageName, await socket.SendAdbRequestAsync($"shell:pidof {packageName}", cancellationToken).ConfigureAwait(false); AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); using StreamReader reader = new(socket.GetShellStream(), Encoding); - string result = (await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false)).TrimStart().Split(' ', StringSplitOptions.RemoveEmptyEntries).FirstOrDefault(); + string result = await reader.ReadToEndAsync(cancellationToken).ContinueWith(x => x.Result.TrimStart().Split(' ', StringSplitOptions.RemoveEmptyEntries).FirstOrDefault()).ConfigureAwait(false); bool intParsed = int.TryParse(result, out int pid); return intParsed && pid > 0; } @@ -984,7 +984,7 @@ public async Task SendKeyEventAsync(DeviceData device, string key, CancellationT await socket.SendAdbRequestAsync($"shell:input keyevent {key}", cancellationToken).ConfigureAwait(false); AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); using StreamReader reader = new(socket.GetShellStream(), Encoding); - string result = (await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false)).TrimStart(); + string result = await reader.ReadToEndAsync(cancellationToken).ContinueWith(x => x.Result.TrimStart()).ConfigureAwait(false); if (result.StartsWith("java.lang.")) { throw JavaException.Parse(result); @@ -1005,7 +1005,7 @@ public async Task SendTextAsync(DeviceData device, string text, CancellationToke await socket.SendAdbRequestAsync($"shell:input text {text}", cancellationToken).ConfigureAwait(false); AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); using StreamReader reader = new(socket.GetShellStream(), Encoding); - string result = (await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false)).TrimStart(); + string result = await reader.ReadToEndAsync(cancellationToken).ContinueWith(x => x.Result.TrimStart()).ConfigureAwait(false); if (result.StartsWith("java.lang.")) { throw JavaException.Parse(result); @@ -1020,7 +1020,7 @@ public async Task SendTextAsync(DeviceData device, string text, CancellationToke public async Task ClearInputAsync(DeviceData device, int charCount, CancellationToken cancellationToken = default) { await SendKeyEventAsync(device, "KEYCODE_MOVE_END", cancellationToken).ConfigureAwait(false); - await SendKeyEventAsync(device, StringExtensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL", charCount))).ConfigureAwait(false); + await SendKeyEventAsync(device, StringExtensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL", charCount)), cancellationToken).ConfigureAwait(false); } /// diff --git a/AdvancedSharpAdbClient/AdbSocket.Async.cs b/AdvancedSharpAdbClient/AdbSocket.Async.cs index 59746414..4ece5902 100644 --- a/AdvancedSharpAdbClient/AdbSocket.Async.cs +++ b/AdvancedSharpAdbClient/AdbSocket.Async.cs @@ -277,7 +277,7 @@ public virtual async ValueTask SendAsync(ReadOnlyMemory data, Cancellation } /// - public virtual async ValueTask ReadAsync(Memory data, CancellationToken cancellationToken) + public virtual async ValueTask ReadAsync(Memory data, CancellationToken cancellationToken = default) { ExceptionExtensions.ThrowIfNull(data); diff --git a/AdvancedSharpAdbClient/Extensions/Extensions.cs b/AdvancedSharpAdbClient/Extensions/Extensions.cs index 154a3df0..ca9aa2a1 100644 --- a/AdvancedSharpAdbClient/Extensions/Extensions.cs +++ b/AdvancedSharpAdbClient/Extensions/Extensions.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading; @@ -113,6 +114,15 @@ public static void Dispose(this WaitHandle waitHandle) #endif #if HAS_TASK + /// + /// Creates an array from a . + /// + /// The type of the elements of . + /// An to create an array from. + /// An array that contains the elements from the input sequence. + public static Task ToArray(this Task> source) => + source.ContinueWith(x => x.Result.ToArray()); + /// /// Creates a task that completes after a specified number of milliseconds. /// diff --git a/AdvancedSharpAdbClient/SyncService.Async.cs b/AdvancedSharpAdbClient/SyncService.Async.cs index f826f6b8..d0db5934 100644 --- a/AdvancedSharpAdbClient/SyncService.Async.cs +++ b/AdvancedSharpAdbClient/SyncService.Async.cs @@ -227,7 +227,7 @@ public virtual async Task StatAsync(string remotePath, Cancellat // create the stat request message. await Socket.SendSyncRequestAsync(SyncCommand.STAT, remotePath, cancellationToken).ConfigureAwait(false); - if (await Socket.ReadSyncResponseAsync(cancellationToken).ConfigureAwait(false) != SyncCommand.STAT) + if (await Socket.ReadSyncResponseAsync(cancellationToken).ContinueWith(x => x.Result != SyncCommand.STAT).ConfigureAwait(false)) { throw new AdbException($"The server returned an invalid sync response."); } From fd7f1bcc6990cbf5af3cd6a4672aeff4ac879a0a Mon Sep 17 00:00:00 2001 From: wherewhere Date: Mon, 9 Oct 2023 01:31:00 +0800 Subject: [PATCH 31/66] Move some method to AdbClientExtensions Change AdbResponse and VersionInfo to struct Change Children of Element to IEnumerable --- .../AdbClientTests.Async.cs | 30 +-- .../AdbClientTests.cs | 14 +- .../AdbSocketTests.cs | 2 +- .../DeviceExtensionsTests.Async.cs | 2 +- .../DeviceCommands/Models/VersionInfoTests.cs | 4 +- .../Dummys/DummyAdbClient.cs | 235 +++++++++--------- .../Dummys/DummySyncService.cs | 20 +- .../Extensions/ExtensionsTests.cs | 12 + .../Models/AdbResponseTests.cs | 12 +- AdvancedSharpAdbClient/AdbClient.Async.cs | 69 +++-- AdvancedSharpAdbClient/AdbClient.cs | 50 ++-- .../DeviceCommands/DeviceExtensions.Async.cs | 19 +- .../DeviceCommands/DeviceExtensions.cs | 17 +- .../DeviceCommands/Models/VersionInfo.cs | 59 ++++- .../DeviceCommands/PackageManager.Async.cs | 18 +- .../DeviceCommands/PackageManager.cs | 2 +- .../Receivers/InfoOutputReceiver.cs | 11 +- .../Receivers/VersionInfoReceiver.cs | 6 +- .../Extensions/AdbClientExtensions.Async.cs | 227 +++++++++++++++-- .../Extensions/AdbClientExtensions.cs | 96 +++++++ .../Extensions/Extensions.cs | 24 +- ...SteamExtensions.cs => StreamExtensions.cs} | 4 +- .../Interfaces/IAdbClient.Async.cs | 143 +++-------- .../Interfaces/IAdbClient.cs | 82 ++---- AdvancedSharpAdbClient/Models/AdbResponse.cs | 49 +++- AdvancedSharpAdbClient/Models/Element.cs | 51 ++-- AdvancedSharpAdbClient/Models/ForwardSpec.cs | 23 +- .../Models/FramebufferHeader.cs | 4 + 28 files changed, 818 insertions(+), 467 deletions(-) rename AdvancedSharpAdbClient/Extensions/{SteamExtensions.cs => StreamExtensions.cs} (98%) diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs index 84135590..b48dbcfc 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs @@ -66,7 +66,7 @@ public async void GetDevicesAsyncTest() OkResponse, responseMessages, requests, - async () => await TestClient.GetDevicesAsync().ToArray()); + async () => await TestClient.GetDevicesAsync().ToArrayAsync()); // Make sure and the correct value is returned. Assert.NotNull(devices); @@ -214,7 +214,7 @@ public async void ListForwardAsyncTest() OkResponse, responseMessages, requests, - async () => await TestClient.ListForwardAsync(Device).ToArray()); + async () => await TestClient.ListForwardAsync(Device).ToArrayAsync()); Assert.NotNull(forwards); Assert.Equal(3, forwards.Length); @@ -241,7 +241,7 @@ public async void ListReverseForwardAsyncTest() OkResponses(2), responseMessages, requests, - async () => await TestClient.ListReverseForwardAsync(Device).ToArray()); + async () => await TestClient.ListReverseForwardAsync(Device).ToArrayAsync()); Assert.NotNull(forwards); Assert.Equal(3, forwards.Length); @@ -392,7 +392,7 @@ public async void GetFrameBufferAsyncTest() } /// - /// Tests the method. + /// Tests the method. /// [Fact] public async void RunLogServiceAsyncTest() @@ -642,7 +642,7 @@ public async void UnrootAsyncTest() } /// - /// Tests the method. + /// Tests the method. /// [Fact] public async void InstallAsyncTest() @@ -692,7 +692,7 @@ await RunTestAsync( } /// - /// Tests the method. + /// Tests the method. /// [Fact] public async void InstallCreateAsyncTest() @@ -802,7 +802,7 @@ public async void GetFeatureSetAsyncTest() OkResponse, responses, requests, - async () => await TestClient.GetFeatureSetAsync(Device).ToArray()); + async () => await TestClient.GetFeatureSetAsync(Device).ToArrayAsync()); Assert.Equal(12, features.Length); Assert.Equal("sendrecv_v2_brotli", features.FirstOrDefault()); @@ -1249,7 +1249,7 @@ public async void FindElementsAsyncTest() NoResponseMessages, requests, [shellStream], - async () => await TestClient.FindElementsAsync(Device).ToArray()); + async () => await TestClient.FindElementsAsync(Device).ToArrayAsync()); int childCount = elements.Length; Array.ForEach(elements, x => childCount += x.GetChildCount()); @@ -1343,7 +1343,7 @@ await RunTestAsync( } /// - /// Tests the method. + /// Tests the method. /// [Fact] public async void ClearInputAsyncTest() @@ -1406,10 +1406,10 @@ await RunTestAsync( } /// - /// Tests the method. + /// Tests the method. /// [Fact] - public async void BackBtnAsyncTest() + public async void ClickBackButtonAsyncTest() { string[] requests = [ @@ -1424,14 +1424,14 @@ await RunTestAsync( NoResponseMessages, requests, [shellStream], - () => TestClient.BackBtnAsync(Device)); + () => TestClient.ClickBackButtonAsync(Device)); } /// - /// Tests the method. + /// Tests the method. /// [Fact] - public async void HomeBtnAsyncTest() + public async void ClickHomeButtonAsyncTest() { string[] requests = [ @@ -1446,7 +1446,7 @@ await RunTestAsync( NoResponseMessages, requests, [shellStream], - () => TestClient.HomeBtnAsync(Device)); + () => TestClient.ClickHomeButtonAsync(Device)); } private Task RunConnectAsyncTest(Func test, string connectString) diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs index e1dc796a..a4611906 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs @@ -1414,7 +1414,7 @@ public void SendTextTest() } /// - /// Tests the method. + /// Tests the method. /// [Fact] public void ClearInputTest() @@ -1477,10 +1477,10 @@ public void StopAppTest() } /// - /// Tests the method. + /// Tests the method. /// [Fact] - public void BackBtnTest() + public void ClickBackButtonTest() { string[] requests = [ @@ -1495,14 +1495,14 @@ public void BackBtnTest() NoResponseMessages, requests, [shellStream], - () => TestClient.BackBtn(Device)); + () => TestClient.ClickBackButton(Device)); } /// - /// Tests the method. + /// Tests the method. /// [Fact] - public void HomeBtnTest() + public void ClickHomeButtonTest() { string[] requests = [ @@ -1517,7 +1517,7 @@ public void HomeBtnTest() NoResponseMessages, requests, [shellStream], - () => TestClient.HomeBtn(Device)); + () => TestClient.ClickHomeButton(Device)); } private void RunConnectTest(Action test, string connectString) diff --git a/AdvancedSharpAdbClient.Tests/AdbSocketTests.cs b/AdvancedSharpAdbClient.Tests/AdbSocketTests.cs index 0f660e04..54c47c13 100644 --- a/AdvancedSharpAdbClient.Tests/AdbSocketTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbSocketTests.cs @@ -169,7 +169,7 @@ public void ReadAdbFailResponseTest() tcpSocket.InputStream.Position = 0; - _ = Assert.Throws(socket.ReadAdbResponse); + _ = Assert.Throws(() => _ = socket.ReadAdbResponse()); } /// diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs index 780cb125..d1fd6aea 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs @@ -344,7 +344,7 @@ public async void ListProcessesAsyncTest() 3 (ksoftirqd/0) S 2 0 0 0 -1 69238848 0 0 0 0 0 23 0 0 20 0 1 0 7 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 18446744071579284070 0 0 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0"; DeviceData device = new(); - AndroidProcess[] processes = await adbClient.ListProcessesAsync(device).ToArray(); + AndroidProcess[] processes = await adbClient.ListProcessesAsync(device).ToArrayAsync(); Assert.Equal(3, processes.Length); Assert.Equal("init", processes[0].Name); diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/Models/VersionInfoTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/Models/VersionInfoTests.cs index 9582f81c..cbf51678 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/Models/VersionInfoTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/Models/VersionInfoTests.cs @@ -26,8 +26,8 @@ public void DeconstructTest(int versionCode, string versionName) [Fact] public void ToStringTest() { - VersionInfo v = new(1234, "1.2.3.4"); - Assert.Equal("1.2.3.4 (1234)", v.ToString()); + VersionInfo version = new(1234, "1.2.3.4"); + Assert.Equal("1.2.3.4 (1234)", version.ToString()); } } } diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs index c2686c26..90fbf439 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs @@ -18,12 +18,34 @@ internal class DummyAdbClient : IAdbClient public EndPoint EndPoint { get; private set; } + public void ExecuteRemoteCommand(string command, DeviceData device, Encoding encoding) => + ExecuteServerCommand("shell", command, encoding); + public void ExecuteRemoteCommand(string command, DeviceData device, IShellOutputReceiver receiver, Encoding encoding) => ExecuteServerCommand("shell", command, receiver, encoding); + public Task ExecuteRemoteCommandAsync(string command, DeviceData device, Encoding encoding, CancellationToken cancellationToken = default) => + ExecuteServerCommandAsync("shell", command, encoding, cancellationToken); + public Task ExecuteRemoteCommandAsync(string command, DeviceData device, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken = default) => ExecuteServerCommandAsync("shell", command, receiver, encoding, cancellationToken); + public void ExecuteServerCommand(string target, string command, Encoding encoding) + { + StringBuilder requestBuilder = new(); + if (!StringExtensions.IsNullOrWhiteSpace(target)) + { + _ = requestBuilder.AppendFormat("{0}:", target); + } + _ = requestBuilder.Append(command); + + string request = requestBuilder.ToString(); + ReceivedCommands.Add(request); + } + + public void ExecuteServerCommand(string target, string command, IAdbSocket socket, Encoding encoding) => + ExecuteServerCommand(target, command, encoding); + public void ExecuteServerCommand(string target, string command, IShellOutputReceiver receiver, Encoding encoding) { StringBuilder requestBuilder = new(); @@ -59,6 +81,23 @@ public void ExecuteServerCommand(string target, string command, IShellOutputRece public void ExecuteServerCommand(string target, string command, IAdbSocket socket, IShellOutputReceiver receiver, Encoding encoding) => ExecuteServerCommand(target, command, receiver, encoding); + public Task ExecuteServerCommandAsync(string target, string command, Encoding encoding, CancellationToken cancellationToken = default) + { + StringBuilder requestBuilder = new(); + if (!StringExtensions.IsNullOrWhiteSpace(target)) + { + _ = requestBuilder.AppendFormat("{0}:", target); + } + _ = requestBuilder.Append(command); + + string request = requestBuilder.ToString(); + ReceivedCommands.Add(request); + return Task.CompletedTask; + } + + public Task ExecuteServerCommandAsync(string target, string command, IAdbSocket socket, Encoding encoding, CancellationToken cancellationToken) => + ExecuteServerCommandAsync(target, command, encoding, cancellationToken); + public async Task ExecuteServerCommandAsync(string target, string command, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken = default) { StringBuilder requestBuilder = new(); @@ -91,204 +130,178 @@ public async Task ExecuteServerCommandAsync(string target, string command, IShel } } - public Task ExecuteServerCommandAsync(string target, string command, IAdbSocket socket, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken = default) => + public Task ExecuteServerCommandAsync(string target, string command, IAdbSocket socket, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken)=> ExecuteServerCommandAsync(target, command, receiver, encoding, cancellationToken); #region Not Implemented - public void BackBtn(DeviceData device) => throw new NotImplementedException(); - - public Task BackBtnAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); - - public void ClearInput(DeviceData device, int charCount) => throw new NotImplementedException(); - - public Task ClearInputAsync(DeviceData device, int charCount, CancellationToken cancellationToken) => throw new NotImplementedException(); - - public void Click(DeviceData device, Cords cords) => throw new NotImplementedException(); - - public void Click(DeviceData device, int x, int y) => throw new NotImplementedException(); - - public Task ClickAsync(DeviceData device, Cords cords, CancellationToken cancellationToken) => throw new NotImplementedException(); - - public Task ClickAsync(DeviceData device, int x, int y, CancellationToken cancellationToken) => throw new NotImplementedException(); - - public string Connect(DnsEndPoint endpoint) => throw new NotImplementedException(); - - public Task ConnectAsync(DnsEndPoint endpoint, CancellationToken cancellationToken) => throw new NotImplementedException(); - - public int CreateForward(DeviceData device, string local, string remote, bool allowRebind) => throw new NotImplementedException(); - - public int CreateForward(DeviceData device, ForwardSpec local, ForwardSpec remote, bool allowRebind) => throw new NotImplementedException(); - - public Task CreateForwardAsync(DeviceData device, string local, string remote, bool allowRebind, CancellationToken cancellationToken) => throw new NotImplementedException(); - - public Task CreateForwardAsync(DeviceData device, ForwardSpec local, ForwardSpec remote, bool allowRebind, CancellationToken cancellationToken) => throw new NotImplementedException(); + void IAdbClient.Click(DeviceData device, Cords cords) => throw new NotImplementedException(); - public Framebuffer CreateRefreshableFramebuffer(DeviceData device) => throw new NotImplementedException(); + void IAdbClient.Click(DeviceData device, int x, int y) => throw new NotImplementedException(); - public int CreateReverseForward(DeviceData device, string remote, string local, bool allowRebind) => throw new NotImplementedException(); + Task IAdbClient.ClickAsync(DeviceData device, Cords cords, CancellationToken cancellationToken) => throw new NotImplementedException(); - public Task CreateReverseForwardAsync(DeviceData device, string remote, string local, bool allowRebind, CancellationToken cancellationToken) => throw new NotImplementedException(); + Task IAdbClient.ClickAsync(DeviceData device, int x, int y, CancellationToken cancellationToken) => throw new NotImplementedException(); - public string Disconnect(DnsEndPoint endpoint) => throw new NotImplementedException(); + string IAdbClient.Connect(DnsEndPoint endpoint) => throw new NotImplementedException(); - public Task DisconnectAsync(DnsEndPoint endpoint, CancellationToken cancellationToken) => throw new NotImplementedException(); + Task IAdbClient.ConnectAsync(DnsEndPoint endpoint, CancellationToken cancellationToken) => throw new NotImplementedException(); - public XmlDocument DumpScreen(DeviceData device) => throw new NotImplementedException(); + int IAdbClient.CreateForward(DeviceData device, string local, string remote, bool allowRebind) => throw new NotImplementedException(); - public Task DumpScreenAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); + Task IAdbClient.CreateForwardAsync(DeviceData device, string local, string remote, bool allowRebind, CancellationToken cancellationToken) => throw new NotImplementedException(); - public string DumpScreenString(DeviceData device) => throw new NotImplementedException(); + Framebuffer IAdbClient.CreateRefreshableFramebuffer(DeviceData device) => throw new NotImplementedException(); - public Task DumpScreenStringAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); + int IAdbClient.CreateReverseForward(DeviceData device, string remote, string local, bool allowRebind) => throw new NotImplementedException(); - public IAsyncEnumerable FindAsyncElements(DeviceData device, string xpath, CancellationToken cancellationToken) => throw new NotImplementedException(); + Task IAdbClient.CreateReverseForwardAsync(DeviceData device, string remote, string local, bool allowRebind, CancellationToken cancellationToken) => throw new NotImplementedException(); - public Element FindElement(DeviceData device, string xpath, TimeSpan timeout = default) => throw new NotImplementedException(); + string IAdbClient.Disconnect(DnsEndPoint endpoint) => throw new NotImplementedException(); - public Task FindElementAsync(DeviceData device, string xpath, CancellationToken cancellationToken) => throw new NotImplementedException(); + Task IAdbClient.DisconnectAsync(DnsEndPoint endpoint, CancellationToken cancellationToken) => throw new NotImplementedException(); - public IEnumerable FindElements(DeviceData device, string xpath, TimeSpan timeout = default) => throw new NotImplementedException(); + XmlDocument IAdbClient.DumpScreen(DeviceData device) => throw new NotImplementedException(); - public Task> FindElementsAsync(DeviceData device, string xpath, CancellationToken cancellationToken) => throw new NotImplementedException(); + Task IAdbClient.DumpScreenAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); - public int GetAdbVersion() => throw new NotImplementedException(); + string IAdbClient.DumpScreenString(DeviceData device) => throw new NotImplementedException(); - public Task GetAdbVersionAsync(CancellationToken cancellationToken) => throw new NotImplementedException(); + Task IAdbClient.DumpScreenStringAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); - public AppStatus GetAppStatus(DeviceData device, string packageName) => throw new NotImplementedException(); + IAsyncEnumerable IAdbClient.FindAsyncElements(DeviceData device, string xpath, CancellationToken cancellationToken) => throw new NotImplementedException(); - public Task GetAppStatusAsync(DeviceData device, string packageName, CancellationToken cancellationToken) => throw new NotImplementedException(); + Element IAdbClient.FindElement(DeviceData device, string xpath, TimeSpan timeout) => throw new NotImplementedException(); - public IEnumerable GetDevices() => throw new NotImplementedException(); + Task IAdbClient.FindElementAsync(DeviceData device, string xpath, CancellationToken cancellationToken) => throw new NotImplementedException(); - public Task> GetDevicesAsync(CancellationToken cancellationToken) => throw new NotImplementedException(); + IEnumerable IAdbClient.FindElements(DeviceData device, string xpath, TimeSpan timeout) => throw new NotImplementedException(); - public IEnumerable GetFeatureSet(DeviceData device) => throw new NotImplementedException(); + Task> IAdbClient.FindElementsAsync(DeviceData device, string xpath, CancellationToken cancellationToken) => throw new NotImplementedException(); - public Task> GetFeatureSetAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); + int IAdbClient.GetAdbVersion() => throw new NotImplementedException(); - public Framebuffer GetFrameBuffer(DeviceData device) => throw new NotImplementedException(); + Task IAdbClient.GetAdbVersionAsync(CancellationToken cancellationToken) => throw new NotImplementedException(); - public Task GetFrameBufferAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); + AppStatus IAdbClient.GetAppStatus(DeviceData device, string packageName) => throw new NotImplementedException(); - public void HomeBtn(DeviceData device) => throw new NotImplementedException(); + Task IAdbClient.GetAppStatusAsync(DeviceData device, string packageName, CancellationToken cancellationToken) => throw new NotImplementedException(); - public Task HomeBtnAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); + IEnumerable IAdbClient.GetDevices() => throw new NotImplementedException(); - public void Install(DeviceData device, Stream apk, params string[] arguments) => throw new NotImplementedException(); + Task> IAdbClient.GetDevicesAsync(CancellationToken cancellationToken) => throw new NotImplementedException(); - public Task InstallAsync(DeviceData device, Stream apk, params string[] arguments) => throw new NotImplementedException(); + IEnumerable IAdbClient.GetFeatureSet(DeviceData device) => throw new NotImplementedException(); - public Task InstallAsync(DeviceData device, Stream apk, CancellationToken cancellationToken, params string[] arguments) => throw new NotImplementedException(); + Task> IAdbClient.GetFeatureSetAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); - public void InstallCommit(DeviceData device, string session) => throw new NotImplementedException(); + Framebuffer IAdbClient.GetFrameBuffer(DeviceData device) => throw new NotImplementedException(); - public Task InstallCommitAsync(DeviceData device, string session, CancellationToken cancellationToken) => throw new NotImplementedException(); + Task IAdbClient.GetFrameBufferAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); - public string InstallCreate(DeviceData device, string packageName = null, params string[] arguments) => throw new NotImplementedException(); + void IAdbClient.Install(DeviceData device, Stream apk, params string[] arguments) => throw new NotImplementedException(); - public Task InstallCreateAsync(DeviceData device, string packageName, params string[] arguments) => throw new NotImplementedException(); + Task IAdbClient.InstallAsync(DeviceData device, Stream apk, CancellationToken cancellationToken, params string[] arguments) => throw new NotImplementedException(); - public Task InstallCreateAsync(DeviceData device, string packageName, CancellationToken cancellationToken, params string[] arguments) => throw new NotImplementedException(); + void IAdbClient.InstallCommit(DeviceData device, string session) => throw new NotImplementedException(); - public void InstallMultiple(DeviceData device, IEnumerable splitAPKs, string packageName, params string[] arguments) => throw new NotImplementedException(); + Task IAdbClient.InstallCommitAsync(DeviceData device, string session, CancellationToken cancellationToken) => throw new NotImplementedException(); - public void InstallMultiple(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, params string[] arguments) => throw new NotImplementedException(); + string IAdbClient.InstallCreate(DeviceData device, string packageName, params string[] arguments) => throw new NotImplementedException(); - public Task InstallMultipleAsync(DeviceData device, IEnumerable splitAPKs, string packageName, params string[] arguments) => throw new NotImplementedException(); + Task IAdbClient.InstallCreateAsync(DeviceData device, string packageName, CancellationToken cancellationToken, params string[] arguments) => throw new NotImplementedException(); - public Task InstallMultipleAsync(DeviceData device, IEnumerable splitAPKs, string packageName, CancellationToken cancellationToken, params string[] arguments) => throw new NotImplementedException(); + void IAdbClient.InstallMultiple(DeviceData device, IEnumerable splitAPKs, string packageName, params string[] arguments) => throw new NotImplementedException(); - public Task InstallMultipleAsync(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, params string[] arguments) => throw new NotImplementedException(); + void IAdbClient.InstallMultiple(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, params string[] arguments) => throw new NotImplementedException(); - public Task InstallMultipleAsync(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, CancellationToken cancellationToken, params string[] arguments) => throw new NotImplementedException(); + Task IAdbClient.InstallMultipleAsync(DeviceData device, IEnumerable splitAPKs, string packageName, CancellationToken cancellationToken, params string[] arguments) => throw new NotImplementedException(); - public void InstallWrite(DeviceData device, Stream apk, string apkName, string session) => throw new NotImplementedException(); + Task IAdbClient.InstallMultipleAsync(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, CancellationToken cancellationToken, params string[] arguments) => throw new NotImplementedException(); - public Task InstallWriteAsync(DeviceData device, Stream apk, string apkName, string session, CancellationToken cancellationToken) => throw new NotImplementedException(); + void IAdbClient.InstallWrite(DeviceData device, Stream apk, string apkName, string session) => throw new NotImplementedException(); - public bool IsAppRunning(DeviceData device, string packageName) => throw new NotImplementedException(); + Task IAdbClient.InstallWriteAsync(DeviceData device, Stream apk, string apkName, string session, CancellationToken cancellationToken) => throw new NotImplementedException(); - public Task IsAppRunningAsync(DeviceData device, string packageName, CancellationToken cancellationToken) => throw new NotImplementedException(); + bool IAdbClient.IsAppInForeground(DeviceData device, string packageName) => throw new NotImplementedException(); - public bool IsAppInForeground(DeviceData device, string packageName) => throw new NotImplementedException(); + Task IAdbClient.IsAppInForegroundAsync(DeviceData device, string packageName, CancellationToken cancellationToken) => throw new NotImplementedException(); - public Task IsAppInForegroundAsync(DeviceData device, string packageName, CancellationToken cancellationToken) => throw new NotImplementedException(); + bool IAdbClient.IsAppRunning(DeviceData device, string packageName) => throw new NotImplementedException(); - public void KillAdb() => throw new NotImplementedException(); + Task IAdbClient.IsAppRunningAsync(DeviceData device, string packageName, CancellationToken cancellationToken) => throw new NotImplementedException(); - public Task KillAdbAsync(CancellationToken cancellationToken) => throw new NotImplementedException(); + void IAdbClient.KillAdb() => throw new NotImplementedException(); - public IEnumerable ListForward(DeviceData device) => throw new NotImplementedException(); + Task IAdbClient.KillAdbAsync(CancellationToken cancellationToken) => throw new NotImplementedException(); - public Task> ListForwardAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); + IEnumerable IAdbClient.ListForward(DeviceData device) => throw new NotImplementedException(); - public IEnumerable ListReverseForward(DeviceData device) => throw new NotImplementedException(); + Task> IAdbClient.ListForwardAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); - public Task> ListReverseForwardAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); + IEnumerable IAdbClient.ListReverseForward(DeviceData device) => throw new NotImplementedException(); - public string Pair(DnsEndPoint endpoint, string code) => throw new NotImplementedException(); + Task> IAdbClient.ListReverseForwardAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); - public Task PairAsync(DnsEndPoint endpoint, string code, CancellationToken cancellationToken) => throw new NotImplementedException(); + string IAdbClient.Pair(DnsEndPoint endpoint, string code) => throw new NotImplementedException(); - public void Reboot(string into, DeviceData device) => throw new NotImplementedException(); + Task IAdbClient.PairAsync(DnsEndPoint endpoint, string code, CancellationToken cancellationToken) => throw new NotImplementedException(); - public Task RebootAsync(string into, DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); + void IAdbClient.Reboot(string into, DeviceData device) => throw new NotImplementedException(); - public void RemoveAllForwards(DeviceData device) => throw new NotImplementedException(); + Task IAdbClient.RebootAsync(string into, DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); - public Task RemoveAllForwardsAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); + void IAdbClient.RemoveAllForwards(DeviceData device) => throw new NotImplementedException(); - public void RemoveAllReverseForwards(DeviceData device) => throw new NotImplementedException(); + Task IAdbClient.RemoveAllForwardsAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); - public Task RemoveAllReverseForwardsAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); + void IAdbClient.RemoveAllReverseForwards(DeviceData device) => throw new NotImplementedException(); - public void RemoveForward(DeviceData device, int localPort) => throw new NotImplementedException(); + Task IAdbClient.RemoveAllReverseForwardsAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); - public Task RemoveForwardAsync(DeviceData device, int localPort, CancellationToken cancellationToken) => throw new NotImplementedException(); + void IAdbClient.RemoveForward(DeviceData device, int localPort) => throw new NotImplementedException(); - public void RemoveReverseForward(DeviceData device, string remote) => throw new NotImplementedException(); + Task IAdbClient.RemoveForwardAsync(DeviceData device, int localPort, CancellationToken cancellationToken) => throw new NotImplementedException(); - public Task RemoveReverseForwardAsync(DeviceData device, string remote, CancellationToken cancellationToken) => throw new NotImplementedException(); + void IAdbClient.RemoveReverseForward(DeviceData device, string remote) => throw new NotImplementedException(); - public void Root(DeviceData device) => throw new NotImplementedException(); + Task IAdbClient.RemoveReverseForwardAsync(DeviceData device, string remote, CancellationToken cancellationToken) => throw new NotImplementedException(); - public Task RootAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); + void IAdbClient.Root(DeviceData device) => throw new NotImplementedException(); - public void RunLogService(DeviceData device, Action messageSink, params LogId[] logNames) => throw new NotImplementedException(); + Task IAdbClient.RootAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); - public Task RunLogServiceAsync(DeviceData device, Action messageSink, params LogId[] logNames) => throw new NotImplementedException(); + void IAdbClient.RunLogService(DeviceData device, Action messageSink, params LogId[] logNames) => throw new NotImplementedException(); - public Task RunLogServiceAsync(DeviceData device, Action messageSink, CancellationToken cancellationToken, params LogId[] logNames) => throw new NotImplementedException(); + Task IAdbClient.RunLogServiceAsync(DeviceData device, Action messageSink, CancellationToken cancellationToken, params LogId[] logNames) => throw new NotImplementedException(); - public void SendKeyEvent(DeviceData device, string key) => throw new NotImplementedException(); + void IAdbClient.SendKeyEvent(DeviceData device, string key) => throw new NotImplementedException(); - public Task SendKeyEventAsync(DeviceData device, string key, CancellationToken cancellationToken) => throw new NotImplementedException(); + Task IAdbClient.SendKeyEventAsync(DeviceData device, string key, CancellationToken cancellationToken) => throw new NotImplementedException(); - public void SendText(DeviceData device, string text) => throw new NotImplementedException(); + void IAdbClient.SendText(DeviceData device, string text) => throw new NotImplementedException(); - public Task SendTextAsync(DeviceData device, string text, CancellationToken cancellationToken) => throw new NotImplementedException(); + Task IAdbClient.SendTextAsync(DeviceData device, string text, CancellationToken cancellationToken) => throw new NotImplementedException(); - public void StartApp(DeviceData device, string packageName) => throw new NotImplementedException(); + void IAdbClient.StartApp(DeviceData device, string packageName) => throw new NotImplementedException(); - public Task StartAppAsync(DeviceData device, string packageName, CancellationToken cancellationToken) => throw new NotImplementedException(); + Task IAdbClient.StartAppAsync(DeviceData device, string packageName, CancellationToken cancellationToken) => throw new NotImplementedException(); - public void StopApp(DeviceData device, string packageName) => throw new NotImplementedException(); + void IAdbClient.StopApp(DeviceData device, string packageName) => throw new NotImplementedException(); - public Task StopAppAsync(DeviceData device, string packageName, CancellationToken cancellationToken) => throw new NotImplementedException(); + Task IAdbClient.StopAppAsync(DeviceData device, string packageName, CancellationToken cancellationToken) => throw new NotImplementedException(); - public void Swipe(DeviceData device, Element first, Element second, long speed) => throw new NotImplementedException(); + void IAdbClient.Swipe(DeviceData device, Element first, Element second, long speed) => throw new NotImplementedException(); - public void Swipe(DeviceData device, int x1, int y1, int x2, int y2, long speed) => throw new NotImplementedException(); + void IAdbClient.Swipe(DeviceData device, int x1, int y1, int x2, int y2, long speed) => throw new NotImplementedException(); - public Task SwipeAsync(DeviceData device, Element first, Element second, long speed, CancellationToken cancellationToken) => throw new NotImplementedException(); + Task IAdbClient.SwipeAsync(DeviceData device, Element first, Element second, long speed, CancellationToken cancellationToken) => throw new NotImplementedException(); - public Task SwipeAsync(DeviceData device, int x1, int y1, int x2, int y2, long speed, CancellationToken cancellationToken) => throw new NotImplementedException(); + Task IAdbClient.SwipeAsync(DeviceData device, int x1, int y1, int x2, int y2, long speed, CancellationToken cancellationToken) => throw new NotImplementedException(); - public void Unroot(DeviceData device) => throw new NotImplementedException(); + void IAdbClient.Unroot(DeviceData device) => throw new NotImplementedException(); - public Task UnrootAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); + Task IAdbClient.UnrootAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); #endregion } diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs index c69db69f..8a768137 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs @@ -16,12 +16,6 @@ internal class DummySyncService : ISyncService public void Dispose() => IsOpen = false; - public IAsyncEnumerable GetDirectoryAsyncListing(string remotePath, CancellationToken cancellationToken) => throw new NotImplementedException(); - - public IEnumerable GetDirectoryListing(string remotePath) => throw new NotImplementedException(); - - public Task> GetDirectoryListingAsync(string remotePath, CancellationToken cancellationToken) => throw new NotImplementedException(); - public void Open() => IsOpen = true; public Task OpenAsync(CancellationToken cancellationToken) @@ -54,8 +48,18 @@ public Task PushAsync(Stream stream, string remotePath, int permissions, DateTim return Task.CompletedTask; } - public FileStatistics Stat(string remotePath) => throw new NotImplementedException(); + #region Not Implemented + + IAsyncEnumerable ISyncService.GetDirectoryAsyncListing(string remotePath, CancellationToken cancellationToken) => throw new NotImplementedException(); + + IEnumerable ISyncService.GetDirectoryListing(string remotePath) => throw new NotImplementedException(); + + Task> ISyncService.GetDirectoryListingAsync(string remotePath, CancellationToken cancellationToken) => throw new NotImplementedException(); + + FileStatistics ISyncService.Stat(string remotePath) => throw new NotImplementedException(); + + Task ISyncService.StatAsync(string remotePath, CancellationToken cancellationToken) => throw new NotImplementedException(); - public Task StatAsync(string remotePath, CancellationToken cancellationToken) => throw new NotImplementedException(); + #endregion } } diff --git a/AdvancedSharpAdbClient.Tests/Extensions/ExtensionsTests.cs b/AdvancedSharpAdbClient.Tests/Extensions/ExtensionsTests.cs index 0e443155..a1442504 100644 --- a/AdvancedSharpAdbClient.Tests/Extensions/ExtensionsTests.cs +++ b/AdvancedSharpAdbClient.Tests/Extensions/ExtensionsTests.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Linq; +using System.Threading.Tasks; using Xunit; namespace AdvancedSharpAdbClient.Tests @@ -32,5 +34,15 @@ public void AddRangeTest() Assert.Equal(10, collection.Count); Assert.Equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], collection); } + + [Fact] + 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)); + Assert.Equal(array, await taskArray.ToArrayAsync()); + Assert.Equal(array, await arrayTask.ToArrayAsync()); + } } } diff --git a/AdvancedSharpAdbClient.Tests/Models/AdbResponseTests.cs b/AdvancedSharpAdbClient.Tests/Models/AdbResponseTests.cs index b7001673..7443b278 100644 --- a/AdvancedSharpAdbClient.Tests/Models/AdbResponseTests.cs +++ b/AdvancedSharpAdbClient.Tests/Models/AdbResponseTests.cs @@ -10,18 +10,16 @@ public class AdbResponseTests [Fact] public void EqualsTest() { - AdbResponse first = new() + AdbResponse first = new("Hi") { IOSuccess = false, - Message = "Hi", Okay = false, Timeout = false }; - AdbResponse second = new() + AdbResponse second = new("Hi") { IOSuccess = true, - Message = "Hi", Okay = false, Timeout = false }; @@ -34,18 +32,16 @@ public void EqualsTest() [Fact] public void GetHashCodeTest() { - AdbResponse first = new() + AdbResponse first = new("Hi") { IOSuccess = false, - Message = "Hi", Okay = false, Timeout = false }; - AdbResponse second = new() + AdbResponse second = new("Hi") { IOSuccess = false, - Message = "Hi", Okay = false, Timeout = false }; diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index 151f22ab..b404e5d3 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -72,10 +72,6 @@ public async Task CreateForwardAsync(DeviceData device, string local, strin return portString != null && int.TryParse(portString, out int port) ? port : 0; } - /// - public Task CreateForwardAsync(DeviceData device, ForwardSpec local, ForwardSpec remote, bool allowRebind, CancellationToken cancellationToken = default) => - CreateForwardAsync(device, local?.ToString(), remote?.ToString(), allowRebind, cancellationToken); - /// public async Task CreateReverseForwardAsync(DeviceData device, string remote, string local, bool allowRebind, CancellationToken cancellationToken = default) { @@ -172,6 +168,38 @@ public async Task> ListReverseForwardAsync(DeviceData d return parts.Select(x => new ForwardData(x)); } + /// + public async Task ExecuteServerCommandAsync(string target, string command, Encoding encoding, CancellationToken cancellationToken = default) + { + using IAdbSocket socket = adbSocketFactory(EndPoint); + await ExecuteServerCommandAsync(target, command, socket, encoding, cancellationToken); + } + + /// + public async Task ExecuteServerCommandAsync(string target, string command, IAdbSocket socket, Encoding encoding, CancellationToken cancellationToken = default) + { + StringBuilder request = new(); + if (!StringExtensions.IsNullOrWhiteSpace(target)) + { + _ = request.AppendFormat("{0}:", target); + } + _ = request.Append(command); + + await socket.SendAdbRequestAsync(request.ToString(), cancellationToken); + await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + } + + /// + public async Task ExecuteRemoteCommandAsync(string command, DeviceData device, Encoding encoding, CancellationToken cancellationToken = default) + { + EnsureDevice(device); + + using IAdbSocket socket = adbSocketFactory(EndPoint); + await socket.SetDeviceAsync(device, cancellationToken); + + await ExecuteServerCommandAsync("shell", command, socket, encoding, cancellationToken); + } + /// public async Task ExecuteServerCommandAsync(string target, string command, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken = default) { @@ -248,10 +276,6 @@ public async Task GetFrameBufferAsync(DeviceData device, Cancellati return framebuffer; } - /// - public Task RunLogServiceAsync(DeviceData device, Action messageSink, params LogId[] logNames) => - RunLogServiceAsync(device, messageSink, default, logNames); - /// public async Task RunLogServiceAsync(DeviceData device, Action messageSink, CancellationToken cancellationToken, params LogId[] logNames) { @@ -400,9 +424,6 @@ protected async Task RootAsync(string request, DeviceData device, CancellationTo } } - /// - public Task InstallAsync(DeviceData device, Stream apk, params string[] arguments) => InstallAsync(device, apk, default, arguments); - /// public async Task InstallAsync(DeviceData device, Stream apk, CancellationToken cancellationToken, params string[] arguments) { @@ -461,10 +482,6 @@ public async Task InstallAsync(DeviceData device, Stream apk, CancellationToken } } - /// - public Task InstallMultipleAsync(DeviceData device, IEnumerable splitAPKs, string packageName, params string[] arguments) => - InstallMultipleAsync(device, splitAPKs, packageName, default, arguments); - /// public async Task InstallMultipleAsync(DeviceData device, IEnumerable splitAPKs, string packageName, CancellationToken cancellationToken, params string[] arguments) { @@ -496,10 +513,6 @@ await Extensions.WhenAll(splitAPKs.Select(async splitAPK => await InstallCommitAsync(device, session, cancellationToken).ConfigureAwait(false); } - /// - public Task InstallMultipleAsync(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, params string[] arguments) => - InstallMultipleAsync(device, baseAPK, splitAPKs, default, arguments); - /// public async Task InstallMultipleAsync(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, CancellationToken cancellationToken, params string[] arguments) { @@ -538,11 +551,6 @@ await Extensions.WhenAll(splitAPKs.Select(async splitAPK => await InstallCommitAsync(device, session, cancellationToken).ConfigureAwait(false); } - /// - public Task InstallCreateAsync(DeviceData device, string packageName = null, params string[] arguments) => - InstallCreateAsync(device, packageName, default, arguments); - - /// public async Task InstallCreateAsync(DeviceData device, string packageName, CancellationToken cancellationToken, params string[] arguments) { @@ -1016,13 +1024,6 @@ public async Task SendTextAsync(DeviceData device, string text, CancellationToke } } - /// - public async Task ClearInputAsync(DeviceData device, int charCount, CancellationToken cancellationToken = default) - { - await SendKeyEventAsync(device, "KEYCODE_MOVE_END", cancellationToken).ConfigureAwait(false); - await SendKeyEventAsync(device, StringExtensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL", charCount)), cancellationToken).ConfigureAwait(false); - } - /// public async Task StartAppAsync(DeviceData device, string packageName, CancellationToken cancellationToken = default) { @@ -1044,12 +1045,6 @@ public async Task StopAppAsync(DeviceData device, string packageName, Cancellati await socket.SendAdbRequestAsync($"shell:am force-stop {packageName}", cancellationToken).ConfigureAwait(false); AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); } - - /// - public Task BackBtnAsync(DeviceData device, CancellationToken cancellationToken = default) => SendKeyEventAsync(device, "KEYCODE_BACK", cancellationToken); - - /// - public Task HomeBtnAsync(DeviceData device, CancellationToken cancellationToken = default) => SendKeyEventAsync(device, "KEYCODE_HOME", cancellationToken); } } #endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index 5c8b12b3..053a9770 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -216,10 +216,6 @@ public int CreateForward(DeviceData device, string local, string remote, bool al return portString != null && int.TryParse(portString, out int port) ? port : 0; } - /// - public int CreateForward(DeviceData device, ForwardSpec local, ForwardSpec remote, bool allowRebind) => - CreateForward(device, local?.ToString(), remote?.ToString(), allowRebind); - /// public int CreateReverseForward(DeviceData device, string remote, string local, bool allowRebind) { @@ -316,6 +312,38 @@ public IEnumerable ListReverseForward(DeviceData device) return parts.Select(x => new ForwardData(x)); } + /// + public void ExecuteServerCommand(string target, string command, Encoding encoding) + { + using IAdbSocket socket = adbSocketFactory(EndPoint); + ExecuteServerCommand(target, command, socket, encoding); + } + + /// + public void ExecuteServerCommand(string target, string command, IAdbSocket socket, Encoding encoding) + { + StringBuilder request = new(); + if (!StringExtensions.IsNullOrWhiteSpace(target)) + { + _ = request.AppendFormat("{0}:", target); + } + _ = request.Append(command); + + socket.SendAdbRequest(request.ToString()); + _ = socket.ReadAdbResponse(); + } + + /// + public void ExecuteRemoteCommand(string command, DeviceData device, Encoding encoding) + { + EnsureDevice(device); + + using IAdbSocket socket = adbSocketFactory(EndPoint); + socket.SetDevice(device); + + ExecuteServerCommand("shell", command, socket, encoding); + } + /// public void ExecuteServerCommand(string target, string command, IShellOutputReceiver receiver, Encoding encoding) { @@ -1075,14 +1103,6 @@ public void SendText(DeviceData device, string text) throw new InvalidTextException(); } } - - /// - public void ClearInput(DeviceData device, int charCount) - { - SendKeyEvent(device, "KEYCODE_MOVE_END"); - SendKeyEvent(device, StringExtensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL", charCount))); - } - /// public void StartApp(DeviceData device, string packageName) { @@ -1105,12 +1125,6 @@ public void StopApp(DeviceData device, string packageName) AdbResponse response = socket.ReadAdbResponse(); } - /// - public void BackBtn(DeviceData device) => SendKeyEvent(device, "KEYCODE_BACK"); - - /// - public void HomeBtn(DeviceData device) => SendKeyEvent(device, "KEYCODE_HOME"); - /// /// Sets default encoding (default - UTF8). /// diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs index 32ea11e2..5d395bf7 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs @@ -14,6 +14,17 @@ namespace AdvancedSharpAdbClient.DeviceCommands { public static partial class DeviceExtensions { + /// + /// Executes a shell command on the device. + /// + /// The to use when executing the command. + /// The device on which to run the command. + /// The command to execute. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + public static Task ExecuteShellCommandAsync(this IAdbClient client, DeviceData device, string command, CancellationToken cancellationToken = default) => + client.ExecuteRemoteCommandAsync(command, device, AdbClient.Encoding, cancellationToken); + /// /// Executes a shell command on the device. /// @@ -24,7 +35,7 @@ public static partial class DeviceExtensions /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. public static Task ExecuteShellCommandAsync(this IAdbClient client, DeviceData device, string command, IShellOutputReceiver receiver, CancellationToken cancellationToken = default) => - client.ExecuteRemoteCommandAsync(command, device, receiver, cancellationToken); + client.ExecuteRemoteCommandAsync(command, device, receiver, AdbClient.Encoding, cancellationToken); /// /// Gets the file statistics of a given file. @@ -115,7 +126,7 @@ public static async Task PushAsync(this IAdbClient client, DeviceData device, public static async Task GetPropertyAsync(this IAdbClient client, DeviceData device, string property, CancellationToken cancellationToken = default) { ConsoleOutputReceiver receiver = new(); - await client.ExecuteRemoteCommandAsync($"{GetPropReceiver.GetPropCommand} {property}", device, receiver, cancellationToken).ConfigureAwait(false); + await client.ExecuteShellCommandAsync(device, $"{GetPropReceiver.GetPropCommand} {property}", receiver, cancellationToken).ConfigureAwait(false); return receiver.ToString(); } @@ -129,7 +140,7 @@ public static async Task GetPropertyAsync(this IAdbClient client, Device public static async Task> GetPropertiesAsync(this IAdbClient client, DeviceData device, CancellationToken cancellationToken = default) { GetPropReceiver receiver = new(); - await client.ExecuteRemoteCommandAsync(GetPropReceiver.GetPropCommand, device, receiver, cancellationToken).ConfigureAwait(false); + await client.ExecuteShellCommandAsync(device, GetPropReceiver.GetPropCommand, receiver, cancellationToken).ConfigureAwait(false); return receiver.Properties; } @@ -143,7 +154,7 @@ public static async Task> GetPropertiesAsync(this IAd public static async Task> GetEnvironmentVariablesAsync(this IAdbClient client, DeviceData device, CancellationToken cancellationToken = default) { EnvironmentVariablesReceiver receiver = new(); - await client.ExecuteRemoteCommandAsync(EnvironmentVariablesReceiver.PrintEnvCommand, device, receiver, cancellationToken).ConfigureAwait(false); + await client.ExecuteShellCommandAsync(device, EnvironmentVariablesReceiver.PrintEnvCommand, receiver, cancellationToken).ConfigureAwait(false); return receiver.EnvironmentVariables; } diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs index 98f44dc1..16a1722d 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs @@ -16,6 +16,15 @@ namespace AdvancedSharpAdbClient.DeviceCommands /// public static partial class DeviceExtensions { + /// + /// Executes a shell command on the device. + /// + /// The to use when executing the command. + /// The device on which to run the command. + /// The command to execute. + public static void ExecuteShellCommand(this IAdbClient client, DeviceData device, string command) => + client.ExecuteRemoteCommand(command, device, AdbClient.Encoding); + /// /// Executes a shell command on the device. /// @@ -24,7 +33,7 @@ public static partial class DeviceExtensions /// The command to execute. /// Optionally, a that processes the command output. public static void ExecuteShellCommand(this IAdbClient client, DeviceData device, string command, IShellOutputReceiver receiver) => - client.ExecuteRemoteCommand(command, device, receiver); + client.ExecuteRemoteCommand(command, device, receiver, AdbClient.Encoding); /// /// Gets the file statistics of a given file. @@ -112,7 +121,7 @@ public static void Push(this IAdbClient client, DeviceData device, public static string GetProperty(this IAdbClient client, DeviceData device, string property) { ConsoleOutputReceiver receiver = new(); - client.ExecuteRemoteCommand($"{GetPropReceiver.GetPropCommand} {property}", device, receiver); + client.ExecuteShellCommand(device, $"{GetPropReceiver.GetPropCommand} {property}", receiver); return receiver.ToString(); } @@ -125,7 +134,7 @@ public static string GetProperty(this IAdbClient client, DeviceData device, stri public static Dictionary GetProperties(this IAdbClient client, DeviceData device) { GetPropReceiver receiver = new(); - client.ExecuteRemoteCommand(GetPropReceiver.GetPropCommand, device, receiver); + client.ExecuteShellCommand(device, GetPropReceiver.GetPropCommand, receiver); return receiver.Properties; } @@ -138,7 +147,7 @@ public static Dictionary GetProperties(this IAdbClient client, D public static Dictionary GetEnvironmentVariables(this IAdbClient client, DeviceData device) { EnvironmentVariablesReceiver receiver = new(); - client.ExecuteRemoteCommand(EnvironmentVariablesReceiver.PrintEnvCommand, device, receiver); + client.ExecuteShellCommand(device, EnvironmentVariablesReceiver.PrintEnvCommand, receiver); return receiver.EnvironmentVariables; } diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/VersionInfo.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/VersionInfo.cs index 600fd8fa..97b637ab 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/VersionInfo.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/VersionInfo.cs @@ -2,6 +2,10 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // +using System; +using System.Collections; +using System.Collections.Generic; + namespace AdvancedSharpAdbClient.DeviceCommands { /// @@ -9,30 +13,75 @@ namespace AdvancedSharpAdbClient.DeviceCommands /// /// The version code of the application. /// The version name of the application. - public class VersionInfo(int versionCode, string versionName) + public struct VersionInfo(int versionCode, string versionName) : IComparer, IComparer { /// /// Gets or sets the version code of an Android application. /// - public int VersionCode { get; } = versionCode; + public int VersionCode { get; set; } = versionCode; /// /// Gets or sets the version name of an Android application. /// - public string VersionName { get; } = versionName; + public string VersionName { get; set; } = versionName; + + /// + public readonly int Compare(object x, object y) => + x is VersionInfo left && y is VersionInfo right + ? left.VersionCode.CompareTo(right.VersionCode) + : throw new NotImplementedException(); + + /// + public readonly int Compare(VersionInfo x, VersionInfo y) => x.VersionCode.CompareTo(y.VersionCode); /// /// Deconstruct the class. /// /// The version code of the application. /// The version name of the application. - public void Deconstruct(out int versionCode, out string versionName) + public readonly void Deconstruct(out int versionCode, out string versionName) { versionCode = VersionCode; versionName = VersionName; } /// - public override string ToString() => $"{VersionName} ({VersionCode})"; + public override readonly string ToString() => $"{VersionName} ({VersionCode})"; + + /// + /// Compares the of two values to determine which is greater. + /// + /// The value to compare with . + /// The value to compare with . + /// if the of + /// is greater than ; otherwise, . + public static bool operator >(VersionInfo left, VersionInfo right) => left.VersionCode > right.VersionCode; + + /// + /// Compares the of two values to determine which is less. + /// + /// The value to compare with . + /// The value to compare with . + /// if the of + /// is less than ; otherwise, . + public static bool operator <(VersionInfo left, VersionInfo right) => left.VersionCode < right.VersionCode; + + /// + /// Compares the of two values to determine which is greater or equal. + /// + /// The value to compare with . + /// The value to compare with . + /// if the of + /// is greater or equal to ; otherwise, . + public static bool operator >=(VersionInfo left, VersionInfo right) => left.VersionCode >= right.VersionCode; + + /// + /// Compares the of two values to determine which is less or equal. + /// + /// The value to compare with . + /// The value to compare with . + /// if the of + /// is less or equal to ; otherwise, . + public static bool operator <=(VersionInfo left, VersionInfo right) => left.VersionCode <= right.VersionCode; } } diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs index 54a420f5..f5744878 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs @@ -113,21 +113,20 @@ void OnSplitSyncProgressChanged(object sender, SyncProgressChangedEventArgs args InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitPackageFilePaths.Count + 1, present)); } - List splitRemoteFilePaths = new(splitPackageFilePaths.Count); - await Extensions.WhenAll(splitPackageFilePaths.Select(async x => splitRemoteFilePaths.Add(await SyncPackageToDeviceAsync(x, OnSplitSyncProgressChanged, cancellationToken).ConfigureAwait(false)))).ConfigureAwait(false); + string[] splitRemoteFilePaths = await splitPackageFilePaths.Select(x => SyncPackageToDeviceAsync(x, OnSplitSyncProgressChanged, cancellationToken)).ToArrayAsync().ConfigureAwait(false); await InstallMultipleRemotePackageAsync(baseRemoteFilePath, splitRemoteFilePaths, reinstall, cancellationToken); - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFilePaths.Count + 1, PackageInstallProgressState.PostInstall)); + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFilePaths.Length + 1, PackageInstallProgressState.PostInstall)); int count = 0; await Extensions.WhenAll(splitRemoteFilePaths.Select(async x => { await RemoveRemotePackageAsync(x, cancellationToken).ConfigureAwait(false); - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(++count, splitRemoteFilePaths.Count + 1, PackageInstallProgressState.PostInstall)); + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(++count, splitRemoteFilePaths.Length + 1, PackageInstallProgressState.PostInstall)); })).ConfigureAwait(false); await RemoveRemotePackageAsync(baseRemoteFilePath, cancellationToken).ConfigureAwait(false); - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(++count, splitRemoteFilePaths.Count + 1, PackageInstallProgressState.PostInstall)); + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(++count, splitRemoteFilePaths.Length + 1, PackageInstallProgressState.PostInstall)); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.Finished)); } @@ -160,17 +159,16 @@ void OnSyncProgressChanged(object sender, SyncProgressChangedEventArgs args) InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitPackageFilePaths.Count, present)); } - List splitRemoteFilePaths = new(splitPackageFilePaths.Count); - await Extensions.WhenAll(splitPackageFilePaths.Select(async x => splitRemoteFilePaths.Add(await SyncPackageToDeviceAsync(x, OnSyncProgressChanged, cancellationToken)))).ConfigureAwait(false); + string[] splitRemoteFilePaths = await splitPackageFilePaths.Select(x => SyncPackageToDeviceAsync(x, OnSyncProgressChanged, cancellationToken)).ToArrayAsync().ConfigureAwait(false); await InstallMultipleRemotePackageAsync(splitRemoteFilePaths, packageName, reinstall, cancellationToken); - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFilePaths.Count, PackageInstallProgressState.PostInstall)); + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFilePaths.Length, PackageInstallProgressState.PostInstall)); int count = 0; await Extensions.WhenAll(splitRemoteFilePaths.Select(async x => { await RemoveRemotePackageAsync(x, cancellationToken).ConfigureAwait(false); - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(++count, splitRemoteFilePaths.Count, PackageInstallProgressState.PostInstall)); + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(++count, splitRemoteFilePaths.Length, PackageInstallProgressState.PostInstall)); })).ConfigureAwait(false); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.Finished)); @@ -367,7 +365,7 @@ protected virtual async Task RemoveRemotePackageAsync(string remoteFilePath, Can // now we delete the app we synced try { - await client.ExecuteShellCommandAsync(Device, $"rm \"{remoteFilePath}\"", null, cancellationToken).ConfigureAwait(false); + await client.ExecuteShellCommandAsync(Device, $"rm \"{remoteFilePath}\"", cancellationToken).ConfigureAwait(false); } catch (IOException e) { diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs index 0b4cb46d..8782e4b9 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs @@ -453,7 +453,7 @@ protected virtual void RemoveRemotePackage(string remoteFilePath) // now we delete the app we synced try { - client.ExecuteShellCommand(Device, $"rm \"{remoteFilePath}\"", null); + client.ExecuteShellCommand(Device, $"rm \"{remoteFilePath}\""); } catch (IOException e) { diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InfoOutputReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InfoOutputReceiver.cs index f1631bdc..714b8a4e 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InfoOutputReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InfoOutputReceiver.cs @@ -32,10 +32,19 @@ public InfoOutputReceiver() { } /// Gets the value of the property out of the Properties dictionary. /// Returns null if the property is not present in the directory. /// - /// The name of the property + /// The name of the property. /// The received value public object GetPropertyValue(string propertyName) => Properties.TryGetValue(propertyName, out object property) ? property : null; + /// + /// Gets the value of the property out of the Properties dictionary. + /// Returns null if the property is not present in the directory. + /// + /// The type of the property + /// The name of the property. + /// The received value. + public T GetPropertyValue(string propertyName) => Properties.TryGetValue(propertyName, out object property) && property is T value ? value : default; + /// /// Adds a new parser to this receiver. /// The parsers parses one received line and extracts the property value if possible. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/VersionInfoReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/VersionInfoReceiver.cs index 3ba183ee..c1416fe8 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/VersionInfoReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/VersionInfoReceiver.cs @@ -45,9 +45,9 @@ public VersionInfoReceiver() /// Gets the version code of the specified package. /// public VersionInfo VersionInfo => - GetPropertyValue(versionCode) != null && GetPropertyValue(versionName) != null - ? new VersionInfo((int)GetPropertyValue(versionCode), (string)GetPropertyValue(versionName)) - : null; + GetPropertyValue(versionName) is string name + ? new VersionInfo(GetPropertyValue(versionCode), name) + : default; private void CheckPackagesSection(string line) { diff --git a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs index ba3bb52d..6f67facf 100644 --- a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs +++ b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs @@ -4,7 +4,11 @@ // using AdvancedSharpAdbClient.Exceptions; +using AdvancedSharpAdbClient.Logs; using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; using System.Net; using System.Text; using System.Threading; @@ -13,6 +17,82 @@ namespace AdvancedSharpAdbClient { public static partial class AdbClientExtensions { + /// + /// Asks the ADB server to forward local connections from + /// to the address on the . + /// + /// An instance of a class that implements the interface. + /// The device on which to forward the connections. + /// The local address to forward. + /// The remote address to forward. + /// If set to , the request will fail if there is already a forward + /// connection from . + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + /// If your requested to start forwarding to local port TCP:0, the port number of the TCP port + /// which has been opened. In all other cases, 0. + public static Task CreateForwardAsync(this IAdbClient client, DeviceData device, ForwardSpec local, ForwardSpec remote, bool allowRebind, CancellationToken cancellationToken = default) => + client.CreateForwardAsync(device, local?.ToString(), remote?.ToString(), allowRebind, cancellationToken); + + /// + /// Creates a port forwarding between a local and a remote port. + /// + /// An instance of a class that implements the interface. + /// The device to which to forward the connections. + /// The local port to forward. + /// The remote port to forward to + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + /// If your requested to start forwarding to local port TCP:0, the port number of the TCP port + /// which has been opened. In all other cases, 0. + /// Failed to submit the forward command. Or Device rejected command: + resp.Message. + public static Task CreateForwardAsync(this IAdbClient client, DeviceData device, int localPort, int remotePort, CancellationToken cancellationToken = default) => + client.CreateForwardAsync(device, $"tcp:{localPort}", $"tcp:{remotePort}", true, cancellationToken); + + /// + /// Forwards a remote Unix socket to a local TCP socket. + /// + /// An instance of a class that implements the interface. + /// The device to which to forward the connections. + /// The local port to forward. + /// The remote Unix socket. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + /// If your requested to start forwarding to local port TCP:0, the port number of the TCP port + /// which has been opened. In all other cases, 0. + /// The client failed to submit the forward command. + /// The device rejected command. The error message will include the error message provided by the device. + public static Task CreateForwardAsync(this IAdbClient client, DeviceData device, int localPort, string remoteSocket, CancellationToken cancellationToken = default) => + client.CreateForwardAsync(device, $"tcp:{localPort}", $"local:{remoteSocket}", true, cancellationToken); + + /// + /// Asks the ADB server to reverse forward local connections from + /// to the address on the . + /// + /// An instance of a class that implements the interface. + /// The device on which to reverse forward the connections. + /// The remote address to reverse forward. + /// The local address to reverse forward. + /// If set to , the request will fail if if the specified + /// socket is already bound through a previous reverse command. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + /// If your requested to start reverse to remote port TCP:0, the port number of the TCP port + /// which has been opened. In all other cases, 0. + public static Task CreateReverseForwardAsync(this IAdbClient client, DeviceData device, ForwardSpec remote, ForwardSpec local, bool allowRebind, CancellationToken cancellationToken = default) => + client.CreateReverseForwardAsync(device, remote?.ToString(), local?.ToString(), allowRebind, cancellationToken); + + /// + /// Remove a reverse port forwarding between a remote and a local port. + /// + /// An instance of a class that implements the interface. + /// The device on which to remove the reverse port forwarding + /// Specification of the remote that was forwarded + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + public static Task RemoveReverseForwardAsync(this IAdbClient client, DeviceData device, ForwardSpec remote, CancellationToken cancellationToken = default) => + client.RemoveReverseForwardAsync(device, remote?.ToString(), cancellationToken); + /// /// Executes a command on the adb server. /// @@ -34,11 +114,10 @@ public static Task ExecuteServerCommandAsync(this IAdbClient client, string targ /// localreserved, localabstract, jdwp, track-jdwp, sync, reverse and so on. /// The command to execute. /// The to send command. - /// Optionally, a that processes the command output. /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. - public static Task ExecuteServerCommandAsync(this IAdbClient client, string target, string command, IAdbSocket socket, IShellOutputReceiver receiver, CancellationToken cancellationToken = default) => - client.ExecuteServerCommandAsync(target, command, socket, receiver, AdbClient.Encoding, cancellationToken); + public static Task ExecuteServerCommandAsync(this IAdbClient client, string target, string command, IAdbSocket socket, CancellationToken cancellationToken = default) => + client.ExecuteServerCommandAsync(target, command, socket, AdbClient.Encoding, cancellationToken); /// /// Executes a command on the device. @@ -46,42 +125,59 @@ public static Task ExecuteServerCommandAsync(this IAdbClient client, string targ /// An instance of a class that implements the interface. /// The command to execute. /// The device on which to run the command. - /// Optionally, a that processes the command output. /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. - public static Task ExecuteRemoteCommandAsync(this IAdbClient client, string command, DeviceData device, IShellOutputReceiver receiver, CancellationToken cancellationToken = default) => - client.ExecuteRemoteCommandAsync(command, device, receiver, AdbClient.Encoding, cancellationToken); + public static Task ExecuteRemoteCommandAsync(this IAdbClient client, string command, DeviceData device, CancellationToken cancellationToken = default) => + client.ExecuteRemoteCommandAsync(command, device, AdbClient.Encoding, cancellationToken); /// - /// Creates a port forwarding between a local and a remote port. + /// Executes a command on the adb server. /// /// An instance of a class that implements the interface. - /// The device to which to forward the connections. - /// The local port to forward. - /// The remote port to forward to + /// The target of command, such as shell, remount, dev, tcp, local, + /// localreserved, localabstract, jdwp, track-jdwp, sync, reverse and so on. + /// The command to execute. /// A which can be used to cancel the asynchronous operation. - /// A which represents the asynchronous operation. - /// If your requested to start forwarding to local port TCP:0, the port number of the TCP port - /// which has been opened. In all other cases, 0. - /// Failed to submit the forward command. Or Device rejected command: + resp.Message. - public static Task CreateForwardAsync(this IAdbClient client, DeviceData device, int localPort, int remotePort, CancellationToken cancellationToken = default) => - client.CreateForwardAsync(device, $"tcp:{localPort}", $"tcp:{remotePort}", true, cancellationToken); + /// A which represents the asynchronous operation. + public static Task ExecuteServerCommandAsync(this IAdbClient client, string target, string command, CancellationToken cancellationToken = default) => + client.ExecuteServerCommandAsync(target, command, AdbClient.Encoding, cancellationToken); /// - /// Forwards a remote Unix socket to a local TCP socket. + /// Executes a command on the adb server. /// /// An instance of a class that implements the interface. - /// The device to which to forward the connections. - /// The local port to forward. - /// The remote Unix socket. + /// The target of command, such as shell, remount, dev, tcp, local, + /// localreserved, localabstract, jdwp, track-jdwp, sync, reverse and so on. + /// The command to execute. + /// The to send command. + /// Optionally, a that processes the command output. /// A which can be used to cancel the asynchronous operation. - /// A which represents the asynchronous operation. - /// If your requested to start forwarding to local port TCP:0, the port number of the TCP port - /// which has been opened. In all other cases, 0. - /// The client failed to submit the forward command. - /// The device rejected command. The error message will include the error message provided by the device. - public static Task CreateForwardAsync(this IAdbClient client, DeviceData device, int localPort, string remoteSocket, CancellationToken cancellationToken = default) => - client.CreateForwardAsync(device, $"tcp:{localPort}", $"local:{remoteSocket}", true, cancellationToken); + /// A which represents the asynchronous operation. + public static Task ExecuteServerCommandAsync(this IAdbClient client, string target, string command, IAdbSocket socket, IShellOutputReceiver receiver, CancellationToken cancellationToken = default) => + client.ExecuteServerCommandAsync(target, command, socket, receiver, AdbClient.Encoding, cancellationToken); + + /// + /// Executes a command on the device. + /// + /// An instance of a class that implements the interface. + /// The command to execute. + /// The device on which to run the command. + /// Optionally, a that processes the command output. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + public static Task ExecuteRemoteCommandAsync(this IAdbClient client, string command, DeviceData device, IShellOutputReceiver receiver, CancellationToken cancellationToken = default) => + client.ExecuteRemoteCommandAsync(command, device, receiver, AdbClient.Encoding, cancellationToken); + + /// + /// Asynchronously runs the event log service on a device. + /// + /// An instance of a class that implements the interface. + /// The device on which to run the event log service. + /// A callback which will receive the event log messages as they are received. + /// Optionally, the names of the logs to receive. + /// A which represents the asynchronous operation. + public static Task RunLogServiceAsync(this IAdbClient client, DeviceData device, Action messageSink, params LogId[] logNames) => + client.RunLogServiceAsync(device, messageSink, default, logNames); /// /// Reboots the specified adb socket address. @@ -208,6 +304,83 @@ public static Task ConnectAsync(this IAdbClient client, string host, int ? throw new ArgumentNullException(nameof(host)) : client.ConnectAsync(new DnsEndPoint(values[0], values.Length > 1 && int.TryParse(values[1], out int _port) ? _port : port), cancellationToken); } + + /// + /// Asynchronously installs an Android application on an device. + /// + /// An instance of a class that implements the interface. + /// The device on which to install the application. + /// A which represents the application to install. + /// The arguments to pass to adb install. + /// A which represents the asynchronous operation. + public static Task InstallAsync(this IAdbClient client, DeviceData device, Stream apk, params string[] arguments) => client.InstallAsync(device, apk, default, arguments); + + /// + /// Asynchronously push multiple APKs to the device and install them. + /// + /// An instance of a class that implements the interface. + /// The device on which to install the application. + /// s which represents the split APKs to install. + /// The package name of the base APK to install. + /// The arguments to pass to adb install-create. + /// A which represents the asynchronous operation. + public static Task InstallMultipleAsync(this IAdbClient client, DeviceData device, IEnumerable splitAPKs, string packageName, params string[] arguments) => + client.InstallMultipleAsync(device, splitAPKs, packageName, default, arguments); + + /// + /// Asynchronously push multiple APKs to the device and install them. + /// + /// An instance of a class that implements the interface. + /// The device on which to install the application. + /// A which represents the base APK to install. + /// s which represents the split APKs to install. + /// The arguments to pass to adb install-create. + /// A which represents the asynchronous operation. + public static Task InstallMultipleAsync(this IAdbClient client, DeviceData device, Stream baseAPK, IEnumerable splitAPKs, params string[] arguments) => + client.InstallMultipleAsync(device, baseAPK, splitAPKs, default, arguments); + + /// + /// Like "install", but starts an install session. + /// + /// An instance of a class that implements the interface. + /// The device on which to install the application. + /// The package name of the baseAPK to install. + /// The arguments to pass to adb install-create. + /// A which return the session ID + public static Task InstallCreateAsync(this IAdbClient client, DeviceData device, string packageName = null, params string[] arguments) => + client.InstallCreateAsync(device, packageName, default, arguments); + + /// + /// Clear the input text. The input should be in focus. Use if the element isn't focused. + /// + /// An instance of a class that implements the interface. + /// The device on which to clear the input text. + /// The length of text to clear. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + public static async Task ClearInputAsync(this IAdbClient client, DeviceData device, int charCount, CancellationToken cancellationToken = default) + { + await client.SendKeyEventAsync(device, "KEYCODE_MOVE_END", cancellationToken).ConfigureAwait(false); + await client.SendKeyEventAsync(device, StringExtensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL", charCount)), cancellationToken).ConfigureAwait(false); + } + + /// + /// Click BACK button. + /// + /// An instance of a class that implements the interface. + /// The device on which to click BACK button. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + public static Task ClickBackButtonAsync(this IAdbClient client, DeviceData device, CancellationToken cancellationToken = default) => client.SendKeyEventAsync(device, "KEYCODE_BACK", cancellationToken); + + /// + /// Click HOME button. + /// + /// An instance of a class that implements the interface. + /// The device on which to click HOME button. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + public static Task ClickHomeButtonAsync(this IAdbClient client, DeviceData device, CancellationToken cancellationToken = default) => client.SendKeyEventAsync(device, "KEYCODE_HOME", cancellationToken); } } #endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.cs b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.cs index bd034d12..71a85734 100644 --- a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.cs +++ b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.cs @@ -4,6 +4,7 @@ using AdvancedSharpAdbClient.Exceptions; using System; +using System.Linq; using System.Net; namespace AdvancedSharpAdbClient @@ -13,6 +14,75 @@ namespace AdvancedSharpAdbClient /// public static partial class AdbClientExtensions { + /// + /// Asks the ADB server to forward local connections from + /// to the address on the . + /// + /// An instance of a class that implements the interface. + /// The device on which to forward the connections. + /// The local address to forward. + /// The remote address to forward. + /// If set to , the request will fail if there is already a forward + /// connection from . + /// If your requested to start forwarding to local port TCP:0, the port number of the TCP port + /// which has been opened. In all other cases, 0. + public static int CreateForward(this IAdbClient client, DeviceData device, ForwardSpec local, ForwardSpec remote, bool allowRebind) => + client.CreateForward(device, local?.ToString(), remote?.ToString(), allowRebind); + + /// + /// Asks the ADB server to reverse forward local connections from + /// to the address on the . + /// + /// An instance of a class that implements the interface. + /// The device on which to reverse forward the connections. + /// The remote address to reverse forward. + /// The local address to reverse forward. + /// If set to , the request will fail if if the specified socket + /// is already bound through a previous reverse command. + /// If your requested to start reverse to remote port TCP:0, the port number of the TCP port + /// which has been opened. In all other cases, 0. + public static int CreateReverseForward(this IAdbClient client, DeviceData device, ForwardSpec remote, ForwardSpec local, bool allowRebind) => + client.CreateReverseForward(device, remote?.ToString(), local?.ToString(), allowRebind); + + /// + /// Remove a reverse port forwarding between a remote and a local port. + /// + /// An instance of a class that implements the interface. + /// The device on which to remove the reverse port forwarding + /// Specification of the remote that was forwarded + public static void RemoveReverseForward(this IAdbClient client, DeviceData device, string remote) => + client.RemoveReverseForward(device, remote); + + /// + /// Executes a command on the adb server. + /// + /// An instance of a class that implements the interface. + /// The target of command, such as shell, remount, dev, tcp, local, + /// localreserved, localabstract, jdwp, track-jdwp, sync, reverse and so on. + /// The command to execute. + public static void ExecuteServerCommand(this IAdbClient client, string target, string command) => + client.ExecuteServerCommand(target, command, AdbClient.Encoding); + + /// + /// Executes a command on the adb server. + /// + /// An instance of a class that implements the interface. + /// The target of command, such as shell, remount, dev, tcp, local, + /// localreserved, localabstract, jdwp, track-jdwp, sync, reverse and so on. + /// The command to execute. + /// The to send command. + public static void ExecuteServerCommand(this IAdbClient client, string target, string command, IAdbSocket socket) => + client.ExecuteServerCommand(target, command, socket, AdbClient.Encoding); + + /// + /// Executes a shell command on the device. + /// + /// An instance of a class that implements the interface. + /// The command to execute. + /// The device on which to run the command. + public static void ExecuteRemoteCommand(this IAdbClient client, string command, DeviceData device) => + client.ExecuteRemoteCommand(command, device, AdbClient.Encoding); + /// /// Executes a command on the adb server. /// @@ -189,5 +259,31 @@ public static string Connect(this IAdbClient client, string host, int port = Adb ? throw new ArgumentNullException(nameof(host)) : client.Connect(new DnsEndPoint(values[0], values.Length > 1 && int.TryParse(values[1], out int _port) ? _port : port)); } + + /// + /// Clear the input text. The input should be in focus. Use if the element isn't focused. + /// + /// An instance of a class that implements the interface. + /// The device on which to clear the input text. + /// The length of text to clear. + public static void ClearInput(this IAdbClient client, DeviceData device, int charCount) + { + client.SendKeyEvent(device, "KEYCODE_MOVE_END"); + client.SendKeyEvent(device, StringExtensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL", charCount))); + } + + /// + /// Click BACK button. + /// + /// An instance of a class that implements the interface. + /// The device on which to click BACK button. + public static void ClickBackButton(this IAdbClient client, DeviceData device) => client.SendKeyEvent(device, "KEYCODE_BACK"); + + /// + /// Click HOME button. + /// + /// An instance of a class that implements the interface. + /// The device on which to click HOME button. + public static void ClickHomeButton(this IAdbClient client, DeviceData device) => client.SendKeyEvent(device, "KEYCODE_HOME"); } } diff --git a/AdvancedSharpAdbClient/Extensions/Extensions.cs b/AdvancedSharpAdbClient/Extensions/Extensions.cs index ca9aa2a1..114c7e1d 100644 --- a/AdvancedSharpAdbClient/Extensions/Extensions.cs +++ b/AdvancedSharpAdbClient/Extensions/Extensions.cs @@ -120,9 +120,17 @@ public static void Dispose(this WaitHandle waitHandle) /// The type of the elements of . /// An to create an array from. /// An array that contains the elements from the input sequence. - public static Task ToArray(this Task> source) => + public static Task ToArrayAsync(this Task> source) => source.ContinueWith(x => x.Result.ToArray()); + /// + /// Creates an array from a . + /// + /// The type of the elements of . + /// An to create an array from. + /// An array that contains the elements from the input sequence. + public static Task ToArrayAsync(this IEnumerable> source) => WhenAll(source); + /// /// Creates a task that completes after a specified number of milliseconds. /// @@ -185,6 +193,20 @@ public static Task WhenAll(IEnumerable tasks) => #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); + #if !NET7_0_OR_GREATER /// /// Reads a line of characters asynchronously and returns the data as a string. diff --git a/AdvancedSharpAdbClient/Extensions/SteamExtensions.cs b/AdvancedSharpAdbClient/Extensions/StreamExtensions.cs similarity index 98% rename from AdvancedSharpAdbClient/Extensions/SteamExtensions.cs rename to AdvancedSharpAdbClient/Extensions/StreamExtensions.cs index 3e5ed553..54e837d8 100644 --- a/AdvancedSharpAdbClient/Extensions/SteamExtensions.cs +++ b/AdvancedSharpAdbClient/Extensions/StreamExtensions.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // @@ -11,7 +11,7 @@ namespace AdvancedSharpAdbClient /// /// Provides extension methods for the class. /// - public static class SteamExtensions + public static class StreamExtensions { #if !HAS_BUFFERS /// diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs b/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs index df456064..34a70934 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs @@ -78,44 +78,6 @@ public partial interface IAdbClient /// which has been opened. In all other cases, 0. Task CreateForwardAsync(DeviceData device, string local, string remote, bool allowRebind, CancellationToken cancellationToken); - /// - /// Asks the ADB server to forward local connections from - /// to the address on the . - /// - /// The device on which to forward the connections. - /// - /// The local address to forward. This value can be in one of: - /// - /// - /// tcp:<port>: TCP connection on localhost:<port> - /// - /// - /// local:<path>: Unix local domain socket on <path> - /// - /// - /// - /// - /// The remote address to forward. This value can be in one of: - /// - /// - /// tcp:<port>: TCP connection on localhost:<port> on device - /// - /// - /// local:<path>: Unix local domain socket on <path> on device - /// - /// - /// jdwp:<pid>: JDWP thread on VM process <pid> on device. - /// - /// - /// - /// If set to , the request will fail if there is already a forward - /// connection from . - /// A which can be used to cancel the asynchronous operation. - /// A which represents the asynchronous operation. - /// If your requested to start forwarding to local port TCP:0, the port number of the TCP port - /// which has been opened. In all other cases, 0. - Task CreateForwardAsync(DeviceData device, ForwardSpec local, ForwardSpec remote, bool allowRebind, CancellationToken cancellationToken); - /// /// Asks the ADB server to reverse forward local connections from /// to the address on the . @@ -204,6 +166,39 @@ public partial interface IAdbClient /// A which return the entry for each existing reverse forward connection. Task> ListReverseForwardAsync(DeviceData device, CancellationToken cancellationToken); + /// + /// Executes a command on the adb server. + /// + /// The target of command, such as shell, remount, dev, tcp, local, + /// localreserved, localabstract, jdwp, track-jdwp, sync, reverse and so on. + /// The command to execute. + /// The encoding to use when parsing the command output. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + Task ExecuteServerCommandAsync(string target, string command, Encoding encoding, CancellationToken cancellationToken); + + /// + /// Executes a command on the adb server. + /// + /// The target of command, such as shell, remount, dev, tcp, local, + /// localreserved, localabstract, jdwp, track-jdwp, sync, reverse and so on. + /// The command to execute. + /// The to send command. + /// The encoding to use when parsing the command output. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + Task ExecuteServerCommandAsync(string target, string command, IAdbSocket socket, Encoding encoding, CancellationToken cancellationToken); + + /// + /// Executes a command on the device. + /// + /// The command to execute. + /// The device on which to run the command. + /// The encoding to use when parsing the command output. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + Task ExecuteRemoteCommandAsync(string command, DeviceData device, Encoding encoding, CancellationToken cancellationToken); + /// /// Executes a command on the adb server. /// @@ -250,15 +245,6 @@ public partial interface IAdbClient /// failed nudging Task GetFrameBufferAsync(DeviceData device, CancellationToken cancellationToken); - /// - /// Asynchronously runs the event log service on a device. - /// - /// The device on which to run the event log service. - /// A callback which will receive the event log messages as they are received. - /// Optionally, the names of the logs to receive. - /// A which represents the asynchronous operation. - Task RunLogServiceAsync(DeviceData device, Action messageSink, params LogId[] logNames); - /// /// Asynchronously runs the event log service on a device. /// @@ -319,15 +305,6 @@ public partial interface IAdbClient /// A which represents the asynchronous operation. Task UnrootAsync(DeviceData device, CancellationToken cancellationToken); - /// - /// Asynchronously installs an Android application on an device. - /// - /// The device on which to install the application. - /// A which represents the application to install. - /// The arguments to pass to adb install. - /// A which represents the asynchronous operation. - Task InstallAsync(DeviceData device, Stream apk, params string[] arguments); - /// /// Asynchronously installs an Android application on an device. /// @@ -338,16 +315,6 @@ public partial interface IAdbClient /// A which represents the asynchronous operation. Task InstallAsync(DeviceData device, Stream apk, CancellationToken cancellationToken, params string[] arguments); - /// - /// Asynchronously push multiple APKs to the device and install them. - /// - /// The device on which to install the application. - /// s which represents the split APKs to install. - /// The package name of the base APK to install. - /// The arguments to pass to adb install-create. - /// A which represents the asynchronous operation. - Task InstallMultipleAsync(DeviceData device, IEnumerable splitAPKs, string packageName, params string[] arguments); - /// /// Asynchronously push multiple APKs to the device and install them. /// @@ -359,16 +326,6 @@ public partial interface IAdbClient /// A which represents the asynchronous operation. Task InstallMultipleAsync(DeviceData device, IEnumerable splitAPKs, string packageName, CancellationToken cancellationToken, params string[] arguments); - /// - /// Asynchronously push multiple APKs to the device and install them. - /// - /// The device on which to install the application. - /// A which represents the base APK to install. - /// s which represents the split APKs to install. - /// The arguments to pass to adb install-create. - /// A which represents the asynchronous operation. - Task InstallMultipleAsync(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, params string[] arguments); - /// /// Asynchronously push multiple APKs to the device and install them. /// @@ -380,15 +337,6 @@ public partial interface IAdbClient /// A which represents the asynchronous operation. Task InstallMultipleAsync(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, CancellationToken cancellationToken, params string[] arguments); - /// - /// Like "install", but starts an install session. - /// - /// The device on which to install the application. - /// The package name of the baseAPK to install. - /// The arguments to pass to adb install-create. - /// A which return the session ID - Task InstallCreateAsync(DeviceData device, string packageName, params string[] arguments); - /// /// Like "install", but starts an install session. /// @@ -574,15 +522,6 @@ public partial interface IAdbClient /// A which represents the asynchronous operation. Task SendTextAsync(DeviceData device, string text, CancellationToken cancellationToken); - /// - /// Clear the input text. The input should be in focus. Use if the element isn't focused. - /// - /// The device on which to clear the input text. - /// The length of text to clear. - /// A which can be used to cancel the asynchronous operation. - /// A which represents the asynchronous operation. - Task ClearInputAsync(DeviceData device, int charCount, CancellationToken cancellationToken); - /// /// Start an Android application on device. /// @@ -600,22 +539,6 @@ public partial interface IAdbClient /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. Task StopAppAsync(DeviceData device, string packageName, CancellationToken cancellationToken); - - /// - /// Click BACK button. - /// - /// The device on which to click BACK button. - /// A which can be used to cancel the asynchronous operation. - /// A which represents the asynchronous operation. - Task BackBtnAsync(DeviceData device, CancellationToken cancellationToken); - - /// - /// Click HOME button. - /// - /// The device on which to click HOME button. - /// A which can be used to cancel the asynchronous operation. - /// A which represents the asynchronous operation. - Task HomeBtnAsync(DeviceData device, CancellationToken cancellationToken); } } #endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs b/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs index 5f330da4..41a43fcd 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs @@ -105,42 +105,6 @@ public partial interface IAdbClient /// which has been opened. In all other cases, 0. int CreateForward(DeviceData device, string local, string remote, bool allowRebind); - /// - /// Asks the ADB server to forward local connections from - /// to the address on the . - /// - /// The device on which to forward the connections. - /// - /// The local address to forward. This value can be in one of: - /// - /// - /// tcp:<port>: TCP connection on localhost:<port> - /// - /// - /// local:<path>: Unix local domain socket on <path> - /// - /// - /// - /// - /// The remote address to forward. This value can be in one of: - /// - /// - /// tcp:<port>: TCP connection on localhost:<port> on device - /// - /// - /// local:<path>: Unix local domain socket on <path> on device - /// - /// - /// jdwp:<pid>: JDWP thread on VM process <pid> on device. - /// - /// - /// - /// If set to , the request will fail if there is already a forward - /// connection from . - /// If your requested to start forwarding to local port TCP:0, the port number of the TCP port - /// which has been opened. In all other cases, 0. - int CreateForward(DeviceData device, ForwardSpec local, ForwardSpec remote, bool allowRebind); - /// /// Asks the ADB server to reverse forward local connections from /// to the address on the . @@ -217,6 +181,33 @@ public partial interface IAdbClient /// A entry for each existing reverse forward connection. IEnumerable ListReverseForward(DeviceData device); + /// + /// Executes a command on the adb server. + /// + /// The target of command, such as shell, remount, dev, tcp, local, + /// localreserved, localabstract, jdwp, track-jdwp, sync, reverse and so on. + /// The command to execute. + /// The encoding to use when parsing the command output. + void ExecuteServerCommand(string target, string command, Encoding encoding); + + /// + /// Executes a command on the adb server. + /// + /// The target of command, such as shell, remount, dev, tcp, local, + /// localreserved, localabstract, jdwp, track-jdwp, sync, reverse and so on. + /// The command to execute. + /// The to send command. + /// The encoding to use when parsing the command output. + void ExecuteServerCommand(string target, string command, IAdbSocket socket, Encoding encoding); + + /// + /// Executes a shell command on the device. + /// + /// The command to execute. + /// The device on which to run the command. + /// The encoding to use when parsing the command output. + void ExecuteRemoteCommand(string command, DeviceData device, Encoding encoding); + /// /// Executes a command on the adb server. /// @@ -502,13 +493,6 @@ public partial interface IAdbClient /// The text to send. void SendText(DeviceData device, string text); - /// - /// Clear the input text. The input should be in focus. Use if the element isn't focused. - /// - /// The device on which to clear the input text. - /// The length of text to clear. - void ClearInput(DeviceData device, int charCount); - /// /// Start an Android application on device. /// @@ -522,18 +506,6 @@ public partial interface IAdbClient /// The device on which to stop an application. /// The package name of the application to stop. void StopApp(DeviceData device, string packageName); - - /// - /// Click BACK button. - /// - /// The device on which to click BACK button. - void BackBtn(DeviceData device); - - /// - /// Click HOME button. - /// - /// The device on which to click HOME button. - void HomeBtn(DeviceData device); } /// diff --git a/AdvancedSharpAdbClient/Models/AdbResponse.cs b/AdvancedSharpAdbClient/Models/AdbResponse.cs index 1bfea2b8..aa868475 100644 --- a/AdvancedSharpAdbClient/Models/AdbResponse.cs +++ b/AdvancedSharpAdbClient/Models/AdbResponse.cs @@ -7,15 +7,21 @@ namespace AdvancedSharpAdbClient { /// - /// An Adb Communication Response. + /// The response returned by ADB server. /// - public class AdbResponse + public struct AdbResponse : IEquatable { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the struct. /// public AdbResponse() => Message = string.Empty; + /// + /// Initializes a new instance of the struct. + /// + /// the message of . + public AdbResponse(string message) => Message = message; + /// /// Gets a that represents the OK response sent by ADB. /// @@ -23,7 +29,6 @@ public class AdbResponse { IOSuccess = true, Okay = true, - Message = string.Empty, Timeout = false }; @@ -57,10 +62,9 @@ public class AdbResponse /// /// The error message returned by adb. /// A new object that represents the error. - public static AdbResponse FromError(string message) => new() + public static AdbResponse FromError(string message) => new(message) { IOSuccess = true, - Message = message, Okay = false, Timeout = false }; @@ -70,18 +74,29 @@ public class AdbResponse /// /// The to compare with the current object. /// if the specified object is equal to the current object; otherwise, . - public override bool Equals(object obj) => + public override readonly bool Equals(object obj) => obj is AdbResponse other && other.IOSuccess == IOSuccess && string.Equals(other.Message, Message, StringComparison.OrdinalIgnoreCase) && other.Okay == Okay && other.Timeout == Timeout; + /// + /// Determines whether the specified is equal to the current object. + /// + /// The to compare with the current object. + /// if the specified object is equal to the current object; otherwise, . + public readonly bool Equals(AdbResponse other) => + other.IOSuccess == IOSuccess + && string.Equals(other.Message, Message, StringComparison.OrdinalIgnoreCase) + && other.Okay == Okay + && other.Timeout == Timeout; + /// /// Gets the hash code for the current . /// /// A hash code for the current . - public override int GetHashCode() + public override readonly int GetHashCode() { int hash = 17; hash = (hash * 23) + IOSuccess.GetHashCode(); @@ -96,6 +111,22 @@ public override int GetHashCode() /// Returns a that represents the current . /// /// OK if the response is an OK response, or Error: {Message} if the response indicates an error. - public override string ToString() => Equals(OK) ? "OK" : $"Error: {Message}"; + public override readonly string ToString() => Equals(OK) ? "OK" : $"Error: {Message}"; + + /// + /// 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 ==(AdbResponse left, AdbResponse 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 !=(AdbResponse left, AdbResponse right) => !left.Equals(right); } } diff --git a/AdvancedSharpAdbClient/Models/Element.cs b/AdvancedSharpAdbClient/Models/Element.cs index 103d00db..8612e85b 100644 --- a/AdvancedSharpAdbClient/Models/Element.cs +++ b/AdvancedSharpAdbClient/Models/Element.cs @@ -54,7 +54,7 @@ public Element(IAdbClient client, DeviceData device, Area area, DictionaryThe children of the element. /// The coordinates and size of the element. /// Gets or sets element attributes. - public Element(IAdbClient client, DeviceData device, XmlNode node, List children, Area area, Dictionary attributes = null) + public Element(IAdbClient client, DeviceData device, XmlNode node, IEnumerable children, Area area, Dictionary attributes = null) { Client = client; Device = device; @@ -75,7 +75,7 @@ public Element(IAdbClient client, DeviceData device, XmlNode node, List /// The children of the element. /// The coordinates and size of the element. /// Gets or sets element attributes. - public Element(IAdbClient client, DeviceData device, Windows.Data.Xml.Dom.IXmlNode node, List children, Area area, Dictionary attributes = null) + public Element(IAdbClient client, DeviceData device, Windows.Data.Xml.Dom.IXmlNode node, IEnumerable children, Area area, Dictionary attributes = null) { XmlDocument doc = new(); doc.LoadXml(node.GetXml()); @@ -113,7 +113,7 @@ public Element(IAdbClient client, DeviceData device, Windows.Data.Xml.Dom.IXmlNo /// /// Gets the children of this element. /// - public List Children { get; } + public IEnumerable Children { get; } /// /// Gets the element attributes. @@ -126,11 +126,12 @@ public Element(IAdbClient client, DeviceData device, Windows.Data.Xml.Dom.IXmlNo public XmlNode Node { get; } /// - /// Gets or sets the element at the specified index. + /// Gets the element at the specified index. /// /// The zero-based index of the element to get or set. /// The element at the specified index. - public Element this[int index] => Children[index]; + /// The index method is index by . + public Element this[int index] => Children.ElementAt(index); /// /// Creates a new with the specified . @@ -151,19 +152,22 @@ public static Element FromXmlNode(IAdbClient client, DeviceData device, XmlNode attributes[at.Name] = at.Value; } Area area = Area.FromLTRB(cords[0], cords[1], cords[2], cords[3]); - XmlNodeList childNodes = xmlNode.ChildNodes; - List elements = new(childNodes?.Count ?? 0); - if (childNodes != null) + IEnumerable FindElements() { - for (int i = 0; i < childNodes.Count; i++) + XmlNodeList childNodes = xmlNode.ChildNodes; + if (childNodes != null) { - Element element = FromXmlNode(client, device, childNodes[i]); - if (element != null) + for (int i = 0; i < childNodes.Count; i++) { - elements.Add(element); + Element element = FromXmlNode(client, device, childNodes[i]); + if (element != null) + { + yield return element; + } } } } + IEnumerable elements = FindElements(); return new Element(client, device, xmlNode, elements, area, attributes); } return null; @@ -189,19 +193,22 @@ public static Element FromIXmlNode(IAdbClient client, DeviceData device, Windows attributes[at.NodeName] = at.NodeValue.ToString(); } Area area = Area.FromLTRB(cords[0], cords[1], cords[2], cords[3]); - Windows.Data.Xml.Dom.XmlNodeList childNodes = xmlNode.ChildNodes; - List elements = new(childNodes?.Count ?? 0); - if (childNodes != null) + IEnumerable FindElements() { - foreach (Windows.Data.Xml.Dom.IXmlNode childNode in childNodes) + Windows.Data.Xml.Dom.XmlNodeList childNodes = xmlNode.ChildNodes; + if (childNodes != null) { - Element element = FromIXmlNode(client, device, childNode); - if (element != null) + foreach (Windows.Data.Xml.Dom.IXmlNode childNode in childNodes) { - elements.Add(element); + Element element = FromIXmlNode(client, device, childNode); + if (element != null) + { + yield return element; + } } } } + IEnumerable elements = FindElements(); return new Element(client, device, xmlNode, elements, area, attributes); } return null; @@ -211,7 +218,7 @@ public static Element FromIXmlNode(IAdbClient client, DeviceData device, Windows /// /// Gets the count of in this element. /// - public virtual int GetChildCount() => Children.Count + Children.Select(x => x.GetChildCount()).Sum(); + public virtual int GetChildCount() => Children.Count() + Children.Select(x => x.GetChildCount()).Sum(); /// /// Clicks on this coordinates. @@ -252,7 +259,7 @@ public async Task SendTextAsync(string text, CancellationToken cancellationToken #endif /// - /// Clear the input text. Use if the element is focused. + /// Clear the input text. Use if the element is focused. /// /// The length of text to clear. public void ClearInput(int charCount = 0) @@ -270,7 +277,7 @@ public void ClearInput(int charCount = 0) #if HAS_TASK /// - /// Clear the input text. Use if the element is focused. + /// Clear the input text. Use if the element is focused. /// /// The length of text to clear. /// A which can be used to cancel the asynchronous operation. diff --git a/AdvancedSharpAdbClient/Models/ForwardSpec.cs b/AdvancedSharpAdbClient/Models/ForwardSpec.cs index 0482df36..62dce560 100644 --- a/AdvancedSharpAdbClient/Models/ForwardSpec.cs +++ b/AdvancedSharpAdbClient/Models/ForwardSpec.cs @@ -11,7 +11,7 @@ namespace AdvancedSharpAdbClient /// /// Represents an adb forward specification as used by the various adb port forwarding functions. /// - public class ForwardSpec + public class ForwardSpec : IEquatable { /// /// Provides a mapping between a and a @@ -143,9 +143,23 @@ public override int GetHashCode() => ^ (SocketName == null ? 1 : SocketName.GetHashCode()); /// - public override bool Equals(object obj) - { - return obj is ForwardSpec other + public override bool Equals(object obj) => + obj is ForwardSpec other + && other.Protocol == Protocol + && Protocol switch + { + ForwardProtocol.JavaDebugWireProtocol => ProcessId == other.ProcessId, + ForwardProtocol.Tcp => Port == other.Port, + ForwardProtocol.LocalAbstract + or ForwardProtocol.LocalFilesystem + or ForwardProtocol.LocalReserved + or ForwardProtocol.Device => string.Equals(SocketName, other.SocketName), + _ => false, + }; + + /// + public bool Equals(ForwardSpec other) => + other != null && other.Protocol == Protocol && Protocol switch { @@ -157,6 +171,5 @@ or ForwardProtocol.LocalReserved or ForwardProtocol.Device => string.Equals(SocketName, other.SocketName), _ => false, }; - } } } diff --git a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs index 1682c86d..50d4a7af 100644 --- a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs +++ b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs @@ -7,6 +7,10 @@ using System.Runtime.InteropServices; using System.Text; +#if WINDOWS_UWP +using System.Threading; +#endif + namespace AdvancedSharpAdbClient { /// From cb48a7a25d2705bd078f46fa5a35b864238e1df9 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Mon, 9 Oct 2023 17:17:03 +0800 Subject: [PATCH 32/66] Improve Element --- .../AdbClientTests.Async.cs | 12 +- .../AdbClientTests.cs | 15 +- AdvancedSharpAdbClient/AdbClient.Async.cs | 2 +- AdvancedSharpAdbClient/AdbClient.cs | 2 +- AdvancedSharpAdbClient/Models/AdbResponse.cs | 7 +- AdvancedSharpAdbClient/Models/Element.cs | 349 +++++++++++------- AdvancedSharpAdbClient/Models/ForwardSpec.cs | 18 +- 7 files changed, 236 insertions(+), 169 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs index b48dbcfc..7c5388a7 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs @@ -1223,9 +1223,11 @@ public async void FindElementAsyncTest() () => TestClient.FindElementAsync(Device)); Assert.Equal(144, element.GetChildCount()); - element = element[0][0][0][0][0][0][0][0][2][1][0][0]; - Assert.Equal("where-where", element.Attributes["text"]); - Assert.Equal(Area.FromLTRB(45, 889, 427, 973), element.Area); + Element child = element[0][0][0][0][0][0][0][0][2][1][0][0]; + Assert.Equal("where-where", child.Text); + Assert.Equal(Area.FromLTRB(45, 889, 427, 973), child.Bounds); + Assert.Equal(child, element.FindDescendantOrSelf(x => x.Text == "where-where")); + Assert.Equal(2, element.FindDescendants().Where(x => x.Text == "where-where").Count()); } /// @@ -1256,7 +1258,7 @@ public async void FindElementsAsyncTest() Assert.Equal(145, childCount); Element element = elements[0][0][0][0][0][0][0][0][0][2][1][0][0]; Assert.Equal("where-where", element.Attributes["text"]); - Assert.Equal(Area.FromLTRB(45, 889, 427, 973), element.Area); + Assert.Equal(Area.FromLTRB(45, 889, 427, 973), element.Bounds); } /// @@ -1295,7 +1297,7 @@ public async void FindAsyncElementsTest() Assert.Equal(145, childCount); Element element = elements[0][0][0][0][0][0][0][0][0][2][1][0][0]; Assert.Equal("where-where", element.Attributes["text"]); - Assert.Equal(Area.FromLTRB(45, 889, 427, 973), element.Area); + Assert.Equal(Area.FromLTRB(45, 889, 427, 973), element.Bounds); } /// diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs index a4611906..06f8fb4f 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs @@ -1333,9 +1333,14 @@ public void FindElementTest() () => TestClient.FindElement(Device)); Assert.Equal(144, element.GetChildCount()); - element = element[0][0][0][0][0][0][0][0][2][1][0][0]; - Assert.Equal("where-where", element.Attributes["text"]); - Assert.Equal(Area.FromLTRB(45, 889, 427, 973), element.Area); + Element child = element[0][0][0][0][0][0][0][0][2][1][0][0]; + Assert.Equal("where-where", child.Text); + Assert.Equal("android.widget.TextView", child.Class); + Assert.Equal("com.bilibili.app.in", child.Package); + Assert.Equal("com.bilibili.app.in:id/header_info_name", child.ResourceID); + Assert.Equal(Area.FromLTRB(45, 889, 427, 973), child.Bounds); + Assert.Equal(child, element.FindDescendantOrSelf(x => x.Text == "where-where")); + Assert.Equal(2, element.FindDescendants().Where(x => x.Text == "where-where").Count()); } /// @@ -1365,8 +1370,8 @@ public void FindElementsTest() Array.ForEach(elements, x => childCount += x.GetChildCount()); Assert.Equal(145, childCount); Element element = elements[0][0][0][0][0][0][0][0][0][2][1][0][0]; - Assert.Equal("where-where", element.Attributes["text"]); - Assert.Equal(Area.FromLTRB(45, 889, 427, 973), element.Area); + Assert.Equal("where-where", element.Text); + Assert.Equal(Area.FromLTRB(45, 889, 427, 973), element.Bounds); } /// diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index b404e5d3..94a301ff 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -774,7 +774,7 @@ public async Task SwipeAsync(DeviceData device, Element first, Element second, l using IAdbSocket socket = adbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); - await socket.SendAdbRequestAsync($"shell:input swipe {first.Cords.X} {first.Cords.Y} {second.Cords.X} {second.Cords.Y} {speed}", cancellationToken).ConfigureAwait(false); + await socket.SendAdbRequestAsync($"shell:input swipe {first.Center.X} {first.Center.Y} {second.Center.X} {second.Center.Y} {speed}", cancellationToken).ConfigureAwait(false); AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); using StreamReader reader = new(socket.GetShellStream(), Encoding); string result = await reader.ReadToEndAsync(cancellationToken).ContinueWith(x => x.Result.TrimStart()).ConfigureAwait(false); diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index 053a9770..33177cb4 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -923,7 +923,7 @@ public void Swipe(DeviceData device, Element first, Element second, long speed) using IAdbSocket socket = adbSocketFactory(EndPoint); socket.SetDevice(device); - socket.SendAdbRequest($"shell:input swipe {first.Cords.X} {first.Cords.Y} {second.Cords.X} {second.Cords.Y} {speed}"); + socket.SendAdbRequest($"shell:input swipe {first.Center.X} {first.Center.Y} {second.Center.X} {second.Center.Y} {speed}"); AdbResponse response = socket.ReadAdbResponse(); using StreamReader reader = new(socket.GetShellStream(), Encoding); string result = reader.ReadToEnd().TrimStart(); diff --git a/AdvancedSharpAdbClient/Models/AdbResponse.cs b/AdvancedSharpAdbClient/Models/AdbResponse.cs index aa868475..704ff25d 100644 --- a/AdvancedSharpAdbClient/Models/AdbResponse.cs +++ b/AdvancedSharpAdbClient/Models/AdbResponse.cs @@ -74,12 +74,7 @@ public struct AdbResponse : IEquatable /// /// The to compare with the current object. /// if the specified object is equal to the current object; otherwise, . - public override readonly bool Equals(object obj) => - obj is AdbResponse other - && other.IOSuccess == IOSuccess - && string.Equals(other.Message, Message, StringComparison.OrdinalIgnoreCase) - && other.Okay == Okay - && other.Timeout == Timeout; + public override readonly bool Equals(object obj) => obj is AdbResponse other && Equals(other); /// /// Determines whether the specified is equal to the current object. diff --git a/AdvancedSharpAdbClient/Models/Element.cs b/AdvancedSharpAdbClient/Models/Element.cs index 8612e85b..4c64b58d 100644 --- a/AdvancedSharpAdbClient/Models/Element.cs +++ b/AdvancedSharpAdbClient/Models/Element.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; using System.Linq; using System.Threading; @@ -12,22 +13,9 @@ namespace AdvancedSharpAdbClient /// /// Implement of screen element, likes Selenium. /// - public class Element + public class Element : IEquatable { - /// - /// Initializes a new instance of the class. - /// - /// The current ADB client that manages the connection. - /// The current device containing the element. - /// The coordinates of the element to click. - /// Gets or sets element attributes. - public Element(IAdbClient client, DeviceData device, Cords cords, Dictionary attributes = null) - { - Client = client; - Device = device; - Cords = cords; - Attributes = attributes; - } + private static readonly char[] separator = ['[', ']', ',', ' ']; /// /// Initializes a new instance of the class. @@ -40,9 +28,8 @@ public Element(IAdbClient client, DeviceData device, Area area, Dictionary @@ -50,19 +37,41 @@ public Element(IAdbClient client, DeviceData device, Area area, Dictionary /// The current ADB client that manages the connection. /// The current device containing the element. - /// The of the element. - /// The children of the element. - /// The coordinates and size of the element. - /// Gets or sets element attributes. - public Element(IAdbClient client, DeviceData device, XmlNode node, IEnumerable children, Area area, Dictionary attributes = null) + /// The of the element. + public Element(IAdbClient client, DeviceData device, XmlNode xmlNode) { Client = client; Device = device; - Node = node; - Children = children; - Area = area; - Attributes = attributes; - Cords = area.Center; // Average x1, y1, x2, y2 + Node = xmlNode; + + if (xmlNode.Attributes["bounds"]?.Value is string bounds) + { + string[] cords = bounds.Split(separator, StringSplitOptions.RemoveEmptyEntries); // x1, y1, x2, y2 + Bounds = Area.FromLTRB(int.Parse(cords[0]), int.Parse(cords[1]), int.Parse(cords[2]), int.Parse(cords[3])); + } + + Attributes = new(xmlNode.Attributes.Count); + foreach (XmlAttribute at in xmlNode.Attributes) + { + Attributes[at.Name] = at.Value; + } + + IEnumerable FindElements() + { + XmlNodeList childNodes = xmlNode.ChildNodes; + if (childNodes != null) + { + for (int i = 0; i < childNodes.Count; i++) + { + Element element = FromXmlNode(client, device, childNodes[i]); + if (element != null) + { + yield return element; + } + } + } + } + Children = FindElements(); } #if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER @@ -71,22 +80,44 @@ public Element(IAdbClient client, DeviceData device, XmlNode node, IEnumerable /// The current ADB client that manages the connection. /// The current device containing the element. - /// The of the element. - /// The children of the element. - /// The coordinates and size of the element. - /// Gets or sets element attributes. - public Element(IAdbClient client, DeviceData device, Windows.Data.Xml.Dom.IXmlNode node, IEnumerable children, Area area, Dictionary attributes = null) + /// The of the element. + public Element(IAdbClient client, DeviceData device, Windows.Data.Xml.Dom.IXmlNode xmlNode) { XmlDocument doc = new(); - doc.LoadXml(node.GetXml()); + doc.LoadXml(xmlNode.GetXml()); Client = client; Device = device; Node = doc.FirstChild; - Children = children; - Area = area; - Attributes = attributes; - Cords = area.Center; // Average x1, y1, x2, y2 + + if (xmlNode.Attributes?.GetNamedItem("bounds")?.NodeValue?.ToString() is string bounds) + { + string[] cords = bounds.Split(separator, StringSplitOptions.RemoveEmptyEntries); // x1, y1, x2, y2 + Bounds = Area.FromLTRB(int.Parse(cords[0]), int.Parse(cords[1]), int.Parse(cords[2]), int.Parse(cords[3])); + } + + Attributes = new(xmlNode.Attributes.Count); + foreach (Windows.Data.Xml.Dom.IXmlNode at in xmlNode.Attributes) + { + Attributes[at.NodeName] = at.NodeValue?.ToString(); + } + + IEnumerable FindElements() + { + Windows.Data.Xml.Dom.XmlNodeList childNodes = xmlNode.ChildNodes; + if (childNodes != null) + { + foreach (Windows.Data.Xml.Dom.IXmlNode childNode in childNodes) + { + Element element = FromIXmlNode(client, device, childNode); + if (element != null) + { + yield return element; + } + } + } + } + Children = FindElements(); } #endif @@ -103,12 +134,7 @@ public Element(IAdbClient client, DeviceData device, Windows.Data.Xml.Dom.IXmlNo /// /// Gets the coordinates and size of the element. /// - public Area Area { get; } - - /// - /// Gets or sets the coordinates of the element to click. Default is the center of area. - /// - public Cords Cords { get; set; } + public Area Bounds { get; } /// /// Gets the children of this element. @@ -125,6 +151,31 @@ public Element(IAdbClient client, DeviceData device, Windows.Data.Xml.Dom.IXmlNo /// public XmlNode Node { get; } + /// + /// Gets the coordinates of the the center of the element. + /// + public Cords Center => Bounds.Center; + + /// + /// Gets the text of the element. + /// + public string Text => Attributes.TryGetValue("text", out string text) ? text : string.Empty; + + /// + /// Gets the class name of the element. + /// + public string Class => Attributes.TryGetValue("class", out string @class) ? @class : string.Empty; + + /// + /// Gets the package name of the element. + /// + public string Package => Attributes.TryGetValue("package", out string package) ? package : string.Empty; + + /// + /// Gets the resource ID of the element. + /// + public string ResourceID => Attributes.TryGetValue("resource-id", out string resource_id) ? resource_id : string.Empty; + /// /// Gets the element at the specified index. /// @@ -140,38 +191,8 @@ public Element(IAdbClient client, DeviceData device, Windows.Data.Xml.Dom.IXmlNo /// The current device containing the element. /// The of the element. /// The new that this method creates. - public static Element FromXmlNode(IAdbClient client, DeviceData device, XmlNode xmlNode) - { - string bounds = xmlNode.Attributes["bounds"].Value; - if (bounds != null) - { - int[] cords = bounds.Replace("][", ",").Replace("[", "").Replace("]", "").Split(',').Select(int.Parse).ToArray(); // x1, y1, x2, y2 - Dictionary attributes = new(xmlNode.Attributes.Count); - foreach (XmlAttribute at in xmlNode.Attributes) - { - attributes[at.Name] = at.Value; - } - Area area = Area.FromLTRB(cords[0], cords[1], cords[2], cords[3]); - IEnumerable FindElements() - { - XmlNodeList childNodes = xmlNode.ChildNodes; - if (childNodes != null) - { - for (int i = 0; i < childNodes.Count; i++) - { - Element element = FromXmlNode(client, device, childNodes[i]); - if (element != null) - { - yield return element; - } - } - } - } - IEnumerable elements = FindElements(); - return new Element(client, device, xmlNode, elements, area, attributes); - } - return null; - } + public static Element FromXmlNode(IAdbClient client, DeviceData device, XmlNode xmlNode) => + xmlNode.Attributes["bounds"] != null ? new Element(client, device, xmlNode) : null; #if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER /// @@ -181,38 +202,8 @@ IEnumerable FindElements() /// The current device containing the element. /// The of the element. /// The new that this method creates. - public static Element FromIXmlNode(IAdbClient client, DeviceData device, Windows.Data.Xml.Dom.IXmlNode xmlNode) - { - string bounds = xmlNode.Attributes?.GetNamedItem("bounds")?.NodeValue?.ToString(); - if (bounds != null) - { - int[] cords = bounds.Replace("][", ",").Replace("[", "").Replace("]", "").Split(',').Select(int.Parse).ToArray(); // x1, y1, x2, y2 - Dictionary attributes = new(xmlNode.Attributes.Count); - foreach (Windows.Data.Xml.Dom.IXmlNode at in xmlNode.Attributes) - { - attributes[at.NodeName] = at.NodeValue.ToString(); - } - Area area = Area.FromLTRB(cords[0], cords[1], cords[2], cords[3]); - IEnumerable FindElements() - { - Windows.Data.Xml.Dom.XmlNodeList childNodes = xmlNode.ChildNodes; - if (childNodes != null) - { - foreach (Windows.Data.Xml.Dom.IXmlNode childNode in childNodes) - { - Element element = FromIXmlNode(client, device, childNode); - if (element != null) - { - yield return element; - } - } - } - } - IEnumerable elements = FindElements(); - return new Element(client, device, xmlNode, elements, area, attributes); - } - return null; - } + public static Element FromIXmlNode(IAdbClient client, DeviceData device, Windows.Data.Xml.Dom.IXmlNode xmlNode) => + xmlNode.Attributes?.GetNamedItem("bounds") != null ? new Element(client, device, xmlNode) : null; #endif /// @@ -223,16 +214,7 @@ IEnumerable FindElements() /// /// Clicks on this coordinates. /// - public void Click() => Client.Click(Device, Cords); - -#if HAS_TASK - /// - /// Clicks on this coordinates. - /// - /// A which can be used to cancel the asynchronous operation. - public Task ClickAsync(CancellationToken cancellationToken = default) => - Client.ClickAsync(Device, Cords, cancellationToken); -#endif + public void Click() => Client.Click(Device, Center); /// /// Send text to device. Doesn't support Russian. @@ -244,7 +226,29 @@ public void SendText(string text) Client.SendText(Device, text); } + /// + /// Clear the input text. Use if the element is focused. + /// + public void ClearInput() => ClearInput(Text.Length); + + /// + /// Clear the input text. Use if the element is focused. + /// + /// The length of text to clear. + public void ClearInput(int charCount) + { + Click(); // focuses + Client.ClearInput(Device, charCount); + } + #if HAS_TASK + /// + /// Clicks on this coordinates. + /// + /// A which can be used to cancel the asynchronous operation. + public Task ClickAsync(CancellationToken cancellationToken = default) => + Client.ClickAsync(Device, Center, cancellationToken); + /// /// Send text to device. Doesn't support Russian. /// @@ -256,44 +260,113 @@ public async Task SendTextAsync(string text, CancellationToken cancellationToken await ClickAsync(cancellationToken).ConfigureAwait(false); await Client.SendTextAsync(Device, text, cancellationToken).ConfigureAwait(false); } -#endif /// - /// Clear the input text. Use if the element is focused. + /// Clear the input text. Use if the element is focused. /// - /// The length of text to clear. - public void ClearInput(int charCount = 0) - { - Click(); // focuses - if (charCount == 0) - { - Client.ClearInput(Device, Attributes["text"].Length); - } - else - { - Client.ClearInput(Device, charCount); - } - } + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + public Task ClearInputAsync(CancellationToken cancellationToken = default) => + ClearInputAsync(Text.Length, cancellationToken); -#if HAS_TASK /// /// Clear the input text. Use if the element is focused. /// /// The length of text to clear. /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. - public async Task ClearInputAsync(int charCount = 0, CancellationToken cancellationToken = default) + public async Task ClearInputAsync(int charCount, CancellationToken cancellationToken = default) { await ClickAsync(cancellationToken).ConfigureAwait(false); // focuses - if (charCount == 0) + await Client.ClearInputAsync(Device, charCount, cancellationToken).ConfigureAwait(false); + } +#endif + + /// + /// Find the first descendant element matching a given predicate, using a depth-first search. + /// + /// The predicate to use to match the descendant nodes. + /// The descendant that was found, or . + public Element FindDescendant(Func predicate) + { + foreach (Element child in Children) { - await Client.ClearInputAsync(Device, Attributes["text"].Length, cancellationToken).ConfigureAwait(false); + if (predicate(child)) + { + return child; + } + + Element descendant = child.FindDescendant(predicate); + + if (descendant != null) + { + return descendant; + } } - else + + return null; + } + + /// + /// Find the first descendant (or self) element matching a given predicate, using a depth-first search. + /// + /// The predicatee to use to match the descendant nodes. + /// The descendant (or self) that was found, or . + public Element FindDescendantOrSelf(Func predicate) => + predicate(this) ? this : FindDescendant(predicate); + + /// + /// Find all descendant elements of the specified element. This method can be chained with + /// LINQ calls to add additional filters or projections on top of the returned results. + /// + /// This method is meant to provide extra flexibility in specific scenarios and it should not + /// be used when only the first item is being looked for. In those cases, use one of the + /// available overloads instead, which will + /// offer a more compact syntax as well as better performance in those cases. + /// + /// + /// All the descendant instance from . + public IEnumerable FindDescendants() + { + foreach (Element child in Children) { - await Client.ClearInputAsync(Device, charCount, cancellationToken).ConfigureAwait(false); + yield return child; + + foreach (Element childOfChild in child.FindDescendants()) + { + yield return childOfChild; + } } } + + /// + public override bool Equals(object obj) => Equals(obj as Element); + + /// + public bool Equals(Element other) => + other is not null + && Client == other.Client + && Device == other.Device + && Node == null + ? Bounds == other.Bounds + && Attributes == other.Attributes + : Node == other.Node; + + /// + public override int GetHashCode() => +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + HashCode.Combine(Client, Device, Node == null ? HashCode.Combine(Bounds, Attributes) : Node.GetHashCode()); +#else + Client.GetHashCode() + ^ Device.GetHashCode() + ^ (Node == null + ? Bounds.GetHashCode() + ^ Attributes.GetHashCode() + : Node.GetHashCode()); #endif + + /// + public override string ToString() => + string.IsNullOrEmpty(Text) ? string.IsNullOrEmpty(Class) ? base.ToString() : Class : Text; } } diff --git a/AdvancedSharpAdbClient/Models/ForwardSpec.cs b/AdvancedSharpAdbClient/Models/ForwardSpec.cs index 62dce560..9e8fbb86 100644 --- a/AdvancedSharpAdbClient/Models/ForwardSpec.cs +++ b/AdvancedSharpAdbClient/Models/ForwardSpec.cs @@ -137,25 +137,17 @@ or ForwardProtocol.LocalReserved /// public override int GetHashCode() => +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + HashCode.Combine((int)Protocol, Port, ProcessId, SocketName == null ? 1 : SocketName.GetHashCode()); +#else (int)Protocol ^ Port ^ ProcessId ^ (SocketName == null ? 1 : SocketName.GetHashCode()); +#endif /// - public override bool Equals(object obj) => - obj is ForwardSpec other - && other.Protocol == Protocol - && Protocol switch - { - ForwardProtocol.JavaDebugWireProtocol => ProcessId == other.ProcessId, - ForwardProtocol.Tcp => Port == other.Port, - ForwardProtocol.LocalAbstract - or ForwardProtocol.LocalFilesystem - or ForwardProtocol.LocalReserved - or ForwardProtocol.Device => string.Equals(SocketName, other.SocketName), - _ => false, - }; + public override bool Equals(object obj) => Equals(obj as ForwardSpec); /// public bool Equals(ForwardSpec other) => From 2f9fbb850155592e37783391cddfb5911560621a Mon Sep 17 00:00:00 2001 From: wherewhere Date: Tue, 10 Oct 2023 17:39:02 +0800 Subject: [PATCH 33/66] Add init and record --- .../Receivers/PackageManagerReceiverTests.cs | 2 +- .../Dummys/DeviceMonitorSink.cs | 10 +- .../Dummys/DummyAdbClient.cs | 6 +- .../Dummys/DummySyncService.cs | 2 +- .../Models/AdbServerStatusTests.cs | 2 +- .../SocketBasedTests.cs | 2 +- AdvancedSharpAdbClient/AdbSocket.Async.cs | 18 ++- AdvancedSharpAdbClient/AdbSocket.cs | 18 ++- .../DeviceCommands/Models/AndroidProcess.cs | 104 +++++++++--------- .../Models/InstallProgressEventArgs.cs | 6 +- .../DeviceCommands/Models/VersionInfo.cs | 10 +- .../DeviceCommands/PackageManager.Async.cs | 2 +- .../DeviceCommands/PackageManager.cs | 6 +- .../Receivers/EnvironmentVariablesReceiver.cs | 4 +- .../Receivers/GetPropReceiver.cs | 2 +- .../Receivers/PackageManagerReceiver.cs | 7 +- .../Receivers/ProcessOutputReceiver.cs | 2 +- AdvancedSharpAdbClient/DeviceMonitor.cs | 18 ++- .../Exceptions/AdbException.cs | 4 +- .../Exceptions/JavaException.cs | 4 +- .../Extensions/Attributes/IsExternalInit.cs | 18 +++ AdvancedSharpAdbClient/Models/AdbResponse.cs | 10 +- .../Models/AdbServerStatus.cs | 10 +- AdvancedSharpAdbClient/Models/ColorData.cs | 6 +- AdvancedSharpAdbClient/Models/DeviceData.cs | 18 +-- AdvancedSharpAdbClient/Models/Element.cs | 14 ++- AdvancedSharpAdbClient/Models/ForwardData.cs | 6 +- AdvancedSharpAdbClient/Models/ForwardSpec.cs | 8 +- AdvancedSharpAdbClient/Models/Framebuffer.cs | 2 +- .../Models/FramebufferHeader.cs | 22 ++-- AdvancedSharpAdbClient/SyncService.cs | 2 +- README.md | 49 +++++---- 32 files changed, 208 insertions(+), 186 deletions(-) create mode 100644 AdvancedSharpAdbClient/Extensions/Attributes/IsExternalInit.cs diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/PackageManagerReceiverTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/PackageManagerReceiverTests.cs index cf944bc2..3473a896 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/PackageManagerReceiverTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/PackageManagerReceiverTests.cs @@ -17,7 +17,7 @@ public void ParseThirdPartyPackage() DummyAdbClient client = new(); PackageManager manager = new(client, device, thirdPartyOnly: false, syncServiceFactory: null, skipInit: true); - PackageManagerReceiver receiver = new(device, manager); + PackageManagerReceiver receiver = new(manager); // Act receiver.AddOutput("package:/data/app/com.google.android.apps.plus-qQaDuXCpNqJuQSbIS6OxGA==/base.apk=com.google.android.apps.plus"); diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DeviceMonitorSink.cs b/AdvancedSharpAdbClient.Tests/Dummys/DeviceMonitorSink.cs index b54bd6b9..0dc88c0d 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DeviceMonitorSink.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DeviceMonitorSink.cs @@ -28,15 +28,15 @@ public void ResetSignals() DisconnectedEvents.Clear(); } - public List DisconnectedEvents { get; private set; } + public List DisconnectedEvents { get; init; } - public List ConnectedEvents { get; private set; } + public List ConnectedEvents { get; init; } - public List NotifiedEvents { get; private set; } + public List NotifiedEvents { get; init; } - public List ChangedEvents { get; private set; } + public List ChangedEvents { get; init; } - public DeviceMonitor Monitor { get; private set; } + public DeviceMonitor Monitor { get; init; } public ManualResetEvent CreateEventSignal() { diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs index 90fbf439..f935a6d0 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs @@ -12,11 +12,11 @@ namespace AdvancedSharpAdbClient.Tests { internal class DummyAdbClient : IAdbClient { - public Dictionary Commands { get; private set; } = new Dictionary(); + public Dictionary Commands { get; } = new Dictionary(); - public List ReceivedCommands { get; private set; } = new List(); + public List ReceivedCommands { get; } = new List(); - public EndPoint EndPoint { get; private set; } + public EndPoint EndPoint { get; init; } public void ExecuteRemoteCommand(string command, DeviceData device, Encoding encoding) => ExecuteServerCommand("shell", command, encoding); diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs index 8a768137..8d2cf25f 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs @@ -8,7 +8,7 @@ namespace AdvancedSharpAdbClient.Tests { internal class DummySyncService : ISyncService { - public Dictionary UploadedFiles { get; private set; } = new Dictionary(); + public Dictionary UploadedFiles { get; } = new Dictionary(); public bool IsOpen { get; private set; } = true; diff --git a/AdvancedSharpAdbClient.Tests/Models/AdbServerStatusTests.cs b/AdvancedSharpAdbClient.Tests/Models/AdbServerStatusTests.cs index 2c95bcf1..bd9f8f7c 100644 --- a/AdvancedSharpAdbClient.Tests/Models/AdbServerStatusTests.cs +++ b/AdvancedSharpAdbClient.Tests/Models/AdbServerStatusTests.cs @@ -13,7 +13,7 @@ public void ToStringTest() { AdbServerStatus status = new(true, new Version(1, 0, 32)); Assert.Equal("Version 1.0.32 of the adb daemon is running.", status.ToString()); - status.IsRunning = false; + status = status with { IsRunning = false }; Assert.Equal("The adb daemon is not running.", status.ToString()); } } diff --git a/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs b/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs index 3f8d45af..fbdcfb87 100644 --- a/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs +++ b/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs @@ -58,7 +58,7 @@ protected SocketBasedTests(bool integrationTest, bool doDispose) State = DeviceState.Online }; - protected AdbClient TestClient { get; private set; } + protected AdbClient TestClient { get; init; } protected IDummyAdbSocket Socket { get; set; } diff --git a/AdvancedSharpAdbClient/AdbSocket.Async.cs b/AdvancedSharpAdbClient/AdbSocket.Async.cs index 4ece5902..72e0020a 100644 --- a/AdvancedSharpAdbClient/AdbSocket.Async.cs +++ b/AdvancedSharpAdbClient/AdbSocket.Async.cs @@ -375,23 +375,19 @@ protected virtual async Task WriteAsync(byte[] data, CancellationToken can /// A that represents the response received from ADB. protected virtual async Task ReadAdbResponseInnerAsync(CancellationToken cancellationToken = default) { - AdbResponse rasps = new(); - byte[] reply = new byte[4]; _ = await ReadAsync(reply, cancellationToken).ConfigureAwait(false); - rasps.IOSuccess = true; - - rasps.Okay = IsOkay(reply); - - if (!rasps.Okay) + if (IsOkay(reply)) + { + return AdbResponse.OK; + } + else { string message = await ReadStringAsync(cancellationToken).ConfigureAwait(false); - rasps.Message = message; - logger.LogError("Got reply '{0}', diag='{1}'", ReplyToString(reply), rasps.Message); + logger.LogError("Got reply '{0}', diag='{1}'", ReplyToString(reply), message); + return AdbResponse.FromError(message); } - - return rasps; } } } diff --git a/AdvancedSharpAdbClient/AdbSocket.cs b/AdvancedSharpAdbClient/AdbSocket.cs index 7668c023..bc5817ec 100644 --- a/AdvancedSharpAdbClient/AdbSocket.cs +++ b/AdvancedSharpAdbClient/AdbSocket.cs @@ -470,23 +470,19 @@ protected virtual bool Write(byte[] data) /// A that represents the response received from ADB. protected virtual AdbResponse ReadAdbResponseInner() { - AdbResponse rasps = new(); - byte[] reply = new byte[4]; Read(reply); - rasps.IOSuccess = true; - - rasps.Okay = IsOkay(reply); - - if (!rasps.Okay) + if (IsOkay(reply)) + { + return AdbResponse.OK; + } + else { string message = ReadString(); - rasps.Message = message; - logger.LogError("Got reply '{0}', diag='{1}'", ReplyToString(reply), rasps.Message); + logger.LogError("Got reply '{0}', diag='{1}'", ReplyToString(reply), message); + return AdbResponse.FromError(message); } - - return rasps; } /// diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs index e8176785..1efe67d9 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs @@ -296,32 +296,32 @@ public AndroidProcess(string line, bool cmdLinePrefix = false) /// /// Gets or sets the state of the process. /// - public AndroidProcessState State { get; set; } + public AndroidProcessState State { get; init; } /// /// Gets or sets the name of the process, including arguments, if any. /// - public string Name { get; set; } + public string Name { get; init; } /// /// Gets or sets the parent Process ID number. /// - public int ParentProcessId { get; set; } + public int ParentProcessId { get; init; } /// /// Gets or sets the Process Group ID number. /// - public int ProcessGroupId { get; set; } + public int ProcessGroupId { get; init; } /// /// Gets or sets the session ID of the process number. /// - public int SessionID { get; set; } + public int SessionID { get; init; } /// /// Gets or sets the Process ID number. /// - public int ProcessId { get; set; } + public int ProcessId { get; init; } /// /// Gets or sets the controlling terminal of the process. @@ -329,40 +329,40 @@ public AndroidProcess(string line, bool cmdLinePrefix = false) /// The minor device number is contained in the combination of bits /// to and to ; /// the major device number is in bits to . - public int TTYNumber { get; set; } + public int TTYNumber { get; init; } /// /// Gets or sets the ID of the foreground process group of the controlling terminal of the process. /// - public int TopProcessGroupId { get; set; } + public int TopProcessGroupId { get; init; } /// /// Gets or sets The kernel flags word of the process. For bit meanings, see the PF_* defines /// in the Linux kernel source file include/linux/sched.h. Details depend on the kernel version. /// - public PerProcessFlags Flags { get; set; } + public PerProcessFlags Flags { get; init; } /// /// Gets or sets the number of minor faults the process has made /// which have not required loading a memory page from disk. /// - public ulong MinorFaults { get; set; } + public ulong MinorFaults { get; init; } /// /// Gets or sets the number of minor faults that the process's waited-for children have made. /// - public ulong ChildMinorFaults { get; set; } + public ulong ChildMinorFaults { get; init; } /// /// Gets or sets the number of major faults the process has made /// which have required loading a memory page from disk. /// - public ulong MajorFaults { get; set; } + public ulong MajorFaults { get; init; } /// /// Gets or sets the number of major faults that the process's waited-for children have made. /// - public ulong ChildMajorFaults { get; set; } + public ulong ChildMajorFaults { get; init; } /// /// Gets or sets the amount of time that this process has been scheduled in user mode, @@ -370,26 +370,26 @@ public AndroidProcess(string line, bool cmdLinePrefix = false) /// guest_time (time spent running a virtual CPU, see below), so that applications /// that are not aware of the guest time field do not lose that time from their calculations. /// - public ulong UserScheduledTime { get; set; } + public ulong UserScheduledTime { get; init; } /// /// Gets or sets the amount of time that this process has been scheduled in kernel mode, /// measured in clock ticks (divide by sysconf(_SC_CLK_TCK)). /// - public ulong ScheduledTime { get; set; } + public ulong ScheduledTime { get; init; } /// /// Gets or sets the amount of time that this process's waited-for children have been scheduled in user mode, /// measured in clock ticks (divide by sysconf(_SC_CLK_TCK)). (See also times(2).) /// This includes guest time, cguest_time (time spent running a virtual CPU, see below). /// - public long ChildUserScheduledTime { get; set; } + public long ChildUserScheduledTime { get; init; } /// /// Gets or sets the Amount of time that this process's waited-for children have been scheduled in kernel mode, /// measured in clock ticks (divide by sysconf(_SC_CLK_TCK)). /// - public long ChildScheduledTime { get; set; } + public long ChildScheduledTime { get; init; } /// /// Gets or sets the value for processes running a real-time scheduling policy @@ -401,25 +401,25 @@ public AndroidProcess(string line, bool cmdLinePrefix = false) /// in the range (high) to (low), /// corresponding to the user-visible nice range of to . /// - public long Priority { get; set; } + public long Priority { get; init; } /// /// Gets or sets the nice value (see setpriority(2)), a value in the range /// (low priority) to (high priority). /// - public long Nice { get; set; } + public long Nice { get; init; } /// /// Gets or sets the number of threads in this process (since Linux 2.6). /// /// Before kernel 2.6, this field was hard coded to 0 as a placeholder for an earlier removed field. - public long ThreadsNumber { get; set; } + public long ThreadsNumber { get; init; } /// /// Gets or sets the time in jiffies before the next SIGALRM is sent to the process due to an interval timer. /// /// Since kernel 2.6.17, this field is no longer maintained, and is hard coded as 0. - public long Interval { get; set; } + public long Interval { get; init; } /// /// Gets or sets The time the process started after system boot. In kernels before Linux 2.6, @@ -427,12 +427,12 @@ public AndroidProcess(string line, bool cmdLinePrefix = false) /// (divide by sysconf(_SC_CLK_TCK)). /// /// The format for this field was %lu before Linux 2.6. - public ulong StartTime { get; set; } + public ulong StartTime { get; init; } /// /// Gets or sets total virtual memory size in bytes. /// - public ulong VirtualSize { get; set; } + public ulong VirtualSize { get; init; } /// /// Gets or sets the process resident set size. @@ -441,62 +441,62 @@ public AndroidProcess(string line, bool cmdLinePrefix = false) /// This is just the pages which count toward text, data, or stack space. /// This does not include pages which have not been demand-loaded in, or which are swapped out. /// This value is inaccurate; see /proc/[pid]/statm below. - public int ResidentSetSize { get; set; } + public int ResidentSetSize { get; init; } /// /// Gets or sets current soft limit in bytes on the rss of the process; /// See the description of RLIMIT_RSS in getrlimit(2). /// - public ulong ResidentSetSizeLimit { get; set; } + public ulong ResidentSetSizeLimit { get; init; } /// /// Gets or sets the address above which program text can run. /// - public ulong StartCode { get; set; } + public ulong StartCode { get; init; } /// /// Gets or sets the address below which program text can run. /// - public ulong EndCode { get; set; } + public ulong EndCode { get; init; } /// /// Gets or sets the address of the start (i.e., bottom) of the stack. /// - public ulong StartStack { get; set; } + public ulong StartStack { get; init; } /// /// Gets or sets the current value of ESP (stack pointer), as found in the kernel stack page for the process. /// - public ulong ESP { get; set; } + public ulong ESP { get; init; } /// /// Gets or sets the current EIP (instruction pointer). /// - public ulong EIP { get; set; } + public ulong EIP { get; init; } /// /// Gets or sets the bitmap of pending signals, displayed as a decimal number. Obsolete, /// because it does not provide information on real-time signals; Use /proc/[pid]/status instead. /// - public ulong Signal { get; set; } + public ulong Signal { get; init; } /// /// Gets or sets the bitmap of blocked signals, displayed as a decimal number. Obsolete, /// because it does not provide information on real-time signals; Use /proc/[pid]/status instead. /// - public ulong Blocked { get; set; } + public ulong Blocked { get; init; } /// /// Gets or sets the bitmap of ignored signals, displayed as a decimal number. Obsolete, /// because it does not provide information on real-time signals; Use /proc/[pid]/status instead. /// - public ulong IgnoredSignals { get; set; } + public ulong IgnoredSignals { get; init; } /// /// Gets or sets the bitmap of caught signals, displayed as a decimal number. Obsolete, /// because it does not provide information on real-time signals; Use /proc/[pid]/status instead. /// - public ulong CaughtSignals { get; set; } + public ulong CaughtSignals { get; init; } /// /// Gets or sets the memory address of the event the process is waiting for. @@ -504,95 +504,95 @@ public AndroidProcess(string line, bool cmdLinePrefix = false) /// This is the "channel" in which the process is waiting. /// It is the address of a location in the kernel where the process is sleeping. /// The corresponding symbolic name can be found in /proc/[pid]/wchan. - public ulong WChan { get; set; } + public ulong WChan { get; init; } /// /// Gets or sets the number of pages swapped (not maintained). /// - public ulong SwappedPagesNumber { get; set; } + public ulong SwappedPagesNumber { get; init; } /// /// Gets or sets the cumulative number of pages swapped for child processes (not maintained). /// - public ulong CumulativeSwappedPagesNumber { get; set; } + public ulong CumulativeSwappedPagesNumber { get; init; } /// /// Gets or sets the signal to be sent to parent when we die. /// - public int ExitSignal { get; set; } + public int ExitSignal { get; init; } /// /// Gets or sets the CPU number last executed on. /// - public int Processor { get; set; } + public int Processor { get; init; } /// /// Gets or sets the real-time scheduling priority, a number in the range 1 to 99 for processes scheduled /// under a real-time policy, or 0, for non-real-time processes (see sched_setscheduler(2)). /// - public uint RealTimePriority { get; set; } + public uint RealTimePriority { get; init; } /// /// Gets or sets the scheduling policy (see sched_setscheduler(2)). /// Decode using the SCHED_* constants in linux/sched.h. /// - public uint Policy { get; set; } + public uint Policy { get; init; } /// /// Gets or sets the aggregated block I/O delays, measured in clock ticks (centiseconds). /// - public ulong IODelays { get; set; } + public ulong IODelays { get; init; } /// /// Gets or sets the guest time of the process (time spent running a virtual CPU for a guest operating system), /// measured in clock ticks (divide by sysconf(_SC_CLK_TCK)). /// - public ulong GuestTime { get; set; } + public ulong GuestTime { get; init; } /// /// Gets or sets the guest time of the process's children, measured in clock ticks (divide by sysconf(_SC_CLK_TCK)). /// - public long ChildGuestTime { get; set; } + public long ChildGuestTime { get; init; } /// /// Gets or sets the address above which program initialized and uninitialized(BSS) data are placed. /// - public ulong StartData { get; set; } + public ulong StartData { get; init; } /// /// Gets or sets the address below which program initialized and uninitialized(BSS) data are placed. /// - public ulong EndData { get; set; } + public ulong EndData { get; init; } /// /// Gets or sets the address above which program heap can be expanded with brk(2). /// - public ulong StartBrk { get; set; } + public ulong StartBrk { get; init; } /// /// Gets or sets the address above which program command-line arguments (argv) are placed. /// - public ulong ArgStart { get; set; } + public ulong ArgStart { get; init; } /// /// Gets or sets the address below program command-line arguments (argv) are placed. /// - public ulong ArgEnd { get; set; } + public ulong ArgEnd { get; init; } /// /// Gets or sets the address above which program environment is placed. /// - public ulong EnvStart { get; set; } + public ulong EnvStart { get; init; } /// /// Gets or sets the address below which program environment is placed. /// - public ulong EnvEnd { get; set; } + public ulong EnvEnd { get; init; } /// /// Gets or sets the thread's exit status in the form reported by waitpid(2). /// - public int ExitCode { get; set; } + public int ExitCode { get; init; } /// /// Creates a from it representation. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/InstallProgressEventArgs.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/InstallProgressEventArgs.cs index 7d39b6b1..002df30d 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/InstallProgressEventArgs.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/InstallProgressEventArgs.cs @@ -22,7 +22,7 @@ public class InstallProgressEventArgs(PackageInstallProgressState state) : Event /// and /// state. /// - public int PackageFinished { get; } + public int PackageFinished { get; init; } /// /// Number of packages required for this operation. @@ -30,13 +30,13 @@ public class InstallProgressEventArgs(PackageInstallProgressState state) : Event /// and /// state. /// - public int PackageRequired { get; } + public int PackageRequired { get; init; } /// /// Upload percentage completed. /// Used only in state. /// - public double UploadProgress { get; } + public double UploadProgress { get; init; } /// /// Initializes a new instance of the class. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/VersionInfo.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/VersionInfo.cs index 97b637ab..d10bc51b 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/VersionInfo.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/VersionInfo.cs @@ -11,19 +11,19 @@ namespace AdvancedSharpAdbClient.DeviceCommands /// /// Represents a version of an Android application. /// - /// The version code of the application. - /// The version name of the application. - public struct VersionInfo(int versionCode, string versionName) : IComparer, IComparer + /// The version code of the application. + /// The version name of the application. + public readonly record struct VersionInfo(int VersionCode, string VersionName) : IComparer, IComparer { /// /// Gets or sets the version code of an Android application. /// - public int VersionCode { get; set; } = versionCode; + public int VersionCode { get; init; } = VersionCode; /// /// Gets or sets the version name of an Android application. /// - public string VersionName { get; set; } = versionName; + public string VersionName { get; init; } = VersionName; /// public readonly int Compare(object x, object y) => diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs index f5744878..dfa02a54 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs @@ -23,7 +23,7 @@ public virtual Task RefreshPackagesAsync(CancellationToken cancellationToken = d { ValidateDevice(); - PackageManagerReceiver pmr = new(Device, this); + PackageManagerReceiver pmr = new(this); return ThirdPartyOnly ? client.ExecuteShellCommandAsync(Device, ListThirdPartyOnly, pmr, cancellationToken) diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs index 8782e4b9..26ad4a5c 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs @@ -88,7 +88,7 @@ public PackageManager(IAdbClient client, DeviceData device, bool thirdPartyOnly /// Gets a value indicating whether this package manager only lists third party applications, /// or also includes built-in applications. /// - public bool ThirdPartyOnly { get; private set; } + public bool ThirdPartyOnly { get; private init; } /// /// Gets the list of packages currently installed on the device. They key is the name of the package; @@ -99,7 +99,7 @@ public PackageManager(IAdbClient client, DeviceData device, bool thirdPartyOnly /// /// Gets the device. /// - public DeviceData Device { get; private set; } + public DeviceData Device { get; private init; } /// /// Refreshes the packages. @@ -108,7 +108,7 @@ public virtual void RefreshPackages() { ValidateDevice(); - PackageManagerReceiver pmr = new(Device, this); + PackageManagerReceiver pmr = new(this); if (ThirdPartyOnly) { diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/EnvironmentVariablesReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/EnvironmentVariablesReceiver.cs index a94514e7..0ae04fc1 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/EnvironmentVariablesReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/EnvironmentVariablesReceiver.cs @@ -25,12 +25,12 @@ public sealed partial class EnvironmentVariablesReceiver : MultiLineReceiver /// /// Initializes a new instance of the class. /// - public EnvironmentVariablesReceiver() => EnvironmentVariables = new Dictionary(); + public EnvironmentVariablesReceiver() { } /// /// Gets the environment variables that are currently defined on the device. /// - public Dictionary EnvironmentVariables { get; private set; } + public Dictionary EnvironmentVariables { get; } = new Dictionary(); /// /// Processes the new lines. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs index 295b09dd..799f9492 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs @@ -30,7 +30,7 @@ public GetPropReceiver() { } /// /// Gets the list of properties which have been retrieved. /// - public Dictionary Properties { get; private set; } = new Dictionary(); + public Dictionary Properties { get; } = new Dictionary(); /// /// Processes the new lines. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/PackageManagerReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/PackageManagerReceiver.cs index 2a7c8a75..a12db1bc 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/PackageManagerReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/PackageManagerReceiver.cs @@ -9,19 +9,18 @@ namespace AdvancedSharpAdbClient.DeviceCommands /// /// Parses the output of the various pm commands. /// - /// The device for which the package information is being received. /// The parent package manager. - public class PackageManagerReceiver(DeviceData device, PackageManager packageManager) : MultiLineReceiver + public class PackageManagerReceiver(PackageManager packageManager) : MultiLineReceiver { /// /// Gets the device. /// - public DeviceData Device { get; private set; } = device; + public DeviceData Device => PackageManager.Device; /// /// Gets the package manager. /// - public PackageManager PackageManager { get; private set; } = packageManager; + public PackageManager PackageManager { get; } = packageManager; /// /// Processes the new lines. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs index 93ac6026..28689266 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs @@ -20,7 +20,7 @@ public ProcessOutputReceiver() { } /// /// Gets a list of all processes that have been received. /// - public List Processes { get; private set; } = new List(); + public List Processes { get; } = new List(); /// protected override void ProcessNewLines(IEnumerable lines) diff --git a/AdvancedSharpAdbClient/DeviceMonitor.cs b/AdvancedSharpAdbClient/DeviceMonitor.cs index 45afac90..67273cc6 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.cs @@ -93,7 +93,7 @@ public DeviceMonitor(IAdbSocket socket, ILogger logger = null) } /// - public ReadOnlyCollection Devices { get; private set; } + public ReadOnlyCollection Devices { get; init; } /// /// Gets the that represents the connection to the @@ -299,7 +299,7 @@ protected virtual void UpdateDevices(IEnumerable devices) // Add or update existing devices foreach (DeviceData device in devices) { - DeviceData existingDevice = Devices.SingleOrDefault(d => d.Serial == device.Serial); + DeviceData existingDevice = this.devices.SingleOrDefault(d => d.Serial == device.Serial); if (existingDevice == null) { @@ -309,13 +309,21 @@ protected virtual void UpdateDevices(IEnumerable devices) else if (existingDevice.State != device.State) { DeviceState oldState = existingDevice.State; - existingDevice.State = device.State; - OnDeviceChanged(new DeviceDataChangeEventArgs(existingDevice, device.State, oldState)); + int index = this.devices.IndexOf(existingDevice); + if (index != -1) + { + this.devices[index] = device; + } + else + { + this.devices.Add(device); + } + OnDeviceChanged(new DeviceDataChangeEventArgs(device, device.State, oldState)); } } // Remove devices - foreach (DeviceData device in Devices.Where(d => !devices.Any(e => e.Serial == d.Serial)).ToArray()) + foreach (DeviceData device in this.devices.Where(d => !devices.Any(e => e.Serial == d.Serial)).ToArray()) { this.devices.Remove(device); OnDeviceDisconnected(new DeviceDataConnectEventArgs(device, false)); diff --git a/AdvancedSharpAdbClient/Exceptions/AdbException.cs b/AdvancedSharpAdbClient/Exceptions/AdbException.cs index e67addee..3c5bdd16 100644 --- a/AdvancedSharpAdbClient/Exceptions/AdbException.cs +++ b/AdvancedSharpAdbClient/Exceptions/AdbException.cs @@ -71,12 +71,12 @@ public AdbException(string message, Exception innerException) : base(message, in /// /// Gets the error message that was sent by adb. /// - public string AdbError { get; private set; } + public string AdbError { get; init; } /// /// Gets the response that was sent by adb. /// - public AdbResponse Response { get; private set; } + public AdbResponse Response { get; init; } /// /// Gets a value indicating whether the underlying error was a where the diff --git a/AdvancedSharpAdbClient/Exceptions/JavaException.cs b/AdvancedSharpAdbClient/Exceptions/JavaException.cs index abcf1d27..daa16843 100644 --- a/AdvancedSharpAdbClient/Exceptions/JavaException.cs +++ b/AdvancedSharpAdbClient/Exceptions/JavaException.cs @@ -20,12 +20,12 @@ public partial class JavaException : Exception /// /// Gets the name of Java exception. /// - public string JavaName { get; } + public string JavaName { get; init; } /// /// Gets a string representation of the immediate frames on the call stack of Java exception. /// - public string JavaStackTrace { get; } + public string JavaStackTrace { get; init; } /// /// Initializes a new instance of the class. diff --git a/AdvancedSharpAdbClient/Extensions/Attributes/IsExternalInit.cs b/AdvancedSharpAdbClient/Extensions/Attributes/IsExternalInit.cs new file mode 100644 index 00000000..7f988bb8 --- /dev/null +++ b/AdvancedSharpAdbClient/Extensions/Attributes/IsExternalInit.cs @@ -0,0 +1,18 @@ +#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/Models/AdbResponse.cs b/AdvancedSharpAdbClient/Models/AdbResponse.cs index 704ff25d..f693c3ea 100644 --- a/AdvancedSharpAdbClient/Models/AdbResponse.cs +++ b/AdvancedSharpAdbClient/Models/AdbResponse.cs @@ -9,7 +9,7 @@ namespace AdvancedSharpAdbClient /// /// The response returned by ADB server. /// - public struct AdbResponse : IEquatable + public readonly struct AdbResponse : IEquatable { /// /// Initializes a new instance of the struct. @@ -36,25 +36,25 @@ public struct AdbResponse : IEquatable /// Gets or sets a value indicating whether the IO communication was a success. /// /// if successful; otherwise, . - public bool IOSuccess { get; set; } + public bool IOSuccess { get; init; } /// /// Gets or sets a value indicating whether this is okay. /// /// if okay; otherwise, . - public bool Okay { get; set; } + public bool Okay { get; init; } /// /// Gets or sets a value indicating whether this is timeout. /// /// if timeout; otherwise, . - public bool Timeout { get; set; } + public bool Timeout { get; init; } /// /// Gets or sets the message. /// /// The message. - public string Message { get; set; } + public string Message { get; init; } /// /// Creates a new instance of the class, based on an diff --git a/AdvancedSharpAdbClient/Models/AdbServerStatus.cs b/AdvancedSharpAdbClient/Models/AdbServerStatus.cs index 49982160..e9f444d2 100644 --- a/AdvancedSharpAdbClient/Models/AdbServerStatus.cs +++ b/AdvancedSharpAdbClient/Models/AdbServerStatus.cs @@ -9,19 +9,19 @@ namespace AdvancedSharpAdbClient /// /// Represents the status of the adb server. /// - /// The value indicating whether the server is currently running. - /// The version of the server when it is running. - public struct AdbServerStatus(bool isRunning, Version version) + /// The value indicating whether the server is currently running. + /// The version of the server when it is running. + public readonly record struct AdbServerStatus(bool IsRunning, Version Version) { /// /// Gets a value indicating whether the server is currently running. /// - public bool IsRunning { get; set; } = isRunning; + public bool IsRunning { get; init; } = IsRunning; /// /// Gets the version of the server when it is running. /// - public Version Version { get; set; } = version; + public Version Version { get; init; } = Version; /// /// Deconstruct the struct. diff --git a/AdvancedSharpAdbClient/Models/ColorData.cs b/AdvancedSharpAdbClient/Models/ColorData.cs index 76e14e4b..92b5ecad 100644 --- a/AdvancedSharpAdbClient/Models/ColorData.cs +++ b/AdvancedSharpAdbClient/Models/ColorData.cs @@ -11,18 +11,18 @@ namespace AdvancedSharpAdbClient /// For example, in a 24-bit RGB structure, the first byte contains the red color, /// the next byte the green color and the last byte the blue color. /// - public struct ColorData + public readonly record struct ColorData(uint Length, uint Offset) { /// /// Gets or sets the number of bits that contain information for this color. /// - public uint Length { get; set; } + public uint Length { get; init; } = Length; /// /// Gets or sets the offset, in bits, within the byte array for a pixel, at which the /// bytes that contain information for this color are stored. /// - public uint Offset { get; set; } + public uint Offset { get; init; } = Offset; /// /// Deconstruct the struct. diff --git a/AdvancedSharpAdbClient/Models/DeviceData.cs b/AdvancedSharpAdbClient/Models/DeviceData.cs index ceeb94e0..6a510c5f 100644 --- a/AdvancedSharpAdbClient/Models/DeviceData.cs +++ b/AdvancedSharpAdbClient/Models/DeviceData.cs @@ -56,47 +56,47 @@ public DeviceData(string data) /// /// Gets or sets the device serial number. /// - public string Serial { get; set; } + public string Serial { get; init; } /// /// Gets or sets the device state. /// - public DeviceState State { get; set; } + public DeviceState State { get; init; } /// /// Gets or sets the device model name. /// - public string Model { get; set; } + public string Model { get; init; } /// /// Gets or sets the device product name. /// - public string Product { get; set; } + public string Product { get; init; } /// /// Gets or sets the device name. /// - public string Name { get; set; } + public string Name { get; init; } /// /// Gets or sets the features available on the device. /// - public string Features { get; set; } + public string Features { get; init; } /// /// Gets or sets the USB port to which this device is connected. Usually available on Linux only. /// - public string Usb { get; set; } + public string Usb { get; init; } /// /// Gets or sets the transport ID for this device. /// - public string TransportId { get; set; } + public string TransportId { get; init; } /// /// Gets or sets the device info message. Currently only seen for NoPermissions state. /// - public string Message { get; set; } + public string Message { get; init; } /// /// Creates a new instance of the class based on diff --git a/AdvancedSharpAdbClient/Models/Element.cs b/AdvancedSharpAdbClient/Models/Element.cs index 4c64b58d..74029e3c 100644 --- a/AdvancedSharpAdbClient/Models/Element.cs +++ b/AdvancedSharpAdbClient/Models/Element.cs @@ -129,27 +129,27 @@ IEnumerable FindElements() /// /// Gets the current device containing the element. /// - protected DeviceData Device { get; } + protected DeviceData Device { get; init; } /// /// Gets the coordinates and size of the element. /// - public Area Bounds { get; } + public Area Bounds { get; init; } /// /// Gets the children of this element. /// - public IEnumerable Children { get; } + public IEnumerable Children { get; init; } /// /// Gets the element attributes. /// - public Dictionary Attributes { get; } + public Dictionary Attributes { get; init; } /// /// Gets the of this element. /// - public XmlNode Node { get; } + public XmlNode Node { get; init; } /// /// Gets the coordinates of the the center of the element. @@ -361,7 +361,9 @@ public override int GetHashCode() => ^ Device.GetHashCode() ^ (Node == null ? Bounds.GetHashCode() - ^ Attributes.GetHashCode() + ^ (Attributes == null + ? 1 + : Attributes.GetHashCode()) : Node.GetHashCode()); #endif diff --git a/AdvancedSharpAdbClient/Models/ForwardData.cs b/AdvancedSharpAdbClient/Models/ForwardData.cs index 782e070b..0bc64481 100644 --- a/AdvancedSharpAdbClient/Models/ForwardData.cs +++ b/AdvancedSharpAdbClient/Models/ForwardData.cs @@ -42,12 +42,12 @@ public ForwardData(string value) /// /// Gets or sets the serial number of the device for which the port forwarding is configured. /// - public string SerialNumber { get; set; } + public string SerialNumber { get; init; } /// /// Gets or sets a that represents the local (PC) endpoint. /// - public string Local { get; set; } + public string Local { get; init; } /// /// Gets a that represents the local (PC) endpoint. @@ -57,7 +57,7 @@ public ForwardData(string value) /// /// Gets or sets a that represents the remote (device) endpoint. /// - public string Remote { get; set; } + public string Remote { get; init; } /// /// Gets a that represents the remote (device) endpoint. diff --git a/AdvancedSharpAdbClient/Models/ForwardSpec.cs b/AdvancedSharpAdbClient/Models/ForwardSpec.cs index 9e8fbb86..5eb4aa55 100644 --- a/AdvancedSharpAdbClient/Models/ForwardSpec.cs +++ b/AdvancedSharpAdbClient/Models/ForwardSpec.cs @@ -90,26 +90,26 @@ or ForwardProtocol.LocalReserved /// /// Gets or sets the protocol which is being forwarded. /// - public ForwardProtocol Protocol { get; set; } + public ForwardProtocol Protocol { get; init; } /// /// Gets or sets, when the is , the port /// number of the port being forwarded. /// - public int Port { get; set; } + public int Port { get; init; } /// /// Gets or sets, when the is , /// or , /// the Unix domain socket name of the socket being forwarded. /// - public string SocketName { get; set; } + public string SocketName { get; init; } /// /// Gets or sets, when the is , /// the process id of the process to which the debugger is attached. /// - public int ProcessId { get; set; } + public int ProcessId { get; init; } /// /// Creates a from its representation. diff --git a/AdvancedSharpAdbClient/Models/Framebuffer.cs b/AdvancedSharpAdbClient/Models/Framebuffer.cs index e8ffa9d3..8f43f88c 100644 --- a/AdvancedSharpAdbClient/Models/Framebuffer.cs +++ b/AdvancedSharpAdbClient/Models/Framebuffer.cs @@ -63,7 +63,7 @@ public Framebuffer(DeviceData device) : this(device, AdbClient.DefaultEndPoint) /// /// Gets the at which the adb server is listening. /// - public EndPoint EndPoint { get; } + public EndPoint EndPoint { get; init; } /// /// Gets the framebuffer header. The header contains information such as the width and height and the color encoding. diff --git a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs index 50d4a7af..7376a64a 100644 --- a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs +++ b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs @@ -18,7 +18,7 @@ namespace AdvancedSharpAdbClient /// of the framebuffer, prefixed with a object that contains more /// information about the framebuffer. /// - public struct FramebufferHeader + public readonly struct FramebufferHeader { /// /// Initializes a new instance of the struct based on a byte array which contains the data. @@ -78,52 +78,52 @@ public FramebufferHeader(byte[] data) /// /// Gets or sets the version of the framebuffer struct. /// - public uint Version { get; set; } + public uint Version { get; init; } /// /// Gets or sets the number of bytes per pixel. Usual values include 32 or 24. /// - public uint Bpp { get; set; } + public uint Bpp { get; init; } /// /// Gets or sets the color space. Only available starting with 2. /// - public uint ColorSpace { get; set; } + public uint ColorSpace { get; init; } /// /// Gets or sets the total size, in bits, of the framebuffer. /// - public uint Size { get; set; } + public uint Size { get; init; } /// /// Gets or sets the width, in pixels, of the framebuffer. /// - public uint Width { get; set; } + public uint Width { get; init; } /// /// Gets or sets the height, in pixels, of the framebuffer. /// - public uint Height { get; set; } + public uint Height { get; init; } /// /// Gets or sets information about the red color channel. /// - public ColorData Red { get; set; } + public ColorData Red { get; init; } /// /// Gets or sets information about the blue color channel. /// - public ColorData Blue { get; set; } + public ColorData Blue { get; init; } /// /// Gets or sets information about the green color channel. /// - public ColorData Green { get; set; } + public ColorData Green { get; init; } /// /// Gets or sets information about the alpha channel. /// - public ColorData Alpha { get; set; } + public ColorData Alpha { get; init; } /// /// Creates a new object based on a byte array which contains the data. diff --git a/AdvancedSharpAdbClient/SyncService.cs b/AdvancedSharpAdbClient/SyncService.cs index cb602164..9b59eb37 100644 --- a/AdvancedSharpAdbClient/SyncService.cs +++ b/AdvancedSharpAdbClient/SyncService.cs @@ -79,7 +79,7 @@ public SyncService(IAdbSocket socket, DeviceData device) /// /// Gets the device on which the file operations are being executed. /// - public DeviceData Device { get; private set; } + public DeviceData Device { get; } /// /// Gets the that enables the connection with the adb server. diff --git a/README.md b/README.md index 7568c077..d9cc9f0a 100644 --- a/README.md +++ b/README.md @@ -88,20 +88,20 @@ static void Main(string[] args) client = new AdbClient(); client.Connect("127.0.0.1:62001"); device = client.GetDevices().FirstOrDefault(); - Element el = client.FindElement(device, "//node[@text='Login']"); + Element element = client.FindElement(device, "//node[@text='Login']"); } ``` You can also specify the waiting time for the element ```cs -Element el = client.FindElement(device, "//node[@text='Login']", TimeSpan.FromSeconds(5)); +Element element = client.FindElement(device, "//node[@text='Login']", TimeSpan.FromSeconds(5)); ``` And you can find several elements ```cs -Element[] els = client.FindElements(device, "//node[@resource-id='Login']", TimeSpan.FromSeconds(5)); +Element[] element = client.FindElements(device, "//node[@resource-id='Login']", TimeSpan.FromSeconds(5)); ``` ### Getting element attributes @@ -111,9 +111,9 @@ You can get all element attributes static void Main(string[] args) { ... - Element el = client.FindElement(device, "//node[@resource-id='Login']", TimeSpan.FromSeconds(3)); - string eltext = el.attributes["text"]; - string bounds = el.attributes["bounds"]; + Element element = client.FindElement(device, "//node[@resource-id='Login']", TimeSpan.FromSeconds(3)); + string eltext = element.Attributes["text"]; + string bounds = element.Attributes["bounds"]; ... } ``` @@ -137,8 +137,8 @@ Or on the element(need xpath) static void Main(string[] args) { ... - Element el = client.FindElement(device, "//node[@text='Login']", TimeSpan.FromSeconds(3)); - el.Click();// Click on element by xpath //node[@text='Login'] + Element element = client.FindElement(device, "//node[@text='Login']", TimeSpan.FromSeconds(3)); + element.Click(); // Click on element by xpath //node[@text='Login'] ... } ``` @@ -148,7 +148,7 @@ The Click() method throw ElementNotFoundException if the element is not found ```cs try { - el.Click(); + element.Click(); } catch (Exception ex) { @@ -188,7 +188,7 @@ The Swipe() method throw ElementNotFoundException if the element is not found ```cs try { - client.Swipe(device, 0x2232323, 0x954,0x9128,0x11111, 200); + client.Swipe(device, 0x2232323, 0x954, 0x9128, 0x11111, 200); ... client.Swipe(device, first, second, 200); } @@ -298,9 +298,9 @@ catch (Exception ex) static void Main(string[] args) { ... - client.BackBtn(device); // Click Back button + client.ClickBackButton(device); // Click Back button ... - client.HomeBtn(device); // Click Home button + client.ClickHomeButton(device); // Click Home button ... } ``` @@ -326,7 +326,10 @@ Or you can use AdbClient.Install static void Main(string[] args) { ... - client.Install(device, File.OpenRead("Application.apk")); + using (FileStream stream = File.OpenRead("Application.apk")) + { + client.Install(device, stream); + } ... } ``` @@ -337,8 +340,8 @@ static void Main(string[] args) { ... PackageManager manager = new PackageManager(client, device); - manager.InstallMultiplePackage(@"C:\Users\me\Documents\base.apk", new string[] { @"C:\Users\me\Documents\split_1.apk", @"C:\Users\me\Documents\split_2.apk" }, reinstall: false); // Install split app whith base app - manager.InstallMultiplePackage(new string[] { @"C:\Users\me\Documents\split_3.apk", @"C:\Users\me\Documents\split_4.apk" }, "com.android.app", reinstall: false); // Add split app to base app which packagename is 'com.android.app' + manager.InstallMultiplePackage(@"C:\Users\me\Documents\base.apk", new[] { @"C:\Users\me\Documents\split_1.apk", @"C:\Users\me\Documents\split_2.apk" }, reinstall: false); // Install split app whith base app + manager.InstallMultiplePackage(new[] { @"C:\Users\me\Documents\split_3.apk", @"C:\Users\me\Documents\split_4.apk" }, "com.android.app", reinstall: false); // Add split app to base app which packagename is 'com.android.app' ... } ``` @@ -349,8 +352,8 @@ Or you can use AdbClient.InstallMultiple static void Main(string[] args) { ... - client.InstallMultiple(device, File.OpenRead("base.apk"), new Stream[] { File.OpenRead("split_1.apk"), File.OpenRead("split_2.apk") }); // Install split app whith base app - client.InstallMultiple(device, new Stream[] { File.OpenRead("split_3.apk"), File.OpenRead("split_4.apk") }, "com.android.app"); // Add split app to base app which packagename is 'com.android.app' + client.InstallMultiple(device, File.OpenRead("base.apk"), new[] { File.OpenRead("split_1.apk"), File.OpenRead("split_2.apk") }); // Install split app whith base app + client.InstallMultiple(device, new[] { File.OpenRead("split_3.apk"), File.OpenRead("split_4.apk") }, "com.android.app"); // Add split app to base app which packagename is 'com.android.app' ... } ``` @@ -373,9 +376,9 @@ static void Main(string[] args) static async void Main(string[] args) { ... - System.Drawing.Image img = client.GetFrameBuffer(device, CancellationToken.None); // synchronously + Image img = client.GetFrameBuffer(device, CancellationToken.None); // synchronously ... - System.Drawing.Image img = await client.GetFrameBufferAsync(device, CancellationToken.None); // asynchronously + Image img = await client.GetFrameBufferAsync(device, CancellationToken.None); // asynchronously ... } ``` @@ -398,9 +401,9 @@ void DownloadFile() { using (SyncService service = new SyncService(new AdbSocket(client.EndPoint), device)) { - using (Stream stream = File.OpenWrite(@"C:\MyFile.txt")) + using (FileStream stream = File.OpenWrite(@"C:\MyFile.txt")) { - service.Pull("/data/local/tmp/MyFile.txt", stream, null, CancellationToken.None); + service.Pull("/data/local/tmp/MyFile.txt", stream, null); } } } @@ -409,9 +412,9 @@ void UploadFile() { using (SyncService service = new SyncService(new AdbSocket(client.EndPoint), device)) { - using (Stream stream = File.OpenRead(@"C:\MyFile.txt")) + using (FileStream stream = File.OpenRead(@"C:\MyFile.txt")) { - service.Push(stream, "/data/local/tmp/MyFile.txt", 777 ,DateTimeOffset.Now, null ,CancellationToken.None); + service.Push(stream, "/data/local/tmp/MyFile.txt", 777, DateTimeOffset.Now, null); } } } From 81c4881dfcd349a081c1ee6bb07d2a48720ec623 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Tue, 10 Oct 2023 23:30:37 +0800 Subject: [PATCH 34/66] Improve DeviceMonitor Use HashCode to create hash code --- .../DeviceMonitorTests.Async.cs | 27 +- .../DeviceMonitorTests.cs | 26 +- .../Dummys/DeviceMonitorSink.cs | 10 +- .../DeviceCommands/LinuxPath.cs | 4 +- .../Receivers/InstallOutputReceiver.cs | 4 +- AdvancedSharpAdbClient/DeviceMonitor.cs | 70 ++- .../Extensions/AdbClientExtensions.Async.cs | 1 - .../Extensions/LoggerExtensions.cs | 48 +- .../Interfaces/IDeviceMonitor.cs | 5 + AdvancedSharpAdbClient/Models/AdbResponse.cs | 11 +- AdvancedSharpAdbClient/Models/Area.cs | 7 +- AdvancedSharpAdbClient/Models/Cords.cs | 7 +- AdvancedSharpAdbClient/Models/DeviceData.cs | 34 +- AdvancedSharpAdbClient/Models/DnsEndPoint.cs | 1 - AdvancedSharpAdbClient/Models/Element.cs | 19 +- .../Models/FileStatistics.cs | 16 +- AdvancedSharpAdbClient/Models/ForwardData.cs | 17 +- AdvancedSharpAdbClient/Models/ForwardSpec.cs | 10 +- AdvancedSharpAdbClient/Models/HashCode.cs | 561 ++++++++++++++++++ .../Receivers/ConsoleOutputReceiver.cs | 4 +- 20 files changed, 750 insertions(+), 132 deletions(-) create mode 100644 AdvancedSharpAdbClient/Models/HashCode.cs diff --git a/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.Async.cs b/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.Async.cs index ae5aa157..de5c5372 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.Async.cs @@ -27,12 +27,15 @@ await RunTestAsync( Assert.Single(sink.ConnectedEvents); Assert.Empty(sink.ChangedEvents); Assert.Single(sink.NotifiedEvents); + Assert.Single(sink.ListChangedEvents); Assert.Empty(sink.DisconnectedEvents); Socket.ResponseMessages.Clear(); Socket.Responses.Clear(); Socket.Requests.Clear(); + sink.ResetSignals(); + // Device disconnects ManualResetEvent eventWaiter = sink.CreateEventSignal(); @@ -43,9 +46,10 @@ await RunTestAsync( () => _ = eventWaiter.WaitOne(1000)); Assert.Empty(monitor.Devices); - Assert.Single(sink.ConnectedEvents); + Assert.Empty(sink.ConnectedEvents); Assert.Empty(sink.ChangedEvents); Assert.Single(sink.NotifiedEvents); + Assert.Single(sink.ListChangedEvents); Assert.Single(sink.DisconnectedEvents); Assert.Equal("169.254.109.177:5555", sink.DisconnectedEvents[0].Device.Serial); } @@ -70,13 +74,16 @@ await RunTestAsync( Assert.Empty(monitor.Devices); Assert.Empty(sink.ConnectedEvents); Assert.Empty(sink.ChangedEvents); - Assert.Empty(sink.NotifiedEvents); + Assert.Single(sink.NotifiedEvents); + Assert.Empty(sink.ListChangedEvents); Assert.Empty(sink.DisconnectedEvents); Socket.ResponseMessages.Clear(); Socket.Responses.Clear(); Socket.Requests.Clear(); + sink.ResetSignals(); + // Device disconnects ManualResetEvent eventWaiter = sink.CreateEventSignal(); @@ -90,6 +97,7 @@ await RunTestAsync( Assert.Single(sink.ConnectedEvents); Assert.Empty(sink.ChangedEvents); Assert.Single(sink.NotifiedEvents); + Assert.Single(sink.ListChangedEvents); Assert.Empty(sink.DisconnectedEvents); Assert.Equal("169.254.109.177:5555", sink.ConnectedEvents[0].Device.Serial); } @@ -114,16 +122,17 @@ await RunTestAsync( () => monitor.StartAsync()); Assert.Single(monitor.Devices); - Assert.Equal("169.254.109.177:5555", monitor.Devices.ElementAt(0).Serial); + Assert.Equal("169.254.109.177:5555", monitor.Devices[0].Serial); Assert.Single(sink.ConnectedEvents); Assert.Equal("169.254.109.177:5555", sink.ConnectedEvents[0].Device.Serial); Assert.Empty(sink.ChangedEvents); Assert.Single(sink.NotifiedEvents); + Assert.Single(sink.ListChangedEvents); Assert.Empty(sink.DisconnectedEvents); } [Fact] - public async void DeviceChanged_TriggeredWhenStatusChangedAsyncTest() + public async void TriggeredWhenStatusChangedAsyncTest() { Socket.WaitForNewData = true; @@ -140,10 +149,11 @@ await RunTestAsync( () => monitor.StartAsync()); Assert.Single(monitor.Devices); - Assert.Equal(DeviceState.Offline, monitor.Devices.ElementAt(0).State); + Assert.Equal(DeviceState.Offline, monitor.Devices[0].State); Assert.Single(sink.ConnectedEvents); Assert.Empty(sink.ChangedEvents); Assert.Single(sink.NotifiedEvents); + Assert.Single(sink.ListChangedEvents); Assert.Empty(sink.DisconnectedEvents); Socket.ResponseMessages.Clear(); @@ -162,16 +172,17 @@ await RunTestAsync( () => eventWaiter.WaitOne(1000)); Assert.Single(monitor.Devices); - Assert.Equal(DeviceState.Online, monitor.Devices.ElementAt(0).State); + Assert.Equal(DeviceState.Online, monitor.Devices[0].State); Assert.Empty(sink.ConnectedEvents); Assert.Single(sink.ChangedEvents); Assert.Single(sink.NotifiedEvents); + Assert.Single(sink.ListChangedEvents); Assert.Empty(sink.DisconnectedEvents); Assert.Equal("169.254.109.177:5555", sink.ChangedEvents[0].Device.Serial); } [Fact] - public async void DeviceChanged_NoTriggerIfStatusIsSameAsyncTest() + public async void NoTriggerIfStatusIsSameAsyncTest() { Socket.WaitForNewData = true; @@ -192,6 +203,7 @@ await RunTestAsync( Assert.Single(sink.ConnectedEvents); Assert.Empty(sink.ChangedEvents); Assert.Single(sink.NotifiedEvents); + Assert.Single(sink.ListChangedEvents); Assert.Empty(sink.DisconnectedEvents); Socket.ResponseMessages.Clear(); @@ -214,6 +226,7 @@ await RunTestAsync( Assert.Empty(sink.ConnectedEvents); Assert.Empty(sink.ChangedEvents); Assert.Single(sink.NotifiedEvents); + Assert.Empty(sink.ListChangedEvents); Assert.Empty(sink.DisconnectedEvents); } diff --git a/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs b/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs index f19882cc..20fabbcf 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs @@ -1,5 +1,6 @@ using AdvancedSharpAdbClient.Logs; using System; +using System.Linq; using System.Threading; using Xunit; @@ -58,8 +59,11 @@ public void DeviceDisconnectedTest() Assert.Single(sink.ConnectedEvents); Assert.Empty(sink.ChangedEvents); Assert.Single(sink.NotifiedEvents); + Assert.Single(sink.ListChangedEvents); Assert.Empty(sink.DisconnectedEvents); + sink.ResetSignals(); + Socket.ResponseMessages.Clear(); Socket.Responses.Clear(); Socket.Requests.Clear(); @@ -74,9 +78,10 @@ public void DeviceDisconnectedTest() () => eventWaiter.WaitOne(1000)); Assert.Empty(monitor.Devices); - Assert.Single(sink.ConnectedEvents); + Assert.Empty(sink.ConnectedEvents); Assert.Empty(sink.ChangedEvents); Assert.Single(sink.NotifiedEvents); + Assert.Single(sink.ListChangedEvents); Assert.Single(sink.DisconnectedEvents); Assert.Equal("169.254.109.177:5555", sink.DisconnectedEvents[0].Device.Serial); } @@ -101,13 +106,16 @@ public void DeviceConnectedTest() Assert.Empty(monitor.Devices); Assert.Empty(sink.ConnectedEvents); Assert.Empty(sink.ChangedEvents); - Assert.Empty(sink.NotifiedEvents); + Assert.Single(sink.NotifiedEvents); + Assert.Empty(sink.ListChangedEvents); Assert.Empty(sink.DisconnectedEvents); Socket.ResponseMessages.Clear(); Socket.Responses.Clear(); Socket.Requests.Clear(); + sink.ResetSignals(); + // Device disconnects ManualResetEvent eventWaiter = sink.CreateEventSignal(); @@ -121,6 +129,7 @@ public void DeviceConnectedTest() Assert.Single(sink.ConnectedEvents); Assert.Empty(sink.ChangedEvents); Assert.Single(sink.NotifiedEvents); + Assert.Single(sink.ListChangedEvents); Assert.Empty(sink.DisconnectedEvents); Assert.Equal("169.254.109.177:5555", sink.ConnectedEvents[0].Device.Serial); } @@ -150,11 +159,12 @@ public void StartInitialDeviceListTest() Assert.Equal("169.254.109.177:5555", sink.ConnectedEvents[0].Device.Serial); Assert.Empty(sink.ChangedEvents); Assert.Single(sink.NotifiedEvents); + Assert.Single(sink.ListChangedEvents); Assert.Empty(sink.DisconnectedEvents); } [Fact] - public void DeviceChanged_TriggeredWhenStatusChangedTest() + public void TriggeredWhenStatusChangedTest() { Socket.WaitForNewData = true; @@ -175,6 +185,7 @@ public void DeviceChanged_TriggeredWhenStatusChangedTest() Assert.Single(sink.ConnectedEvents); Assert.Empty(sink.ChangedEvents); Assert.Single(sink.NotifiedEvents); + Assert.Single(sink.ListChangedEvents); Assert.Empty(sink.DisconnectedEvents); Socket.ResponseMessages.Clear(); @@ -197,12 +208,13 @@ public void DeviceChanged_TriggeredWhenStatusChangedTest() Assert.Empty(sink.ConnectedEvents); Assert.Single(sink.ChangedEvents); Assert.Single(sink.NotifiedEvents); + Assert.Single(sink.ListChangedEvents); Assert.Empty(sink.DisconnectedEvents); Assert.Equal("169.254.109.177:5555", sink.ChangedEvents[0].Device.Serial); } [Fact] - public void DeviceChanged_NoTriggerIfStatusIsSameTest() + public void NoTriggerIfStatusIsSameTest() { Socket.WaitForNewData = true; @@ -219,10 +231,11 @@ public void DeviceChanged_NoTriggerIfStatusIsSameTest() monitor.Start); Assert.Single(monitor.Devices); - Assert.Equal(DeviceState.Offline, monitor.Devices[0].State); + Assert.Equal(DeviceState.Offline, monitor.Devices.ElementAt(0).State); Assert.Single(sink.ConnectedEvents); Assert.Empty(sink.ChangedEvents); Assert.Single(sink.NotifiedEvents); + Assert.Single(sink.ListChangedEvents); Assert.Empty(sink.DisconnectedEvents); Socket.ResponseMessages.Clear(); @@ -241,10 +254,11 @@ public void DeviceChanged_NoTriggerIfStatusIsSameTest() () => eventWaiter.WaitOne(1000)); Assert.Single(monitor.Devices); - Assert.Equal(DeviceState.Offline, monitor.Devices[0].State); + Assert.Equal(DeviceState.Offline, monitor.Devices.ElementAt(0).State); Assert.Empty(sink.ConnectedEvents); Assert.Empty(sink.ChangedEvents); Assert.Single(sink.NotifiedEvents); + Assert.Empty(sink.ListChangedEvents); Assert.Empty(sink.DisconnectedEvents); } diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DeviceMonitorSink.cs b/AdvancedSharpAdbClient.Tests/Dummys/DeviceMonitorSink.cs index 0dc88c0d..78ca3a29 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DeviceMonitorSink.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DeviceMonitorSink.cs @@ -12,11 +12,13 @@ public DeviceMonitorSink(DeviceMonitor monitor) Monitor.DeviceChanged += OnDeviceChanged; Monitor.DeviceNotified += OnDeviceNotified; Monitor.DeviceConnected += OnDeviceConnected; + Monitor.DeviceListChanged += OnDeviceListChanged; Monitor.DeviceDisconnected += OnDeviceDisconnected; ChangedEvents = []; NotifiedEvents = []; ConnectedEvents = []; + ListChangedEvents = []; DisconnectedEvents = []; } @@ -25,13 +27,16 @@ public void ResetSignals() ChangedEvents.Clear(); NotifiedEvents.Clear(); ConnectedEvents.Clear(); + ListChangedEvents.Clear(); DisconnectedEvents.Clear(); } public List DisconnectedEvents { get; init; } - public List ConnectedEvents { get; init; } + public List ListChangedEvents { get; init; } + public List ConnectedEvents { get; init; } + public List NotifiedEvents { get; init; } public List ChangedEvents { get; init; } @@ -42,12 +47,13 @@ public ManualResetEvent CreateEventSignal() { ManualResetEvent signal = new(false); Monitor.DeviceNotified += (sender, e) => signal.Set(); - Monitor.DeviceDisconnected += (sender, e) => signal.Set(); return signal; } protected virtual void OnDeviceDisconnected(object sender, DeviceDataConnectEventArgs e) => DisconnectedEvents.Add(e); + protected virtual void OnDeviceListChanged(object sender, DeviceDataNotifyEventArgs e) => ListChangedEvents.Add(e); + protected virtual void OnDeviceConnected(object sender, DeviceDataConnectEventArgs e) => ConnectedEvents.Add(e); protected virtual void OnDeviceNotified(object sender, DeviceDataNotifyEventArgs e) => NotifiedEvents.Add(e); diff --git a/AdvancedSharpAdbClient/DeviceCommands/LinuxPath.cs b/AdvancedSharpAdbClient/DeviceCommands/LinuxPath.cs index 87d32e60..75df540c 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/LinuxPath.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/LinuxPath.cs @@ -139,7 +139,9 @@ public static string GetDirectoryName(string path) return null; } - /// Returns the file name and extension of the specified path string. + /// + /// Returns the file name and extension of the specified path string. + /// /// A consisting of the characters after the last directory character in path. /// If the last character of path is a directory or volume separator character, /// this method returns . If path is null, this method returns null. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs index ba306688..8ca8b924 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs @@ -139,9 +139,9 @@ protected override void ProcessNewLines(IEnumerable lines) private static partial Regex ErrorRegex(); #else private static Regex SuccessRegex() => new(SuccessPattern, RegexOptions.IgnoreCase); - + private static Regex FailureRegex() => new(FailurePattern, RegexOptions.IgnoreCase); - + private static Regex ErrorRegex() => new(ErrorPattern, RegexOptions.IgnoreCase); #endif } diff --git a/AdvancedSharpAdbClient/DeviceMonitor.cs b/AdvancedSharpAdbClient/DeviceMonitor.cs index 67273cc6..9f876dbd 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.cs @@ -76,6 +76,9 @@ public partial class DeviceMonitor : IDeviceMonitor /// public event EventHandler DeviceConnected; + /// + public event EventHandler DeviceListChanged; + /// public event EventHandler DeviceDisconnected; @@ -207,7 +210,7 @@ public void Dispose() /// /// Raises the event. /// - /// The instance containing the event data. + /// The instance containing the event data. protected void OnDeviceNotified(DeviceDataNotifyEventArgs e) => DeviceNotified?.Invoke(this, e); /// @@ -216,6 +219,12 @@ public void Dispose() /// The instance containing the event data. protected void OnDeviceConnected(DeviceDataConnectEventArgs e) => DeviceConnected?.Invoke(this, e); + /// + /// Raises the event. + /// + /// The instance containing the event data. + protected void OnDeviceListChanged(DeviceDataNotifyEventArgs e) => DeviceListChanged?.Invoke(this, e); + /// /// Raises the event. /// @@ -278,14 +287,15 @@ private void ProcessIncomingDeviceData(string result) IEnumerable currentDevices = deviceValues.Select(x => new DeviceData(x)); UpdateDevices(currentDevices); + OnDeviceNotified(new DeviceDataNotifyEventArgs(currentDevices)); } /// /// Processes the incoming . /// - protected virtual void UpdateDevices(IEnumerable devices) + protected virtual void UpdateDevices(IEnumerable collection) { - lock (this.devices) + lock (devices) { // For each device in the current list, we look for a matching the new list. // * if we find it, we update the current object with whatever new information @@ -296,42 +306,50 @@ protected virtual void UpdateDevices(IEnumerable devices) // Once this is done, the new list contains device we aren't monitoring yet, so we // add them to the list, and start monitoring them. - // Add or update existing devices - foreach (DeviceData device in devices) + bool isChanged = false; + int length = this.devices.Count; + List devices = collection.ToList(); + for (int i = 0; i < length;) { - DeviceData existingDevice = this.devices.SingleOrDefault(d => d.Serial == device.Serial); - - if (existingDevice == null) + DeviceData currentDevice = this.devices[i]; + int index = devices.FindIndex(d => d.Serial == currentDevice.Serial); + if (index == -1) { - this.devices.Add(device); - OnDeviceConnected(new DeviceDataConnectEventArgs(device, true)); + // Remove disconnected devices + this.devices.RemoveAt(i); + OnDeviceDisconnected(new DeviceDataConnectEventArgs(currentDevice, false)); + isChanged = true; + length--; } - else if (existingDevice.State != device.State) + else { - DeviceState oldState = existingDevice.State; - int index = this.devices.IndexOf(existingDevice); - if (index != -1) - { - this.devices[index] = device; - } - else + DeviceData device = devices[index]; + if (currentDevice.State != device.State) { - this.devices.Add(device); + // Change device state + this.devices[i] = device; + OnDeviceChanged(new DeviceDataChangeEventArgs(device, device.State, currentDevice.State)); + isChanged = true; } - OnDeviceChanged(new DeviceDataChangeEventArgs(device, device.State, oldState)); + devices.RemoveAt(index); + i++; } } - // Remove devices - foreach (DeviceData device in this.devices.Where(d => !devices.Any(e => e.Serial == d.Serial)).ToArray()) + if (devices.Count > 0) { - this.devices.Remove(device); - OnDeviceDisconnected(new DeviceDataConnectEventArgs(device, false)); + // Add connected devices + foreach (DeviceData device in devices) + { + this.devices.Add(device); + OnDeviceConnected(new DeviceDataConnectEventArgs(device, false)); + } + isChanged = true; } - if (devices.Any()) + if (isChanged) { - OnDeviceNotified(new DeviceDataNotifyEventArgs(devices)); + OnDeviceListChanged(new DeviceDataNotifyEventArgs(devices)); } } } diff --git a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs index 6f67facf..4de105f4 100644 --- a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs +++ b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs @@ -10,7 +10,6 @@ using System.IO; using System.Linq; using System.Net; -using System.Text; using System.Threading; namespace AdvancedSharpAdbClient diff --git a/AdvancedSharpAdbClient/Extensions/LoggerExtensions.cs b/AdvancedSharpAdbClient/Extensions/LoggerExtensions.cs index 69fb4c58..ac88cf8f 100644 --- a/AdvancedSharpAdbClient/Extensions/LoggerExtensions.cs +++ b/AdvancedSharpAdbClient/Extensions/LoggerExtensions.cs @@ -22,10 +22,8 @@ public static class LoggerExtensions /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogDebug(exception, "Error while processing request from {Address}", address) - public static void LogDebug(this ILogger logger, Exception exception, string message, params object[] args) - { + public static void LogDebug(this ILogger logger, Exception exception, string message, params object[] args) => logger.Log(LogLevel.Debug, exception, message, args); - } /// /// Formats and writes a debug log message. @@ -34,10 +32,8 @@ public static void LogDebug(this ILogger logger, Exception exception, string mes /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogDebug("Processing request from {Address}", address) - public static void LogDebug(this ILogger logger, string message, params object[] args) - { + public static void LogDebug(this ILogger logger, string message, params object[] args) => logger.Log(LogLevel.Debug, message, args); - } //------------------------------------------TRACE------------------------------------------// @@ -49,10 +45,8 @@ public static void LogDebug(this ILogger logger, string message, params object[] /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogTrace(exception, "Error while processing request from {Address}", address) - public static void LogTrace(this ILogger logger, Exception exception, string message, params object[] args) - { + public static void LogTrace(this ILogger logger, Exception exception, string message, params object[] args) => logger.Log(LogLevel.Trace, exception, message, args); - } /// /// Formats and writes a trace log message. @@ -61,10 +55,8 @@ public static void LogTrace(this ILogger logger, Exception exception, string mes /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogTrace("Processing request from {Address}", address) - public static void LogTrace(this ILogger logger, string message, params object[] args) - { + public static void LogTrace(this ILogger logger, string message, params object[] args) => logger.Log(LogLevel.Trace, message, args); - } //------------------------------------------INFORMATION------------------------------------------// @@ -76,10 +68,8 @@ public static void LogTrace(this ILogger logger, string message, params object[] /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogInformation(exception, "Error while processing request from {Address}", address) - public static void LogInformation(this ILogger logger, Exception exception, string message, params object[] args) - { + public static void LogInformation(this ILogger logger, Exception exception, string message, params object[] args) => logger.Log(LogLevel.Information, exception, message, args); - } /// /// Formats and writes an informational log message. @@ -88,10 +78,8 @@ public static void LogInformation(this ILogger logger, Exception exception, stri /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogInformation("Processing request from {Address}", address) - public static void LogInformation(this ILogger logger, string message, params object[] args) - { + public static void LogInformation(this ILogger logger, string message, params object[] args) => logger.Log(LogLevel.Information, message, args); - } //------------------------------------------WARNING------------------------------------------// @@ -103,10 +91,8 @@ public static void LogInformation(this ILogger logger, string message, params ob /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogWarning(exception, "Error while processing request from {Address}", address) - public static void LogWarning(this ILogger logger, Exception exception, string message, params object[] args) - { + public static void LogWarning(this ILogger logger, Exception exception, string message, params object[] args) => logger.Log(LogLevel.Warning, exception, message, args); - } /// /// Formats and writes a warning log message. @@ -115,10 +101,8 @@ public static void LogWarning(this ILogger logger, Exception exception, string m /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogWarning("Processing request from {Address}", address) - public static void LogWarning(this ILogger logger, string message, params object[] args) - { + public static void LogWarning(this ILogger logger, string message, params object[] args) => logger.Log(LogLevel.Warning, message, args); - } //------------------------------------------ERROR------------------------------------------// @@ -130,10 +114,8 @@ public static void LogWarning(this ILogger logger, string message, params object /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogError(exception, "Error while processing request from {Address}", address) - public static void LogError(this ILogger logger, Exception exception, string message, params object[] args) - { + public static void LogError(this ILogger logger, Exception exception, string message, params object[] args) => logger.Log(LogLevel.Error, exception, message, args); - } /// /// Formats and writes an error log message. @@ -142,10 +124,8 @@ public static void LogError(this ILogger logger, Exception exception, string mes /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogError("Processing request from {Address}", address) - public static void LogError(this ILogger logger, string message, params object[] args) - { + public static void LogError(this ILogger logger, string message, params object[] args) => logger.Log(LogLevel.Error, message, args); - } //------------------------------------------CRITICAL------------------------------------------// @@ -157,10 +137,8 @@ public static void LogError(this ILogger logger, string message, params object[] /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogCritical(exception, "Error while processing request from {Address}", address) - public static void LogCritical(this ILogger logger, Exception exception, string message, params object[] args) - { + public static void LogCritical(this ILogger logger, Exception exception, string message, params object[] args) => logger.Log(LogLevel.Critical, exception, message, args); - } /// /// Formats and writes a critical log message. @@ -169,10 +147,8 @@ public static void LogCritical(this ILogger logger, Exception exception, string /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogCritical("Processing request from {Address}", address) - public static void LogCritical(this ILogger logger, string message, params object[] args) - { + public static void LogCritical(this ILogger logger, string message, params object[] args) => logger.Log(LogLevel.Critical, message, args); - } /// /// Formats and writes a log message at the specified log level. diff --git a/AdvancedSharpAdbClient/Interfaces/IDeviceMonitor.cs b/AdvancedSharpAdbClient/Interfaces/IDeviceMonitor.cs index 15072324..1a84a083 100644 --- a/AdvancedSharpAdbClient/Interfaces/IDeviceMonitor.cs +++ b/AdvancedSharpAdbClient/Interfaces/IDeviceMonitor.cs @@ -30,6 +30,11 @@ public partial interface IDeviceMonitor : IDisposable /// event EventHandler DeviceConnected; + /// + /// Occurs when the list of the connected devices has changed. + /// + event EventHandler DeviceListChanged; + /// /// Occurs when a device has disconnected from the Android Debug Bridge. /// diff --git a/AdvancedSharpAdbClient/Models/AdbResponse.cs b/AdvancedSharpAdbClient/Models/AdbResponse.cs index f693c3ea..e0f2f76a 100644 --- a/AdvancedSharpAdbClient/Models/AdbResponse.cs +++ b/AdvancedSharpAdbClient/Models/AdbResponse.cs @@ -91,16 +91,7 @@ public readonly bool Equals(AdbResponse other) => /// Gets the hash code for the current . /// /// A hash code for the current . - public override readonly int GetHashCode() - { - int hash = 17; - hash = (hash * 23) + IOSuccess.GetHashCode(); - hash = (hash * 23) + Message == null ? 0 : Message.GetHashCode(); - hash = (hash * 23) + Okay.GetHashCode(); - hash = (hash * 23) + Timeout.GetHashCode(); - - return hash; - } + public override readonly int GetHashCode() => HashCode.Combine(IOSuccess, Message, Okay, Timeout); /// /// Returns a that represents the current . diff --git a/AdvancedSharpAdbClient/Models/Area.cs b/AdvancedSharpAdbClient/Models/Area.cs index 1fc55e52..6886cfb6 100644 --- a/AdvancedSharpAdbClient/Models/Area.cs +++ b/AdvancedSharpAdbClient/Models/Area.cs @@ -453,12 +453,7 @@ public readonly bool Contains(Area rect) => /// Returns the hash code for this structure. /// /// An integer that represents the hash code for this rectangle. - public override readonly int GetHashCode() => -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - HashCode.Combine(X, Y, Width, Height); -#else - X ^ Y ^ Width ^ Height; -#endif + public override readonly int GetHashCode() => HashCode.Combine(X, Y, Width, Height); /// /// Inflates this by the specified amount. diff --git a/AdvancedSharpAdbClient/Models/Cords.cs b/AdvancedSharpAdbClient/Models/Cords.cs index 14b0869a..e6b924d8 100644 --- a/AdvancedSharpAdbClient/Models/Cords.cs +++ b/AdvancedSharpAdbClient/Models/Cords.cs @@ -320,12 +320,7 @@ public Cords(Windows.Foundation.Size sz) /// Returns a hash code for this . /// /// An integer value that specifies a hash value for this . - public override readonly int GetHashCode() => -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - HashCode.Combine(X, Y); -#else - X ^ Y; -#endif + public override readonly int GetHashCode() => HashCode.Combine(X, Y); /// /// Translates this by the specified amount. diff --git a/AdvancedSharpAdbClient/Models/DeviceData.cs b/AdvancedSharpAdbClient/Models/DeviceData.cs index 6a510c5f..603a020e 100644 --- a/AdvancedSharpAdbClient/Models/DeviceData.cs +++ b/AdvancedSharpAdbClient/Models/DeviceData.cs @@ -10,7 +10,7 @@ namespace AdvancedSharpAdbClient /// /// Represents a device that is connected to the Android Debug Bridge. /// - public partial class DeviceData + public partial class DeviceData : IEquatable { /// /// A regular expression that can be used to parse the device information that is returned by the Android Debut Bridge. @@ -106,6 +106,38 @@ public DeviceData(string data) /// A object that represents the device. public static DeviceData CreateFromAdbData(string data) => new(data); + /// + public override bool Equals(object obj) => Equals(obj as DeviceData); + + /// + public bool Equals(DeviceData other) => + other is not null + && Serial == other.Serial + && State == other.State + && Model == other.Model + && Product == other.Product + && Name == other.Name + && Features == other.Features + && Usb == other.Usb + && TransportId == other.TransportId + && Message == other.Message; + + /// + public override int GetHashCode() + { + HashCode hash = new(); + hash.Add(Serial); + hash.Add(State); + hash.Add(Model); + hash.Add(Product); + hash.Add(Name); + hash.Add(Features); + hash.Add(Usb); + hash.Add(TransportId); + hash.Add(Message); + return hash.ToHashCode(); + } + /// public override string ToString() => Serial; diff --git a/AdvancedSharpAdbClient/Models/DnsEndPoint.cs b/AdvancedSharpAdbClient/Models/DnsEndPoint.cs index 9bd51f32..da375e03 100644 --- a/AdvancedSharpAdbClient/Models/DnsEndPoint.cs +++ b/AdvancedSharpAdbClient/Models/DnsEndPoint.cs @@ -1,5 +1,4 @@ #if NETFRAMEWORK && !NET40_OR_GREATER -using AdvancedSharpAdbClient.Exceptions; using System; using System.Net; using System.Net.Sockets; diff --git a/AdvancedSharpAdbClient/Models/Element.cs b/AdvancedSharpAdbClient/Models/Element.cs index 74029e3c..410baee9 100644 --- a/AdvancedSharpAdbClient/Models/Element.cs +++ b/AdvancedSharpAdbClient/Models/Element.cs @@ -49,7 +49,7 @@ public Element(IAdbClient client, DeviceData device, XmlNode xmlNode) string[] cords = bounds.Split(separator, StringSplitOptions.RemoveEmptyEntries); // x1, y1, x2, y2 Bounds = Area.FromLTRB(int.Parse(cords[0]), int.Parse(cords[1]), int.Parse(cords[2]), int.Parse(cords[3])); } - + Attributes = new(xmlNode.Attributes.Count); foreach (XmlAttribute at in xmlNode.Attributes) { @@ -310,7 +310,7 @@ public Element FindDescendant(Func predicate) /// /// Find the first descendant (or self) element matching a given predicate, using a depth-first search. /// - /// The predicatee to use to match the descendant nodes. + /// The predicate to use to match the descendant nodes. /// The descendant (or self) that was found, or . public Element FindDescendantOrSelf(Func predicate) => predicate(this) ? this : FindDescendant(predicate); @@ -354,18 +354,9 @@ other is not null /// public override int GetHashCode() => -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - HashCode.Combine(Client, Device, Node == null ? HashCode.Combine(Bounds, Attributes) : Node.GetHashCode()); -#else - Client.GetHashCode() - ^ Device.GetHashCode() - ^ (Node == null - ? Bounds.GetHashCode() - ^ (Attributes == null - ? 1 - : Attributes.GetHashCode()) - : Node.GetHashCode()); -#endif + Node == null + ? HashCode.Combine(Client, Device, Bounds, Attributes) + : HashCode.Combine(Client, Device, Node); /// public override string ToString() => diff --git a/AdvancedSharpAdbClient/Models/FileStatistics.cs b/AdvancedSharpAdbClient/Models/FileStatistics.cs index 88a38419..8822925e 100644 --- a/AdvancedSharpAdbClient/Models/FileStatistics.cs +++ b/AdvancedSharpAdbClient/Models/FileStatistics.cs @@ -9,7 +9,7 @@ namespace AdvancedSharpAdbClient /// /// Contains information about a file on the remote device. /// - public class FileStatistics + public class FileStatistics : IEquatable { /// /// Initializes a new instance of the class. @@ -41,5 +41,19 @@ public FileStatistics() { } /// /// The of the current object. public override string ToString() => Path; + + /// + public override bool Equals(object obj) => Equals(obj as FileStatistics); + + /// + public bool Equals(FileStatistics other) => + other is not null + && Path == other.Path + && FileType == other.FileType + && Size == other.Size + && Time == other.Time; + + /// + public override int GetHashCode() => HashCode.Combine(Path, FileType, Size, Time); } } diff --git a/AdvancedSharpAdbClient/Models/ForwardData.cs b/AdvancedSharpAdbClient/Models/ForwardData.cs index 0bc64481..6fadec06 100644 --- a/AdvancedSharpAdbClient/Models/ForwardData.cs +++ b/AdvancedSharpAdbClient/Models/ForwardData.cs @@ -2,12 +2,14 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // +using System; + namespace AdvancedSharpAdbClient { /// /// Contains information about port forwarding configured by the Android Debug Bridge. /// - public class ForwardData + public class ForwardData : IEquatable { /// /// Initializes a new instance of the class. @@ -71,6 +73,19 @@ public ForwardData(string value) /// A object that represents the port forwarding information contained in . public static ForwardData FromString(string value) => value == null ? null : new ForwardData(value); + /// + public override bool Equals(object obj) => Equals(obj as ForwardData); + + /// + public bool Equals(ForwardData other) => + other is not null + && SerialNumber == other.SerialNumber + && Local == other.Local + && Remote == other.Remote; + + /// + public override int GetHashCode() => HashCode.Combine(SerialNumber, Local, Remote); + /// public override string ToString() => $"{SerialNumber} {Local} {Remote}"; } diff --git a/AdvancedSharpAdbClient/Models/ForwardSpec.cs b/AdvancedSharpAdbClient/Models/ForwardSpec.cs index 5eb4aa55..39233ccd 100644 --- a/AdvancedSharpAdbClient/Models/ForwardSpec.cs +++ b/AdvancedSharpAdbClient/Models/ForwardSpec.cs @@ -136,15 +136,7 @@ or ForwardProtocol.LocalReserved } /// - public override int GetHashCode() => -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - HashCode.Combine((int)Protocol, Port, ProcessId, SocketName == null ? 1 : SocketName.GetHashCode()); -#else - (int)Protocol - ^ Port - ^ ProcessId - ^ (SocketName == null ? 1 : SocketName.GetHashCode()); -#endif + public override int GetHashCode() => HashCode.Combine(Protocol, Port, ProcessId, SocketName); /// public override bool Equals(object obj) => Equals(obj as ForwardSpec); diff --git a/AdvancedSharpAdbClient/Models/HashCode.cs b/AdvancedSharpAdbClient/Models/HashCode.cs new file mode 100644 index 00000000..05136cb3 --- /dev/null +++ b/AdvancedSharpAdbClient/Models/HashCode.cs @@ -0,0 +1,561 @@ +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/* + +The xxHash32 implementation is based on the code published by Yann Collet: +https://raw.githubusercontent.com/Cyan4973/xxHash/5c174cfa4e45a42f94082dc0d4539b39696afea1/xxhash.c + + xxHash - Fast Hash algorithm + Copyright (C) 2012-2016, Yann Collet + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - xxHash homepage: http://www.xxhash.com + - xxHash source repository : https://github.com/Cyan4973/xxHash + +*/ + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace AdvancedSharpAdbClient +{ + // xxHash32 is used for the hash code. + // https://github.com/Cyan4973/xxHash + + /// + /// Combines the hash code for multiple values into a single hash code. + /// + public struct HashCode + { + private static readonly uint s_seed = GenerateGlobalSeed(); + + private const uint Prime1 = 2654435761U; + private const uint Prime2 = 2246822519U; + private const uint Prime3 = 3266489917U; + private const uint Prime4 = 668265263U; + private const uint Prime5 = 374761393U; + + private uint _v1, _v2, _v3, _v4; + private uint _queue1, _queue2, _queue3; + private uint _length; + + private static uint GenerateGlobalSeed() => (uint)new Random().Next(int.MinValue, int.MaxValue); + + /// + /// Diffuses the hash code returned by the specified value. + /// + /// The type of the value to add the hash code. + /// The value to add to the hash code. + /// The hash code that represents the single value. + public static int Combine(T1 value1) + { + // Provide a way of diffusing bits from something with a limited + // input hash space. For example, many enums only have a few + // possible hashes, only using the bottom few bits of the code. Some + // collections are built on the assumption that hashes are spread + // over a larger space, so diffusing the bits may help the + // collection work more efficiently. + + uint hc1 = (uint)(value1?.GetHashCode() ?? 0); + + uint hash = MixEmptyState(); + hash += 4; + + hash = QueueRound(hash, hc1); + + hash = MixFinal(hash); + return (int)hash; + } + + /// + /// Combines two values into a hash code. + /// + /// The type of the first value to combine into the hash code. + /// The type of the second value to combine into the hash code. + /// The first value to combine into the hash code. + /// The second value to combine into the hash code. + /// The hash code that represents the two values. + public static int Combine(T1 value1, T2 value2) + { + uint hc1 = (uint)(value1?.GetHashCode() ?? 0); + uint hc2 = (uint)(value2?.GetHashCode() ?? 0); + + uint hash = MixEmptyState(); + hash += 8; + + hash = QueueRound(hash, hc1); + hash = QueueRound(hash, hc2); + + hash = MixFinal(hash); + return (int)hash; + } + + /// + /// Combines three values into a hash code. + /// + /// The type of the first value to combine into the hash code. + /// The type of the second value to combine into the hash code. + /// The type of the third value to combine into the hash code. + /// The first value to combine into the hash code. + /// The second value to combine into the hash code. + /// The third value to combine into the hash code. + /// The hash code that represents the three values. + public static int Combine(T1 value1, T2 value2, T3 value3) + { + uint hc1 = (uint)(value1?.GetHashCode() ?? 0); + uint hc2 = (uint)(value2?.GetHashCode() ?? 0); + uint hc3 = (uint)(value3?.GetHashCode() ?? 0); + + uint hash = MixEmptyState(); + hash += 12; + + hash = QueueRound(hash, hc1); + hash = QueueRound(hash, hc2); + hash = QueueRound(hash, hc3); + + hash = MixFinal(hash); + return (int)hash; + } + + /// + /// Combines four values into a hash code. + /// + /// The type of the first value to combine into the hash code. + /// The type of the second value to combine into the hash code. + /// The type of the third value to combine into the hash code. + /// The type of the fourth value to combine into the hash code. + /// The first value to combine into the hash code. + /// The second value to combine into the hash code. + /// The third value to combine into the hash code. + /// The fourth value to combine into the hash code. + /// The hash code that represents the four values. + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4) + { + uint hc1 = (uint)(value1?.GetHashCode() ?? 0); + uint hc2 = (uint)(value2?.GetHashCode() ?? 0); + uint hc3 = (uint)(value3?.GetHashCode() ?? 0); + uint hc4 = (uint)(value4?.GetHashCode() ?? 0); + + Initialize(out uint v1, out uint v2, out uint v3, out uint v4); + + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + uint hash = MixState(v1, v2, v3, v4); + hash += 16; + + hash = MixFinal(hash); + return (int)hash; + } + + /// + /// Combines five values into a hash code. + /// + /// The type of the first value to combine into the hash code. + /// The type of the second value to combine into the hash code. + /// The type of the third value to combine into the hash code. + /// The type of the fourth value to combine into the hash code. + /// The type of the fifth value to combine into the hash code. + /// The first value to combine into the hash code. + /// The second value to combine into the hash code. + /// The third value to combine into the hash code. + /// The fourth value to combine into the hash code. + /// The fifth value to combine into the hash code. + /// The hash code that represents the five values. + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5) + { + uint hc1 = (uint)(value1?.GetHashCode() ?? 0); + uint hc2 = (uint)(value2?.GetHashCode() ?? 0); + uint hc3 = (uint)(value3?.GetHashCode() ?? 0); + uint hc4 = (uint)(value4?.GetHashCode() ?? 0); + uint hc5 = (uint)(value5?.GetHashCode() ?? 0); + + Initialize(out uint v1, out uint v2, out uint v3, out uint v4); + + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + uint hash = MixState(v1, v2, v3, v4); + hash += 20; + + hash = QueueRound(hash, hc5); + + hash = MixFinal(hash); + return (int)hash; + } + + /// + /// Combines six values into a hash code. + /// + /// The type of the first value to combine into the hash code. + /// The type of the second value to combine into the hash code. + /// The type of the third value to combine into the hash code. + /// The type of the fourth value to combine into the hash code. + /// The type of the fifth value to combine into the hash code. + /// The type of the sixth value to combine into the hash code. + /// The first value to combine into the hash code. + /// The second value to combine into the hash code. + /// The third value to combine into the hash code. + /// The fourth value to combine into the hash code. + /// The fifth value to combine into the hash code. + /// The sixth value to combine into the hash code. + /// The hash code that represents the six values. + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6) + { + uint hc1 = (uint)(value1?.GetHashCode() ?? 0); + uint hc2 = (uint)(value2?.GetHashCode() ?? 0); + uint hc3 = (uint)(value3?.GetHashCode() ?? 0); + uint hc4 = (uint)(value4?.GetHashCode() ?? 0); + uint hc5 = (uint)(value5?.GetHashCode() ?? 0); + uint hc6 = (uint)(value6?.GetHashCode() ?? 0); + + Initialize(out uint v1, out uint v2, out uint v3, out uint v4); + + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + uint hash = MixState(v1, v2, v3, v4); + hash += 24; + + hash = QueueRound(hash, hc5); + hash = QueueRound(hash, hc6); + + hash = MixFinal(hash); + return (int)hash; + } + + /// + /// Combines seven values into a hash code. + /// + /// The type of the first value to combine into the hash code. + /// The type of the second value to combine into the hash code. + /// The type of the third value to combine into the hash code. + /// The type of the fourth value to combine into the hash code. + /// The type of the fifth value to combine into the hash code. + /// The type of the sixth value to combine into the hash code. + /// The type of the seventh value to combine into the hash code. + /// The first value to combine into the hash code. + /// The second value to combine into the hash code. + /// The third value to combine into the hash code. + /// The fourth value to combine into the hash code. + /// The fifth value to combine into the hash code. + /// The sixth value to combine into the hash code. + /// The seventh value to combine into the hash code. + /// The hash code that represents the seven values. + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7) + { + uint hc1 = (uint)(value1?.GetHashCode() ?? 0); + uint hc2 = (uint)(value2?.GetHashCode() ?? 0); + uint hc3 = (uint)(value3?.GetHashCode() ?? 0); + uint hc4 = (uint)(value4?.GetHashCode() ?? 0); + uint hc5 = (uint)(value5?.GetHashCode() ?? 0); + uint hc6 = (uint)(value6?.GetHashCode() ?? 0); + uint hc7 = (uint)(value7?.GetHashCode() ?? 0); + + Initialize(out uint v1, out uint v2, out uint v3, out uint v4); + + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + uint hash = MixState(v1, v2, v3, v4); + hash += 28; + + hash = QueueRound(hash, hc5); + hash = QueueRound(hash, hc6); + hash = QueueRound(hash, hc7); + + hash = MixFinal(hash); + return (int)hash; + } + + /// + /// Combines eight values into a hash code. + /// + /// The type of the first value to combine into the hash code. + /// The type of the second value to combine into the hash code. + /// The type of the third value to combine into the hash code. + /// The type of the fourth value to combine into the hash code. + /// The type of the fifth value to combine into the hash code. + /// The type of the sixth value to combine into the hash code. + /// The type of the seventh value to combine into the hash code. + /// The type of the eighth value to combine into the hash code. + /// The first value to combine into the hash code. + /// The second value to combine into the hash code. + /// The third value to combine into the hash code. + /// The fourth value to combine into the hash code. + /// The fifth value to combine into the hash code. + /// The sixth value to combine into the hash code. + /// The seventh value to combine into the hash code. + /// The eighth value to combine into the hash code. + /// The hash code that represents the eight values. + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8) + { + uint hc1 = (uint)(value1?.GetHashCode() ?? 0); + uint hc2 = (uint)(value2?.GetHashCode() ?? 0); + uint hc3 = (uint)(value3?.GetHashCode() ?? 0); + uint hc4 = (uint)(value4?.GetHashCode() ?? 0); + uint hc5 = (uint)(value5?.GetHashCode() ?? 0); + uint hc6 = (uint)(value6?.GetHashCode() ?? 0); + uint hc7 = (uint)(value7?.GetHashCode() ?? 0); + uint hc8 = (uint)(value8?.GetHashCode() ?? 0); + + Initialize(out uint v1, out uint v2, out uint v3, out uint v4); + + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + v1 = Round(v1, hc5); + v2 = Round(v2, hc6); + v3 = Round(v3, hc7); + v4 = Round(v4, hc8); + + uint hash = MixState(v1, v2, v3, v4); + hash += 32; + + hash = MixFinal(hash); + return (int)hash; + } + + [MethodImpl((MethodImplOptions)256)] + private static void Initialize(out uint v1, out uint v2, out uint v3, out uint v4) + { + v1 = s_seed + Prime1 + Prime2; + v2 = s_seed + Prime2; + v3 = s_seed; + v4 = s_seed - Prime1; + } + + [MethodImpl((MethodImplOptions)256)] + private static uint Round(uint hash, uint input) + { + return RotateLeft(hash + input * Prime2, 13) * Prime1; + } + + [MethodImpl((MethodImplOptions)256)] + private static uint QueueRound(uint hash, uint queuedValue) + { + return RotateLeft(hash + queuedValue * Prime3, 17) * Prime4; + } + + [MethodImpl((MethodImplOptions)256)] + private static uint MixState(uint v1, uint v2, uint v3, uint v4) + { + return RotateLeft(v1, 1) + RotateLeft(v2, 7) + RotateLeft(v3, 12) + RotateLeft(v4, 18); + } + + private static uint MixEmptyState() + { + return s_seed + Prime5; + } + + [MethodImpl((MethodImplOptions)256)] + private static uint MixFinal(uint hash) + { + hash ^= hash >> 15; + hash *= Prime2; + hash ^= hash >> 13; + hash *= Prime3; + hash ^= hash >> 16; + return hash; + } + + /// + /// Rotates the specified value left by the specified number of bits. + /// Similar in behavior to the x86 instruction ROL. + /// + /// The value to rotate. + /// The number of bits to rotate by. + /// Any value outside the range [0..31] is treated as congruent mod 32. + /// The rotated value. + [MethodImpl((MethodImplOptions)256)] + private static uint RotateLeft(uint value, int offset) + => (value << offset) | (value >> (32 - offset)); + + /// + /// Adds a single value to the hash code. + /// + /// The type of the value to add to the hash code. + /// The value to add to the hash code. + public void Add(T value) + { + Add(value?.GetHashCode() ?? 0); + } + + /// + /// Adds a single value to the hash code, specifying the type that provides the hash code function. + /// + /// The type of the value to add to the hash code. + /// The value to add to the hash code. + /// The to use to calculate the hash code. This value can be a null reference (Nothing in Visual Basic), which will use the default equality comparer for . + public void Add(T value, IEqualityComparer comparer) + { + Add(value is null ? 0 : (comparer?.GetHashCode(value) ?? value.GetHashCode())); + } + + private void Add(int value) + { + // The original xxHash works as follows: + // 0. Initialize immediately. We can't do this in a struct (no + // default ctor). + // 1. Accumulate blocks of length 16 (4 uints) into 4 accumulators. + // 2. Accumulate remaining blocks of length 4 (1 uint) into the + // hash. + // 3. Accumulate remaining blocks of length 1 into the hash. + + // There is no need for #3 as this type only accepts ints. _queue1, + // _queue2 and _queue3 are basically a buffer so that when + // ToHashCode is called we can execute #2 correctly. + + // We need to initialize the xxHash32 state (_v1 to _v4) lazily (see + // #0) nd the last place that can be done if you look at the + // original code is just before the first block of 16 bytes is mixed + // in. The xxHash32 state is never used for streams containing fewer + // than 16 bytes. + + // To see what's really going on here, have a look at the Combine + // methods. + + uint val = (uint)value; + + // Storing the value of _length locally shaves of quite a few bytes + // in the resulting machine code. + uint previousLength = _length++; + uint position = previousLength % 4; + + // Switch can't be inlined. + + if (position == 0) + _queue1 = val; + else if (position == 1) + _queue2 = val; + else if (position == 2) + _queue3 = val; + else // position == 3 + { + if (previousLength == 3) + Initialize(out _v1, out _v2, out _v3, out _v4); + + _v1 = Round(_v1, _queue1); + _v2 = Round(_v2, _queue2); + _v3 = Round(_v3, _queue3); + _v4 = Round(_v4, val); + } + } + + /// + /// Calculates the final hash code after consecutive invocations. + /// + /// The calculated hash code. + public readonly int ToHashCode() + { + // Storing the value of _length locally shaves of quite a few bytes + // in the resulting machine code. + uint length = _length; + + // position refers to the *next* queue position in this method, so + // position == 1 means that _queue1 is populated; _queue2 would have + // been populated on the next call to Add. + uint position = length % 4; + + // If the length is less than 4, _v1 to _v4 don't contain anything + // yet. xxHash32 treats this differently. + + uint hash = length < 4 ? MixEmptyState() : MixState(_v1, _v2, _v3, _v4); + + // _length is incremented once per Add(Int32) and is therefore 4 + // times too small (xxHash length is in bytes, not ints). + + hash += length * 4; + + // Mix what remains in the queue + + // Switch can't be inlined right now, so use as few branches as + // possible by manually excluding impossible scenarios (position > 1 + // is always false if position is not > 0). + if (position > 0) + { + hash = QueueRound(hash, _queue1); + if (position > 1) + { + hash = QueueRound(hash, _queue2); + if (position > 2) + hash = QueueRound(hash, _queue3); + } + } + + hash = MixFinal(hash); + return (int)hash; + } + +#pragma warning disable 0809 + // Obsolete member 'memberA' overrides non-obsolete member 'memberB'. + // Disallowing GetHashCode and Equals is by design + + // * We decided to not override GetHashCode() to produce the hash code + // as this would be weird, both naming-wise as well as from a + // behavioral standpoint (GetHashCode() should return the object's + // hash code, not the one being computed). + + // * Even though ToHashCode() can be called safely multiple times on + // this implementation, it is not part of the contract. If the + // implementation has to change in the future we don't want to worry + // about people who might have incorrectly used this type. + + /// + [Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes. Use ToHashCode to retrieve the computed hash code.", error: true)] + [EditorBrowsable(EditorBrowsableState.Never)] + public override readonly int GetHashCode() => throw new NotSupportedException("HashCode is a mutable struct and should not be compared with other HashCodes. Use ToHashCode to retrieve the computed hash code."); + + /// + [Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes.", error: true)] + [EditorBrowsable(EditorBrowsableState.Never)] + public override readonly bool Equals(object obj) => throw new NotSupportedException("HashCode is a mutable struct and should not be compared with other HashCodes."); + + /// + public static bool operator ==(HashCode left, HashCode right) => left.Equals(right); + + /// + public static bool operator !=(HashCode left, HashCode right) => !(left == right); +#pragma warning restore 0809 + } +} +#endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs b/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs index 97082d73..438329ca 100644 --- a/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs +++ b/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs @@ -119,9 +119,9 @@ protected override void ProcessNewLines(IEnumerable lines) private static partial Regex DeniedRegex(); #else private static Regex AbortingRegex() => new("Aborting.$", DefaultRegexOptions); - + private static Regex AppletRegex() => new("applet not found$", DefaultRegexOptions); - + private static Regex DeniedRegex() => new("(permission|access) denied$", DefaultRegexOptions); #endif } From a6e1b21b2bee7d5abd21b19d6cf0f5aa25930c25 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Wed, 11 Oct 2023 16:51:12 +0800 Subject: [PATCH 35/66] Add DoesNotReturn --- .../AdbCommandLineClient.Async.cs | 4 ++ .../AdbCommandLineClient.cs | 4 ++ AdvancedSharpAdbClient/DeviceMonitor.Async.cs | 12 ++--- AdvancedSharpAdbClient/DeviceMonitor.cs | 46 +++---------------- .../Attributes/DoesNotReturnAttribute.cs | 16 +++++++ .../Attributes/DoesNotReturnIfAttribute.cs | 25 ++++++++++ .../ExcludeFromCodeCoverageAttribute.cs | 3 ++ .../Attributes/StackTraceHiddenAttribute.cs | 2 +- .../Extensions/ExceptionExtensions.cs | 6 +-- AdvancedSharpAdbClient/Models/DeviceData.cs | 10 ++-- AdvancedSharpAdbClient/Models/HashCode.cs | 3 ++ AdvancedSharpAdbClient/Models/ShellStream.cs | 5 ++ 12 files changed, 77 insertions(+), 59 deletions(-) create mode 100644 AdvancedSharpAdbClient/Extensions/Attributes/DoesNotReturnAttribute.cs create mode 100644 AdvancedSharpAdbClient/Extensions/Attributes/DoesNotReturnIfAttribute.cs diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs index 5fd47357..58976085 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Threading; namespace AdvancedSharpAdbClient @@ -132,6 +133,9 @@ protected virtual async Task RunAdbProcessInnerAsync(string command, IColle /// Runs process, invoking a specific command, and reads the standard output and standard error output. /// /// The return code of the process. +#if !HAS_PROCESS + [DoesNotReturn] +#endif protected virtual async Task RunProcessAsync(string filename, string command, ICollection errorOutput, ICollection standardOutput, CancellationToken cancellationToken = default) { #if HAS_PROCESS diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.cs index 57e177af..3a5e3cea 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text.RegularExpressions; @@ -236,6 +237,9 @@ protected virtual int RunAdbProcessInner(string command, ICollection err /// Runs process, invoking a specific command, and reads the standard output and standard error output. /// /// The return code of the process. +#if !HAS_PROCESS + [DoesNotReturn] +#endif protected virtual int RunProcess(string filename, string command, ICollection errorOutput, ICollection standardOutput) { #if HAS_PROCESS diff --git a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs index fd2830ae..b6d3bd7e 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs @@ -45,13 +45,11 @@ public virtual async Task StartAsync(CancellationToken cancellationToken = defau /// /// Stops the monitoring /// - protected virtual async #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - ValueTask + protected virtual async ValueTask DisposeAsyncCore() #else - Task + protected virtual async Task DisposeAsyncCore() #endif - DisposeAsyncCore() { if (disposed) { return; } @@ -85,13 +83,11 @@ protected virtual async } /// - public async #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - ValueTask + public async ValueTask DisposeAsync() #else - Task + public async Task DisposeAsync() #endif - DisposeAsync() { await DisposeAsyncCore().ConfigureAwait(false); Dispose(disposing: false); diff --git a/AdvancedSharpAdbClient/DeviceMonitor.cs b/AdvancedSharpAdbClient/DeviceMonitor.cs index 9f876dbd..e0680be0 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.cs @@ -201,36 +201,6 @@ public void Dispose() GC.SuppressFinalize(this); } - /// - /// Raises the event. - /// - /// The instance containing the event data. - protected void OnDeviceChanged(DeviceDataChangeEventArgs e) => DeviceChanged?.Invoke(this, e); - - /// - /// Raises the event. - /// - /// The instance containing the event data. - protected void OnDeviceNotified(DeviceDataNotifyEventArgs e) => DeviceNotified?.Invoke(this, e); - - /// - /// Raises the event. - /// - /// The instance containing the event data. - protected void OnDeviceConnected(DeviceDataConnectEventArgs e) => DeviceConnected?.Invoke(this, e); - - /// - /// Raises the event. - /// - /// The instance containing the event data. - protected void OnDeviceListChanged(DeviceDataNotifyEventArgs e) => DeviceListChanged?.Invoke(this, e); - - /// - /// Raises the event. - /// - /// The instance containing the event data. - protected void OnDeviceDisconnected(DeviceDataConnectEventArgs e) => DeviceDisconnected?.Invoke(this, e); - #if !HAS_TASK /// /// Monitors the devices. This connects to the Debug Bridge @@ -284,10 +254,9 @@ private void InitializeSocket() private void ProcessIncomingDeviceData(string result) { string[] deviceValues = result.Split(separator, StringSplitOptions.RemoveEmptyEntries); - IEnumerable currentDevices = deviceValues.Select(x => new DeviceData(x)); UpdateDevices(currentDevices); - OnDeviceNotified(new DeviceDataNotifyEventArgs(currentDevices)); + DeviceNotified?.Invoke(this, new DeviceDataNotifyEventArgs(currentDevices)); } /// @@ -307,9 +276,8 @@ protected virtual void UpdateDevices(IEnumerable collection) // add them to the list, and start monitoring them. bool isChanged = false; - int length = this.devices.Count; List devices = collection.ToList(); - for (int i = 0; i < length;) + for (int i = this.devices.Count; --i >= 0;) { DeviceData currentDevice = this.devices[i]; int index = devices.FindIndex(d => d.Serial == currentDevice.Serial); @@ -317,9 +285,8 @@ protected virtual void UpdateDevices(IEnumerable collection) { // Remove disconnected devices this.devices.RemoveAt(i); - OnDeviceDisconnected(new DeviceDataConnectEventArgs(currentDevice, false)); + DeviceDisconnected?.Invoke(this, new DeviceDataConnectEventArgs(currentDevice, false)); isChanged = true; - length--; } else { @@ -328,11 +295,10 @@ protected virtual void UpdateDevices(IEnumerable collection) { // Change device state this.devices[i] = device; - OnDeviceChanged(new DeviceDataChangeEventArgs(device, device.State, currentDevice.State)); + DeviceChanged?.Invoke(this, new DeviceDataChangeEventArgs(device, device.State, currentDevice.State)); isChanged = true; } devices.RemoveAt(index); - i++; } } @@ -342,14 +308,14 @@ protected virtual void UpdateDevices(IEnumerable collection) foreach (DeviceData device in devices) { this.devices.Add(device); - OnDeviceConnected(new DeviceDataConnectEventArgs(device, false)); + DeviceConnected?.Invoke(this, new DeviceDataConnectEventArgs(device, false)); } isChanged = true; } if (isChanged) { - OnDeviceListChanged(new DeviceDataNotifyEventArgs(devices)); + DeviceListChanged?.Invoke(this, new DeviceDataNotifyEventArgs(devices)); } } } diff --git a/AdvancedSharpAdbClient/Extensions/Attributes/DoesNotReturnAttribute.cs b/AdvancedSharpAdbClient/Extensions/Attributes/DoesNotReturnAttribute.cs new file mode 100644 index 00000000..574e30e1 --- /dev/null +++ b/AdvancedSharpAdbClient/Extensions/Attributes/DoesNotReturnAttribute.cs @@ -0,0 +1,16 @@ +#if !HAS_INDEXRANGE +namespace System.Diagnostics.CodeAnalysis +{ + /// + /// Specifies that a method that will never return under any circumstance. + /// + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + internal sealed class DoesNotReturnAttribute : Attribute + { + /// + /// Initializes a new instance of the class. + /// + public DoesNotReturnAttribute() { } + } +} +#endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Extensions/Attributes/DoesNotReturnIfAttribute.cs b/AdvancedSharpAdbClient/Extensions/Attributes/DoesNotReturnIfAttribute.cs new file mode 100644 index 00000000..1f1a8345 --- /dev/null +++ b/AdvancedSharpAdbClient/Extensions/Attributes/DoesNotReturnIfAttribute.cs @@ -0,0 +1,25 @@ +#if !HAS_INDEXRANGE +// 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 method will not return if the associated parameter is passed the specified value. + /// + 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 \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Extensions/Attributes/ExcludeFromCodeCoverageAttribute.cs b/AdvancedSharpAdbClient/Extensions/Attributes/ExcludeFromCodeCoverageAttribute.cs index 93ef6252..2ac26093 100644 --- a/AdvancedSharpAdbClient/Extensions/Attributes/ExcludeFromCodeCoverageAttribute.cs +++ b/AdvancedSharpAdbClient/Extensions/Attributes/ExcludeFromCodeCoverageAttribute.cs @@ -16,6 +16,9 @@ namespace System.Diagnostics.CodeAnalysis )] internal sealed class ExcludeFromCodeCoverageAttribute : Attribute { + /// + /// Initializes a new instance of the class. + /// public ExcludeFromCodeCoverageAttribute() { } } } diff --git a/AdvancedSharpAdbClient/Extensions/Attributes/StackTraceHiddenAttribute.cs b/AdvancedSharpAdbClient/Extensions/Attributes/StackTraceHiddenAttribute.cs index 4ebf69ce..232d640e 100644 --- a/AdvancedSharpAdbClient/Extensions/Attributes/StackTraceHiddenAttribute.cs +++ b/AdvancedSharpAdbClient/Extensions/Attributes/StackTraceHiddenAttribute.cs @@ -6,7 +6,7 @@ namespace System.Diagnostics { /// /// Types and Methods attributed with StackTraceHidden will be omitted from the stack trace text shown in StackTrace.ToString() - /// and Exception.StackTrace + /// and Exception.StackTrace. /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Struct, Inherited = false)] internal sealed class StackTraceHiddenAttribute : Attribute diff --git a/AdvancedSharpAdbClient/Extensions/ExceptionExtensions.cs b/AdvancedSharpAdbClient/Extensions/ExceptionExtensions.cs index 4ab9d9f9..794f6250 100644 --- a/AdvancedSharpAdbClient/Extensions/ExceptionExtensions.cs +++ b/AdvancedSharpAdbClient/Extensions/ExceptionExtensions.cs @@ -89,11 +89,7 @@ public static void ThrowIfNegative(int value, [CallerArgumentExpression(nameof(v /// The object whose type's full name should be included in any resulting . /// The is . [StackTraceHidden] - public static void ThrowIf( -#if HAS_INDEXRANGE - [DoesNotReturnIf(true)] -#endif - bool condition, object instance) + public static void ThrowIf([DoesNotReturnIf(true)] bool condition, object instance) { #if NET7_0_OR_GREATER ObjectDisposedException.ThrowIf(condition, instance); diff --git a/AdvancedSharpAdbClient/Models/DeviceData.cs b/AdvancedSharpAdbClient/Models/DeviceData.cs index 603a020e..43b94b68 100644 --- a/AdvancedSharpAdbClient/Models/DeviceData.cs +++ b/AdvancedSharpAdbClient/Models/DeviceData.cs @@ -35,11 +35,7 @@ public DeviceData() { } public DeviceData(string data) { Match match = Regex.Match(data); - if (!match.Success) - { - throw new ArgumentException($"Invalid device list data '{data}'"); - } - else + if (match.Success) { Serial = match.Groups["serial"].Value; State = GetStateFromString(match.Groups["state"].Value); @@ -51,6 +47,10 @@ public DeviceData(string data) TransportId = match.Groups["transport_id"].Value; Message = match.Groups["message"].Value; } + else + { + throw new ArgumentException($"Invalid device list data '{data}'"); + } } /// diff --git a/AdvancedSharpAdbClient/Models/HashCode.cs b/AdvancedSharpAdbClient/Models/HashCode.cs index 05136cb3..af1497b4 100644 --- a/AdvancedSharpAdbClient/Models/HashCode.cs +++ b/AdvancedSharpAdbClient/Models/HashCode.cs @@ -44,6 +44,7 @@ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT using System; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; namespace AdvancedSharpAdbClient @@ -543,11 +544,13 @@ public readonly int ToHashCode() /// [Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes. Use ToHashCode to retrieve the computed hash code.", error: true)] [EditorBrowsable(EditorBrowsableState.Never)] + [DoesNotReturn] public override readonly int GetHashCode() => throw new NotSupportedException("HashCode is a mutable struct and should not be compared with other HashCodes. Use ToHashCode to retrieve the computed hash code."); /// [Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes.", error: true)] [EditorBrowsable(EditorBrowsableState.Never)] + [DoesNotReturn] public override readonly bool Equals(object obj) => throw new NotSupportedException("HashCode is a mutable struct and should not be compared with other HashCodes."); /// diff --git a/AdvancedSharpAdbClient/Models/ShellStream.cs b/AdvancedSharpAdbClient/Models/ShellStream.cs index 2ae4bc74..25190ac1 100644 --- a/AdvancedSharpAdbClient/Models/ShellStream.cs +++ b/AdvancedSharpAdbClient/Models/ShellStream.cs @@ -3,6 +3,7 @@ // using System; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Threading; @@ -485,15 +486,19 @@ async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToke #endif /// + [DoesNotReturn] public override void Flush() => throw new NotImplementedException(); /// + [DoesNotReturn] public override long Seek(long offset, SeekOrigin origin) => throw new NotImplementedException(); /// + [DoesNotReturn] public override void SetLength(long value) => throw new NotImplementedException(); /// + [DoesNotReturn] public override void Write(byte[] buffer, int offset, int count) => throw new NotImplementedException(); /// From c448e38995aa4a4398d069abe2d60ec5ae7850e8 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Fri, 13 Oct 2023 15:21:34 +0800 Subject: [PATCH 36/66] Fix DeviceMonitor Dispose #78 --- AdvancedSharpAdbClient/AdbServer.Async.cs | 30 +++++----- AdvancedSharpAdbClient/AdbServer.cs | 30 +++++----- AdvancedSharpAdbClient/DeviceMonitor.Async.cs | 36 +++++++++++- AdvancedSharpAdbClient/DeviceMonitor.cs | 55 ++++++++++++++++++- 4 files changed, 119 insertions(+), 32 deletions(-) diff --git a/AdvancedSharpAdbClient/AdbServer.Async.cs b/AdvancedSharpAdbClient/AdbServer.Async.cs index 70221dc3..1f066a80 100644 --- a/AdvancedSharpAdbClient/AdbServer.Async.cs +++ b/AdvancedSharpAdbClient/AdbServer.Async.cs @@ -13,7 +13,7 @@ namespace AdvancedSharpAdbClient public partial class AdbServer { /// - public virtual async Task StartServerAsync(string adbPath, bool restartServerIfNewer, CancellationToken cancellationToken = default) + public virtual async Task StartServerAsync(string adbPath, bool restartServerIfNewer = false, CancellationToken cancellationToken = default) { AdbServerStatus serverStatus = await GetStatusAsync(cancellationToken).ConfigureAwait(false); Version commandLineVersion = null; @@ -37,27 +37,29 @@ public virtual async Task StartServerAsync(string adbPath, bo : throw new AdbException($"The adb daemon is running an outdated version ${commandLineVersion}, but not valid path to the adb.exe executable was provided. A more recent version of the adb server cannot be started."); } - if (serverStatus.IsRunning - && ((serverStatus.Version < RequiredAdbVersion) - || ((serverStatus.Version < commandLineVersion) && restartServerIfNewer))) + if (serverStatus.IsRunning) { - ExceptionExtensions.ThrowIfNull(adbPath); + if (serverStatus.Version < RequiredAdbVersion + || (serverStatus.Version < commandLineVersion && restartServerIfNewer)) + { + ExceptionExtensions.ThrowIfNull(adbPath); - await adbClient.KillAdbAsync(cancellationToken).ConfigureAwait(false); - await commandLineClient.StartServerAsync(cancellationToken).ConfigureAwait(false); - return StartServerResult.RestartedOutdatedDaemon; + await adbClient.KillAdbAsync(cancellationToken); + await commandLineClient.StartServerAsync(cancellationToken); + return StartServerResult.RestartedOutdatedDaemon; + } + else + { + return StartServerResult.AlreadyRunning; + } } - else if (!serverStatus.IsRunning) + else { ExceptionExtensions.ThrowIfNull(adbPath); - await commandLineClient.StartServerAsync(cancellationToken).ConfigureAwait(false); + await commandLineClient.StartServerAsync(cancellationToken); return StartServerResult.Started; } - else - { - return StartServerResult.AlreadyRunning; - } } /// diff --git a/AdvancedSharpAdbClient/AdbServer.cs b/AdvancedSharpAdbClient/AdbServer.cs index 33697502..09f0ee87 100644 --- a/AdvancedSharpAdbClient/AdbServer.cs +++ b/AdvancedSharpAdbClient/AdbServer.cs @@ -100,7 +100,7 @@ public AdbServer(Func adbCommandLineClientFactory protected static Func CheckFileExists { get; set; } = Factories.CheckFileExists; /// - public virtual StartServerResult StartServer(string adbPath, bool restartServerIfNewer) + public virtual StartServerResult StartServer(string adbPath, bool restartServerIfNewer = false) { AdbServerStatus serverStatus = GetStatus(); Version commandLineVersion = null; @@ -124,27 +124,29 @@ public virtual StartServerResult StartServer(string adbPath, bool restartServerI : throw new AdbException($"The adb daemon is running an outdated version ${commandLineVersion}, but not valid path to the adb.exe executable was provided. A more recent version of the adb server cannot be started."); } - if (serverStatus.IsRunning - && ((serverStatus.Version < RequiredAdbVersion) - || ((serverStatus.Version < commandLineVersion) && restartServerIfNewer))) + if (serverStatus.IsRunning) { - ExceptionExtensions.ThrowIfNull(adbPath); + if (serverStatus.Version < RequiredAdbVersion + || (serverStatus.Version < commandLineVersion && restartServerIfNewer)) + { + ExceptionExtensions.ThrowIfNull(adbPath); - adbClient.KillAdb(); - commandLineClient.StartServer(); - return StartServerResult.RestartedOutdatedDaemon; + adbClient.KillAdb(); + commandLineClient.StartServer(); + return StartServerResult.RestartedOutdatedDaemon; + } + else + { + return StartServerResult.AlreadyRunning; + } } - else if (!serverStatus.IsRunning) + else { ExceptionExtensions.ThrowIfNull(adbPath); commandLineClient.StartServer(); return StartServerResult.Started; } - else - { - return StartServerResult.AlreadyRunning; - } } /// @@ -159,7 +161,7 @@ public virtual StartServerResult RestartServer(string adbPath = null) lock (RestartLock) { - return StartServer(adbPath, false); + return StartServer(adbPath, true); } } diff --git a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs index b6d3bd7e..a6c5ae52 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs @@ -5,6 +5,7 @@ using AdvancedSharpAdbClient.Exceptions; using System; +using System.Net.Sockets; using System.Threading; namespace AdvancedSharpAdbClient @@ -137,7 +138,23 @@ protected virtual async Task DeviceMonitorLoopAsync(CancellationToken cancellati } catch (ObjectDisposedException ex) { - // ... but an ObjectDisposedException on .NET Core on Linux and macOS. + // ... but an ObjectDisposedException on .NET Core App on Linux and macOS. + if (cancellationToken.IsCancellationRequested) + { + // The DeviceMonitor is shutting down (disposing) and Dispose() + // has called cancellationToken.Cancel(). This exception is expected, + // so we can safely swallow it. + } + else + { + // The exception was unexpected, so log it & rethrow. + logger.LogError(ex, ex.Message); + throw; + } + } + catch (OperationCanceledException ex) + { + // ... and an OperationCanceledException on .NET Core App 2.1 or greater. if (cancellationToken.IsCancellationRequested) { // The DeviceMonitor is shutting down (disposing) and Dispose() @@ -153,7 +170,22 @@ protected virtual async Task DeviceMonitorLoopAsync(CancellationToken cancellati } catch (AdbException adbException) { - if (adbException.ConnectionReset) + if (adbException.InnerException is SocketException ex) + { + if (cancellationToken.IsCancellationRequested) + { + // The DeviceMonitor is shutting down (disposing) and Dispose() + // has called Socket.Close(). This exception is expected, + // so we can safely swallow it. + } + else + { + // The exception was unexpected, so log it & rethrow. + logger.LogError(ex, ex.Message); + throw ex; + } + } + else if (adbException.ConnectionReset) { // The adb server was killed, for whatever reason. Try to restart it and recover from this. await AdbServer.Instance.RestartServerAsync(cancellationToken).ConfigureAwait(false); diff --git a/AdvancedSharpAdbClient/DeviceMonitor.cs b/AdvancedSharpAdbClient/DeviceMonitor.cs index e0680be0..909e1a92 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.cs @@ -8,6 +8,8 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using System.Net; +using System.Net.Sockets; using System.Threading; namespace AdvancedSharpAdbClient @@ -56,6 +58,12 @@ public partial class DeviceMonitor : IDeviceMonitor /// 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); + /// /// A that can be used to cancel the . /// @@ -82,6 +90,25 @@ public partial class DeviceMonitor : IDeviceMonitor /// public event EventHandler DeviceDisconnected; + /// + /// Initializes a new instance of the class. + /// + /// The logger to use when logging. + public DeviceMonitor(ILogger logger = null) + : this(Factories.AdbSocketFactory(new IPEndPoint(IPAddress.Loopback, AdbClient.AdbServerPort)), logger) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The at which the adb server is listening. + /// The logger to use when logging. + public DeviceMonitor(EndPoint endPoint, ILogger logger = null) + : this(Factories.AdbSocketFactory(endPoint), logger) + { + } + /// /// Initializes a new instance of the class. /// @@ -128,7 +155,12 @@ public virtual void Start() { _ = firstDeviceListParsed.Reset(); - monitorThread = new Thread(DeviceMonitorLoop); + monitorThread = new Thread(DeviceMonitorLoop) + { + Name = nameof(DeviceMonitorLoop), + IsBackground = true + }; + monitorThread.Start(); // Wait for the worker thread to have read the first list of devices. _ = firstDeviceListParsed.WaitOne(); @@ -178,6 +210,8 @@ protected virtual void Dispose(bool disposing) // Stop the thread. The tread will keep waiting for updated information from adb // eternally, so we need to forcefully abort it here. isMonitorThreadCancel = true; + Socket?.Close(); + _ = monitorLoopFinished.WaitOne(); monitorThread = null; } @@ -208,6 +242,7 @@ public void Dispose() protected virtual void DeviceMonitorLoop() { IsRunning = true; + monitorLoopFinished.Reset(); // Set up the connection to track the list of devices. InitializeSocket(); @@ -223,7 +258,22 @@ protected virtual void DeviceMonitorLoop() } catch (AdbException adbException) { - if (adbException.ConnectionReset) + if (adbException.InnerException is SocketException ex) + { + if (isMonitorThreadCancel) + { + // The DeviceMonitor is shutting down (disposing) and Dispose() + // has called Socket.Close(). This exception is expected, + // so we can safely swallow it. + } + else + { + // The exception was unexpected, so log it & rethrow. + logger.LogError(ex, ex.Message); + throw ex; + } + } + else if (adbException.ConnectionReset) { // The adb server was killed, for whatever reason. Try to restart it and recover from this. AdbServer.Instance.RestartServer(); @@ -238,6 +288,7 @@ protected virtual void DeviceMonitorLoop() } while (!isMonitorThreadCancel); isMonitorThreadCancel = false; + monitorLoopFinished.Set(); } private void InitializeSocket() From 52b9872e403178ad5e6881f2eb8ace4aca8aa02c Mon Sep 17 00:00:00 2001 From: wherewhere Date: Fri, 13 Oct 2023 18:17:27 +0800 Subject: [PATCH 37/66] Fix DeviceMonitorLoop exception catch --- AdvancedSharpAdbClient/DeviceMonitor.Async.cs | 16 +++-- AdvancedSharpAdbClient/DeviceMonitor.cs | 72 ++++++++++--------- 2 files changed, 49 insertions(+), 39 deletions(-) diff --git a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs index a6c5ae52..e807d5f1 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs @@ -178,6 +178,13 @@ protected virtual async Task DeviceMonitorLoopAsync(CancellationToken cancellati // has called Socket.Close(). This exception is expected, // so we can safely swallow it. } + else if (adbException.ConnectionReset) + { + // The adb server was killed, for whatever reason. Try to restart it and recover from this. + await AdbServer.Instance.RestartServerAsync(cancellationToken).ConfigureAwait(false); + Socket.Reconnect(); + await InitializeSocketAsync(cancellationToken).ConfigureAwait(false); + } else { // The exception was unexpected, so log it & rethrow. @@ -185,15 +192,10 @@ protected virtual async Task DeviceMonitorLoopAsync(CancellationToken cancellati throw ex; } } - else if (adbException.ConnectionReset) - { - // The adb server was killed, for whatever reason. Try to restart it and recover from this. - await AdbServer.Instance.RestartServerAsync(cancellationToken).ConfigureAwait(false); - Socket.Reconnect(); - await InitializeSocketAsync(cancellationToken).ConfigureAwait(false); - } else { + // The exception was unexpected, so log it & rethrow. + logger.LogError(adbException, adbException.Message); throw; } } diff --git a/AdvancedSharpAdbClient/DeviceMonitor.cs b/AdvancedSharpAdbClient/DeviceMonitor.cs index 909e1a92..3ebc2fb4 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.cs @@ -244,51 +244,59 @@ protected virtual void DeviceMonitorLoop() IsRunning = true; monitorLoopFinished.Reset(); - // Set up the connection to track the list of devices. - InitializeSocket(); - - do + try { - try - { - string value = Socket.ReadString(); - ProcessIncomingDeviceData(value); + // Set up the connection to track the list of devices. + InitializeSocket(); - firstDeviceListParsed.Set(); - } - catch (AdbException adbException) + do { - if (adbException.InnerException is SocketException ex) + try + { + string value = Socket.ReadString(); + ProcessIncomingDeviceData(value); + + firstDeviceListParsed.Set(); + } + catch (AdbException adbException) { - if (isMonitorThreadCancel) + if (adbException.InnerException is SocketException ex) { - // The DeviceMonitor is shutting down (disposing) and Dispose() - // has called Socket.Close(). This exception is expected, - // so we can safely swallow it. + if (isMonitorThreadCancel) + { + // The DeviceMonitor is shutting down (disposing) and Dispose() + // has called Socket.Close(). This exception is expected, + // so we can safely swallow it. + } + else if (adbException.ConnectionReset) + { + // The adb server was killed, for whatever reason. Try to restart it and recover from this. + AdbServer.Instance.RestartServer(); + Socket.Reconnect(); + InitializeSocket(); + } + else + { + // The exception was unexpected, so log it & rethrow. + logger.LogError(ex, ex.Message); + throw ex; + } } else { // The exception was unexpected, so log it & rethrow. - logger.LogError(ex, ex.Message); - throw ex; + logger.LogError(adbException, adbException.Message); + throw; } } - else if (adbException.ConnectionReset) - { - // The adb server was killed, for whatever reason. Try to restart it and recover from this. - AdbServer.Instance.RestartServer(); - Socket.Reconnect(); - InitializeSocket(); - } - else - { - throw; - } } + while (!isMonitorThreadCancel); + isMonitorThreadCancel = false; + } + finally + { + monitorLoopFinished.Set(); } - while (!isMonitorThreadCancel); - isMonitorThreadCancel = false; - monitorLoopFinished.Set(); } private void InitializeSocket() From 92aecdd370ee231f22795fbd976b17b941a52575 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Fri, 13 Oct 2023 20:55:40 +0800 Subject: [PATCH 38/66] Fix AdbServer test --- .../AdbServerTests.Async.cs | 5 +- .../AdbServerTests.cs | 5 +- .../AdvancedSharpAdbClient.Tests.csproj | 4 +- .../DeviceMonitorTests.cs | 2 +- .../Dummys/DummyAdbServer.cs | 5 +- AdvancedSharpAdbClient/AdbServer.Async.cs | 107 +++++++----------- AdvancedSharpAdbClient/AdbServer.cs | 97 ++++++++-------- .../Interfaces/IAdbServer.Async.cs | 44 +++---- .../Interfaces/IAdbServer.cs | 46 ++++---- .../Models/Enums/StartServerResult.cs | 7 +- .../Receivers/MultilineReceiver.cs | 6 +- 11 files changed, 155 insertions(+), 173 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/AdbServerTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbServerTests.Async.cs index ee4b9c26..11698e8c 100644 --- a/AdvancedSharpAdbClient.Tests/AdbServerTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbServerTests.Async.cs @@ -214,10 +214,11 @@ public async void RestartServerAsyncTest() Assert.False(commandLineClient.ServerStarted); _ = await adbServer.RestartServerAsync(ServerName); - Assert.False(commandLineClient.ServerStarted); + Assert.True(commandLineClient.ServerStarted); - Assert.Single(socket.Requests); + Assert.Equal(2, socket.Requests.Count); Assert.Equal("host:version", socket.Requests[0]); + Assert.Equal("host:kill", socket.Requests[1]); } } } diff --git a/AdvancedSharpAdbClient.Tests/AdbServerTests.cs b/AdvancedSharpAdbClient.Tests/AdbServerTests.cs index 29084960..ec1b19ff 100644 --- a/AdvancedSharpAdbClient.Tests/AdbServerTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbServerTests.cs @@ -237,10 +237,11 @@ public void RestartServerTest() Assert.False(commandLineClient.ServerStarted); _ = adbServer.RestartServer(ServerName); - Assert.False(commandLineClient.ServerStarted); + Assert.True(commandLineClient.ServerStarted); - Assert.Single(socket.Requests); + Assert.Equal(2, socket.Requests.Count); Assert.Equal("host:version", socket.Requests[0]); + Assert.Equal("host:kill", socket.Requests[1]); } /// diff --git a/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj b/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj index e31f3bb1..a8021212 100644 --- a/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj +++ b/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj @@ -12,8 +12,8 @@ - - + + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs b/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs index 20fabbcf..972eba33 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs @@ -36,7 +36,7 @@ public void ConstructorTest() /// Tests the method. /// [Fact] - public void ConstructorNullTest() => _ = Assert.Throws(() => new DeviceMonitor(null)); + public void ConstructorNullTest() => _ = Assert.Throws(() => new DeviceMonitor((IAdbSocket)null)); [Fact] public void DeviceDisconnectedTest() diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbServer.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbServer.cs index 3be2783a..521f9cde 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbServer.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbServer.cs @@ -39,7 +39,10 @@ public Task GetStatusAsync(CancellationToken cancellationToken } /// - public StartServerResult RestartServer(string adbPath = null) + public StartServerResult RestartServer() => RestartServer(null); + + /// + public StartServerResult RestartServer(string adbPath) { WasRestarted = true; return StartServer(adbPath, false); diff --git a/AdvancedSharpAdbClient/AdbServer.Async.cs b/AdvancedSharpAdbClient/AdbServer.Async.cs index 1f066a80..72b2e10e 100644 --- a/AdvancedSharpAdbClient/AdbServer.Async.cs +++ b/AdvancedSharpAdbClient/AdbServer.Async.cs @@ -15,87 +15,68 @@ public partial class AdbServer /// public virtual async Task StartServerAsync(string adbPath, bool restartServerIfNewer = false, CancellationToken cancellationToken = default) { - AdbServerStatus serverStatus = await GetStatusAsync(cancellationToken).ConfigureAwait(false); - Version commandLineVersion = null; + if (IsStarting) { return StartServerResult.Starting; } + try + { + AdbServerStatus serverStatus = await GetStatusAsync(cancellationToken).ConfigureAwait(false); + Version commandLineVersion = null; - IAdbCommandLineClient commandLineClient = adbCommandLineClientFactory(adbPath); - CheckFileExists = commandLineClient.CheckFileExists; + IAdbCommandLineClient commandLineClient = adbCommandLineClientFactory(adbPath); + CheckFileExists = commandLineClient.CheckFileExists; - if (commandLineClient.CheckFileExists(adbPath)) - { - CachedAdbPath = adbPath; - commandLineVersion = await commandLineClient.GetVersionAsync(cancellationToken).ConfigureAwait(false); - } + if (commandLineClient.CheckFileExists(adbPath)) + { + CachedAdbPath = adbPath; + commandLineVersion = await commandLineClient.GetVersionAsync(cancellationToken).ConfigureAwait(false); + } - // If the server is running, and no adb path is provided, check if we have the minimum version - if (adbPath == null) - { - return !serverStatus.IsRunning - ? throw new AdbException("The adb server is not running, but no valid path to the adb.exe executable was provided. The adb server cannot be started.") - : serverStatus.Version >= RequiredAdbVersion - ? StartServerResult.AlreadyRunning - : throw new AdbException($"The adb daemon is running an outdated version ${commandLineVersion}, but not valid path to the adb.exe executable was provided. A more recent version of the adb server cannot be started."); - } + // If the server is running, and no adb path is provided, check if we have the minimum version + if (adbPath == null) + { + return !serverStatus.IsRunning + ? throw new AdbException("The adb server is not running, but no valid path to the adb.exe executable was provided. The adb server cannot be started.") + : serverStatus.Version >= RequiredAdbVersion + ? StartServerResult.AlreadyRunning + : throw new AdbException($"The adb daemon is running an outdated version ${commandLineVersion}, but not valid path to the adb.exe executable was provided. A more recent version of the adb server cannot be started."); + } - if (serverStatus.IsRunning) - { - if (serverStatus.Version < RequiredAdbVersion - || (serverStatus.Version < commandLineVersion && restartServerIfNewer)) + if (serverStatus.IsRunning) { - ExceptionExtensions.ThrowIfNull(adbPath); + if (serverStatus.Version < RequiredAdbVersion + || (restartServerIfNewer && serverStatus.Version < commandLineVersion)) + { + ExceptionExtensions.ThrowIfNull(adbPath); - await adbClient.KillAdbAsync(cancellationToken); - await commandLineClient.StartServerAsync(cancellationToken); - return StartServerResult.RestartedOutdatedDaemon; + await adbClient.KillAdbAsync(cancellationToken); + await commandLineClient.StartServerAsync(cancellationToken); + return StartServerResult.RestartedOutdatedDaemon; + } + else + { + return StartServerResult.AlreadyRunning; + } } else { - return StartServerResult.AlreadyRunning; + ExceptionExtensions.ThrowIfNull(adbPath); + + await commandLineClient.StartServerAsync(cancellationToken); + return StartServerResult.Started; } } - else + finally { - ExceptionExtensions.ThrowIfNull(adbPath); - - await commandLineClient.StartServerAsync(cancellationToken); - return StartServerResult.Started; + IsStarting = false; } } /// - public Task RestartServerAsync(CancellationToken cancellationToken = default) => RestartServerAsync(null, cancellationToken); + public Task RestartServerAsync(CancellationToken cancellationToken = default) => + StartServerAsync(CachedAdbPath, true, cancellationToken); /// - public virtual async Task RestartServerAsync(string adbPath, CancellationToken cancellationToken = default) - { - adbPath ??= CachedAdbPath; - - if (!CheckFileExists(adbPath)) - { - throw new InvalidOperationException($"The adb server was not started via {nameof(AdbServer)}.{nameof(this.StartServer)} or no path to adb was specified. The adb server cannot be restarted."); - } - - using ManualResetEvent manualResetEvent = - await Extensions.Run(() => - { - lock (RestartLock) - { - return new ManualResetEvent(false); - } - }, cancellationToken).ConfigureAwait(false); - - _ = Extensions.Run(() => - { - lock (RestartLock) - { - manualResetEvent.WaitOne(); - } - }, cancellationToken); - - StartServerResult result = await StartServerAsync(adbPath, false, cancellationToken).ConfigureAwait(false); - manualResetEvent.Set(); - return result; - } + public virtual Task RestartServerAsync(string adbPath, CancellationToken cancellationToken = default) => + StringExtensions.IsNullOrWhiteSpace(adbPath) ? RestartServerAsync(cancellationToken) : StartServerAsync(adbPath, true, cancellationToken); /// public virtual async Task GetStatusAsync(CancellationToken cancellationToken = default) diff --git a/AdvancedSharpAdbClient/AdbServer.cs b/AdvancedSharpAdbClient/AdbServer.cs index 09f0ee87..a93d08eb 100644 --- a/AdvancedSharpAdbClient/AdbServer.cs +++ b/AdvancedSharpAdbClient/AdbServer.cs @@ -31,7 +31,7 @@ public partial class AdbServer(IAdbClient adbClient, Func /// No connection could be made because the target computer actively refused it.This usually /// results from trying to connect to a service that is inactive on the foreign host—that is, - /// one with no server application running. + /// one with no server application running. public const int ConnectionRefused = 10061; /// @@ -44,9 +44,9 @@ public partial class AdbServer(IAdbClient adbClient, Func - /// A lock used to ensure only one caller at a time can attempt to restart adb. + /// if is starting adb server; otherwise, . /// - protected static readonly object RestartLock = new(); + protected static bool IsStarting = false; /// /// The current ADB client that manages the connection. @@ -102,68 +102,67 @@ public AdbServer(Func adbCommandLineClientFactory /// public virtual StartServerResult StartServer(string adbPath, bool restartServerIfNewer = false) { - AdbServerStatus serverStatus = GetStatus(); - Version commandLineVersion = null; - - IAdbCommandLineClient commandLineClient = adbCommandLineClientFactory(adbPath); - CheckFileExists = commandLineClient.CheckFileExists; - - if (commandLineClient.CheckFileExists(adbPath)) + if (IsStarting) { return StartServerResult.Starting; } + try { - CachedAdbPath = adbPath; - commandLineVersion = commandLineClient.GetVersion(); - } + AdbServerStatus serverStatus = GetStatus(); + Version commandLineVersion = null; - // If the server is running, and no adb path is provided, check if we have the minimum version - if (adbPath == null) - { - return !serverStatus.IsRunning - ? throw new AdbException("The adb server is not running, but no valid path to the adb.exe executable was provided. The adb server cannot be started.") - : serverStatus.Version >= RequiredAdbVersion - ? StartServerResult.AlreadyRunning - : throw new AdbException($"The adb daemon is running an outdated version ${commandLineVersion}, but not valid path to the adb.exe executable was provided. A more recent version of the adb server cannot be started."); - } + IAdbCommandLineClient commandLineClient = adbCommandLineClientFactory(adbPath); + CheckFileExists = commandLineClient.CheckFileExists; - if (serverStatus.IsRunning) - { - if (serverStatus.Version < RequiredAdbVersion - || (serverStatus.Version < commandLineVersion && restartServerIfNewer)) + if (commandLineClient.CheckFileExists(adbPath)) { - ExceptionExtensions.ThrowIfNull(adbPath); + CachedAdbPath = adbPath; + commandLineVersion = commandLineClient.GetVersion(); + } - adbClient.KillAdb(); - commandLineClient.StartServer(); - return StartServerResult.RestartedOutdatedDaemon; + // If the server is running, and no adb path is provided, check if we have the minimum version + if (adbPath == null) + { + return !serverStatus.IsRunning + ? throw new AdbException("The adb server is not running, but no valid path to the adb.exe executable was provided. The adb server cannot be started.") + : serverStatus.Version >= RequiredAdbVersion + ? StartServerResult.AlreadyRunning + : throw new AdbException($"The adb daemon is running an outdated version ${commandLineVersion}, but not valid path to the adb.exe executable was provided. A more recent version of the adb server cannot be started."); + } + + if (serverStatus.IsRunning) + { + if (serverStatus.Version < RequiredAdbVersion + || (restartServerIfNewer && serverStatus.Version < commandLineVersion)) + { + ExceptionExtensions.ThrowIfNull(adbPath); + + adbClient.KillAdb(); + commandLineClient.StartServer(); + return StartServerResult.RestartedOutdatedDaemon; + } + else + { + return StartServerResult.AlreadyRunning; + } } else { - return StartServerResult.AlreadyRunning; + ExceptionExtensions.ThrowIfNull(adbPath); + + commandLineClient.StartServer(); + return StartServerResult.Started; } } - else + finally { - ExceptionExtensions.ThrowIfNull(adbPath); - - commandLineClient.StartServer(); - return StartServerResult.Started; + IsStarting = false; } } /// - public virtual StartServerResult RestartServer(string adbPath = null) - { - adbPath ??= CachedAdbPath; + public virtual StartServerResult RestartServer() => StartServer(CachedAdbPath, true); - if (!CheckFileExists(adbPath)) - { - throw new InvalidOperationException($"The adb server was not started via {nameof(AdbServer)}.{nameof(this.StartServer)} or no path to adb was specified. The adb server cannot be restarted."); - } - - lock (RestartLock) - { - return StartServer(adbPath, true); - } - } + /// + public virtual StartServerResult RestartServer(string adbPath) => + StringExtensions.IsNullOrWhiteSpace(adbPath) ? RestartServer() : StartServer(adbPath, true); /// public virtual AdbServerStatus GetStatus() diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbServer.Async.cs b/AdvancedSharpAdbClient/Interfaces/IAdbServer.Async.cs index 8e40fa49..f315af0d 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbServer.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbServer.Async.cs @@ -13,16 +13,12 @@ public partial interface IAdbServer /// /// Starts the adb server if it was not previously running. /// - /// - /// The path to the adb.exe executable that can be used to start the adb server. + /// The path to the adb.exe executable that can be used to start the adb server. /// If this path is not provided, this method will throw an exception if the server - /// is not running or is not up to date. - /// - /// - /// to restart the adb server if the version of the adb.exe + /// is not running or is not up to date. + /// to restart the adb server if the version of the adb.exe /// executable at is newer than the version that is currently - /// running; to keep a previous version of the server running. - /// + /// running; to keep a previous version of the server running. /// A which can be used to cancel the asynchronous operation. /// /// A which return @@ -38,15 +34,17 @@ public partial interface IAdbServer /// flag was set. /// /// - /// /// if the adb server was not running, /// and the server was started. + /// + /// + /// if a + /// operation is already in progress. + /// /// /// - /// - /// The server was not running, or an outdated version of the server was running, - /// and the parameter was not specified. - /// + /// The server was not running, or an outdated version of the server was running, + /// and the parameter was not specified. Task StartServerAsync(string adbPath, bool restartServerIfNewer, CancellationToken cancellationToken); /// @@ -56,28 +54,22 @@ public partial interface IAdbServer /// /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. - /// - /// You can only call this method if you have previously started the adb server via - /// and passed the full path to the adb server. - /// + /// You can only call this method if you have previously started the adb server via + /// and passed the full path to the adb server. Task RestartServerAsync(CancellationToken cancellationToken); /// - /// Restarts the adb server if it suddenly became unavailable. Call this class if, for example, + /// Restarts the adb server with new adb path if it suddenly became unavailable. Call this class if, for example, /// you receive an with the flag /// set to - a clear indicating the ADB server died. /// - /// - /// The path to the adb.exe executable that can be used to start the adb server. + /// The path to the adb.exe executable that can be used to start the adb server. /// If this path is not provided, this method will use the path that was cached by - /// - /// + /// /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. - /// - /// You can only call this method if you have previously started the adb server via - /// and passed the full path to the adb server. - /// + /// You can only call this method if you have previously started the adb server via + /// and passed the full path to the adb server. Task RestartServerAsync(string adbPath, CancellationToken cancellationToken); /// diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbServer.cs b/AdvancedSharpAdbClient/Interfaces/IAdbServer.cs index cbbc3912..1b885d18 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbServer.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbServer.cs @@ -14,16 +14,12 @@ public partial interface IAdbServer /// /// Starts the adb server if it was not previously running. /// - /// - /// The path to the adb.exe executable that can be used to start the adb server. + /// The path to the adb.exe executable that can be used to start the adb server. /// If this path is not provided, this method will throw an exception if the server - /// is not running or is not up to date. - /// - /// - /// to restart the adb server if the version of the adb.exe + /// is not running or is not up to date. + /// to restart the adb server if the version of the adb.exe /// executable at is newer than the version that is currently - /// running; to keep a previous version of the server running. - /// + /// running; to keep a previous version of the server running. /// /// /// @@ -37,15 +33,17 @@ public partial interface IAdbServer /// flag was set. /// /// - /// /// if the adb server was not running, /// and the server was started. + /// + /// + /// if an + /// operation is already in progress. + /// /// /// - /// - /// The server was not running, or an outdated version of the server was running, - /// and the parameter was not specified. - /// + /// The server was not running, or an outdated version of the server was running, + /// and the parameter was not specified. StartServerResult StartServer(string adbPath, bool restartServerIfNewer); /// @@ -53,16 +51,20 @@ public partial interface IAdbServer /// you receive an with the flag /// set to - a clear indicating the ADB server died. /// - /// - /// The path to the adb.exe executable that can be used to start the adb server. + /// You can only call this method if you have previously started the adb server via + /// and passed the full path to the adb server. + StartServerResult RestartServer(); + + /// Restarts the adb server with new adb path if it suddenly became unavailable. Call this class if, for example, + /// you receive an with the flag + /// set to - a clear indicating the ADB server died. + /// + /// The path to the adb.exe executable that can be used to start the adb server. /// If this path is not provided, this method will use the path that was cached by - /// - /// - /// - /// You can only call this method if you have previously started the adb server via - /// and passed the full path to the adb server. - /// - StartServerResult RestartServer(string adbPath = null); + /// + /// You can only call this method if you have previously started the adb server via + /// and passed the full path to the adb server. + StartServerResult RestartServer(string adbPath); /// /// Gets the status of the adb server. diff --git a/AdvancedSharpAdbClient/Models/Enums/StartServerResult.cs b/AdvancedSharpAdbClient/Models/Enums/StartServerResult.cs index 31a92d51..75192dc0 100644 --- a/AdvancedSharpAdbClient/Models/Enums/StartServerResult.cs +++ b/AdvancedSharpAdbClient/Models/Enums/StartServerResult.cs @@ -23,6 +23,11 @@ public enum StartServerResult : byte /// /// The adb server was not running, and a new instance of the adb server was started. /// - Started + Started, + + /// + /// An operation is already in progress. + /// + Starting } } diff --git a/AdvancedSharpAdbClient/Receivers/MultilineReceiver.cs b/AdvancedSharpAdbClient/Receivers/MultilineReceiver.cs index 095a797f..15ccd798 100644 --- a/AdvancedSharpAdbClient/Receivers/MultilineReceiver.cs +++ b/AdvancedSharpAdbClient/Receivers/MultilineReceiver.cs @@ -26,10 +26,8 @@ public abstract class MultiLineReceiver : IShellOutputReceiver /// Gets or sets a value indicating whether the receiver parses error messages. /// /// if this receiver parsers error messages; otherwise . - /// - /// The default value is . If set to , the - /// will detect common error messages and throw an exception. - /// + /// The default value is . If set to , the + /// will detect common error messages and throw an exception. public virtual bool ParsesErrors { get; protected set; } /// From f0e0522214d2d2180f4f6d5760bb68df1a2f9c5b Mon Sep 17 00:00:00 2001 From: where where Date: Fri, 13 Oct 2023 22:22:30 +0800 Subject: [PATCH 39/66] Update VersionPrefix From now on, the version code should update after last version released. --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index d02fd8da..1665b799 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -24,7 +24,7 @@ https://github.com/yungd1plomat/AdvancedSharpAdbClient snupkg .NET client for adb, Android Debug Bridge (AdvancedSharpAdbClient) - 2.5.7 + 2.6.8 From 5340d05b88b601cf51ff077f9999eea03d634216 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Thu, 19 Oct 2023 01:26:45 +0800 Subject: [PATCH 40/66] Use byte[] when using byte[] --- .../AdvancedSharpAdbClient.Tests.csproj | 2 +- AdvancedSharpAdbClient/AdbSocket.Async.cs | 24 +++------ AdvancedSharpAdbClient/AdbSocket.cs | 23 ++------ .../Extensions/DateTimeExtensions.cs | 4 +- AdvancedSharpAdbClient/Models/Area.cs | 12 ++--- AdvancedSharpAdbClient/Models/Cords.cs | 12 ++--- AdvancedSharpAdbClient/Models/HashCode.cs | 4 +- AdvancedSharpAdbClient/Models/ShellStream.cs | 53 ++++--------------- AdvancedSharpAdbClient/TcpSocket.Async.cs | 34 ++++-------- AdvancedSharpAdbClient/TcpSocket.cs | 16 ------ 10 files changed, 47 insertions(+), 137 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj b/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj index a8021212..87aad724 100644 --- a/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj +++ b/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj @@ -12,7 +12,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/AdvancedSharpAdbClient/AdbSocket.Async.cs b/AdvancedSharpAdbClient/AdbSocket.Async.cs index 72e0020a..908a3d52 100644 --- a/AdvancedSharpAdbClient/AdbSocket.Async.cs +++ b/AdvancedSharpAdbClient/AdbSocket.Async.cs @@ -24,12 +24,8 @@ public virtual async Task SendAsync(byte[] data, int length, CancellationToken c { try { - int count = -#if HAS_BUFFERS - await socket.SendAsync(data.AsMemory(0, length != -1 ? length : data.Length), SocketFlags.None, cancellationToken).ConfigureAwait(false); -#else - await socket.SendAsync(data, length != -1 ? length : data.Length, SocketFlags.None, cancellationToken).ConfigureAwait(false); -#endif + int count = await socket.SendAsync(data, length != -1 ? length : data.Length, SocketFlags.None, cancellationToken).ConfigureAwait(false); + if (count < 0) { throw new AdbException("channel EOF"); @@ -47,12 +43,8 @@ public virtual async Task SendAsync(byte[] data, int offset, int length, Cancell { try { - int count = -#if HAS_BUFFERS - await socket.SendAsync(data.AsMemory(offset, length != -1 ? length : data.Length - offset), SocketFlags.None, cancellationToken).ConfigureAwait(false); -#else - await socket.SendAsync(data, offset, length != -1 ? length : data.Length - offset, SocketFlags.None, cancellationToken).ConfigureAwait(false); -#endif + int count = await socket.SendAsync(data, offset, length != -1 ? length : data.Length - offset, SocketFlags.None, cancellationToken).ConfigureAwait(false); + if (count < 0) { throw new AdbException("channel EOF"); @@ -136,12 +128,7 @@ public virtual async Task ReadAsync(byte[] data, int offset, int length, Ca int left = length - totalRead; int bufferLength = left < ReceiveBufferSize ? left : ReceiveBufferSize; - count = -#if HAS_BUFFERS - await socket.ReceiveAsync(data.AsMemory(totalRead, bufferLength), SocketFlags.None, cancellationToken).ConfigureAwait(false); -#else - await socket.ReceiveAsync(data, totalRead, bufferLength, SocketFlags.None, cancellationToken).ConfigureAwait(false); -#endif + count = await socket.ReceiveAsync(data, totalRead, bufferLength, SocketFlags.None, cancellationToken).ConfigureAwait(false); if (count < 0) { @@ -325,6 +312,7 @@ public virtual async Task SendAsync(byte[] data, CancellationToken cancellationT try { int count = await socket.SendAsync(data, SocketFlags.None, cancellationToken).ConfigureAwait(false); + if (count < 0) { throw new AdbException("channel EOF"); diff --git a/AdvancedSharpAdbClient/AdbSocket.cs b/AdvancedSharpAdbClient/AdbSocket.cs index bc5817ec..0fa97c8c 100644 --- a/AdvancedSharpAdbClient/AdbSocket.cs +++ b/AdvancedSharpAdbClient/AdbSocket.cs @@ -116,12 +116,8 @@ public virtual void Send(byte[] data, int length) { try { - int count = -#if HAS_BUFFERS - socket.Send(data.AsSpan(0, length != -1 ? length : data.Length), SocketFlags.None); -#else - socket.Send(data, length != -1 ? length : data.Length, SocketFlags.None); -#endif + int count = socket.Send(data, length != -1 ? length : data.Length, SocketFlags.None); + if (count < 0) { throw new AdbException("channel EOF"); @@ -139,12 +135,8 @@ public virtual void Send(byte[] data, int offset, int length) { try { - int count = -#if HAS_BUFFERS - socket.Send(data.AsSpan(offset, length != -1 ? length : data.Length), SocketFlags.None); -#else - socket.Send(data, offset, length != -1 ? length : data.Length, SocketFlags.None); -#endif + int count = socket.Send(data, offset, length != -1 ? length : data.Length, SocketFlags.None); + if (count < 0) { throw new AdbException("channel EOF"); @@ -225,12 +217,7 @@ public virtual int Read(byte[] data, int offset, int length) int left = length - totalRead; int bufferLength = left < ReceiveBufferSize ? left : ReceiveBufferSize; - count = -#if HAS_BUFFERS - socket.Receive(data.AsSpan(totalRead, bufferLength), SocketFlags.None); -#else - socket.Receive(data, totalRead, bufferLength, SocketFlags.None); -#endif + count = socket.Receive(data, totalRead, bufferLength, SocketFlags.None); if (count < 0) { diff --git a/AdvancedSharpAdbClient/Extensions/DateTimeExtensions.cs b/AdvancedSharpAdbClient/Extensions/DateTimeExtensions.cs index c38fecab..bf2b2702 100644 --- a/AdvancedSharpAdbClient/Extensions/DateTimeExtensions.cs +++ b/AdvancedSharpAdbClient/Extensions/DateTimeExtensions.cs @@ -54,7 +54,7 @@ public static class DateTimeExtensions public static DateTime Epoch { get; } = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); #if NET20 -#pragma warning disable CS1574 // XML 注释中有无法解析的 cref 特性 +#pragma warning disable CS1574 #endif /// /// Converts a Unix time expressed as the number of seconds that have elapsed @@ -85,7 +85,7 @@ public static DateTimeOffset FromUnixTimeSeconds(long seconds) #endif } #if NET20 -#pragma warning restore CS1574 // XML 注释中有无法解析的 cref 特性 +#pragma warning restore CS1574 #endif #if NETFRAMEWORK && !NET46_OR_GREATER diff --git a/AdvancedSharpAdbClient/Models/Area.cs b/AdvancedSharpAdbClient/Models/Area.cs index 6886cfb6..c2ee8111 100644 --- a/AdvancedSharpAdbClient/Models/Area.cs +++ b/AdvancedSharpAdbClient/Models/Area.cs @@ -74,7 +74,7 @@ public Area(System.Drawing.Point location, System.Drawing.Size size) #endif #if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER -#pragma warning disable CS0419 // cref 特性中有不明确的引用 +#pragma warning disable CS0419 /// /// Initializes a new instance of the class with the specified rectangle. /// @@ -112,7 +112,7 @@ public Area(Windows.Foundation.Point location, Windows.Foundation.Size size) width = unchecked((int)size.Width); height = unchecked((int)size.Height); } -#pragma warning restore CS0419 // cref 特性中有不明确的引用 +#pragma warning restore CS0419 #endif /// @@ -281,7 +281,7 @@ public int Height #endif #if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER -#pragma warning disable CS0419 // cref 特性中有不明确的引用 +#pragma warning disable CS0419 /// /// Creates a with the specified . /// @@ -295,7 +295,7 @@ public int Height /// The to convert. /// The that results from the conversion. public static implicit operator Area(Rect rect) => new(rect); -#pragma warning restore CS0419 // cref 特性中有不明确的引用 +#pragma warning restore CS0419 #endif /// @@ -368,7 +368,7 @@ public static Area Round(RectangleF value) #endif #if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER -#pragma warning disable CS0419 // cref 特性中有不明确的引用 +#pragma warning disable CS0419 /// /// Converts a to a by performing a ceiling operation on all the coordinates. /// @@ -416,7 +416,7 @@ public static Area Round(Rect value) (int)Math.Round(value.Height)); } } -#pragma warning restore CS0419 // cref 特性中有不明确的引用 +#pragma warning restore CS0419 #endif /// diff --git a/AdvancedSharpAdbClient/Models/Cords.cs b/AdvancedSharpAdbClient/Models/Cords.cs index e6b924d8..889269f8 100644 --- a/AdvancedSharpAdbClient/Models/Cords.cs +++ b/AdvancedSharpAdbClient/Models/Cords.cs @@ -57,7 +57,7 @@ public Cords(System.Drawing.Size sz) #endif #if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER -#pragma warning disable CS0419 // cref 特性中有不明确的引用 +#pragma warning disable CS0419 /// /// Initializes a new instance of the class from a . /// @@ -75,7 +75,7 @@ public Cords(Windows.Foundation.Size sz) X = unchecked((int)sz.Width); Y = unchecked((int)sz.Height); } -#pragma warning restore CS0419 // cref 特性中有不明确的引用 +#pragma warning restore CS0419 #endif /// @@ -149,7 +149,7 @@ public Cords(Windows.Foundation.Size sz) #endif #if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER -#pragma warning disable CS0419 // cref 特性中有不明确的引用 +#pragma warning disable CS0419 /// /// Creates a with the coordinates of the specified . /// @@ -195,7 +195,7 @@ public Cords(Windows.Foundation.Size sz) /// to subtract from the coordinates of . /// A structure that is translated by the negative of a given structure. public static Cords operator -(Cords pt, Windows.Foundation.Size sz) => Subtract(pt, sz); -#pragma warning restore CS0419 // cref 特性中有不明确的引用 +#pragma warning restore CS0419 #endif /// @@ -260,7 +260,7 @@ public Cords(Windows.Foundation.Size sz) #endif #if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER -#pragma warning disable CS0419 // cref 特性中有不明确的引用 +#pragma warning disable CS0419 /// /// Translates a by a given . /// @@ -297,7 +297,7 @@ public Cords(Windows.Foundation.Size sz) /// The to convert. /// The this method converts to. public static Cords Round(Windows.Foundation.Point value) => new(unchecked((int)Math.Round(value.X)), unchecked((int)Math.Round(value.Y))); -#pragma warning restore CS0419 // cref 特性中有不明确的引用 +#pragma warning restore CS0419 #endif /// diff --git a/AdvancedSharpAdbClient/Models/HashCode.cs b/AdvancedSharpAdbClient/Models/HashCode.cs index af1497b4..7a0eca0f 100644 --- a/AdvancedSharpAdbClient/Models/HashCode.cs +++ b/AdvancedSharpAdbClient/Models/HashCode.cs @@ -527,7 +527,7 @@ public readonly int ToHashCode() return (int)hash; } -#pragma warning disable 0809 +#pragma warning disable CS0809 // Obsolete member 'memberA' overrides non-obsolete member 'memberB'. // Disallowing GetHashCode and Equals is by design @@ -558,7 +558,7 @@ public readonly int ToHashCode() /// public static bool operator !=(HashCode left, HashCode right) => !(left == right); -#pragma warning restore 0809 +#pragma warning restore CS0809 } } #endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Models/ShellStream.cs b/AdvancedSharpAdbClient/Models/ShellStream.cs index 25190ac1..f929f006 100644 --- a/AdvancedSharpAdbClient/Models/ShellStream.cs +++ b/AdvancedSharpAdbClient/Models/ShellStream.cs @@ -172,23 +172,13 @@ public override int Read(byte[] buffer, int offset, int count) if (pendingByte != null) { buffer[offset] = pendingByte.Value; - read = -#if HAS_BUFFERS - Inner.Read(buffer.AsSpan(offset + 1, count - 1)); -#else - Inner.Read(buffer, offset + 1, count - 1); -#endif + read = Inner.Read(buffer, offset + 1, count - 1); read++; pendingByte = null; } else { - read = -#if HAS_BUFFERS - Inner.Read(buffer.AsSpan(offset, count)); -#else - Inner.Read(buffer, offset, count); -#endif + read = Inner.Read(buffer, offset, count); } // Loop over the data, and find a LF (0x0d) character. If it is @@ -220,12 +210,7 @@ public override int Read(byte[] buffer, int offset, int count) } byte[] miniBuffer = new byte[1]; - int miniRead = -#if HAS_BUFFERS - Inner.Read(miniBuffer.AsSpan(0, 1)); -#else - Inner.Read(miniBuffer, 1); -#endif + int miniRead = Inner.Read(miniBuffer, 1); if (miniRead == 0) { @@ -364,6 +349,7 @@ public override async ValueTask ReadAsync(Memory buffer, Cancellation #endif #if HAS_TASK +#pragma warning disable CA1835 /// public #if !NETFRAMEWORK || NET45_OR_GREATER @@ -379,28 +365,18 @@ async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToke // Read the raw data from the base stream. There may be a // 'pending byte' from a previous operation; if that's the case, // consume it. - int read = 0; + int read; if (pendingByte != null) { buffer[offset] = pendingByte.Value; - read = -#if HAS_BUFFERS - await Inner.ReadAsync(buffer.AsMemory(offset + 1, count - 1), cancellationToken).ConfigureAwait(false); -#else - await Inner.ReadAsync(buffer, offset + 1, count - 1, cancellationToken).ConfigureAwait(false); -#endif + read = await Inner.ReadAsync(buffer, offset + 1, count - 1, cancellationToken).ConfigureAwait(false); read++; pendingByte = null; } else { - read = -#if HAS_BUFFERS - await Inner.ReadAsync(buffer.AsMemory(offset, count), cancellationToken).ConfigureAwait(false); -#else - await Inner.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); -#endif + read = await Inner.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); } byte[] miniBuffer = new byte[1]; @@ -433,12 +409,7 @@ async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToke continue; } - int miniRead = -#if HAS_BUFFERS - await Inner.ReadAsync(miniBuffer.AsMemory(0, 1), cancellationToken).ConfigureAwait(false); -#else - await Inner.ReadAsync(miniBuffer, 1, cancellationToken).ConfigureAwait(false); -#endif + int miniRead = await Inner.ReadAsync(miniBuffer, 1, cancellationToken).ConfigureAwait(false); if (miniRead == 0) { @@ -458,12 +429,7 @@ async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToke // we need to read one more byte from the inner stream. if (read > 0 && buffer[offset + read - 1] == 0x0d) { - int miniRead = -#if HAS_BUFFERS - await Inner.ReadAsync(miniBuffer.AsMemory(0, 1), cancellationToken).ConfigureAwait(false); -#else - await Inner.ReadAsync(miniBuffer, 1, cancellationToken).ConfigureAwait(false); -#endif + _ = await Inner.ReadAsync(miniBuffer, 1, cancellationToken).ConfigureAwait(false); int nextByte = miniBuffer[0]; if (nextByte == 0x0a) @@ -483,6 +449,7 @@ async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToke return read; } +#pragma warning restore CA1835 #endif /// diff --git a/AdvancedSharpAdbClient/TcpSocket.Async.cs b/AdvancedSharpAdbClient/TcpSocket.Async.cs index 8eeba38d..a2098244 100644 --- a/AdvancedSharpAdbClient/TcpSocket.Async.cs +++ b/AdvancedSharpAdbClient/TcpSocket.Async.cs @@ -43,26 +43,26 @@ public virtual ValueTask ReconnectAsync(CancellationToken cancellationToken = de } #endif -#if HAS_BUFFERS /// public virtual Task SendAsync(byte[] buffer, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) => - socket.SendAsync(buffer.AsMemory(0, size), socketFlags, cancellationToken).AsTask(); + socket.SendAsync(buffer, size, socketFlags, cancellationToken); /// public virtual Task SendAsync(byte[] buffer, int offset, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) => - socket.SendAsync(buffer.AsMemory(offset, size), socketFlags, cancellationToken).AsTask(); - - /// - public ValueTask SendAsync(ReadOnlyMemory buffer, SocketFlags socketFlags, CancellationToken cancellationToken = default) => - socket.SendAsync(buffer, socketFlags, cancellationToken); + socket.SendAsync(buffer, offset, size, socketFlags, cancellationToken); /// public virtual Task ReceiveAsync(byte[] buffer, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) => - socket.ReceiveAsync(buffer.AsMemory(0, size), socketFlags, cancellationToken).AsTask(); + socket.ReceiveAsync(buffer, size, socketFlags, cancellationToken); /// public virtual Task ReceiveAsync(byte[] buffer, int offset, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) => - socket.ReceiveAsync(buffer.AsMemory(offset, size), socketFlags, cancellationToken).AsTask(); + socket.ReceiveAsync(buffer, offset, size, socketFlags, cancellationToken); + +#if HAS_BUFFERS + /// + public ValueTask SendAsync(ReadOnlyMemory buffer, SocketFlags socketFlags, CancellationToken cancellationToken = default) => + socket.SendAsync(buffer, socketFlags, cancellationToken); /// public ValueTask ReceiveAsync(Memory buffer, SocketFlags socketFlags, CancellationToken cancellationToken = default) => @@ -72,25 +72,9 @@ public ValueTask ReceiveAsync(Memory buffer, SocketFlags socketFlags, public virtual Task SendAsync(byte[] buffer, SocketFlags socketFlags, CancellationToken cancellationToken = default) => socket.SendAsync(buffer, socketFlags, cancellationToken); - /// - public virtual Task SendAsync(byte[] buffer, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) => - socket.SendAsync(buffer, size, socketFlags, cancellationToken); - - /// - public virtual Task SendAsync(byte[] buffer, int offset, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) => - socket.SendAsync(buffer, offset, size, socketFlags, cancellationToken); - /// public virtual Task ReceiveAsync(byte[] buffer, SocketFlags socketFlags, CancellationToken cancellationToken = default) => socket.ReceiveAsync(buffer, socketFlags, cancellationToken); - - /// - public virtual Task ReceiveAsync(byte[] buffer, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) => - socket.ReceiveAsync(buffer, size, socketFlags, cancellationToken); - - /// - public virtual Task ReceiveAsync(byte[] buffer, int offset, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) => - socket.ReceiveAsync(buffer, offset, size, socketFlags, cancellationToken); #endif } } diff --git a/AdvancedSharpAdbClient/TcpSocket.cs b/AdvancedSharpAdbClient/TcpSocket.cs index 2beb4bc2..f409948a 100644 --- a/AdvancedSharpAdbClient/TcpSocket.cs +++ b/AdvancedSharpAdbClient/TcpSocket.cs @@ -89,35 +89,19 @@ public void Dispose() /// public virtual int Send(byte[] buffer, int size, SocketFlags socketFlags) => -#if HAS_BUFFERS - socket.Send(buffer.AsSpan(0, size), socketFlags); -#else socket.Send(buffer, size, socketFlags); -#endif /// public virtual int Send(byte[] buffer, int offset, int size, SocketFlags socketFlags) => -#if HAS_BUFFERS - socket.Send(buffer.AsSpan(offset, size), socketFlags); -#else socket.Send(buffer, offset, size, socketFlags); -#endif /// public virtual int Receive(byte[] buffer, int size, SocketFlags socketFlags) => -#if HAS_BUFFERS - socket.Receive(buffer.AsSpan(0, size), socketFlags); -#else socket.Receive(buffer, size, socketFlags); -#endif /// public virtual int Receive(byte[] buffer, int offset, int size, SocketFlags socketFlags) => -#if HAS_BUFFERS - socket.Send(buffer.AsSpan(offset, size), socketFlags); -#else socket.Receive(buffer, offset, size, socketFlags); -#endif #if HAS_BUFFERS /// From 6000ff0dfa765a15de6577c79e6154756411c376 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Thu, 19 Oct 2023 04:58:40 +0800 Subject: [PATCH 41/66] Remove System.Drawing.Common from non-windows platform --- .../AdbClientTests.Async.cs | 75 ++- .../AdbClientTests.cs | 73 ++- .../AdvancedSharpAdbClient.Tests.csproj | 3 +- .../DeviceExtensionsTests.Async.cs | 1 - .../Dummys/DeviceMonitorSink.cs | 2 +- .../Dummys/DummyAdbClient.cs | 17 +- .../Dummys/DummyAdbSocket.cs | 4 +- .../Dummys/DummySyncService.cs | 2 +- .../Dummys/TracingAdbSocket.cs | 4 +- .../Logs/Dummys/DummyLogger.cs | 2 +- AdvancedSharpAdbClient.Tests/Logs/LogTests.cs | 1 - .../Models/AreaTests.cs | 304 --------- .../Models/CordsTests.cs | 224 ------- .../Models/FramebufferHeaderTests.cs | 7 +- .../Models/FramebufferTests.cs | 59 +- AdvancedSharpAdbClient/AdbClient.Async.cs | 2 +- AdvancedSharpAdbClient/AdbClient.cs | 2 +- AdvancedSharpAdbClient/AdbSocket.Async.cs | 4 +- AdvancedSharpAdbClient/AdbSocket.cs | 4 +- .../AdvancedSharpAdbClient.csproj | 42 +- .../Receivers/EnvironmentVariablesReceiver.cs | 2 +- .../Receivers/GetPropReceiver.cs | 2 +- .../Receivers/InfoOutputReceiver.cs | 4 +- .../Receivers/InstallOutputReceiver.cs | 4 +- .../Receivers/ProcessOutputReceiver.cs | 2 +- .../Interfaces/IAdbClient.Async.cs | 4 +- .../Interfaces/IAdbClient.cs | 4 +- AdvancedSharpAdbClient/Logs/EventLogEntry.cs | 2 +- AdvancedSharpAdbClient/Models/Area.cs | 587 ------------------ AdvancedSharpAdbClient/Models/ColorData.cs | 2 +- AdvancedSharpAdbClient/Models/Cords.cs | 366 ----------- AdvancedSharpAdbClient/Models/Element.cs | 14 +- AdvancedSharpAdbClient/Models/Framebuffer.cs | 37 +- .../Models/FramebufferHeader.cs | 2 +- AdvancedSharpAdbClient/Models/HashCode.cs | 18 +- AdvancedSharpAdbClient/Models/Point.cs | 151 +++++ AdvancedSharpAdbClient/Models/Rectangle.cs | 282 +++++++++ .../Properties/GlobalUsings.cs | 8 +- .../Receivers/ConsoleOutputReceiver.cs | 4 +- 39 files changed, 657 insertions(+), 1670 deletions(-) delete mode 100644 AdvancedSharpAdbClient.Tests/Models/AreaTests.cs delete mode 100644 AdvancedSharpAdbClient.Tests/Models/CordsTests.cs delete mode 100644 AdvancedSharpAdbClient/Models/Area.cs delete mode 100644 AdvancedSharpAdbClient/Models/Cords.cs create mode 100644 AdvancedSharpAdbClient/Models/Point.cs create mode 100644 AdvancedSharpAdbClient/Models/Rectangle.cs diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs index 7c5388a7..6426a416 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs @@ -8,7 +8,6 @@ using System.IO; using System.Linq; using System.Net; -using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -372,21 +371,20 @@ public async void GetFrameBufferAsyncTest() Assert.Equal(1u, header.Version); Assert.Equal(0u, header.ColorSpace); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - using Bitmap image = framebuffer.ToImage(); - Assert.NotNull(image); - Assert.Equal(PixelFormat.Format32bppArgb, image.PixelFormat); - - Assert.Equal(1, image.Width); - Assert.Equal(1, image.Height); - - Color pixel = image.GetPixel(0, 0); - Assert.Equal(0x35, pixel.R); - Assert.Equal(0x4a, pixel.G); - Assert.Equal(0x4c, pixel.B); - Assert.Equal(0xff, pixel.A); - } +#if WINDOWS + using Bitmap image = framebuffer.ToImage(); + Assert.NotNull(image); + Assert.Equal(PixelFormat.Format32bppArgb, image.PixelFormat); + + Assert.Equal(1, image.Width); + Assert.Equal(1, image.Height); + + Color pixel = image.GetPixel(0, 0); + Assert.Equal(0x35, pixel.R); + Assert.Equal(0x4a, pixel.G); + Assert.Equal(0x4c, pixel.B); + Assert.Equal(0xff, pixel.A); +#endif framebuffer.Dispose(); } @@ -942,6 +940,39 @@ public async void DumpScreenAsyncTest() Assert.Equal(doc, xml); } +#if WINDOWS10_0_17763_0_OR_GREATER + /// + /// Tests the method. + /// + [Fact] + public async void DumpScreenWinRTAsyncTest() + { + string[] requests = + [ + "host:transport:169.254.109.177:5555", + "shell:uiautomator dump /dev/tty" + ]; + + string dump = File.ReadAllText(@"Assets/dumpscreen.txt"); + byte[] streamData = Encoding.UTF8.GetBytes(dump); + await using MemoryStream shellStream = new(streamData); + + Windows.Data.Xml.Dom.XmlDocument xml = await RunTestAsync( + OkResponses(2), + NoResponseMessages, + requests, + [shellStream], + () => TestClient.DumpScreenWinRTAsync(Device)); + + string cleanDump = File.ReadAllText(@"Assets/dumpscreen_clean.txt"); + Windows.Data.Xml.Dom.XmlDocument doc = new(); + doc.LoadXml(cleanDump); + + Assert.Equal(doc.InnerText, xml.InnerText); + } + +#endif + /// /// Tests the method. /// @@ -1012,7 +1043,7 @@ at android.os.Binder.execTransactInternal(Binder.java:1165) } /// - /// Tests the method. + /// Tests the method. /// [Fact] public async void ClickCordsAsyncTest() @@ -1032,7 +1063,7 @@ public async void ClickCordsAsyncTest() NoResponseMessages, requests, [shellStream], - () => TestClient.ClickAsync(Device, new Cords(100, 100)))); + () => TestClient.ClickAsync(Device, new Point(100, 100)))); } /// @@ -1076,7 +1107,7 @@ await RunTestAsync( NoResponseMessages, requests, [shellStream], - () => TestClient.SwipeAsync(Device, new Element(TestClient, Device, new Area(0, 0, 200, 400)), new Element(TestClient, Device, new Area(0, 0, 600, 800)), 500)); + () => TestClient.SwipeAsync(Device, new Element(TestClient, Device, new Rectangle(0, 0, 200, 400)), new Element(TestClient, Device, new Rectangle(0, 0, 600, 800)), 500)); } /// @@ -1225,7 +1256,7 @@ public async void FindElementAsyncTest() Assert.Equal(144, element.GetChildCount()); Element child = element[0][0][0][0][0][0][0][0][2][1][0][0]; Assert.Equal("where-where", child.Text); - Assert.Equal(Area.FromLTRB(45, 889, 427, 973), child.Bounds); + Assert.Equal(Rectangle.FromLTRB(45, 889, 427, 973), child.Bounds); Assert.Equal(child, element.FindDescendantOrSelf(x => x.Text == "where-where")); Assert.Equal(2, element.FindDescendants().Where(x => x.Text == "where-where").Count()); } @@ -1258,7 +1289,7 @@ public async void FindElementsAsyncTest() Assert.Equal(145, childCount); Element element = elements[0][0][0][0][0][0][0][0][0][2][1][0][0]; Assert.Equal("where-where", element.Attributes["text"]); - Assert.Equal(Area.FromLTRB(45, 889, 427, 973), element.Bounds); + Assert.Equal(Rectangle.FromLTRB(45, 889, 427, 973), element.Bounds); } /// @@ -1297,7 +1328,7 @@ public async void FindAsyncElementsTest() Assert.Equal(145, childCount); Element element = elements[0][0][0][0][0][0][0][0][0][2][1][0][0]; Assert.Equal("where-where", element.Attributes["text"]); - Assert.Equal(Area.FromLTRB(45, 889, 427, 973), element.Bounds); + Assert.Equal(Rectangle.FromLTRB(45, 889, 427, 973), element.Bounds); } /// diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs index 06f8fb4f..c1595d5f 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs @@ -8,7 +8,6 @@ using System.IO; using System.Linq; using System.Net; -using System.Runtime.InteropServices; using System.Text; using System.Xml; using Xunit; @@ -483,21 +482,20 @@ public void GetFrameBufferTest() Assert.Equal(1u, header.Version); Assert.Equal(0u, header.ColorSpace); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - using Bitmap image = framebuffer.ToImage(); - Assert.NotNull(image); - Assert.Equal(PixelFormat.Format32bppArgb, image.PixelFormat); - - Assert.Equal(1, image.Width); - Assert.Equal(1, image.Height); - - Color pixel = image.GetPixel(0, 0); - Assert.Equal(0x35, pixel.R); - Assert.Equal(0x4a, pixel.G); - Assert.Equal(0x4c, pixel.B); - Assert.Equal(0xff, pixel.A); - } +#if WINDOWS + using Bitmap image = framebuffer.ToImage(); + Assert.NotNull(image); + Assert.Equal(PixelFormat.Format32bppArgb, image.PixelFormat); + + Assert.Equal(1, image.Width); + Assert.Equal(1, image.Height); + + Color pixel = image.GetPixel(0, 0); + Assert.Equal(0x35, pixel.R); + Assert.Equal(0x4a, pixel.G); + Assert.Equal(0x4c, pixel.B); + Assert.Equal(0xff, pixel.A); +#endif framebuffer.Dispose(); } @@ -1051,6 +1049,39 @@ public void DumpScreenTest() Assert.Equal(doc, xml); } +#if WINDOWS10_0_17763_0_OR_GREATER + /// + /// Tests the method. + /// + [Fact] + public void DumpScreenWinRTTest() + { + string[] requests = + [ + "host:transport:169.254.109.177:5555", + "shell:uiautomator dump /dev/tty" + ]; + + string dump = File.ReadAllText(@"Assets/dumpscreen.txt"); + byte[] streamData = Encoding.UTF8.GetBytes(dump); + using MemoryStream shellStream = new(streamData); + + Windows.Data.Xml.Dom.XmlDocument xml = RunTest( + OkResponses(2), + NoResponseMessages, + requests, + [shellStream], + () => TestClient.DumpScreenWinRT(Device)); + + string cleanDump = File.ReadAllText(@"Assets/dumpscreen_clean.txt"); + Windows.Data.Xml.Dom.XmlDocument doc = new(); + doc.LoadXml(cleanDump); + + Assert.Equal(doc.InnerText, xml.InnerText); + } + +#endif + /// /// Tests the method. /// @@ -1121,7 +1152,7 @@ at android.os.Binder.execTransactInternal(Binder.java:1165) } /// - /// Tests the method. + /// Tests the method. /// [Fact] public void ClickCordsTest() @@ -1141,7 +1172,7 @@ public void ClickCordsTest() NoResponseMessages, requests, [shellStream], - () => TestClient.Click(Device, new Cords(100, 100)))); + () => TestClient.Click(Device, new Point(100, 100)))); } /// @@ -1185,7 +1216,7 @@ public void SwipeElementTest() NoResponseMessages, requests, [shellStream], - () => TestClient.Swipe(Device, new Element(TestClient, Device, new Area(0, 0, 200, 400)), new Element(TestClient, Device, new Area(0, 0, 600, 800)), 500)); + () => TestClient.Swipe(Device, new Element(TestClient, Device, new Rectangle(0, 0, 200, 400)), new Element(TestClient, Device, new Rectangle(0, 0, 600, 800)), 500)); } /// @@ -1338,7 +1369,7 @@ public void FindElementTest() Assert.Equal("android.widget.TextView", child.Class); Assert.Equal("com.bilibili.app.in", child.Package); Assert.Equal("com.bilibili.app.in:id/header_info_name", child.ResourceID); - Assert.Equal(Area.FromLTRB(45, 889, 427, 973), child.Bounds); + Assert.Equal(Rectangle.FromLTRB(45, 889, 427, 973), child.Bounds); Assert.Equal(child, element.FindDescendantOrSelf(x => x.Text == "where-where")); Assert.Equal(2, element.FindDescendants().Where(x => x.Text == "where-where").Count()); } @@ -1371,7 +1402,7 @@ public void FindElementsTest() Assert.Equal(145, childCount); Element element = elements[0][0][0][0][0][0][0][0][0][2][1][0][0]; Assert.Equal("where-where", element.Text); - Assert.Equal(Area.FromLTRB(45, 889, 427, 973), element.Bounds); + Assert.Equal(Rectangle.FromLTRB(45, 889, 427, 973), element.Bounds); } /// diff --git a/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj b/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj index 87aad724..3aacef3a 100644 --- a/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj +++ b/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj @@ -3,7 +3,8 @@ latest CS1591 - net8.0 + net8.0 + net8.0-windows10.0.17763.0 diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs index d1fd6aea..db7567f4 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs @@ -1,7 +1,6 @@ using AdvancedSharpAdbClient.Tests; using NSubstitute; using System.Collections.Generic; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Xunit; diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DeviceMonitorSink.cs b/AdvancedSharpAdbClient.Tests/Dummys/DeviceMonitorSink.cs index 78ca3a29..ff039f1c 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DeviceMonitorSink.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DeviceMonitorSink.cs @@ -36,7 +36,7 @@ public void ResetSignals() public List ListChangedEvents { get; init; } public List ConnectedEvents { get; init; } - + public List NotifiedEvents { get; init; } public List ChangedEvents { get; init; } diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs index f935a6d0..c9e31e10 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs @@ -1,6 +1,7 @@ using AdvancedSharpAdbClient.Logs; using System; using System.Collections.Generic; +using System.Drawing; using System.IO; using System.Net; using System.Text; @@ -12,9 +13,9 @@ namespace AdvancedSharpAdbClient.Tests { internal class DummyAdbClient : IAdbClient { - public Dictionary Commands { get; } = new Dictionary(); + public Dictionary Commands { get; } = []; - public List ReceivedCommands { get; } = new List(); + public List ReceivedCommands { get; } = []; public EndPoint EndPoint { get; init; } @@ -130,16 +131,16 @@ public async Task ExecuteServerCommandAsync(string target, string command, IShel } } - public Task ExecuteServerCommandAsync(string target, string command, IAdbSocket socket, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken)=> + public Task ExecuteServerCommandAsync(string target, string command, IAdbSocket socket, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken) => ExecuteServerCommandAsync(target, command, receiver, encoding, cancellationToken); #region Not Implemented - void IAdbClient.Click(DeviceData device, Cords cords) => throw new NotImplementedException(); + void IAdbClient.Click(DeviceData device, Point cords) => throw new NotImplementedException(); void IAdbClient.Click(DeviceData device, int x, int y) => throw new NotImplementedException(); - Task IAdbClient.ClickAsync(DeviceData device, Cords cords, CancellationToken cancellationToken) => throw new NotImplementedException(); + Task IAdbClient.ClickAsync(DeviceData device, Point cords, CancellationToken cancellationToken) => throw new NotImplementedException(); Task IAdbClient.ClickAsync(DeviceData device, int x, int y, CancellationToken cancellationToken) => throw new NotImplementedException(); @@ -169,6 +170,12 @@ public Task ExecuteServerCommandAsync(string target, string command, IAdbSocket Task IAdbClient.DumpScreenStringAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); +#if WINDOWS10_0_17763_0_OR_GREATER + Windows.Data.Xml.Dom.XmlDocument IAdbClient.DumpScreenWinRT(DeviceData device) => throw new NotImplementedException(); + + Task IAdbClient.DumpScreenWinRTAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); +#endif + IAsyncEnumerable IAdbClient.FindAsyncElements(DeviceData device, string xpath, CancellationToken cancellationToken) => throw new NotImplementedException(); Element IAdbClient.FindElement(DeviceData device, string xpath, TimeSpan timeout) => throw new NotImplementedException(); diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs index 4ee8d2fe..168af890 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs @@ -29,9 +29,9 @@ internal class DummyAdbSocket : IDummyAdbSocket public Queue ResponseMessages { get; } = new Queue(); - public List Requests { get; } = new List(); + public List Requests { get; } = []; - public List<(SyncCommand, string)> SyncRequests { get; } = new List<(SyncCommand, string)>(); + public List<(SyncCommand, string)> SyncRequests { get; } = []; public Queue ShellStreams { get; } = new Queue(); diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs index 8d2cf25f..1274c0be 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs @@ -8,7 +8,7 @@ namespace AdvancedSharpAdbClient.Tests { internal class DummySyncService : ISyncService { - public Dictionary UploadedFiles { get; } = new Dictionary(); + public Dictionary UploadedFiles { get; } = []; public bool IsOpen { get; private set; } = true; diff --git a/AdvancedSharpAdbClient.Tests/Dummys/TracingAdbSocket.cs b/AdvancedSharpAdbClient.Tests/Dummys/TracingAdbSocket.cs index 0146fa26..8742595b 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/TracingAdbSocket.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/TracingAdbSocket.cs @@ -23,9 +23,9 @@ internal class TracingAdbSocket(EndPoint endPoint) : AdbSocket(endPoint), IDummy public Queue SyncDataSent { get; } = new Queue(); - public List Requests { get; } = new List(); + public List Requests { get; } = []; - public List<(SyncCommand, string)> SyncRequests { get; } = new List<(SyncCommand, string)>(); + public List<(SyncCommand, string)> SyncRequests { get; } = []; public Queue ShellStreams { get; } = new Queue(); diff --git a/AdvancedSharpAdbClient.Tests/Logs/Dummys/DummyLogger.cs b/AdvancedSharpAdbClient.Tests/Logs/Dummys/DummyLogger.cs index 0efff112..25f6e9e7 100644 --- a/AdvancedSharpAdbClient.Tests/Logs/Dummys/DummyLogger.cs +++ b/AdvancedSharpAdbClient.Tests/Logs/Dummys/DummyLogger.cs @@ -7,7 +7,7 @@ public class DummyLogger(string name) : ILogger { public string Name { get; } = name; - public List LogMessages { get; } = new(); + public List LogMessages { get; } = []; public void Log(LogLevel logLevel, Exception exception, string message, params object[] args) => LogMessages.Add(new LogMessage(logLevel, exception, message, args)); diff --git a/AdvancedSharpAdbClient.Tests/Logs/LogTests.cs b/AdvancedSharpAdbClient.Tests/Logs/LogTests.cs index 6faaa446..d75c7f19 100644 --- a/AdvancedSharpAdbClient.Tests/Logs/LogTests.cs +++ b/AdvancedSharpAdbClient.Tests/Logs/LogTests.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Threading; using Xunit; namespace AdvancedSharpAdbClient.Logs.Tests diff --git a/AdvancedSharpAdbClient.Tests/Models/AreaTests.cs b/AdvancedSharpAdbClient.Tests/Models/AreaTests.cs deleted file mode 100644 index 9ee87729..00000000 --- a/AdvancedSharpAdbClient.Tests/Models/AreaTests.cs +++ /dev/null @@ -1,304 +0,0 @@ -using System; -using System.Drawing; -using System.Globalization; -using Xunit; - -namespace AdvancedSharpAdbClient.Tests -{ - /// - /// Tests the struct. - /// - public class AreaTests - { - [Fact] - public void DefaultConstructorTest() - { - Assert.Equal(Area.Empty, new Area()); - } - - [Theory] - [InlineData(int.MaxValue, int.MinValue, int.MaxValue, int.MinValue)] - [InlineData(int.MaxValue, 0, int.MinValue, 0)] - [InlineData(0, 0, 0, 0)] - [InlineData(0, int.MinValue, 0, int.MaxValue)] - public void NonDefaultConstructorTest(int x, int y, int width, int height) - { - Area rect1 = new(x, y, width, height); - Area rect2 = new(new Cords(x, y), new Size(width, height)); - - Assert.Equal(rect1, rect2); - } - - [Theory] - [InlineData(int.MaxValue, int.MinValue, int.MaxValue, int.MinValue)] - [InlineData(int.MaxValue, 0, int.MinValue, 0)] - [InlineData(0, 0, 0, 0)] - [InlineData(0, int.MinValue, 0, int.MaxValue)] - public void FromLTRBTest(int left, int top, int right, int bottom) - { - Area rect1 = new(left, top, unchecked(right - left), unchecked(bottom - top)); - Area rect2 = Area.FromLTRB(left, top, right, bottom); - - Assert.Equal(rect1, rect2); - } - - [Fact] - public void EmptyTest() - { - Assert.True(Area.Empty.IsEmpty); - Assert.True(new Area(0, 0, 0, 0).IsEmpty); - Assert.True(new Area().IsEmpty); - } - - [Theory] - [InlineData(int.MaxValue, int.MinValue, int.MaxValue, int.MinValue)] - [InlineData(int.MaxValue, 0, int.MinValue, 0)] - [InlineData(int.MinValue, int.MaxValue, int.MinValue, int.MaxValue)] - [InlineData(0, int.MinValue, 0, int.MaxValue)] - public void NonEmptyTest(int x, int y, int width, int height) - { - Assert.False(new Area(x, y, width, height).IsEmpty); - } - - [Theory] - [InlineData(int.MaxValue, int.MinValue, int.MaxValue, int.MinValue)] - [InlineData(int.MaxValue, 0, int.MinValue, 0)] - [InlineData(0, 0, 0, 0)] - [InlineData(0, int.MinValue, 0, int.MaxValue)] - [InlineData(int.MinValue, int.MaxValue, int.MinValue, int.MaxValue)] - public void DimensionsTest(int x, int y, int width, int height) - { - Area rect = new(x, y, width, height); - Assert.Equal(new Cords(x, y), rect.Location); - Assert.Equal(new Size(width, height), rect.Size); - - Assert.Equal(x, rect.X); - Assert.Equal(y, rect.Y); - Assert.Equal(width, rect.Width); - Assert.Equal(height, rect.Height); - Assert.Equal(x, rect.Left); - Assert.Equal(y, rect.Top); - Assert.Equal(unchecked(x + width), rect.Right); - Assert.Equal(unchecked(y + height), rect.Bottom); - - Cords p = new(width, height); - Size s = new(x, y); - rect.Location = p; - rect.Size = s; - - Assert.Equal(p, rect.Location); - Assert.Equal(s, rect.Size); - - Assert.Equal(width, rect.X); - Assert.Equal(height, rect.Y); - Assert.Equal(x, rect.Width); - Assert.Equal(y, rect.Height); - Assert.Equal(width, rect.Left); - Assert.Equal(height, rect.Top); - Assert.Equal(unchecked(x + width), rect.Right); - Assert.Equal(unchecked(y + height), rect.Bottom); - } - - [Theory] - [InlineData(0, 0)] - [InlineData(int.MaxValue, int.MinValue)] - public static void LocationSetTest(int x, int y) - { - Cords Cords = new(x, y); - Area rect = new(10, 10, 10, 10) - { - Location = Cords - }; - Assert.Equal(Cords, rect.Location); - Assert.Equal(Cords.X, rect.X); - Assert.Equal(Cords.Y, rect.Y); - } - - [Theory] - [InlineData(0, 0)] - [InlineData(int.MaxValue, int.MinValue)] - public static void SizeSetTest(int x, int y) - { - Size size = new(x, y); - Area rect = new(10, 10, 10, 10) - { - Size = size - }; - Assert.Equal(size, rect.Size); - Assert.Equal(size.Width, rect.Width); - Assert.Equal(size.Height, rect.Height); - } - - [Theory] - [InlineData(int.MaxValue, int.MinValue, int.MaxValue, int.MinValue)] - [InlineData(int.MaxValue, 0, int.MinValue, 0)] - [InlineData(0, int.MinValue, 0, int.MaxValue)] - [InlineData(int.MinValue, int.MaxValue, int.MinValue, int.MaxValue)] - public void EqualityTest(int x, int y, int width, int height) - { - Area rect1 = new(x, y, width, height); - Area rect2 = new(width / 2, height / 2, x, y); - - Assert.True(rect1 != rect2); - Assert.False(rect1 == rect2); - Assert.False(rect1.Equals(rect2)); - Assert.False(rect1.Equals((object)rect2)); - } - - [Fact] - public static void EqualityTest_NotArea() - { - Area Area = new(0, 0, 0, 0); - Assert.False(Area.Equals(null)); - Assert.False(Area.Equals(0)); - Assert.False(Area.Equals(new RectangleF(0, 0, 0, 0))); - } - - [Fact] - public static void GetHashCodeTest() - { - Area rect1 = new(10, 10, 10, 10); - Area rect2 = new(10, 10, 10, 10); - Assert.Equal(rect1.GetHashCode(), rect2.GetHashCode()); - Assert.NotEqual(rect1.GetHashCode(), new Area(20, 10, 10, 10).GetHashCode()); - Assert.NotEqual(rect1.GetHashCode(), new Area(10, 20, 10, 10).GetHashCode()); - Assert.NotEqual(rect1.GetHashCode(), new Area(10, 10, 20, 10).GetHashCode()); - Assert.NotEqual(rect1.GetHashCode(), new Area(10, 10, 10, 20).GetHashCode()); - } - - [Theory] - [InlineData(float.MaxValue, float.MinValue, float.MaxValue, float.MinValue)] - [InlineData(float.MinValue, float.MaxValue, float.MinValue, float.MaxValue)] - [InlineData(0, 0, 0, 0)] - public void AreaFConversionTest(float x, float y, float width, float height) - { - RectangleF rect = new(x, y, width, height); - Area rCeiling, rTruncate, rRound; - - unchecked - { - rCeiling = new Area((int)Math.Ceiling(x), (int)Math.Ceiling(y), - (int)Math.Ceiling(width), (int)Math.Ceiling(height)); - rTruncate = new Area((int)x, (int)y, (int)width, (int)height); - rRound = new Area((int)Math.Round(x), (int)Math.Round(y), - (int)Math.Round(width), (int)Math.Round(height)); - } - - Assert.Equal(rCeiling, Area.Ceiling(rect)); - Assert.Equal(rTruncate, Area.Truncate(rect)); - Assert.Equal(rRound, Area.Round(rect)); - } - - [Theory] - [InlineData(int.MaxValue, int.MinValue, int.MinValue, int.MaxValue)] - [InlineData(0, int.MinValue, int.MaxValue, 0)] - public void ContainsTest(int x, int y, int width, int height) - { - Area rect = new(unchecked((2 * x) - width), unchecked((2 * y) - height), width, height); - Cords p = new(x, y); - Area r = new(x, y, width / 2, height / 2); - - Assert.False(rect.Contains(x, y)); - Assert.False(rect.Contains(p)); - Assert.False(rect.Contains(r)); - } - - [Theory] - [InlineData(0, 0, 0, 0)] - [InlineData(int.MaxValue, int.MinValue, int.MinValue, int.MaxValue)] - [InlineData(0, int.MinValue, int.MaxValue, 0)] - public void InflateTest(int x, int y, int width, int height) - { - Area inflatedRect, rect = new(x, y, width, height); - unchecked - { - inflatedRect = new Area(x - width, y - height, width + (2 * width), height + (2 * height)); - } - - Assert.Equal(inflatedRect, Area.Inflate(rect, width, height)); - - rect.Inflate(width, height); - Assert.Equal(inflatedRect, rect); - - Size s = new(x, y); - unchecked - { - inflatedRect = new Area(rect.X - x, rect.Y - y, rect.Width + (2 * x), rect.Height + (2 * y)); - } - - rect.Inflate(s); - Assert.Equal(inflatedRect, rect); - } - - [Theory] - [InlineData(0, 0, 0, 0)] - [InlineData(int.MaxValue, int.MinValue, int.MinValue, int.MaxValue)] - [InlineData(0, int.MinValue, int.MaxValue, 0)] - public void IntersectTest(int x, int y, int width, int height) - { - Area rect = new(x, y, width, height); - Area expectedRect = Area.Intersect(rect, rect); - rect.Intersect(rect); - Assert.Equal(expectedRect, rect); - Assert.False(rect.IntersectsWith(expectedRect)); - } - - [Fact] - public static void Intersect_IntersectingAreas_Test() - { - Area rect1 = new(0, 0, 5, 5); - Area rect2 = new(1, 1, 3, 3); - Area expected = new(1, 1, 3, 3); - - Assert.Equal(expected, Area.Intersect(rect1, rect2)); - } - - [Theory] - [InlineData(0, 0, 0, 0)] - [InlineData(int.MaxValue, int.MinValue, int.MinValue, int.MaxValue)] - [InlineData(int.MaxValue, 0, 0, int.MaxValue)] - [InlineData(0, int.MinValue, int.MaxValue, 0)] - public void UnionTest(int x, int y, int width, int height) - { - Area a = new(x, y, width, height); - Area b = new(width, height, x, y); - - int x1 = Math.Min(a.X, b.X); - int x2 = Math.Max(a.X + a.Width, b.X + b.Width); - int y1 = Math.Min(a.Y, b.Y); - int y2 = Math.Max(a.Y + a.Height, b.Y + b.Height); - - Area expectedArea = new(x1, y1, x2 - x1, y2 - y1); - - Assert.Equal(expectedArea, Area.Union(a, b)); - } - - [Theory] - [InlineData(0, 0, 0, 0)] - [InlineData(int.MaxValue, int.MinValue, int.MinValue, int.MaxValue)] - [InlineData(int.MaxValue, 0, 0, int.MaxValue)] - [InlineData(0, int.MinValue, int.MaxValue, 0)] - public void OffsetTest(int x, int y, int width, int height) - { - Area r1 = new(x, y, width, height); - Area expectedRect = new(x + width, y + height, width, height); - Cords p = new(width, height); - - r1.Offset(p); - Assert.Equal(expectedRect, r1); - - expectedRect.Offset(p); - r1.Offset(width, height); - Assert.Equal(expectedRect, r1); - } - - [Theory] - [InlineData(0, 0, 0, 0)] - [InlineData(5, -5, 0, 1)] - public void ToStringTest(int x, int y, int width, int height) - { - Area r = new(x, y, width, height); - Assert.Equal(string.Format(CultureInfo.CurrentCulture, "{{X={0},Y={1},Width={2},Height={3}}}", r.X, r.Y, r.Width, r.Height), r.ToString()); - } - } -} diff --git a/AdvancedSharpAdbClient.Tests/Models/CordsTests.cs b/AdvancedSharpAdbClient.Tests/Models/CordsTests.cs deleted file mode 100644 index 194bc1f5..00000000 --- a/AdvancedSharpAdbClient.Tests/Models/CordsTests.cs +++ /dev/null @@ -1,224 +0,0 @@ -using System; -using System.Drawing; -using System.Globalization; -using Xunit; - -namespace AdvancedSharpAdbClient.Tests -{ - /// - /// Tests the struct. - /// - public class CordsTests - { - [Fact] - public void DefaultConstructorTest() - { - Assert.Equal(Cords.Empty, new Cords()); - } - - [Theory] - [InlineData(int.MaxValue, int.MinValue)] - [InlineData(int.MinValue, int.MinValue)] - [InlineData(int.MaxValue, int.MaxValue)] - [InlineData(0, 0)] - public void NonDefaultConstructorTest(int x, int y) - { - Cords p1 = new(x, y); - Cords p2 = new(new Size(x, y)); - - Assert.Equal(p1, p2); - } - - [Theory] - [InlineData(int.MaxValue)] - [InlineData(int.MinValue)] - [InlineData(0)] - public void SingleIntConstructorTest(int x) - { - Cords p1 = new(x); - Cords p2 = new(unchecked((short)(x & 0xFFFF)), unchecked((short)((x >> 16) & 0xFFFF))); - - Assert.Equal(p1, p2); - } - - [Fact] - public void IsEmptyDefaultsTest() - { - Assert.True(Cords.Empty.IsEmpty); - Assert.True(new Cords().IsEmpty); - Assert.True(new Cords(0, 0).IsEmpty); - } - - [Theory] - [InlineData(int.MaxValue, int.MinValue)] - [InlineData(int.MinValue, int.MinValue)] - [InlineData(int.MaxValue, int.MaxValue)] - public void IsEmptyRandomTest(int x, int y) - { - Assert.False(new Cords(x, y).IsEmpty); - } - - [Theory] - [InlineData(int.MaxValue, int.MinValue)] - [InlineData(int.MinValue, int.MinValue)] - [InlineData(int.MaxValue, int.MaxValue)] - [InlineData(0, 0)] - public void CoordinatesTest(int x, int y) - { - Cords p = new(x, y); - Assert.Equal(x, p.X); - Assert.Equal(y, p.Y); - } - - [Theory] - [InlineData(int.MaxValue, int.MinValue)] - [InlineData(int.MinValue, int.MinValue)] - [InlineData(int.MaxValue, int.MaxValue)] - [InlineData(0, 0)] - public void CordsFConversionTest(int x, int y) - { - PointF p = new Cords(x, y); - Assert.Equal(new PointF(x, y), p); - } - - [Theory] - [InlineData(int.MaxValue, int.MinValue)] - [InlineData(int.MinValue, int.MinValue)] - [InlineData(int.MaxValue, int.MaxValue)] - [InlineData(0, 0)] - public void SizeConversionTest(int x, int y) - { - Size sz = (Size)new Cords(x, y); - Assert.Equal(new Size(x, y), sz); - } - - [Theory] - [InlineData(int.MaxValue, int.MinValue)] - [InlineData(int.MinValue, int.MinValue)] - [InlineData(int.MaxValue, int.MaxValue)] - [InlineData(0, 0)] - public void ArithmeticTest(int x, int y) - { - Cords addExpected, subExpected, p = new(x, y); - Size s = new(y, x); - - unchecked - { - addExpected = new Cords(x + y, y + x); - subExpected = new Cords(x - y, y - x); - } - - Assert.Equal(addExpected, p + s); - Assert.Equal(subExpected, p - s); - Assert.Equal(addExpected, Cords.Add(p, s)); - Assert.Equal(subExpected, Cords.Subtract(p, s)); - } - - [Theory] - [InlineData(float.MaxValue, float.MinValue)] - [InlineData(float.MinValue, float.MinValue)] - [InlineData(float.MaxValue, float.MaxValue)] - [InlineData(0, 0)] - public void CordsFMathematicalTest(float x, float y) - { - PointF pf = new(x, y); - Cords pCeiling, pTruncate, pRound; - - unchecked - { - pCeiling = new Cords((int)Math.Ceiling(x), (int)Math.Ceiling(y)); - pTruncate = new Cords((int)x, (int)y); - pRound = new Cords((int)Math.Round(x), (int)Math.Round(y)); - } - - Assert.Equal(pCeiling, Cords.Ceiling(pf)); - Assert.Equal(pRound, Cords.Round(pf)); - Assert.Equal(pTruncate, Cords.Truncate(pf)); - } - - [Theory] - [InlineData(int.MaxValue, int.MinValue)] - [InlineData(int.MinValue, int.MinValue)] - [InlineData(int.MaxValue, int.MaxValue)] - [InlineData(0, 0)] - public void OffsetTest(int x, int y) - { - Cords p1 = new(x, y); - Cords p2 = new(y, x); - - p1.Offset(p2); - - Assert.Equal(unchecked(p2.X + p2.Y), p1.X); - Assert.Equal(p1.X, p1.Y); - - p2.Offset(x, y); - Assert.Equal(p1, p2); - } - - [Theory] - [InlineData(int.MaxValue, int.MinValue)] - [InlineData(int.MinValue, int.MinValue)] - [InlineData(int.MaxValue, int.MaxValue)] - [InlineData(0, 0)] - public void EqualityTest(int x, int y) - { - Cords p1 = new(x, y); - Cords p2 = new((x / 2) - 1, (y / 2) - 1); - Cords p3 = new(x, y); - - Assert.True(p1 == p3); - Assert.True(p1 != p2); - Assert.True(p2 != p3); - - Assert.True(p1.Equals(p3)); - Assert.False(p1.Equals(p2)); - Assert.False(p2.Equals(p3)); - - Assert.True(p1.Equals((object)p3)); - Assert.False(p1.Equals((object)p2)); - Assert.False(p2.Equals((object)p3)); - - Assert.Equal(p1.GetHashCode(), p3.GetHashCode()); - } - - [Fact] - public static void EqualityTest_NotCords() - { - Cords Cords = new(0, 0); - Assert.False(Cords.Equals(null)); - Assert.False(Cords.Equals(0)); - Assert.False(Cords.Equals(new PointF(0, 0))); - } - - [Fact] - public static void GetHashCodeTest() - { - Cords Cords = new(10, 10); - Assert.Equal(Cords.GetHashCode(), new Cords(10, 10).GetHashCode()); - Assert.NotEqual(Cords.GetHashCode(), new Cords(20, 10).GetHashCode()); - Assert.NotEqual(Cords.GetHashCode(), new Cords(10, 20).GetHashCode()); - } - - [Theory] - [InlineData(0, 0, 0, 0)] - [InlineData(1, -2, 3, -4)] - public void ConversionTest(int x, int y, int width, int height) - { - Area rect = new(x, y, width, height); - RectangleF rectF = rect; - Assert.Equal(x, rectF.X); - Assert.Equal(y, rectF.Y); - Assert.Equal(width, rectF.Width); - Assert.Equal(height, rectF.Height); - } - - [Theory] - [InlineData(0, 0)] - [InlineData(5, -5)] - public void ToStringTest(int x, int y) - { - Cords p = new(x, y); - Assert.Equal(string.Format(CultureInfo.CurrentCulture, "{{X={0},Y={1}}}", p.X, p.Y), p.ToString()); - } - } -} diff --git a/AdvancedSharpAdbClient.Tests/Models/FramebufferHeaderTests.cs b/AdvancedSharpAdbClient.Tests/Models/FramebufferHeaderTests.cs index 9be1a8d1..4373c24e 100644 --- a/AdvancedSharpAdbClient.Tests/Models/FramebufferHeaderTests.cs +++ b/AdvancedSharpAdbClient.Tests/Models/FramebufferHeaderTests.cs @@ -1,7 +1,6 @@ using System.Drawing; using System.Drawing.Imaging; using System.IO; -using System.Runtime.InteropServices; using System.Runtime.Versioning; using Xunit; @@ -58,12 +57,11 @@ public void ReadFramebufferV2Test() Assert.Equal(0u, header.ColorSpace); } +#if WINDOWS [Fact] [SupportedOSPlatform("windows")] public void ToImageTest() { - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { return; } - byte[] data = File.ReadAllBytes("Assets/framebufferheader.bin"); FramebufferHeader header = FramebufferHeader.Read(data); byte[] framebuffer = File.ReadAllBytes("Assets/framebuffer.bin"); @@ -85,8 +83,6 @@ public void ToImageTest() [SupportedOSPlatform("windows")] public void ToImageEmptyTest() { - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { return; } - byte[] data = File.ReadAllBytes("Assets/framebufferheader-empty.bin"); FramebufferHeader header = FramebufferHeader.Read(data); @@ -95,5 +91,6 @@ public void ToImageEmptyTest() Bitmap image = header.ToImage(framebuffer); Assert.Null(image); } +#endif } } diff --git a/AdvancedSharpAdbClient.Tests/Models/FramebufferTests.cs b/AdvancedSharpAdbClient.Tests/Models/FramebufferTests.cs index a07615c3..b113192b 100644 --- a/AdvancedSharpAdbClient.Tests/Models/FramebufferTests.cs +++ b/AdvancedSharpAdbClient.Tests/Models/FramebufferTests.cs @@ -3,7 +3,6 @@ using System.Drawing.Imaging; using System.IO; using System.Net; -using System.Runtime.InteropServices; using Xunit; namespace AdvancedSharpAdbClient.Tests.Models @@ -74,21 +73,20 @@ public void RefreshTest() Assert.Equal(1u, header.Version); Assert.Equal(0u, header.ColorSpace); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - using Bitmap image = (Bitmap)framebuffer; - Assert.NotNull(image); - Assert.Equal(PixelFormat.Format32bppArgb, image.PixelFormat); - - Assert.Equal(1, image.Width); - Assert.Equal(1, image.Height); - - Color pixel = image.GetPixel(0, 0); - Assert.Equal(0x35, pixel.R); - Assert.Equal(0x4a, pixel.G); - Assert.Equal(0x4c, pixel.B); - Assert.Equal(0xff, pixel.A); - } +#if WINDOWS + using Bitmap image = (Bitmap)framebuffer; + Assert.NotNull(image); + Assert.Equal(PixelFormat.Format32bppArgb, image.PixelFormat); + + Assert.Equal(1, image.Width); + Assert.Equal(1, image.Height); + + Color pixel = image.GetPixel(0, 0); + Assert.Equal(0x35, pixel.R); + Assert.Equal(0x4a, pixel.G); + Assert.Equal(0x4c, pixel.B); + Assert.Equal(0xff, pixel.A); +#endif } [Fact] @@ -141,21 +139,20 @@ public async void RefreshAsyncTest() Assert.Equal(1u, header.Version); Assert.Equal(0u, header.ColorSpace); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - using Bitmap image = (Bitmap)framebuffer; - Assert.NotNull(image); - Assert.Equal(PixelFormat.Format32bppArgb, image.PixelFormat); - - Assert.Equal(1, image.Width); - Assert.Equal(1, image.Height); - - Color pixel = image.GetPixel(0, 0); - Assert.Equal(0x35, pixel.R); - Assert.Equal(0x4a, pixel.G); - Assert.Equal(0x4c, pixel.B); - Assert.Equal(0xff, pixel.A); - } +#if WINDOWS + using Bitmap image = (Bitmap)framebuffer; + Assert.NotNull(image); + Assert.Equal(PixelFormat.Format32bppArgb, image.PixelFormat); + + Assert.Equal(1, image.Width); + Assert.Equal(1, image.Height); + + Color pixel = image.GetPixel(0, 0); + Assert.Equal(0x35, pixel.R); + Assert.Equal(0x4a, pixel.G); + Assert.Equal(0x4c, pixel.B); + Assert.Equal(0xff, pixel.A); +#endif } } } diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index 94a301ff..637ac538 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -726,7 +726,7 @@ public async Task DumpScreenAsync(DeviceData device, CancellationTo #endif /// - public async Task ClickAsync(DeviceData device, Cords cords, CancellationToken cancellationToken = default) + public async Task ClickAsync(DeviceData device, Point cords, CancellationToken cancellationToken = default) { EnsureDevice(device); diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index 33177cb4..156a767a 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -875,7 +875,7 @@ public Windows.Data.Xml.Dom.XmlDocument DumpScreenWinRT(DeviceData device) #endif /// - public void Click(DeviceData device, Cords cords) + public void Click(DeviceData device, Point cords) { EnsureDevice(device); diff --git a/AdvancedSharpAdbClient/AdbSocket.Async.cs b/AdvancedSharpAdbClient/AdbSocket.Async.cs index 908a3d52..99c41a73 100644 --- a/AdvancedSharpAdbClient/AdbSocket.Async.cs +++ b/AdvancedSharpAdbClient/AdbSocket.Async.cs @@ -25,7 +25,7 @@ public virtual async Task SendAsync(byte[] data, int length, CancellationToken c try { int count = await socket.SendAsync(data, length != -1 ? length : data.Length, SocketFlags.None, cancellationToken).ConfigureAwait(false); - + if (count < 0) { throw new AdbException("channel EOF"); @@ -312,7 +312,7 @@ public virtual async Task SendAsync(byte[] data, CancellationToken cancellationT try { int count = await socket.SendAsync(data, SocketFlags.None, cancellationToken).ConfigureAwait(false); - + if (count < 0) { throw new AdbException("channel EOF"); diff --git a/AdvancedSharpAdbClient/AdbSocket.cs b/AdvancedSharpAdbClient/AdbSocket.cs index 0fa97c8c..5ccd19ec 100644 --- a/AdvancedSharpAdbClient/AdbSocket.cs +++ b/AdvancedSharpAdbClient/AdbSocket.cs @@ -117,7 +117,7 @@ public virtual void Send(byte[] data, int length) try { int count = socket.Send(data, length != -1 ? length : data.Length, SocketFlags.None); - + if (count < 0) { throw new AdbException("channel EOF"); @@ -136,7 +136,7 @@ public virtual void Send(byte[] data, int offset, int length) try { int count = socket.Send(data, offset, length != -1 ? length : data.Length, SocketFlags.None); - + if (count < 0) { throw new AdbException("channel EOF"); diff --git a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj index d0f8b1b3..5f158df2 100644 --- a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj +++ b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj @@ -3,21 +3,20 @@ False True - True $(NoWarn);CS1685;CA2254 $(NoWarn);NU1603;NU1605 - net6.0;net8.0;netstandard1.3;netstandard2.0;netstandard2.1 - $(TargetFrameworks);netcoreapp2.1;netcoreapp3.1 - $(TargetFrameworks);net2.0-client;net3.5-client;net4.0-client;net4.5.2;net4.6.2;net4.7.2;net4.8.1;net6.0-windows10.0.17763.0;net8.0-windows10.0.17763.0;netcore50;uap10.0;uap10.0.15138.0 + net6.0;net8.0;netcoreapp2.1;netcoreapp3.1;netstandard1.3;netstandard2.0;netstandard2.1 + $(TargetFrameworks);net6.0-windows10.0.17763.0;net8.0-windows10.0.17763.0 + $(TargetFrameworks);net2.0-client;net3.5-client;net4.0-client;net4.5.2;net4.6.2;net4.7.2;net4.8.1;netcore50;uap10.0;uap10.0.15138.0 - net8.0;netstandard1.3;netstandard2.0;netstandard2.1 - $(TargetFrameworks);net6.0;netcoreapp3.1 + net8.0;netcoreapp3.1;netstandard1.3;netstandard2.0;netstandard2.1 + $(TargetFrameworks);net8.0-windows10.0.17763.0 $(TargetFrameworks);net2.0-client;net3.5-client;net4.5.2;net4.8.1 @@ -61,16 +60,8 @@ - + @@ -87,21 +78,10 @@ or '$(TargetFramework)' == 'net4.5.2' or '$(TargetFramework)' == 'net4.6.2' or '$(TargetFramework)' == 'net4.7.2' - or '$(TargetFramework)' == 'net4.8.1'"> - $(DefineConstants);HAS_DRAWING - - - - $(DefineConstants);HAS_DRAWING + or '$(TargetFramework)' == 'net8.0-windows10.0.17763.0'"> + $(DefineConstants);HAS_IMAGING - $(DefineConstants);HAS_PROCESS;HAS_SERIALIZATION + $(DefineConstants);HAS_PROCESS;HAS_DRAWING;HAS_SERIALIZATION diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/EnvironmentVariablesReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/EnvironmentVariablesReceiver.cs index 0ae04fc1..009c5b0e 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/EnvironmentVariablesReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/EnvironmentVariablesReceiver.cs @@ -30,7 +30,7 @@ public EnvironmentVariablesReceiver() { } /// /// Gets the environment variables that are currently defined on the device. /// - public Dictionary EnvironmentVariables { get; } = new Dictionary(); + public Dictionary EnvironmentVariables { get; } = []; /// /// Processes the new lines. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs index 799f9492..78399fb8 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs @@ -30,7 +30,7 @@ public GetPropReceiver() { } /// /// Gets the list of properties which have been retrieved. /// - public Dictionary Properties { get; } = new Dictionary(); + public Dictionary Properties { get; } = []; /// /// Processes the new lines. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InfoOutputReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InfoOutputReceiver.cs index 714b8a4e..1c72cf39 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InfoOutputReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InfoOutputReceiver.cs @@ -20,13 +20,13 @@ public InfoOutputReceiver() { } /// /// Gets or sets a dictionary with the extracted properties and their corresponding values. /// - private Dictionary Properties { get; set; } = new Dictionary(); + private Dictionary Properties { get; set; } = []; /// /// Gets or sets the dictionary with all properties and their corresponding property parsers. /// A property parser extracts the property value out of a if possible. /// - private Dictionary> PropertyParsers { get; set; } = new Dictionary>(); + private Dictionary> PropertyParsers { get; set; } = []; /// /// Gets the value of the property out of the Properties dictionary. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs index 8ca8b924..ba306688 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs @@ -139,9 +139,9 @@ protected override void ProcessNewLines(IEnumerable lines) private static partial Regex ErrorRegex(); #else private static Regex SuccessRegex() => new(SuccessPattern, RegexOptions.IgnoreCase); - + private static Regex FailureRegex() => new(FailurePattern, RegexOptions.IgnoreCase); - + private static Regex ErrorRegex() => new(ErrorPattern, RegexOptions.IgnoreCase); #endif } diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs index 28689266..9d4e2a47 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs @@ -20,7 +20,7 @@ public ProcessOutputReceiver() { } /// /// Gets a list of all processes that have been received. /// - public List Processes { get; } = new List(); + public List Processes { get; } = []; /// protected override void ProcessNewLines(IEnumerable lines) diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs b/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs index 34a70934..59160ca6 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs @@ -406,10 +406,10 @@ public partial interface IAdbClient /// Clicks on the specified coordinates. /// /// The device on which to click. - /// The to click. + /// The to click. /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. - Task ClickAsync(DeviceData device, Cords cords, CancellationToken cancellationToken); + Task ClickAsync(DeviceData device, Point cords, CancellationToken cancellationToken); /// /// Clicks on the specified coordinates. diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs b/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs index 41a43fcd..d875d63a 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs @@ -404,8 +404,8 @@ public partial interface IAdbClient /// Clicks on the specified coordinates. /// /// The device on which to click. - /// The to click. - void Click(DeviceData device, Cords cords); + /// The to click. + void Click(DeviceData device, Point cords); /// /// Clicks on the specified coordinates. diff --git a/AdvancedSharpAdbClient/Logs/EventLogEntry.cs b/AdvancedSharpAdbClient/Logs/EventLogEntry.cs index 2ef2054c..2b7de5ea 100644 --- a/AdvancedSharpAdbClient/Logs/EventLogEntry.cs +++ b/AdvancedSharpAdbClient/Logs/EventLogEntry.cs @@ -25,6 +25,6 @@ public EventLogEntry() { } /// /// Gets or sets the values of this event log entry. /// - public List Values { get; set; } = new List(); + public List Values { get; set; } = []; } } diff --git a/AdvancedSharpAdbClient/Models/Area.cs b/AdvancedSharpAdbClient/Models/Area.cs deleted file mode 100644 index c2ee8111..00000000 --- a/AdvancedSharpAdbClient/Models/Area.cs +++ /dev/null @@ -1,587 +0,0 @@ -using System; - -namespace AdvancedSharpAdbClient -{ - /// - /// Stores the location and size of a rectangular region. - /// - public struct Area : IEquatable - { - /// - /// Represents a structure with its properties left uninitialized. - /// - public static readonly Area Empty; - - private int x; // Do not rename (binary serialization) - private int y; // Do not rename (binary serialization) - private int width; // Do not rename (binary serialization) - private int height; // Do not rename (binary serialization) - - /// - /// Initializes a new instance of the class with the specified location - /// and size. - /// - /// The x-coordinate of the upper-left corner of the area. - /// The y-coordinate of the upper-left corner of the area. - /// The width of the area. - /// The height of the area. - public Area(int x, int y, int width, int height) - { - this.x = x; - this.y = y; - this.width = width; - this.height = height; - } - -#if HAS_DRAWING - /// - /// Initializes a new instance of the class with the specified rectangle. - /// - /// A that represents the rectangular region. - public Area(Rectangle rectangle) - { - x = rectangle.X; - y = rectangle.Y; - width = rectangle.Width; - height = rectangle.Height; - } - - /// - /// Initializes a new instance of the class with the specified location and size. - /// - /// A that represents the upper-left corner of the rectangular region. - /// A that represents the width and height of the rectangular region. - public Area(Cords location, System.Drawing.Size size) - { - x = location.X; - y = location.Y; - width = size.Width; - height = size.Height; - } - - /// - /// Initializes a new instance of the class with the specified location and size. - /// - /// A that represents the upper-left corner of the rectangular region. - /// A that represents the width and height of the rectangular region. - public Area(System.Drawing.Point location, System.Drawing.Size size) - { - x = location.X; - y = location.Y; - width = size.Width; - height = size.Height; - } -#endif - -#if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER -#pragma warning disable CS0419 - /// - /// Initializes a new instance of the class with the specified rectangle. - /// - /// A that represents the rectangular region. - public Area(Rect rectangle) - { - x = unchecked((int)rectangle.X); - y = unchecked((int)rectangle.Y); - width = unchecked((int)rectangle.Width); - height = unchecked((int)rectangle.Height); - } - - /// - /// Initializes a new instance of the class with the specified location and size. - /// - /// A that represents the upper-left corner of the rectangular region. - /// A that represents the width and height of the rectangular region. - public Area(Cords location, Windows.Foundation.Size size) - { - x = location.X; - y = location.Y; - width = unchecked((int)size.Width); - height = unchecked((int)size.Height); - } - - /// - /// Initializes a new instance of the class with the specified location and size. - /// - /// A that represents the upper-left corner of the rectangular region. - /// A that represents the width and height of the rectangular region. - public Area(Windows.Foundation.Point location, Windows.Foundation.Size size) - { - x = unchecked((int)location.X); - y = unchecked((int)location.Y); - width = unchecked((int)size.Width); - height = unchecked((int)size.Height); - } -#pragma warning restore CS0419 -#endif - - /// - /// Creates a new with the specified location and size. - /// - /// The x-coordinate of the upper-left corner of this structure. - /// The y-coordinate of the upper-left corner of this structure. - /// The x-coordinate of the lower-right corner of this structure. - /// The y-coordinate of the lower-right corner of this structure. - /// The new that this method creates. - public static Area FromLTRB(int left, int top, int right, int bottom) => - new(left, top, unchecked(right - left), unchecked(bottom - top)); - - /// - /// Gets or sets the coordinates of the upper-left corner of the rectangular region represented by this - /// . - /// - public Cords Location - { - readonly get => new(X, Y); - set - { - X = value.X; - Y = value.Y; - } - } - - /// - /// Gets or sets the coordinates of the center of the rectangular region represented by this - /// . - /// - public readonly Cords Center => unchecked(new(X + (Width / 2), Y + (Height / 2))); - -#if HAS_DRAWING - /// - /// Gets or sets the size of this . - /// - public System.Drawing.Size Size - { - readonly get => new(Width, Height); - set - { - Width = value.Width; - Height = value.Height; - } - } -#endif - -#if !HAS_DRAWING && WINDOWS_UWP - /// - /// Gets or sets the size of this . - /// - public Size Size - { - readonly get => new(Width, Height); - set - { - Width = unchecked((int)value.Width); - Height = unchecked((int)value.Height); - } - } -#endif - - /// - /// Gets or sets the x-coordinate of the upper-left corner of the rectangular region defined by this - /// . - /// - public int X - { - readonly get => x; - set => x = value; - } - - /// - /// Gets or sets the y-coordinate of the upper-left corner of the rectangular region defined by this - /// . - /// - public int Y - { - readonly get => y; - set => y = value; - } - - /// - /// Gets or sets the width of the rectangular region defined by this . - /// - public int Width - { - readonly get => width; - set => width = value; - } - - /// - /// Gets or sets the width of the rectangular region defined by this . - /// - public int Height - { - readonly get => height; - set => height = value; - } - - /// - /// Gets the x-coordinate of the upper-left corner of the rectangular region defined by this - /// . - /// - public readonly int Left => X; - - /// - /// Gets the y-coordinate of the upper-left corner of the rectangular region defined by this - /// . - /// - public readonly int Top => Y; - - /// - /// Gets the x-coordinate of the lower-right corner of the rectangular region defined by this - /// . - /// - public readonly int Right => unchecked(X + Width); - - /// - /// Gets the y-coordinate of the lower-right corner of the rectangular region defined by this - /// . - /// - public readonly int Bottom => unchecked(Y + Height); - - /// - /// Tests whether this has a - /// or a of 0. - /// - public readonly bool IsEmpty => height == 0 && width == 0 && x == 0 && y == 0; - - /// - /// Tests whether is a with the same location - /// and size of this Area. - /// - /// The to test. - /// This method returns if is a structure - /// and its , , , and properties are equal to - /// the corresponding properties of this structure; otherwise, . - public override readonly bool Equals(object obj) => obj is Area area && Equals(area); - - /// - public readonly bool Equals(Area other) => this == other; - -#if HAS_DRAWING - /// - /// Creates a with the specified . - /// - /// The to convert. - /// The that results from the conversion. - public static implicit operator Rectangle(Area rect) => new(rect.X, rect.Y, rect.Width, rect.Height); - - /// - /// Creates a with the specified . - /// - /// The to convert. - /// The that results from the conversion. - public static implicit operator RectangleF(Area rect) => new(rect.X, rect.Y, rect.Width, rect.Height); - - /// - /// Creates a with the specified . - /// - /// The to convert. - /// The that results from the conversion. - public static implicit operator Area(Rectangle rect) => new(rect); -#endif - -#if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER -#pragma warning disable CS0419 - /// - /// Creates a with the specified . - /// - /// The to convert. - /// The that results from the conversion. - public static implicit operator Rect(Area rect) => new(rect.X, rect.Y, rect.Width, rect.Height); - - /// - /// Creates a with the specified . - /// - /// The to convert. - /// The that results from the conversion. - public static implicit operator Area(Rect rect) => new(rect); -#pragma warning restore CS0419 -#endif - - /// - /// Tests whether two objects have equal location and size. - /// - /// The Rectangle structure that is to the left of the equality operator. - /// The Rectangle structure that is to the right of the equality operator. - /// This operator returns if the two structures have equal - /// , , , and properties. - public static bool operator ==(Area left, Area right) => - left.X == right.X && left.Y == right.Y && left.Width == right.Width && left.Height == right.Height; - - /// - /// Tests whether two objects differ in location or size. - /// - /// The Rectangle structure that is to the left of the inequality operator. - /// The Rectangle structure that is to the right of the inequality operator. - /// This operator returns if any of the , , - /// properties of the two structures are unequal; otherwise . - public static bool operator !=(Area left, Area right) => !(left == right); - -#if HAS_DRAWING - /// - /// Converts a to a by performing a ceiling operation on all the coordinates. - /// - /// The structure to be converted. - public static Area Ceiling(RectangleF value) - { - unchecked - { - return new Area( - (int)Math.Ceiling(value.X), - (int)Math.Ceiling(value.Y), - (int)Math.Ceiling(value.Width), - (int)Math.Ceiling(value.Height)); - } - } - - /// - /// Converts a to a by performing a truncate operation on all the coordinates. - /// - /// The structure to be converted. - public static Area Truncate(RectangleF value) - { - unchecked - { - return new Area( - (int)value.X, - (int)value.Y, - (int)value.Width, - (int)value.Height); - } - } - - /// - /// Converts a to a by performing a round operation on all the coordinates. - /// - /// The structure to be converted. - public static Area Round(RectangleF value) - { - unchecked - { - return new Area( - (int)Math.Round(value.X), - (int)Math.Round(value.Y), - (int)Math.Round(value.Width), - (int)Math.Round(value.Height)); - } - } -#endif - -#if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER -#pragma warning disable CS0419 - /// - /// Converts a to a by performing a ceiling operation on all the coordinates. - /// - /// The structure to be converted. - public static Area Ceiling(Rect value) - { - unchecked - { - return new Area( - (int)Math.Ceiling(value.X), - (int)Math.Ceiling(value.Y), - (int)Math.Ceiling(value.Width), - (int)Math.Ceiling(value.Height)); - } - } - - /// - /// Converts a to a by performing a truncate operation on all the coordinates. - /// - /// The structure to be converted. - public static Area Truncate(Rect value) - { - unchecked - { - return new Area( - (int)value.X, - (int)value.Y, - (int)value.Width, - (int)value.Height); - } - } - - /// - /// Converts a to a by performing a round operation on all the coordinates. - /// - /// The structure to be converted. - public static Area Round(Rect value) - { - unchecked - { - return new Area( - (int)Math.Round(value.X), - (int)Math.Round(value.Y), - (int)Math.Round(value.Width), - (int)Math.Round(value.Height)); - } - } -#pragma warning restore CS0419 -#endif - - /// - /// Determines if the specified point is contained within the rectangular region defined by this - /// . - /// - /// The x-coordinate of the point to test. - /// The y-coordinate of the point to test. - /// This method returns if the point defined by and - /// is contained within this structure; otherwise . - public readonly bool Contains(int x, int y) => X <= x && x < X + Width && Y <= y && y < Y + Height; - - /// - /// Determines if the specified point is contained within the rectangular region defined by this - /// . - /// - /// The to test. - /// This method returns if the point represented by - /// is contained within this structure; otherwise . - public readonly bool Contains(Cords pt) => Contains(pt.X, pt.Y); - - /// - /// Determines if the rectangular region represented by is entirely contained within the - /// rectangular region represented by this . - /// - /// The to test. - /// This method returns if the rectangular region represented by - /// is entirely contained within this structure; otherwise . - public readonly bool Contains(Area rect) => - (X <= rect.X) && (rect.X + rect.Width <= X + Width) && - (Y <= rect.Y) && (rect.Y + rect.Height <= Y + Height); - - /// - /// Returns the hash code for this structure. - /// - /// An integer that represents the hash code for this rectangle. - public override readonly int GetHashCode() => HashCode.Combine(X, Y, Width, Height); - - /// - /// Inflates this by the specified amount. - /// - /// The amount to inflate this horizontally. - /// The amount to inflate this vertically. - public void Inflate(int width, int height) - { - unchecked - { - X -= width; - Y -= height; - - Width += 2 * width; - Height += 2 * height; - } - } - -#if HAS_DRAWING - /// - /// Inflates this by the specified amount. - /// - /// The amount to inflate this rectangle. - public void Inflate(System.Drawing.Size size) => Inflate(size.Width, size.Height); -#endif - -#if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER - /// - /// Inflates this by the specified amount. - /// - /// The amount to inflate this rectangle. - public void Inflate(Windows.Foundation.Size size) => Inflate(unchecked((int)size.Width), unchecked((int)size.Height)); -#endif - - /// - /// Creates a that is inflated by the specified amount. - /// - /// The with which to start. This rectangle is not modified. - /// The amount to inflate this horizontally. - /// The amount to inflate this vertically. - public static Area Inflate(Area rect, int x, int y) - { - Area r = rect; - r.Inflate(x, y); - return r; - } - - /// - /// Creates a Area that represents the intersection between this Area and rect. - /// - /// The with which to intersect. - public void Intersect(Area rect) - { - Area result = Intersect(rect, this); - - X = result.X; - Y = result.Y; - Width = result.Width; - Height = result.Height; - } - - /// - /// Creates a rectangle that represents the intersection between a and b. If there is no intersection, an - /// empty rectangle is returned. - /// - /// A rectangle to intersect. - /// A rectangle to intersect. - /// A that represents the intersection of and . - public static Area Intersect(Area a, Area b) - { - int x1 = Math.Max(a.X, b.X); - int x2 = Math.Min(a.X + a.Width, b.X + b.Width); - int y1 = Math.Max(a.Y, b.Y); - int y2 = Math.Min(a.Y + a.Height, b.Y + b.Height); - - return x2 >= x1 && y2 >= y1 ? new Area(x1, y1, x2 - x1, y2 - y1) : Empty; - } - - /// - /// Determines if this rectangle intersects with rect. - /// - /// The rectangle to test. - /// This method returns if there is any intersection, otherwise . - public readonly bool IntersectsWith(Area rect) => - (rect.X < X + Width) && (X < rect.X + rect.Width) && - (rect.Y < Y + Height) && (Y < rect.Y + rect.Height); - - /// - /// Creates a rectangle that represents the union between a and b. - /// - /// A rectangle to union. - /// A rectangle to union. - /// A structure that bounds the union of the two structures. - public static Area Union(Area a, Area b) - { - int x1 = Math.Min(a.X, b.X); - int x2 = Math.Max(a.X + a.Width, b.X + b.Width); - int y1 = Math.Min(a.Y, b.Y); - int y2 = Math.Max(a.Y + a.Height, b.Y + b.Height); - - return new Area(x1, y1, x2 - x1, y2 - y1); - } - - /// - /// Adjusts the location of this rectangle by the specified amount. - /// - /// Amount to offset the location. - public void Offset(Cords pos) => Offset(pos.X, pos.Y); - - /// - /// Adjusts the location of this rectangle by the specified amount. - /// - /// The horizontal offset. - /// The vertical offset. - public void Offset(int x, int y) - { - unchecked - { - X += x; - Y += y; - } - } - - /// - /// Converts the attributes of this to a human readable string. - /// - /// A string that contains the position, width, and height of this structure ¾ - /// for example, {X=20, Y=20, Width=100, Height=50}. - public override readonly string ToString() => $"{{X={X},Y={Y},Width={Width},Height={Height}}}"; - } -} diff --git a/AdvancedSharpAdbClient/Models/ColorData.cs b/AdvancedSharpAdbClient/Models/ColorData.cs index 92b5ecad..2938cc7c 100644 --- a/AdvancedSharpAdbClient/Models/ColorData.cs +++ b/AdvancedSharpAdbClient/Models/ColorData.cs @@ -25,7 +25,7 @@ public readonly record struct ColorData(uint Length, uint Offset) public uint Offset { get; init; } = Offset; /// - /// Deconstruct the struct. + /// Deconstruct the struct. /// /// The number of bits that contain information for this color. /// The offset, in bits, within the byte array for a pixel, at which the diff --git a/AdvancedSharpAdbClient/Models/Cords.cs b/AdvancedSharpAdbClient/Models/Cords.cs deleted file mode 100644 index 889269f8..00000000 --- a/AdvancedSharpAdbClient/Models/Cords.cs +++ /dev/null @@ -1,366 +0,0 @@ -// -// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. -// - -using System; - -namespace AdvancedSharpAdbClient -{ - /// - /// Represents an ordered pair of integer x- and y-coordinates that defines a point in a two-dimensional plane. - /// - public struct Cords : IEquatable - { - /// - /// Creates a new instance of the class with member data left uninitialized. - /// - public static readonly Cords Empty; - - /// - /// Initializes a new instance of the class. - /// - /// The horizontal "X" coordinate. - /// The vertical "Y" coordinate. - public Cords(int cx, int cy) - { - X = cx; - Y = cy; - } - - /// - /// Initializes a new instance of the class using coordinates specified by an integer value. - /// - public Cords(int dw) - { - X = LowInt16(dw); - Y = HighInt16(dw); - } - -#if HAS_DRAWING - /// - /// Initializes a new instance of the class from a . - /// - public Cords(System.Drawing.Point sz) - { - X = sz.X; - Y = sz.Y; - } - - /// - /// Initializes a new instance of the class from a . - /// - public Cords(System.Drawing.Size sz) - { - X = sz.Width; - Y = sz.Height; - } -#endif - -#if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER -#pragma warning disable CS0419 - /// - /// Initializes a new instance of the class from a . - /// - public Cords(Windows.Foundation.Point sz) - { - X = unchecked((int)sz.X); - Y = unchecked((int)sz.Y); - } - - /// - /// Initializes a new instance of the class from a . - /// - public Cords(Windows.Foundation.Size sz) - { - X = unchecked((int)sz.Width); - Y = unchecked((int)sz.Height); - } -#pragma warning restore CS0419 -#endif - - /// - /// Gets a value indicating whether this is empty. - /// - public readonly bool IsEmpty => X == 0 && Y == 0; - - /// - /// Gets or sets the horizontal "X" coordinate. - /// - public int X { readonly get; set; } - - /// - /// Gets or sets the vertical "Y" coordinate. - /// - public int Y { readonly get; set; } - -#if HAS_DRAWING - /// - /// Creates a with the coordinates of the specified . - /// - /// The to convert. - /// The that results from the conversion. - public static implicit operator System.Drawing.Point(Cords p) => new(p.X, p.Y); - - /// - /// Creates a with the coordinates of the specified . - /// - /// The to convert. - /// The that results from the conversion. - public static implicit operator System.Drawing.PointF(Cords p) => new(p.X, p.Y); - - /// - /// Creates a with the coordinates of the specified . - /// - /// The to convert. - /// The that results from the conversion. - public static explicit operator System.Drawing.Size(Cords p) => new(p.X, p.Y); - - /// - /// Creates a with the coordinates of the specified . - /// - /// The to convert. - /// The that results from the conversion. - public static implicit operator Cords(System.Drawing.Point p) => new(p); - - /// - /// Creates a with the coordinates of the specified . - /// - /// The to convert. - /// The that results from the conversion. - public static explicit operator Cords(System.Drawing.Size p) => new(p); - - /// - /// Translates a by a given . - /// - /// The to translate. - /// A that specifies the pair of numbers - /// to add to the coordinates of . - /// The translated . - public static Cords operator +(Cords pt, System.Drawing.Size sz) => Add(pt, sz); - - /// - /// Translates a by the negative of a given . - /// - /// The to translate. - /// A that specifies the pair of numbers - /// to subtract from the coordinates of . - /// A structure that is translated by the negative of a given structure. - public static Cords operator -(Cords pt, System.Drawing.Size sz) => Subtract(pt, sz); -#endif - -#if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER -#pragma warning disable CS0419 - /// - /// Creates a with the coordinates of the specified . - /// - /// The to convert. - /// The that results from the conversion. - public static implicit operator Windows.Foundation.Point(Cords p) => new(p.X, p.Y); - - /// - /// Creates a with the coordinates of the specified . - /// - /// The to convert. - /// The that results from the conversion. - public static explicit operator Windows.Foundation.Size(Cords p) => new(p.X, p.Y); - - /// - /// Creates a with the coordinates of the specified . - /// - /// The to convert. - /// The that results from the conversion. - public static explicit operator Cords(Windows.Foundation.Point p) => new(p); - - /// - /// Creates a with the coordinates of the specified . - /// - /// The to convert. - /// The that results from the conversion. - public static explicit operator Cords(Windows.Foundation.Size p) => new(p); - - /// - /// Translates a by a given . - /// - /// The to translate. - /// A that specifies the pair of numbers - /// to add to the coordinates of . - /// The translated . - public static Cords operator +(Cords pt, Windows.Foundation.Size sz) => Add(pt, sz); - - /// - /// Translates a by the negative of a given . - /// - /// The to translate. - /// A that specifies the pair of numbers - /// to subtract from the coordinates of . - /// A structure that is translated by the negative of a given structure. - public static Cords operator -(Cords pt, Windows.Foundation.Size sz) => Subtract(pt, sz); -#pragma warning restore CS0419 -#endif - - /// - /// Compares two objects. The result specifies whether the values of the - /// and properties of the two - /// objects are equal. - /// - /// A to compare. - /// A to compare. - /// if the and values - /// of and are equal; otherwise, . - public static bool operator ==(Cords left, Cords right) => left.X == right.X && left.Y == right.Y; - - /// - /// Compares two objects. The result specifies whether the values of the - /// or properties of the two - /// objects are unequal. - /// - /// A to compare. - /// A to compare. - /// if the values of either the or values - /// of and differ; otherwise, . - public static bool operator !=(Cords left, Cords right) => !(left == right); - -#if HAS_DRAWING - /// - /// Translates a by a given . - /// - /// The to add. - /// The to add. - /// The that is the result of the addition operation. - public static Cords Add(Cords pt, System.Drawing.Size sz) => new(unchecked(pt.X + sz.Width), unchecked(pt.Y + sz.Height)); - - /// - /// Translates a by the negative of a given . - /// - /// The to be subtracted from. - /// The to subtract from the Point. - /// The that is the result of the subtraction operation. - public static Cords Subtract(Cords pt, System.Drawing.Size sz) => new(unchecked(pt.X - sz.Width), unchecked(pt.Y - sz.Height)); - - /// - /// Converts a to a by performing a ceiling operation on all the coordinates. - /// - /// The to convert. - /// The this method converts to. - public static Cords Ceiling(System.Drawing.PointF value) => new(unchecked((int)Math.Ceiling(value.X)), unchecked((int)Math.Ceiling(value.Y))); - - /// - /// Converts a to a by performing a truncate operation on all the coordinates. - /// - /// The to convert. - /// The this method converts to. - public static Cords Truncate(System.Drawing.PointF value) => new(unchecked((int)value.X), unchecked((int)value.Y)); - - /// - /// Converts a to a by performing a round operation on all the coordinates. - /// - /// The to convert. - /// The this method converts to. - public static Cords Round(System.Drawing.PointF value) => new(unchecked((int)Math.Round(value.X)), unchecked((int)Math.Round(value.Y))); -#endif - -#if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER -#pragma warning disable CS0419 - /// - /// Translates a by a given . - /// - /// The to add. - /// The to add. - /// The that is the result of the addition operation. - public static Cords Add(Cords pt, Windows.Foundation.Size sz) => new(unchecked((int)(pt.X + sz.Width)), unchecked((int)(pt.Y + sz.Height))); - - /// - /// Translates a by the negative of a given . - /// - /// The to be subtracted from. - /// The to subtract from the Point. - /// The that is the result of the subtraction operation. - public static Cords Subtract(Cords pt, Windows.Foundation.Size sz) => new(unchecked((int)(pt.X - sz.Width)), unchecked((int)(pt.Y - sz.Height))); - - /// - /// Converts a to a by performing a ceiling operation on all the coordinates. - /// - /// The to convert. - /// The this method converts to. - public static Cords Ceiling(Windows.Foundation.Point value) => new(unchecked((int)Math.Ceiling(value.X)), unchecked((int)Math.Ceiling(value.Y))); - - /// - /// Converts a to a by performing a truncate operation on all the coordinates. - /// - /// The to convert. - /// The this method converts to. - public static Cords Truncate(Windows.Foundation.Point value) => new(unchecked((int)value.X), unchecked((int)value.Y)); - - /// - /// Converts a to a by performing a round operation on all the coordinates. - /// - /// The to convert. - /// The this method converts to. - public static Cords Round(Windows.Foundation.Point value) => new(unchecked((int)Math.Round(value.X)), unchecked((int)Math.Round(value.Y))); -#pragma warning restore CS0419 -#endif - - /// - /// Specifies whether this contains the same coordinates as the specified - /// . - /// - /// The to test for equality. - /// if is a and has the same coordinates as this point instance. - public override readonly bool Equals(object obj) => obj is Cords cords && Equals(cords); - - /// - /// Specifies whether this contains the same coordinates as the specified - /// . - /// - /// The point to test for equality. - /// if has the same coordinates as this point instance. - public readonly bool Equals(Cords other) => this == other; - - /// - /// Returns a hash code for this . - /// - /// An integer value that specifies a hash value for this . - public override readonly int GetHashCode() => HashCode.Combine(X, Y); - - /// - /// Translates this by the specified amount. - /// - /// The amount to offset the x-coordinate. - /// The amount to offset the y-coordinate. - public void Offset(int dx, int dy) - { - unchecked - { - X += dx; - Y += dy; - } - } - - /// - /// Translates this by the specified amount. - /// - /// The used offset this . - public void Offset(Cords p) => Offset(p.X, p.Y); - - /// - /// Converts this to a human readable string. - /// - /// A string that represents this . - public override readonly string ToString() => $"{{X={X},Y={Y}}}"; - - /// - /// Deconstruct the class. - /// - /// The horizontal "X" coordinate. - /// The vertical "Y" coordinate. - public readonly void Deconstruct(out int cx, out int cy) - { - cx = X; - cy = Y; - } - - private static short HighInt16(int n) => unchecked((short)((n >> 16) & 0xffff)); - - private static short LowInt16(int n) => unchecked((short)(n & 0xffff)); - } -} diff --git a/AdvancedSharpAdbClient/Models/Element.cs b/AdvancedSharpAdbClient/Models/Element.cs index 410baee9..a795c90a 100644 --- a/AdvancedSharpAdbClient/Models/Element.cs +++ b/AdvancedSharpAdbClient/Models/Element.cs @@ -22,13 +22,13 @@ public class Element : IEquatable /// /// The current ADB client that manages the connection. /// The current device containing the element. - /// The coordinates and size of the element. + /// The coordinates and size of the element. /// Gets or sets element attributes. - public Element(IAdbClient client, DeviceData device, Area area, Dictionary attributes = null) + public Element(IAdbClient client, DeviceData device, Rectangle rectangle, Dictionary attributes = null) { Client = client; Device = device; - Bounds = area; + Bounds = rectangle; Attributes = attributes; } @@ -47,7 +47,7 @@ public Element(IAdbClient client, DeviceData device, XmlNode xmlNode) if (xmlNode.Attributes["bounds"]?.Value is string bounds) { string[] cords = bounds.Split(separator, StringSplitOptions.RemoveEmptyEntries); // x1, y1, x2, y2 - Bounds = Area.FromLTRB(int.Parse(cords[0]), int.Parse(cords[1]), int.Parse(cords[2]), int.Parse(cords[3])); + Bounds = Rectangle.FromLTRB(int.Parse(cords[0]), int.Parse(cords[1]), int.Parse(cords[2]), int.Parse(cords[3])); } Attributes = new(xmlNode.Attributes.Count); @@ -93,7 +93,7 @@ public Element(IAdbClient client, DeviceData device, Windows.Data.Xml.Dom.IXmlNo if (xmlNode.Attributes?.GetNamedItem("bounds")?.NodeValue?.ToString() is string bounds) { string[] cords = bounds.Split(separator, StringSplitOptions.RemoveEmptyEntries); // x1, y1, x2, y2 - Bounds = Area.FromLTRB(int.Parse(cords[0]), int.Parse(cords[1]), int.Parse(cords[2]), int.Parse(cords[3])); + Bounds = Rectangle.FromLTRB(int.Parse(cords[0]), int.Parse(cords[1]), int.Parse(cords[2]), int.Parse(cords[3])); } Attributes = new(xmlNode.Attributes.Count); @@ -134,7 +134,7 @@ IEnumerable FindElements() /// /// Gets the coordinates and size of the element. /// - public Area Bounds { get; init; } + public Rectangle Bounds { get; init; } /// /// Gets the children of this element. @@ -154,7 +154,7 @@ IEnumerable FindElements() /// /// Gets the coordinates of the the center of the element. /// - public Cords Center => Bounds.Center; + public Point Center => unchecked(new(Bounds.X + (Bounds.Width / 2), Bounds.Y + (Bounds.Height / 2))); /// /// Gets the text of the element. diff --git a/AdvancedSharpAdbClient/Models/Framebuffer.cs b/AdvancedSharpAdbClient/Models/Framebuffer.cs index 8f43f88c..57e8fbd4 100644 --- a/AdvancedSharpAdbClient/Models/Framebuffer.cs +++ b/AdvancedSharpAdbClient/Models/Framebuffer.cs @@ -4,7 +4,6 @@ using System; using System.Net; -using System.Runtime.InteropServices; using System.Threading; namespace AdvancedSharpAdbClient @@ -12,36 +11,14 @@ namespace AdvancedSharpAdbClient /// /// Provides access to the framebuffer (that is, a copy of the image being displayed on the device screen). /// - public class Framebuffer : IDisposable + /// The device for which to fetch the frame buffer. + /// The at which the adb server is listening. + public class Framebuffer(DeviceData device, EndPoint endPoint) : IDisposable { - private byte[] headerData; + private byte[] headerData = new byte[56]; private bool headerInitialized; private bool disposed = false; - /// - /// Initializes a new instance of the class. - /// - /// The device for which to fetch the frame buffer. - /// The at which the adb server is listening. - public Framebuffer(DeviceData device, EndPoint endPoint) - { - Device = device ?? throw new ArgumentNullException(nameof(device)); - - EndPoint = endPoint ?? throw new ArgumentNullException(nameof(endPoint)); - - // Initialize the headerData buffer -#if NETCORE - headerData = new byte[56]; -#else -#if !NETFRAMEWORK || NET451_OR_GREATER - int size = Marshal.SizeOf(); -#else - int size = Marshal.SizeOf(default(FramebufferHeader)); -#endif - headerData = new byte[size]; -#endif - } - /// /// Initializes a new instance of the class. /// @@ -58,12 +35,12 @@ public Framebuffer(DeviceData device) : this(device, AdbClient.DefaultEndPoint) /// /// Gets the device for which to fetch the frame buffer. /// - public DeviceData Device { get; } + public DeviceData Device { get; } = device ?? throw new ArgumentNullException(nameof(device)); /// /// Gets the at which the adb server is listening. /// - public EndPoint EndPoint { get; init; } + public EndPoint EndPoint { get; init; } = endPoint ?? throw new ArgumentNullException(nameof(endPoint)); /// /// Gets the framebuffer header. The header contains information such as the width and height and the color encoding. @@ -179,7 +156,7 @@ public virtual async Task RefreshAsync(bool reset = false, CancellationToken can } #endif -#if HAS_DRAWING +#if HAS_IMAGING /// /// Converts the framebuffer data to a . /// diff --git a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs index 7376a64a..a06fc391 100644 --- a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs +++ b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs @@ -132,7 +132,7 @@ public FramebufferHeader(byte[] data) /// A new object. public static FramebufferHeader Read(byte[] data) => new(data); -#if HAS_DRAWING +#if HAS_IMAGING /// /// Converts a array containing the raw frame buffer data to a . /// diff --git a/AdvancedSharpAdbClient/Models/HashCode.cs b/AdvancedSharpAdbClient/Models/HashCode.cs index 7a0eca0f..2c2f0716 100644 --- a/AdvancedSharpAdbClient/Models/HashCode.cs +++ b/AdvancedSharpAdbClient/Models/HashCode.cs @@ -68,7 +68,7 @@ public struct HashCode private uint _v1, _v2, _v3, _v4; private uint _queue1, _queue2, _queue3; private uint _length; - + private static uint GenerateGlobalSeed() => (uint)new Random().Next(int.MinValue, int.MaxValue); /// @@ -368,13 +368,13 @@ private static void Initialize(out uint v1, out uint v2, out uint v3, out uint v [MethodImpl((MethodImplOptions)256)] private static uint Round(uint hash, uint input) { - return RotateLeft(hash + input * Prime2, 13) * Prime1; + return RotateLeft(hash + (input * Prime2), 13) * Prime1; } [MethodImpl((MethodImplOptions)256)] private static uint QueueRound(uint hash, uint queuedValue) { - return RotateLeft(hash + queuedValue * Prime3, 17) * Prime4; + return RotateLeft(hash + (queuedValue * Prime3), 17) * Prime4; } [MethodImpl((MethodImplOptions)256)] @@ -387,7 +387,7 @@ private static uint MixEmptyState() { return s_seed + Prime5; } - + [MethodImpl((MethodImplOptions)256)] private static uint MixFinal(uint hash) { @@ -465,15 +465,23 @@ private void Add(int value) // Switch can't be inlined. if (position == 0) + { _queue1 = val; + } else if (position == 1) + { _queue2 = val; + } else if (position == 2) + { _queue3 = val; + } else // position == 3 { if (previousLength == 3) + { Initialize(out _v1, out _v2, out _v3, out _v4); + } _v1 = Round(_v1, _queue1); _v2 = Round(_v2, _queue2); @@ -519,7 +527,9 @@ public readonly int ToHashCode() { hash = QueueRound(hash, _queue2); if (position > 2) + { hash = QueueRound(hash, _queue3); + } } } diff --git a/AdvancedSharpAdbClient/Models/Point.cs b/AdvancedSharpAdbClient/Models/Point.cs new file mode 100644 index 00000000..6e516112 --- /dev/null +++ b/AdvancedSharpAdbClient/Models/Point.cs @@ -0,0 +1,151 @@ +#if !HAS_DRAWING +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace AdvancedSharpAdbClient +{ + /// + /// Represents an ordered pair of x and y coordinates that define a point in a two-dimensional plane. + /// + public struct Point : IEquatable + { + /// + /// Creates a new instance of the class with member data left uninitialized. + /// + public static readonly Point Empty; + + private int x; // Do not rename (binary serialization) + private int y; // Do not rename (binary serialization) + + /// + /// Initializes a new instance of the class with the specified coordinates. + /// + /// The horizontal "X" coordinate. + /// The vertical "Y" coordinate. + public Point(int x, int y) + { + this.x = x; + this.y = y; + } + + /// + /// Initializes a new instance of the Point class using coordinates specified by an integer value. + /// + public Point(int dw) + { + x = LowInt16(dw); + y = HighInt16(dw); + } + + /// + /// Gets a value indicating whether this is empty. + /// + public readonly bool IsEmpty => x == 0 && y == 0; + + /// + /// Gets the x-coordinate of this . + /// + public int X + { + readonly get => x; + set => x = value; + } + + /// + /// Gets the y-coordinate of this . + /// + public int Y + { + readonly get => y; + set => y = value; + } + + /// + /// Compares two objects. The result specifies whether the values of the + /// and properties of the two + /// objects are equal. + /// + /// A to compare. + /// A to compare. + /// if the and values + /// of and are equal; otherwise, . + public static bool operator ==(Point left, Point right) => left.X == right.X && left.Y == right.Y; + + /// + /// Compares two objects. The result specifies whether the values of the + /// or properties of the two + /// objects are unequal. + /// + /// A to compare. + /// A to compare. + /// if the values of either the or values + /// of and differ; otherwise, . + public static bool operator !=(Point left, Point right) => !(left == right); + + /// + /// Specifies whether this contains the same coordinates as the specified + /// . + /// + /// The to test for equality. + /// if is a and has the same coordinates as this point instance. + public override readonly bool Equals(object obj) => obj is Point point && Equals(point); + + /// + /// Specifies whether this contains the same coordinates as the specified + /// . + /// + /// The point to test for equality. + /// if has the same coordinates as this point instance. + public readonly bool Equals(Point other) => this == other; + + /// + /// Returns a hash code. + /// + /// An integer value that specifies a hash value for this . + public override readonly int GetHashCode() => HashCode.Combine(X, Y); + + /// + /// Translates this by the specified amount. + /// + /// The amount to offset the x-coordinate. + /// The amount to offset the y-coordinate. + public void Offset(int dx, int dy) + { + unchecked + { + X += dx; + Y += dy; + } + } + + /// + /// Translates this by the specified amount. + /// + /// The used offset this . + public void Offset(Point p) => Offset(p.X, p.Y); + + /// + /// Deconstruct the class. + /// + /// The horizontal "X" coordinate. + /// The vertical "Y" coordinate. + public readonly void Deconstruct(out int cx, out int cy) + { + cx = X; + cy = Y; + } + + /// + /// Converts this to a human readable string. + /// + /// A string that represents this . + public override readonly string ToString() => $"{{X={X},Y={Y}}}"; + + private static short HighInt16(int n) => unchecked((short)((n >> 16) & 0xffff)); + + private static short LowInt16(int n) => unchecked((short)(n & 0xffff)); + } +} +#endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Models/Rectangle.cs b/AdvancedSharpAdbClient/Models/Rectangle.cs new file mode 100644 index 00000000..adf65eba --- /dev/null +++ b/AdvancedSharpAdbClient/Models/Rectangle.cs @@ -0,0 +1,282 @@ +#if !HAS_DRAWING +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace AdvancedSharpAdbClient +{ + /// + /// Stores the location and size of a rectangular region. + /// + /// The x-coordinate of the upper-left corner of the rectangle. + /// The y-coordinate of the upper-left corner of the rectangle. + /// The width of the rectangle. + /// The height of the rectangle. + public struct Rectangle(int x, int y, int width, int height) : IEquatable + { + /// + /// Represents a structure with its properties left uninitialized. + /// + public static readonly Rectangle Empty; + + /// + /// Creates a new with the specified location and size. + /// + /// The x-coordinate of the upper-left corner of this structure. + /// The y-coordinate of the upper-left corner of this structure. + /// The x-coordinate of the lower-right corner of this structure. + /// The y-coordinate of the lower-right corner of this structure. + /// The new that this method creates. + public static Rectangle FromLTRB(int left, int top, int right, int bottom) => + new(left, top, unchecked(right - left), unchecked(bottom - top)); + + /// + /// Gets or sets the coordinates of the upper-left corner of the rectangular region represented by this + /// . + /// + public Point Location + { + readonly get => new(X, Y); + set + { + X = value.X; + Y = value.Y; + } + } + + /// + /// Gets or sets the x-coordinate of the upper-left corner of the rectangular region defined by this + /// . + /// + public int X { readonly get; set; } = x; + + /// + /// Gets or sets the y-coordinate of the upper-left corner of the rectangular region defined by this + /// . + /// + public int Y { readonly get; set; } = y; + + /// + /// Gets or sets the width of the rectangular region defined by this . + /// + public int Width { readonly get; set; } = width; + + /// + /// Gets or sets the width of the rectangular region defined by this . + /// + public int Height { readonly get; set; } = height; + + /// + /// Gets the x-coordinate of the upper-left corner of the rectangular region defined by this + /// . + /// + public readonly int Left => X; + + /// + /// Gets the y-coordinate of the upper-left corner of the rectangular region defined by this + /// . + /// + public readonly int Top => Y; + + /// + /// Gets the x-coordinate of the lower-right corner of the rectangular region defined by this + /// . + /// + public readonly int Right => unchecked(X + Width); + + /// + /// Gets the y-coordinate of the lower-right corner of the rectangular region defined by this + /// . + /// + public readonly int Bottom => unchecked(Y + Height); + + /// + /// Tests whether this has a + /// or a of 0. + /// + public readonly bool IsEmpty => Height == 0 && Width == 0 && X == 0 && Y == 0; + + /// + /// Tests whether is a with the same location + /// and size of this Rectangle. + /// + /// The to test. + /// This method returns if is a structure + /// and its , , , and properties are equal to + /// the corresponding properties of this structure; otherwise, . + public override readonly bool Equals(object obj) => obj is Rectangle rectangle && Equals(rectangle); + + /// + public readonly bool Equals(Rectangle other) => this == other; + + /// + /// Tests whether two objects have equal location and size. + /// + /// The Rectangle structure that is to the left of the equality operator. + /// The Rectangle structure that is to the right of the equality operator. + /// This operator returns if the two structures have equal + /// , , , and properties. + public static bool operator ==(Rectangle left, Rectangle right) => + left.X == right.X && left.Y == right.Y && left.Width == right.Width && left.Height == right.Height; + + /// + /// Tests whether two objects differ in location or size. + /// + /// The Rectangle structure that is to the left of the inequality operator. + /// The Rectangle structure that is to the right of the inequality operator. + /// This operator returns if any of the , , + /// properties of the two structures are unequal; otherwise . + public static bool operator !=(Rectangle left, Rectangle right) => !(left == right); + + /// + /// Determines if the specified point is contained within the rectangular region defined by this + /// . + /// + /// The x-coordinate of the point to test. + /// The y-coordinate of the point to test. + /// This method returns if the point defined by and + /// is contained within this structure; otherwise . + public readonly bool Contains(int x, int y) => X <= x && x < X + Width && Y <= y && y < Y + Height; + + /// + /// Determines if the specified point is contained within the rectangular region defined by this + /// . + /// + /// The to test. + /// This method returns if the point represented by + /// is contained within this structure; otherwise . + public readonly bool Contains(Point pt) => Contains(pt.X, pt.Y); + + /// + /// Determines if the rectangular region represented by is entirely contained within the + /// rectangular region represented by this . + /// + /// The to test. + /// This method returns if the rectangular region represented by + /// is entirely contained within this structure; otherwise . + public readonly bool Contains(Rectangle rect) => + (X <= rect.X) && (rect.X + rect.Width <= X + Width) && + (Y <= rect.Y) && (rect.Y + rect.Height <= Y + Height); + + /// + /// Returns the hash code for this structure. + /// + /// An integer that represents the hash code for this rectangle. + public override readonly int GetHashCode() => HashCode.Combine(X, Y, Width, Height); + + /// + /// Inflates this by the specified amount. + /// + /// The amount to inflate this horizontally. + /// The amount to inflate this vertically. + public void Inflate(int width, int height) + { + unchecked + { + X -= width; + Y -= height; + + Width += 2 * width; + Height += 2 * height; + } + } + + /// + /// Creates a that is inflated by the specified amount. + /// + /// The with which to start. This rectangle is not modified. + /// The amount to inflate this horizontally. + /// The amount to inflate this vertically. + public static Rectangle Inflate(Rectangle rect, int x, int y) + { + Rectangle r = rect; + r.Inflate(x, y); + return r; + } + + /// + /// Creates a Rectangle that represents the intersection between this Rectangle and rect. + /// + /// The with which to intersect. + public void Intersect(Rectangle rect) + { + Rectangle result = Intersect(rect, this); + + X = result.X; + Y = result.Y; + Width = result.Width; + Height = result.Height; + } + + /// + /// Creates a rectangle that represents the intersection between a and b. If there is no intersection, an + /// empty rectangle is returned. + /// + /// A rectangle to intersect. + /// A rectangle to intersect. + /// A that represents the intersection of and . + public static Rectangle Intersect(Rectangle a, Rectangle b) + { + int x1 = Math.Max(a.X, b.X); + int x2 = Math.Min(a.X + a.Width, b.X + b.Width); + int y1 = Math.Max(a.Y, b.Y); + int y2 = Math.Min(a.Y + a.Height, b.Y + b.Height); + + return x2 >= x1 && y2 >= y1 ? new Rectangle(x1, y1, x2 - x1, y2 - y1) : Empty; + } + + /// + /// Determines if this rectangle intersects with rect. + /// + /// The rectangle to test. + /// This method returns if there is any intersection, otherwise . + public readonly bool IntersectsWith(Rectangle rect) => + (rect.X < X + Width) && (X < rect.X + rect.Width) && + (rect.Y < Y + Height) && (Y < rect.Y + rect.Height); + + /// + /// Creates a rectangle that represents the union between a and b. + /// + /// A rectangle to union. + /// A rectangle to union. + /// A structure that bounds the union of the two structures. + public static Rectangle Union(Rectangle a, Rectangle b) + { + int x1 = Math.Min(a.X, b.X); + int x2 = Math.Max(a.X + a.Width, b.X + b.Width); + int y1 = Math.Min(a.Y, b.Y); + int y2 = Math.Max(a.Y + a.Height, b.Y + b.Height); + + return new Rectangle(x1, y1, x2 - x1, y2 - y1); + } + + /// + /// Adjusts the location of this rectangle by the specified amount. + /// + /// Amount to offset the location. + public void Offset(Point pos) => Offset(pos.X, pos.Y); + + /// + /// Adjusts the location of this rectangle by the specified amount. + /// + /// The horizontal offset. + /// The vertical offset. + public void Offset(int x, int y) + { + unchecked + { + X += x; + Y += y; + } + } + + /// + /// Converts the attributes of this to a human readable string. + /// + /// A string that contains the position, width, and height of this structure ¾ + /// for example, {X=20, Y=20, Width=100, Height=50}. + public override readonly string ToString() => $"{{X={X},Y={Y},Width={Width},Height={Height}}}"; + } +} +#endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Properties/GlobalUsings.cs b/AdvancedSharpAdbClient/Properties/GlobalUsings.cs index 24e9282d..7b52be14 100644 --- a/AdvancedSharpAdbClient/Properties/GlobalUsings.cs +++ b/AdvancedSharpAdbClient/Properties/GlobalUsings.cs @@ -17,12 +17,15 @@ global using Buffer = System.Buffer; global using DateTime = System.DateTime; global using TimeSpan = System.TimeSpan; +#if HAS_DRAWING +global using Point = System.Drawing.Point; +#endif #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; global using TimeSpan = System.TimeSpan; #endif @@ -36,6 +39,9 @@ #if HAS_DRAWING global using System.Drawing; +#endif + +#if HAS_IMAGING global using System.Drawing.Imaging; #endif diff --git a/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs b/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs index 438329ca..97082d73 100644 --- a/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs +++ b/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs @@ -119,9 +119,9 @@ protected override void ProcessNewLines(IEnumerable lines) private static partial Regex DeniedRegex(); #else private static Regex AbortingRegex() => new("Aborting.$", DefaultRegexOptions); - + private static Regex AppletRegex() => new("applet not found$", DefaultRegexOptions); - + private static Regex DeniedRegex() => new("(permission|access) denied$", DefaultRegexOptions); #endif } From d94477725aa54952d4ff5e92790fd95f5e07dd00 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Thu, 19 Oct 2023 17:33:54 +0800 Subject: [PATCH 42/66] Fix SendAsync of .netstandard1.3 Remove .net4.7.2 --- .../AdbCommandLineClient.Async.cs | 2 +- .../AdbCommandLineClient.cs | 2 +- .../AdvancedSharpAdbClient.csproj | 21 ++++++++++++------- .../Extensions/SocketExtensions.cs | 2 +- Directory.Build.props | 2 +- Directory.Build.targets | 2 +- README.md | 1 - 7 files changed, 18 insertions(+), 14 deletions(-) diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs index 58976085..e92af4e9 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs @@ -176,7 +176,7 @@ protected virtual async Task RunProcessAsync(string filename, string comman #else TaskCompletionSource source = new(); source.SetException(new PlatformNotSupportedException()); - return await source.Task; + return await source.Task.ConfigureAwait(false); #endif } } diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.cs index 3a5e3cea..b4354373 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.cs @@ -251,7 +251,7 @@ protected virtual int RunProcess(string filename, string command, ICollection - $(NoWarn);NU1603;NU1605 + $(NoWarn);NU1603;NU1605;NU1902;NU1903 net6.0;net8.0;netcoreapp2.1;netcoreapp3.1;netstandard1.3;netstandard2.0;netstandard2.1 $(TargetFrameworks);net6.0-windows10.0.17763.0;net8.0-windows10.0.17763.0 - $(TargetFrameworks);net2.0-client;net3.5-client;net4.0-client;net4.5.2;net4.6.2;net4.7.2;net4.8.1;netcore50;uap10.0;uap10.0.15138.0 + $(TargetFrameworks);net2.0-client;net3.5-client;net4.0-client;net4.5.2;net4.6.2;net4.8.1;netcore5.0;uap10.0;uap10.0.15138.0 @@ -36,7 +36,8 @@ - + @@ -48,11 +49,11 @@ - + - - @@ -73,6 +75,7 @@ $(DefineConstants);HAS_RUNTIMEINFORMATION @@ -113,7 +118,7 @@ $(DefineConstants);HAS_BUFFERS;HAS_INDEXRANGE - SendAsync(this Socket socket, byte[] buffer, int offset, return taskCompletionSource.Task; #else - return Extensions.Run(() => socket.Receive(buffer, offset, size, socketFlags), cancellationToken); + return Extensions.Run(() => socket.Send(buffer, offset, size, socketFlags), cancellationToken); #endif } #endif diff --git a/Directory.Build.props b/Directory.Build.props index 1665b799..ba740a47 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -35,7 +35,7 @@ $(MSBuildProjectName.Contains('.Test')) - + .NETCore,Version=v5.0 diff --git a/Directory.Build.targets b/Directory.Build.targets index 94686618..90a9371a 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -6,7 +6,7 @@ - + diff --git a/README.md b/README.md index d9cc9f0a..ef43737c 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,6 @@ Added important features. - .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.7.2 - .NET Framework 4.8.1 - .NET Standard 1.3 - .NET Standard 2.0 From a3ba686ffcf99126723b813d42da4ef8e5490b75 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Thu, 19 Oct 2023 23:16:47 +0800 Subject: [PATCH 43/66] Catch NotSupportedException on Process.GetProcessesByName --- .../AdbCommandLineClient.Async.cs | 57 ++++++++++++------- .../AdbCommandLineClient.cs | 43 ++++++++------ 2 files changed, 60 insertions(+), 40 deletions(-) diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs index e92af4e9..0a20df13 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs @@ -55,27 +55,34 @@ public virtual async Task StartServerAsync(CancellationToken cancellationToken = } #if HAS_PROCESS && !WINDOWS_UWP - // Starting the adb server failed for whatever reason. This can happen if adb.exe - // is running but is not accepting requests. In that case, try to kill it & start again. - // It kills all processes named "adb", so let's hope nobody else named their process that way. - foreach (Process adbProcess in Process.GetProcessesByName("adb")) + try { - try - { - adbProcess.Kill(); - } - catch (Win32Exception) + // Starting the adb server failed for whatever reason. This can happen if adb.exe + // is running but is not accepting requests. In that case, try to kill it & start again. + // It kills all processes named "adb", so let's hope nobody else named their process that way. + foreach (Process adbProcess in Process.GetProcessesByName("adb")) { - // The associated process could not be terminated - // or - // The process is terminating. - } - catch (InvalidOperationException) - { - // The process has already exited. - // There is no process associated with this Process object. + try + { + adbProcess.Kill(); + } + catch (Win32Exception) + { + // The associated process could not be terminated + // or + // The process is terminating. + } + catch (InvalidOperationException) + { + // The process has already exited. + // There is no process associated with this Process object. + } } } + catch (NotSupportedException) + { + // This platform does not support getting a list of processes. + } #endif // Try again. This time, we don't call "Inner", and an exception will be thrown if the start operation fails @@ -159,23 +166,29 @@ protected virtual async Task RunProcessAsync(string filename, string comman #if NET5_0_OR_GREATER using (CancellationTokenSource completionSource = new(TimeSpan.FromMilliseconds(5000))) { - await process.WaitForExitAsync(completionSource.Token).ConfigureAwait(false); - if (!process.HasExited) + try + { + await process.WaitForExitAsync(completionSource.Token).ConfigureAwait(false); + } + catch (OperationCanceledException) when (completionSource.IsCancellationRequested) { - process.Kill(); + if (!process.HasExited) + { + process.Kill(); + } } } #else - // get the return code from the process if (!process.WaitForExit(5000)) { process.Kill(); } #endif + // get the return code from the process return process.ExitCode; #else TaskCompletionSource source = new(); - source.SetException(new PlatformNotSupportedException()); + source.SetException(new PlatformNotSupportedException("This platform is not support System.Diagnostics.Process. You can start adb server by running `adb start-server` manually.")); return await source.Task.ConfigureAwait(false); #endif } diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.cs index b4354373..4147d29c 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.cs @@ -100,27 +100,34 @@ public virtual void StartServer() } #if HAS_PROCESS && !WINDOWS_UWP - // Starting the adb server failed for whatever reason. This can happen if adb.exe - // is running but is not accepting requests. In that case, try to kill it & start again. - // It kills all processes named "adb", so let's hope nobody else named their process that way. - foreach (Process adbProcess in Process.GetProcessesByName("adb")) + try { - try + // Starting the adb server failed for whatever reason. This can happen if adb.exe + // is running but is not accepting requests. In that case, try to kill it & start again. + // It kills all processes named "adb", so let's hope nobody else named their process that way. + foreach (Process adbProcess in Process.GetProcessesByName("adb")) { - adbProcess.Kill(); - } - catch (Win32Exception) - { - // The associated process could not be terminated - // or - // The process is terminating. - } - catch (InvalidOperationException) - { - // The process has already exited. - // There is no process associated with this Process object. + try + { + adbProcess.Kill(); + } + catch (Win32Exception) + { + // The associated process could not be terminated + // or + // The process is terminating. + } + catch (InvalidOperationException) + { + // The process has already exited. + // There is no process associated with this Process object. + } } } + catch (NotSupportedException) + { + // This platform does not support getting a list of processes. + } #endif // Try again. This time, we don't call "Inner", and an exception will be thrown if the start operation fails @@ -268,7 +275,7 @@ protected virtual int RunProcess(string filename, string command, ICollection Date: Fri, 20 Oct 2023 03:44:52 +0800 Subject: [PATCH 44/66] Use Microsoft.Bcl.HashCode on support platforms --- .../AdvancedSharpAdbClient.csproj | 8 +++++++ .../Exceptions/AdbException.cs | 1 + .../Exceptions/CommandAbortingException.cs | 1 + .../Exceptions/DeviceNotFoundException.cs | 1 + .../Exceptions/ElementNotFoundException.cs | 1 + .../Exceptions/InvalidKeyEventException.cs | 1 + .../Exceptions/InvalidTextException.cs | 1 + .../Exceptions/JavaException.cs | 1 + .../PackageInstallationException.cs | 2 -- .../Exceptions/PermissionDeniedException.cs | 1 + .../ShellCommandUnresponsiveException.cs | 1 + .../Exceptions/UnknownOptionException.cs | 1 + .../Attributes/SerializableAttribute.cs | 22 +++++++++++++++++++ AdvancedSharpAdbClient/Models/HashCode.cs | 2 +- 14 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 AdvancedSharpAdbClient/Extensions/Attributes/SerializableAttribute.cs diff --git a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj index cd6b7d9f..b8032abd 100644 --- a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj +++ b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj @@ -61,6 +61,14 @@ + + + + diff --git a/AdvancedSharpAdbClient/Exceptions/AdbException.cs b/AdvancedSharpAdbClient/Exceptions/AdbException.cs index 3c5bdd16..38a0a9ac 100644 --- a/AdvancedSharpAdbClient/Exceptions/AdbException.cs +++ b/AdvancedSharpAdbClient/Exceptions/AdbException.cs @@ -10,6 +10,7 @@ namespace AdvancedSharpAdbClient.Exceptions /// /// Represents an exception with communicating with ADB. /// + [Serializable] public class AdbException : Exception { /// diff --git a/AdvancedSharpAdbClient/Exceptions/CommandAbortingException.cs b/AdvancedSharpAdbClient/Exceptions/CommandAbortingException.cs index beb8b78b..8f4de81f 100644 --- a/AdvancedSharpAdbClient/Exceptions/CommandAbortingException.cs +++ b/AdvancedSharpAdbClient/Exceptions/CommandAbortingException.cs @@ -9,6 +9,7 @@ namespace AdvancedSharpAdbClient.Exceptions /// /// Thrown when an executed command identifies that it is being aborted. /// + [Serializable] public class CommandAbortingException : Exception { /// diff --git a/AdvancedSharpAdbClient/Exceptions/DeviceNotFoundException.cs b/AdvancedSharpAdbClient/Exceptions/DeviceNotFoundException.cs index 6d00a6d3..ca691438 100644 --- a/AdvancedSharpAdbClient/Exceptions/DeviceNotFoundException.cs +++ b/AdvancedSharpAdbClient/Exceptions/DeviceNotFoundException.cs @@ -9,6 +9,7 @@ namespace AdvancedSharpAdbClient.Exceptions /// /// Unable to connect to the device because it was not found in the list of available devices. /// + [Serializable] public class DeviceNotFoundException : AdbException { /// diff --git a/AdvancedSharpAdbClient/Exceptions/ElementNotFoundException.cs b/AdvancedSharpAdbClient/Exceptions/ElementNotFoundException.cs index 34b5807c..6da75c13 100644 --- a/AdvancedSharpAdbClient/Exceptions/ElementNotFoundException.cs +++ b/AdvancedSharpAdbClient/Exceptions/ElementNotFoundException.cs @@ -9,6 +9,7 @@ namespace AdvancedSharpAdbClient.Exceptions /// /// Represents an exception with Element. /// + [Serializable] public class ElementNotFoundException : Exception { /// diff --git a/AdvancedSharpAdbClient/Exceptions/InvalidKeyEventException.cs b/AdvancedSharpAdbClient/Exceptions/InvalidKeyEventException.cs index 93f67c34..4ec903a2 100644 --- a/AdvancedSharpAdbClient/Exceptions/InvalidKeyEventException.cs +++ b/AdvancedSharpAdbClient/Exceptions/InvalidKeyEventException.cs @@ -9,6 +9,7 @@ namespace AdvancedSharpAdbClient.Exceptions /// /// Represents an exception with key event. /// + [Serializable] public class InvalidKeyEventException : Exception { /// diff --git a/AdvancedSharpAdbClient/Exceptions/InvalidTextException.cs b/AdvancedSharpAdbClient/Exceptions/InvalidTextException.cs index 44b0ad5f..a7518943 100644 --- a/AdvancedSharpAdbClient/Exceptions/InvalidTextException.cs +++ b/AdvancedSharpAdbClient/Exceptions/InvalidTextException.cs @@ -9,6 +9,7 @@ namespace AdvancedSharpAdbClient.Exceptions /// /// Represents an exception with Element. /// + [Serializable] public class InvalidTextException : Exception { /// diff --git a/AdvancedSharpAdbClient/Exceptions/JavaException.cs b/AdvancedSharpAdbClient/Exceptions/JavaException.cs index daa16843..81e6e36b 100644 --- a/AdvancedSharpAdbClient/Exceptions/JavaException.cs +++ b/AdvancedSharpAdbClient/Exceptions/JavaException.cs @@ -8,6 +8,7 @@ namespace AdvancedSharpAdbClient.Exceptions /// /// Represents an exception with the Java exception output. /// + [Serializable] public partial class JavaException : Exception { private const string UnknownError = "An error occurred in Java"; diff --git a/AdvancedSharpAdbClient/Exceptions/PackageInstallationException.cs b/AdvancedSharpAdbClient/Exceptions/PackageInstallationException.cs index 76d3c9fa..6d6ce5ed 100644 --- a/AdvancedSharpAdbClient/Exceptions/PackageInstallationException.cs +++ b/AdvancedSharpAdbClient/Exceptions/PackageInstallationException.cs @@ -9,9 +9,7 @@ namespace AdvancedSharpAdbClient.DeviceCommands /// /// An exception while installing a package on the device. /// -#if HAS_SERIALIZATION [Serializable] -#endif public class PackageInstallationException : Exception { /// diff --git a/AdvancedSharpAdbClient/Exceptions/PermissionDeniedException.cs b/AdvancedSharpAdbClient/Exceptions/PermissionDeniedException.cs index fd7ae804..4a39bf6f 100644 --- a/AdvancedSharpAdbClient/Exceptions/PermissionDeniedException.cs +++ b/AdvancedSharpAdbClient/Exceptions/PermissionDeniedException.cs @@ -9,6 +9,7 @@ namespace AdvancedSharpAdbClient.Exceptions /// /// The exception that is thrown when the permission to a resource was denied. /// + [Serializable] public class PermissionDeniedException : Exception { /// diff --git a/AdvancedSharpAdbClient/Exceptions/ShellCommandUnresponsiveException.cs b/AdvancedSharpAdbClient/Exceptions/ShellCommandUnresponsiveException.cs index a1e0bc93..043fea9a 100644 --- a/AdvancedSharpAdbClient/Exceptions/ShellCommandUnresponsiveException.cs +++ b/AdvancedSharpAdbClient/Exceptions/ShellCommandUnresponsiveException.cs @@ -9,6 +9,7 @@ namespace AdvancedSharpAdbClient.Exceptions /// /// The exception that is thrown when a shell command becomes unresponsive. /// + [Serializable] public class ShellCommandUnresponsiveException : AdbException { /// diff --git a/AdvancedSharpAdbClient/Exceptions/UnknownOptionException.cs b/AdvancedSharpAdbClient/Exceptions/UnknownOptionException.cs index 57d2eb56..0816fb5e 100644 --- a/AdvancedSharpAdbClient/Exceptions/UnknownOptionException.cs +++ b/AdvancedSharpAdbClient/Exceptions/UnknownOptionException.cs @@ -9,6 +9,7 @@ namespace AdvancedSharpAdbClient.Exceptions /// /// Thrown when a command has an unknown option passed. /// + [Serializable] public class UnknownOptionException : Exception { /// diff --git a/AdvancedSharpAdbClient/Extensions/Attributes/SerializableAttribute.cs b/AdvancedSharpAdbClient/Extensions/Attributes/SerializableAttribute.cs new file mode 100644 index 00000000..2d81e84e --- /dev/null +++ b/AdvancedSharpAdbClient/Extensions/Attributes/SerializableAttribute.cs @@ -0,0 +1,22 @@ +#if NETSTANDARD && !NETSTANDARD2_0_OR_GREATER || NETCORE && !UAP10_0_15138_0 +// 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 +{ + /// + /// Indicates that a class can be serialized using binary or XML serialization. This class cannot be inherited. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Delegate, Inherited = false)] + [EditorBrowsable(EditorBrowsableState.Never)] + internal sealed class SerializableAttribute : Attribute + { + /// + /// Initializes a new instance of the class. + /// + public SerializableAttribute() { } + } +} +#endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Models/HashCode.cs b/AdvancedSharpAdbClient/Models/HashCode.cs index 2c2f0716..d11ab852 100644 --- a/AdvancedSharpAdbClient/Models/HashCode.cs +++ b/AdvancedSharpAdbClient/Models/HashCode.cs @@ -1,4 +1,4 @@ -#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER +#if !NET461_OR_GREATER && !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_0_OR_GREATER && !UAP10_0_15138_0 // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. From e3d1349606004f2baa8ead2220429f95022189dc Mon Sep 17 00:00:00 2001 From: wherewhere Date: Fri, 20 Oct 2023 15:44:29 +0800 Subject: [PATCH 45/66] Add StopServer for AdbServer --- AdvancedSharpAdbClient.Tests/AdbServerTests.Async.cs | 12 ++++++++++++ AdvancedSharpAdbClient.Tests/AdbServerTests.cs | 12 ++++++++++++ .../Dummys/DummyAdbServer.cs | 10 ++++++++++ AdvancedSharpAdbClient/AdbServer.Async.cs | 3 +++ AdvancedSharpAdbClient/AdbServer.cs | 3 +++ .../Extensions/StreamExtensions.cs | 6 +++--- .../Interfaces/IAdbServer.Async.cs | 7 ++++++- AdvancedSharpAdbClient/Interfaces/IAdbServer.cs | 8 +++++++- 8 files changed, 56 insertions(+), 5 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/AdbServerTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbServerTests.Async.cs index 11698e8c..61555ff0 100644 --- a/AdvancedSharpAdbClient.Tests/AdbServerTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbServerTests.Async.cs @@ -220,5 +220,17 @@ public async void RestartServerAsyncTest() Assert.Equal("host:version", socket.Requests[0]); Assert.Equal("host:kill", socket.Requests[1]); } + + /// + /// Tests the method. + /// + [Fact] + public async void StopServerAsyncTest() + { + await adbServer.StopServerAsync(); + + Assert.Single(socket.Requests); + Assert.Equal("host:kill", socket.Requests[0]); + } } } diff --git a/AdvancedSharpAdbClient.Tests/AdbServerTests.cs b/AdvancedSharpAdbClient.Tests/AdbServerTests.cs index ec1b19ff..0b768157 100644 --- a/AdvancedSharpAdbClient.Tests/AdbServerTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbServerTests.cs @@ -244,6 +244,18 @@ public void RestartServerTest() Assert.Equal("host:kill", socket.Requests[1]); } + /// + /// Tests the method. + /// + [Fact] + public void StopServerTest() + { + adbServer.StopServer(); + + Assert.Single(socket.Requests); + Assert.Equal("host:kill", socket.Requests[0]); + } + /// /// Tests the method. /// diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbServer.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbServer.cs index 521f9cde..6af5a5f6 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbServer.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbServer.cs @@ -77,5 +77,15 @@ public Task StartServerAsync(string adbPath, bool restartServ tcs.SetResult(result); return tcs.Task; } + + /// + public void StopServer() => Status = Status with { IsRunning = false }; + + /// + public Task StopServerAsync(CancellationToken cancellationToken) + { + StopServer(); + return Task.CompletedTask; + } } } diff --git a/AdvancedSharpAdbClient/AdbServer.Async.cs b/AdvancedSharpAdbClient/AdbServer.Async.cs index 72b2e10e..5f1bfc13 100644 --- a/AdvancedSharpAdbClient/AdbServer.Async.cs +++ b/AdvancedSharpAdbClient/AdbServer.Async.cs @@ -78,6 +78,9 @@ public Task RestartServerAsync(CancellationToken cancellation public virtual Task RestartServerAsync(string adbPath, CancellationToken cancellationToken = default) => StringExtensions.IsNullOrWhiteSpace(adbPath) ? RestartServerAsync(cancellationToken) : StartServerAsync(adbPath, true, cancellationToken); + /// + public virtual Task StopServerAsync(CancellationToken cancellationToken = default) => adbClient.KillAdbAsync(cancellationToken); + /// public virtual async Task GetStatusAsync(CancellationToken cancellationToken = default) { diff --git a/AdvancedSharpAdbClient/AdbServer.cs b/AdvancedSharpAdbClient/AdbServer.cs index a93d08eb..d4c0268a 100644 --- a/AdvancedSharpAdbClient/AdbServer.cs +++ b/AdvancedSharpAdbClient/AdbServer.cs @@ -164,6 +164,9 @@ public virtual StartServerResult StartServer(string adbPath, bool restartServerI public virtual StartServerResult RestartServer(string adbPath) => StringExtensions.IsNullOrWhiteSpace(adbPath) ? RestartServer() : StartServer(adbPath, true); + /// + public virtual void StopServer() => adbClient.KillAdb(); + /// public virtual AdbServerStatus GetStatus() { diff --git a/AdvancedSharpAdbClient/Extensions/StreamExtensions.cs b/AdvancedSharpAdbClient/Extensions/StreamExtensions.cs index 54e837d8..2f0fe1be 100644 --- a/AdvancedSharpAdbClient/Extensions/StreamExtensions.cs +++ b/AdvancedSharpAdbClient/Extensions/StreamExtensions.cs @@ -180,19 +180,19 @@ public static Task WriteAsync(this Stream stream, byte[] buffer, int offset, int // and convert to a TaskCancelledException - which is the exception we expect. CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.Register(stream.Close); - TaskCompletionSource taskCompletionSource = new(stream); + TaskCompletionSource taskCompletionSource = new(stream); IAsyncResult asyncResult = stream.BeginWrite(buffer, offset, count, iar => { // this is the callback - TaskCompletionSource taskCompletionSource = (TaskCompletionSource)iar.AsyncState; + TaskCompletionSource taskCompletionSource = (TaskCompletionSource)iar.AsyncState; Stream stream = (Stream)taskCompletionSource.Task.AsyncState; try { stream.EndWrite(iar); - taskCompletionSource.TrySetResult(true); + taskCompletionSource.TrySetResult(null); } catch (ObjectDisposedException) when (cancellationToken.IsCancellationRequested) { diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbServer.Async.cs b/AdvancedSharpAdbClient/Interfaces/IAdbServer.Async.cs index f315af0d..abc63422 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbServer.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbServer.Async.cs @@ -73,7 +73,12 @@ public partial interface IAdbServer Task RestartServerAsync(string adbPath, CancellationToken cancellationToken); /// - /// Gets the status of the adb server. + /// Stop the adb server asynchronously. + /// + Task StopServerAsync(CancellationToken cancellationToken); + + /// + /// Gets the status of the adb server asynchronously. /// /// A which can be used to cancel the asynchronous operation. /// A which return a object that describes the status of the adb server. diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbServer.cs b/AdvancedSharpAdbClient/Interfaces/IAdbServer.cs index 1b885d18..3e3ffe71 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbServer.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbServer.cs @@ -55,7 +55,8 @@ public partial interface IAdbServer /// and passed the full path to the adb server. StartServerResult RestartServer(); - /// Restarts the adb server with new adb path if it suddenly became unavailable. Call this class if, for example, + /// + /// Restarts the adb server with new adb path if it suddenly became unavailable. Call this class if, for example, /// you receive an with the flag /// set to - a clear indicating the ADB server died. /// @@ -66,6 +67,11 @@ public partial interface IAdbServer /// and passed the full path to the adb server. StartServerResult RestartServer(string adbPath); + /// + /// Stop the adb server. + /// + void StopServer(); + /// /// Gets the status of the adb server. /// From a681ee73f00cacc8351e961e0adbcff4db43d4df Mon Sep 17 00:00:00 2001 From: wherewhere Date: Sat, 21 Oct 2023 02:36:47 +0800 Subject: [PATCH 46/66] Use IAdbSocket directly on AdbServer --- .../AdbServerTests.Async.cs | 18 +-- .../AdbServerTests.cs | 27 ++-- AdvancedSharpAdbClient/AdbClient.cs | 2 +- .../AdbCommandLineClient.cs | 2 +- AdvancedSharpAdbClient/AdbServer.Async.cs | 19 ++- AdvancedSharpAdbClient/AdbServer.cs | 124 +++++++++++++++--- AdvancedSharpAdbClient/AdbSocket.Async.cs | 16 +-- AdvancedSharpAdbClient/AdbSocket.cs | 48 +++---- .../DeviceCommands/PackageManager.Async.cs | 22 ++-- .../DeviceCommands/PackageManager.cs | 38 +++--- AdvancedSharpAdbClient/DeviceMonitor.cs | 4 +- .../Attributes/SerializableAttribute.cs | 2 +- AdvancedSharpAdbClient/Models/Element.cs | 4 +- AdvancedSharpAdbClient/Models/Framebuffer.cs | 4 +- AdvancedSharpAdbClient/Models/ShellStream.cs | 4 + AdvancedSharpAdbClient/SyncService.cs | 2 +- AdvancedSharpAdbClient/TcpSocket.Async.cs | 26 ++-- AdvancedSharpAdbClient/TcpSocket.cs | 52 ++++---- 18 files changed, 254 insertions(+), 160 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/AdbServerTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbServerTests.Async.cs index 61555ff0..c4c60d0b 100644 --- a/AdvancedSharpAdbClient.Tests/AdbServerTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbServerTests.Async.cs @@ -16,10 +16,10 @@ public partial class AdbServerTests [Fact] public async void GetStatusAsyncNotRunningTest() { - IAdbClient adbClientMock = Substitute.For(); - adbClientMock.GetAdbVersionAsync(Arg.Any()).Throws(new AggregateException(new SocketException(AdbServer.ConnectionRefused))); + IAdbSocket adbSocketMock = Substitute.For(); + adbSocketMock.SendAdbRequestAsync("host:version", Arg.Any()).Throws(new AggregateException(new SocketException(AdbServer.ConnectionRefused))); - AdbServer adbServer = new(adbClientMock, adbCommandLineClientFactory); + AdbServer adbServer = new((endPoint) => adbSocketMock, adbCommandLineClientFactory); AdbServerStatus status = await adbServer.GetStatusAsync(); Assert.False(status.IsRunning); @@ -54,8 +54,7 @@ public async void GetStatusAsyncOtherSocketExceptionTest() { adbSocketFactory = (endPoint) => throw new SocketException(); - adbClient = new AdbClient(AdbClient.DefaultEndPoint, adbSocketFactory); - adbServer = new AdbServer(adbClient, adbCommandLineClientFactory); + adbServer = new AdbServer(adbSocketFactory, adbCommandLineClientFactory); _ = await Assert.ThrowsAsync(async () => await adbServer.GetStatusAsync()); } @@ -68,8 +67,7 @@ public async void GetStatusAsyncOtherExceptionTest() { adbSocketFactory = (endPoint) => throw new Exception(); - adbClient = new AdbClient(AdbClient.DefaultEndPoint, adbSocketFactory); - adbServer = new AdbServer(adbClient, adbCommandLineClientFactory); + adbServer = new AdbServer(adbSocketFactory, adbCommandLineClientFactory); _ = await Assert.ThrowsAsync(async () => await adbServer.GetStatusAsync()); } @@ -112,8 +110,7 @@ public async void StartServerAsyncNotRunningNoExecutableTest() { adbSocketFactory = (endPoint) => throw new SocketException(AdbServer.ConnectionRefused); - adbClient = new AdbClient(AdbClient.DefaultEndPoint, adbSocketFactory); - adbServer = new AdbServer(adbClient, adbCommandLineClientFactory); + adbServer = new AdbServer(adbSocketFactory, adbCommandLineClientFactory); _ = await Assert.ThrowsAsync(async () => await adbServer.StartServerAsync(null, false)); } @@ -147,8 +144,7 @@ public async void StartServerAsyncNotRunningTest() { adbSocketFactory = (endPoint) => throw new SocketException(AdbServer.ConnectionRefused); - adbClient = new AdbClient(AdbClient.DefaultEndPoint, adbSocketFactory); - adbServer = new AdbServer(adbClient, adbCommandLineClientFactory); + adbServer = new AdbServer(adbSocketFactory, adbCommandLineClientFactory); commandLineClient.Version = new Version(1, 0, 32); diff --git a/AdvancedSharpAdbClient.Tests/AdbServerTests.cs b/AdvancedSharpAdbClient.Tests/AdbServerTests.cs index 0b768157..ddb218de 100644 --- a/AdvancedSharpAdbClient.Tests/AdbServerTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbServerTests.cs @@ -1,6 +1,5 @@ using AdvancedSharpAdbClient.Exceptions; using NSubstitute; -using NSubstitute.ExceptionExtensions; using System; using System.Net; using System.Net.Sockets; @@ -18,7 +17,6 @@ public partial class AdbServerTests private readonly DummyAdbSocket socket; private readonly DummyAdbCommandLineClient commandLineClient; private Func adbSocketFactory; - private AdbClient adbClient; private AdbServer adbServer; public AdbServerTests() @@ -29,8 +27,7 @@ public AdbServerTests() commandLineClient = new DummyAdbCommandLineClient(); adbCommandLineClientFactory = (version) => commandLineClient; - adbClient = new AdbClient(AdbClient.DefaultEndPoint, adbSocketFactory); - adbServer = new AdbServer(adbClient, adbCommandLineClientFactory); + adbServer = new AdbServer(adbSocketFactory, adbCommandLineClientFactory); } /// @@ -39,10 +36,10 @@ public AdbServerTests() [Fact] public void GetStatusNotRunningTest() { - IAdbClient adbClientMock = Substitute.For(); - adbClientMock.GetAdbVersion().Throws(new SocketException(AdbServer.ConnectionRefused)); + IAdbSocket adbSocketMock = Substitute.For(); + adbSocketMock.When(x => x.SendAdbRequest("host:version")).Do(x => throw new SocketException(AdbServer.ConnectionRefused)); - AdbServer adbServer = new(adbClientMock, adbCommandLineClientFactory); + AdbServer adbServer = new((endPoint) => adbSocketMock, adbCommandLineClientFactory); AdbServerStatus status = adbServer.GetStatus(); Assert.False(status.IsRunning); @@ -77,8 +74,7 @@ public void GetStatusOtherSocketExceptionTest() { adbSocketFactory = (endPoint) => throw new SocketException(); - adbClient = new AdbClient(AdbClient.DefaultEndPoint, adbSocketFactory); - adbServer = new AdbServer(adbClient, adbCommandLineClientFactory); + adbServer = new AdbServer(adbSocketFactory, adbCommandLineClientFactory); _ = Assert.Throws(() => adbServer.GetStatus()); } @@ -91,8 +87,7 @@ public void GetStatusOtherExceptionTest() { adbSocketFactory = (endPoint) => throw new Exception(); - adbClient = new AdbClient(AdbClient.DefaultEndPoint, adbSocketFactory); - adbServer = new AdbServer(adbClient, adbCommandLineClientFactory); + adbServer = new AdbServer(adbSocketFactory, adbCommandLineClientFactory); _ = Assert.Throws(() => adbServer.GetStatus()); } @@ -135,8 +130,7 @@ public void StartServerNotRunningNoExecutableTest() { adbSocketFactory = (endPoint) => throw new SocketException(AdbServer.ConnectionRefused); - adbClient = new AdbClient(AdbClient.DefaultEndPoint, adbSocketFactory); - adbServer = new AdbServer(adbClient, adbCommandLineClientFactory); + adbServer = new AdbServer(adbSocketFactory, adbCommandLineClientFactory); _ = Assert.Throws(() => adbServer.StartServer(null, false)); } @@ -170,8 +164,7 @@ public void StartServerNotRunningTest() { adbSocketFactory = (endPoint) => throw new SocketException(AdbServer.ConnectionRefused); - adbClient = new AdbClient(AdbClient.DefaultEndPoint, adbSocketFactory); - adbServer = new AdbServer(adbClient, adbCommandLineClientFactory); + adbServer = new AdbServer(adbSocketFactory, adbCommandLineClientFactory); commandLineClient.Version = new Version(1, 0, 32); @@ -257,11 +250,11 @@ public void StopServerTest() } /// - /// Tests the method. + /// Tests the method. /// [Fact] public void ConstructorAdbClientNullTest() => - _ = Assert.Throws(() => new AdbServer(null, adbCommandLineClientFactory)); + _ = Assert.Throws(() => new AdbServer((EndPoint)null, adbSocketFactory, adbCommandLineClientFactory)); private static string ServerName => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "adb.exe" : "adb"; } diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index 156a767a..5db85a92 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -95,10 +95,10 @@ public AdbClient(string host, int port, Func adbSocketFact string[] values = host.Split(':'); + this.adbSocketFactory = adbSocketFactory ?? throw new ArgumentNullException(nameof(adbSocketFactory)); EndPoint = values.Length <= 0 ? throw new ArgumentNullException(nameof(host)) : new DnsEndPoint(values[0], values.Length > 1 && int.TryParse(values[1], out int _port) ? _port : port); - this.adbSocketFactory = adbSocketFactory ?? throw new ArgumentNullException(nameof(adbSocketFactory)); } /// diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.cs index 4147d29c..4dfc904a 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.cs @@ -258,7 +258,7 @@ protected virtual int RunProcess(string filename, string command, ICollection StartServerAsync(string adbPath, bo { ExceptionExtensions.ThrowIfNull(adbPath); - await adbClient.KillAdbAsync(cancellationToken); + await StopServerAsync(cancellationToken); await commandLineClient.StartServerAsync(cancellationToken); return StartServerResult.RestartedOutdatedDaemon; } @@ -79,7 +80,14 @@ public virtual Task RestartServerAsync(string adbPath, Cancel StringExtensions.IsNullOrWhiteSpace(adbPath) ? RestartServerAsync(cancellationToken) : StartServerAsync(adbPath, true, cancellationToken); /// - public virtual Task StopServerAsync(CancellationToken cancellationToken = default) => adbClient.KillAdbAsync(cancellationToken); + public virtual async Task StopServerAsync(CancellationToken cancellationToken = default) + { + using IAdbSocket socket = adbSocketFactory(EndPoint); + await socket.SendAdbRequestAsync("host:kill", cancellationToken).ConfigureAwait(false); + + // The host will immediately close the connection after the kill + // command has been sent; no need to read the response. + } /// public virtual async Task GetStatusAsync(CancellationToken cancellationToken = default) @@ -87,7 +95,12 @@ public virtual async Task GetStatusAsync(CancellationToken canc // Try to connect to a running instance of the adb server try { - int versionCode = await adbClient.GetAdbVersionAsync(cancellationToken).ConfigureAwait(false); + 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); + + int versionCode = int.Parse(version, NumberStyles.HexNumber); return new AdbServerStatus(true, new Version(1, 0, versionCode)); } catch (AggregateException ex) diff --git a/AdvancedSharpAdbClient/AdbServer.cs b/AdvancedSharpAdbClient/AdbServer.cs index d4c0268a..8490a6ad 100644 --- a/AdvancedSharpAdbClient/AdbServer.cs +++ b/AdvancedSharpAdbClient/AdbServer.cs @@ -4,6 +4,8 @@ using AdvancedSharpAdbClient.Exceptions; using System; +using System.Globalization; +using System.Net; using System.Net.Sockets; namespace AdvancedSharpAdbClient @@ -17,9 +19,7 @@ namespace AdvancedSharpAdbClient /// giant multiplexing loop whose purpose is to orchestrate the exchange of data /// between clients and devices. /// - /// The current ADB client that manages the connection. - /// The to create . - public partial class AdbServer(IAdbClient adbClient, Func adbCommandLineClientFactory) : IAdbServer + public partial class AdbServer : IAdbServer { /// /// The minimum version of adb.exe that is supported by this library. @@ -44,42 +44,108 @@ public partial class AdbServer(IAdbClient adbClient, Func - /// if is starting adb server; otherwise, . - /// - protected static bool IsStarting = false; - - /// - /// The current ADB client that manages the connection. + /// The to create . /// - protected readonly IAdbClient adbClient = adbClient ?? throw new ArgumentNullException(nameof(adbClient)); + 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 = adbCommandLineClientFactory ?? throw new ArgumentNullException(nameof(adbCommandLineClientFactory)); + protected readonly Func adbCommandLineClientFactory; + + /// + /// Initializes a new instance of the class. + /// + public AdbServer() : this(new IPEndPoint(IPAddress.Loopback, AdbClient.AdbServerPort), Factories.AdbSocketFactory, Factories.AdbCommandLineClientFactory) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// A connection to an adb server. + public AdbServer(IAdbClient adbClient) : this(adbClient.EndPoint, Factories.AdbSocketFactory, Factories.AdbCommandLineClientFactory) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The at which the adb server is listening. + public AdbServer(EndPoint endPoint) : this(endPoint, Factories.AdbSocketFactory, Factories.AdbCommandLineClientFactory) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The host address at which the adb server is listening. + /// The port at which the adb server is listening. + public AdbServer(string host, int port) : this(host, port, Factories.AdbSocketFactory, Factories.AdbCommandLineClientFactory) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// A connection to an adb server. + /// The to create . + /// The to create . + public AdbServer(IAdbClient adbClient, Func adbSocketFactory, Func adbCommandLineClientFactory) : this(adbClient.EndPoint, adbSocketFactory, adbCommandLineClientFactory) + { + } /// /// Initializes a new instance of the class. /// - public AdbServer() : this(new AdbClient(), Factories.AdbCommandLineClientFactory) + /// The host address at which the adb server is listening. + /// The port at which the adb server is listening. + /// The to create . + /// The to create . + public AdbServer(string host, int port, Func adbSocketFactory, Func adbCommandLineClientFactory) { + if (string.IsNullOrEmpty(host)) + { + throw new ArgumentNullException(nameof(host)); + } + + string[] values = host.Split(':'); + + this.adbSocketFactory = adbSocketFactory ?? throw new ArgumentNullException(nameof(adbSocketFactory)); + this.adbCommandLineClientFactory = adbCommandLineClientFactory ?? throw new ArgumentNullException(nameof(adbCommandLineClientFactory)); + EndPoint = values.Length <= 0 + ? throw new ArgumentNullException(nameof(host)) + : new DnsEndPoint(values[0], values.Length > 1 && int.TryParse(values[1], out int _port) ? _port : port); } /// /// Initializes a new instance of the class. /// - /// The current ADB client that manages the connection. - public AdbServer(IAdbClient adbClient) : this(adbClient, Factories.AdbCommandLineClientFactory) + /// The at which the adb server is listening. + /// The to create . + /// The to create . + public AdbServer(EndPoint endPoint, Func adbSocketFactory, Func adbCommandLineClientFactory) { + ExceptionExtensions.ThrowIfNull(endPoint); + + if (endPoint is not (IPEndPoint or DnsEndPoint)) + { + throw new NotSupportedException("Only TCP endpoints are supported"); + } + + EndPoint = endPoint; + this.adbSocketFactory = adbSocketFactory ?? throw new ArgumentNullException(nameof(adbSocketFactory)); + this.adbCommandLineClientFactory = adbCommandLineClientFactory ?? throw new ArgumentNullException(nameof(adbCommandLineClientFactory)); } /// /// Initializes a new instance of the class. /// + /// The to create . /// The to create . - public AdbServer(Func adbCommandLineClientFactory) : this(new AdbClient(), adbCommandLineClientFactory) + public AdbServer(Func adbSocketFactory, Func adbCommandLineClientFactory) : this(new IPEndPoint(IPAddress.Loopback, AdbClient.AdbServerPort), adbSocketFactory, adbCommandLineClientFactory) { } @@ -99,6 +165,16 @@ public AdbServer(Func adbCommandLineClientFactory /// protected static Func CheckFileExists { get; set; } = Factories.CheckFileExists; + /// + /// if is starting adb server; otherwise, . + /// + protected static bool IsStarting { get; set; } = false; + + /// + /// Gets the at which the adb server is listening. + /// + public EndPoint EndPoint { get; protected set; } + /// public virtual StartServerResult StartServer(string adbPath, bool restartServerIfNewer = false) { @@ -134,7 +210,7 @@ public virtual StartServerResult StartServer(string adbPath, bool restartServerI { ExceptionExtensions.ThrowIfNull(adbPath); - adbClient.KillAdb(); + StopServer(); commandLineClient.StartServer(); return StartServerResult.RestartedOutdatedDaemon; } @@ -165,7 +241,14 @@ public virtual StartServerResult RestartServer(string adbPath) => StringExtensions.IsNullOrWhiteSpace(adbPath) ? RestartServer() : StartServer(adbPath, true); /// - public virtual void StopServer() => adbClient.KillAdb(); + public virtual void StopServer() + { + using IAdbSocket socket = adbSocketFactory(EndPoint); + socket.SendAdbRequest("host:kill"); + + // The host will immediately close the connection after the kill + // command has been sent; no need to read the response. + } /// public virtual AdbServerStatus GetStatus() @@ -173,7 +256,12 @@ public virtual AdbServerStatus GetStatus() // Try to connect to a running instance of the adb server try { - int versionCode = adbClient.GetAdbVersion(); + using IAdbSocket socket = adbSocketFactory(EndPoint); + socket.SendAdbRequest("host:version"); + AdbResponse response = socket.ReadAdbResponse(); + string version = socket.ReadString(); + + int versionCode = int.Parse(version, NumberStyles.HexNumber); return new AdbServerStatus(true, new Version(1, 0, versionCode)); } catch (SocketException ex) diff --git a/AdvancedSharpAdbClient/AdbSocket.Async.cs b/AdvancedSharpAdbClient/AdbSocket.Async.cs index 99c41a73..c81aed21 100644 --- a/AdvancedSharpAdbClient/AdbSocket.Async.cs +++ b/AdvancedSharpAdbClient/AdbSocket.Async.cs @@ -16,7 +16,7 @@ public partial class AdbSocket { #if NET6_0_OR_GREATER /// - public virtual ValueTask ReconnectAsync(CancellationToken cancellationToken = default) => socket.ReconnectAsync(cancellationToken); + public virtual ValueTask ReconnectAsync(CancellationToken cancellationToken = default) => Socket.ReconnectAsync(cancellationToken); #endif /// @@ -24,7 +24,7 @@ public virtual async Task SendAsync(byte[] data, int length, CancellationToken c { try { - int count = await socket.SendAsync(data, length != -1 ? length : data.Length, SocketFlags.None, cancellationToken).ConfigureAwait(false); + int count = await Socket.SendAsync(data, length != -1 ? length : data.Length, SocketFlags.None, cancellationToken).ConfigureAwait(false); if (count < 0) { @@ -43,7 +43,7 @@ public virtual async Task SendAsync(byte[] data, int offset, int length, Cancell { try { - int count = await socket.SendAsync(data, offset, length != -1 ? length : data.Length - offset, SocketFlags.None, cancellationToken).ConfigureAwait(false); + int count = await Socket.SendAsync(data, offset, length != -1 ? length : data.Length - offset, SocketFlags.None, cancellationToken).ConfigureAwait(false); if (count < 0) { @@ -128,7 +128,7 @@ public virtual async Task ReadAsync(byte[] data, int offset, int length, Ca int left = length - totalRead; int bufferLength = left < ReceiveBufferSize ? left : ReceiveBufferSize; - count = await socket.ReceiveAsync(data, totalRead, bufferLength, SocketFlags.None, cancellationToken).ConfigureAwait(false); + count = await Socket.ReceiveAsync(data, totalRead, bufferLength, SocketFlags.None, cancellationToken).ConfigureAwait(false); if (count < 0) { @@ -210,7 +210,7 @@ public virtual async Task ReadAdbResponseAsync(CancellationToken ca if (!response.IOSuccess || !response.Okay) { - socket.Dispose(); + Socket.Dispose(); throw new AdbException($"An error occurred while reading a response from ADB: {response.Message}", response); } @@ -250,7 +250,7 @@ public virtual async ValueTask SendAsync(ReadOnlyMemory data, Cancellation { try { - int count = await socket.SendAsync(data, SocketFlags.None, cancellationToken).ConfigureAwait(false); + int count = await Socket.SendAsync(data, SocketFlags.None, cancellationToken).ConfigureAwait(false); if (count < 0) { throw new AdbException("channel EOF"); @@ -281,7 +281,7 @@ public virtual async ValueTask ReadAsync(Memory data, CancellationTok int left = length - totalRead; int bufferLength = left < ReceiveBufferSize ? left : ReceiveBufferSize; - count = await socket.ReceiveAsync(data.Slice(totalRead, bufferLength), SocketFlags.None, cancellationToken).ConfigureAwait(false); + count = await Socket.ReceiveAsync(data.Slice(totalRead, bufferLength), SocketFlags.None, cancellationToken).ConfigureAwait(false); if (count < 0) { @@ -311,7 +311,7 @@ public virtual async Task SendAsync(byte[] data, CancellationToken cancellationT { try { - int count = await socket.SendAsync(data, SocketFlags.None, cancellationToken).ConfigureAwait(false); + int count = await Socket.SendAsync(data, SocketFlags.None, cancellationToken).ConfigureAwait(false); if (count < 0) { diff --git a/AdvancedSharpAdbClient/AdbSocket.cs b/AdvancedSharpAdbClient/AdbSocket.cs index 5ccd19ec..5dbeb836 100644 --- a/AdvancedSharpAdbClient/AdbSocket.cs +++ b/AdvancedSharpAdbClient/AdbSocket.cs @@ -25,11 +25,6 @@ namespace AdvancedSharpAdbClient /// public partial class AdbSocket : IAdbSocket { - /// - /// The underlying TCP socket that manages the connection with the ADB server. - /// - protected readonly ITcpSocket socket; - /// /// The logger to use when logging messages. /// @@ -42,9 +37,9 @@ public partial class AdbSocket : IAdbSocket /// The logger to use when logging. public AdbSocket(EndPoint endPoint, ILogger logger = null) { - socket = new TcpSocket(); - socket.Connect(endPoint); - socket.ReceiveBufferSize = ReceiveBufferSize; + Socket = new TcpSocket(); + Socket.Connect(endPoint); + Socket.ReceiveBufferSize = ReceiveBufferSize; this.logger = logger ?? LoggerProvider.CreateLogger(); } @@ -67,9 +62,9 @@ public AdbSocket(string host, int port, ILogger logger = null) ? throw new ArgumentNullException(nameof(host)) : new DnsEndPoint(values[0], values.Length > 1 && int.TryParse(values[1], out int _port) ? _port : port); - socket = new TcpSocket(); - socket.Connect(endPoint); - socket.ReceiveBufferSize = ReceiveBufferSize; + Socket = new TcpSocket(); + Socket.Connect(endPoint); + Socket.ReceiveBufferSize = ReceiveBufferSize; this.logger = logger ?? LoggerProvider.CreateLogger(); } @@ -80,7 +75,7 @@ public AdbSocket(string host, int port, ILogger logger = null) /// The logger to use when logging. public AdbSocket(ITcpSocket socket, ILogger logger = null) { - this.socket = socket; + this.Socket = socket; this.logger = logger ?? LoggerProvider.CreateLogger(); } @@ -94,6 +89,11 @@ public AdbSocket(ITcpSocket socket, ILogger logger = null) /// public static int WriteBufferSize { get; set; } = 1024; + /// + /// The underlying TCP socket that manages the connection with the ADB server. + /// + public ITcpSocket Socket { get; init; } + /// /// Determines whether the specified reply is okay. /// @@ -106,17 +106,17 @@ public AdbSocket(ITcpSocket socket, ILogger logger = null) #endif /// - public bool Connected => socket.Connected; + public bool Connected => Socket.Connected; /// - public virtual void Reconnect() => socket.Reconnect(); + public virtual void Reconnect() => Socket.Reconnect(); /// public virtual void Send(byte[] data, int length) { try { - int count = socket.Send(data, length != -1 ? length : data.Length, SocketFlags.None); + int count = Socket.Send(data, length != -1 ? length : data.Length, SocketFlags.None); if (count < 0) { @@ -135,7 +135,7 @@ public virtual void Send(byte[] data, int offset, int length) { try { - int count = socket.Send(data, offset, length != -1 ? length : data.Length, SocketFlags.None); + int count = Socket.Send(data, offset, length != -1 ? length : data.Length, SocketFlags.None); if (count < 0) { @@ -217,7 +217,7 @@ public virtual int Read(byte[] data, int offset, int length) int left = length - totalRead; int bufferLength = left < ReceiveBufferSize ? left : ReceiveBufferSize; - count = socket.Receive(data, totalRead, bufferLength, SocketFlags.None); + count = Socket.Receive(data, totalRead, bufferLength, SocketFlags.None); if (count < 0) { @@ -305,7 +305,7 @@ public virtual AdbResponse ReadAdbResponse() if (!response.IOSuccess || !response.Okay) { - socket.Dispose(); + Socket.Dispose(); throw new AdbException($"An error occurred while reading a response from ADB: {response.Message}", response); } @@ -318,7 +318,7 @@ public virtual void Send(ReadOnlySpan data) { try { - int count = socket.Send(data, SocketFlags.None); + int count = Socket.Send(data, SocketFlags.None); if (count < 0) { throw new AdbException("channel EOF"); @@ -345,7 +345,7 @@ public virtual int Read(Span data) int left = length - totalRead; int bufferLength = left < ReceiveBufferSize ? left : ReceiveBufferSize; - count = socket.Receive(data.Slice(totalRead, bufferLength), SocketFlags.None); + count = Socket.Receive(data.Slice(totalRead, bufferLength), SocketFlags.None); if (count < 0) { @@ -375,7 +375,7 @@ public virtual void Send(byte[] data) { try { - int count = socket.Send(data, SocketFlags.None); + int count = Socket.Send(data, SocketFlags.None); if (count < 0) { throw new AdbException("channel EOF"); @@ -395,7 +395,7 @@ public virtual void Send(byte[] data) /// public virtual Stream GetShellStream() { - Stream stream = socket.GetStream(); + Stream stream = Socket.GetStream(); return new ShellStream(stream, closeStream: true); } @@ -503,7 +503,7 @@ protected virtual void Dispose(bool disposing) { if (disposing) { - socket.Dispose(); + Socket.Dispose(); } } @@ -515,6 +515,6 @@ public void Dispose() } /// - public virtual void Close() => socket.Dispose(); + public virtual void Close() => Socket.Dispose(); } } diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs index dfa02a54..26009afa 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs @@ -26,8 +26,8 @@ public virtual Task RefreshPackagesAsync(CancellationToken cancellationToken = d PackageManagerReceiver pmr = new(this); return ThirdPartyOnly - ? client.ExecuteShellCommandAsync(Device, ListThirdPartyOnly, pmr, cancellationToken) - : client.ExecuteShellCommandAsync(Device, ListFull, pmr, cancellationToken); + ? AdbClient.ExecuteShellCommandAsync(Device, ListThirdPartyOnly, pmr, cancellationToken) + : AdbClient.ExecuteShellCommandAsync(Device, ListFull, pmr, cancellationToken); } /// @@ -72,7 +72,7 @@ public virtual async Task InstallRemotePackageAsync(string remoteFilePath, bool string reinstallSwitch = reinstall ? "-r " : string.Empty; string cmd = $"pm install {reinstallSwitch}\"{remoteFilePath}\""; - await client.ExecuteShellCommandAsync(Device, cmd, receiver, cancellationToken).ConfigureAwait(false); + await AdbClient.ExecuteShellCommandAsync(Device, cmd, receiver, cancellationToken).ConfigureAwait(false); if (!string.IsNullOrEmpty(receiver.ErrorMessage)) { @@ -213,7 +213,7 @@ await Extensions.WhenAll(splitRemoteFilePaths.Select(async (splitRemoteFilePath) InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.Installing)); InstallOutputReceiver receiver = new(); - await client.ExecuteShellCommandAsync(Device, $"pm install-commit {session}", receiver, cancellationToken).ConfigureAwait(false); + await AdbClient.ExecuteShellCommandAsync(Device, $"pm install-commit {session}", receiver, cancellationToken).ConfigureAwait(false); if (!string.IsNullOrEmpty(receiver.ErrorMessage)) { @@ -256,7 +256,7 @@ await Extensions.WhenAll(splitRemoteFilePaths.Select(async (splitRemoteFilePath) InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.Installing)); InstallOutputReceiver receiver = new(); - await client.ExecuteShellCommandAsync(Device, $"pm install-commit {session}", receiver, cancellationToken).ConfigureAwait(false); + await AdbClient.ExecuteShellCommandAsync(Device, $"pm install-commit {session}", receiver, cancellationToken).ConfigureAwait(false); if (!string.IsNullOrEmpty(receiver.ErrorMessage)) { @@ -275,7 +275,7 @@ public virtual async Task UninstallPackageAsync(string packageName, Cancellation ValidateDevice(); InstallOutputReceiver receiver = new(); - await client.ExecuteShellCommandAsync(Device, $"pm uninstall {packageName}", receiver, cancellationToken).ConfigureAwait(false); + await AdbClient.ExecuteShellCommandAsync(Device, $"pm uninstall {packageName}", receiver, cancellationToken).ConfigureAwait(false); if (!string.IsNullOrEmpty(receiver.ErrorMessage)) { throw new PackageInstallationException(receiver.ErrorMessage); @@ -293,7 +293,7 @@ public virtual async Task GetVersionInfoAsync(string packageName, C ValidateDevice(); VersionInfoReceiver receiver = new(); - await client.ExecuteShellCommandAsync(Device, $"dumpsys package {packageName}", receiver, cancellationToken).ConfigureAwait(false); + await AdbClient.ExecuteShellCommandAsync(Device, $"dumpsys package {packageName}", receiver, cancellationToken).ConfigureAwait(false); return receiver.VersionInfo; } @@ -322,7 +322,7 @@ protected virtual async Task SyncPackageToDeviceAsync(string localFilePa logger.LogDebug("Uploading {0} onto device '{1}'", packageFileName, Device.Serial); - using (ISyncService sync = syncServiceFactory(client, Device)) + using (ISyncService sync = syncServiceFactory(AdbClient, Device)) { if (progress != null) { @@ -365,7 +365,7 @@ protected virtual async Task RemoveRemotePackageAsync(string remoteFilePath, Can // now we delete the app we synced try { - await client.ExecuteShellCommandAsync(Device, $"rm \"{remoteFilePath}\"", cancellationToken).ConfigureAwait(false); + await AdbClient.ExecuteShellCommandAsync(Device, $"rm \"{remoteFilePath}\"", cancellationToken).ConfigureAwait(false); } catch (IOException e) { @@ -390,7 +390,7 @@ protected virtual async Task CreateInstallSessionAsync(bool reinstall, s string addon = StringExtensions.IsNullOrWhiteSpace(packageName) ? string.Empty : $" -p {packageName}"; string cmd = $"pm install-create{reinstallSwitch}{addon}"; - await client.ExecuteShellCommandAsync(Device, cmd, receiver, cancellationToken).ConfigureAwait(false); + await AdbClient.ExecuteShellCommandAsync(Device, cmd, receiver, cancellationToken).ConfigureAwait(false); if (string.IsNullOrEmpty(receiver.SuccessMessage)) { @@ -417,7 +417,7 @@ protected virtual async Task WriteInstallSessionAsync(string session, string apk ValidateDevice(); InstallOutputReceiver receiver = new(); - await client.ExecuteShellCommandAsync(Device, $"pm install-write {session} {apkName}.apk \"{path}\"", receiver, cancellationToken).ConfigureAwait(false); + await AdbClient.ExecuteShellCommandAsync(Device, $"pm install-write {session} {apkName}.apk \"{path}\"", receiver, cancellationToken).ConfigureAwait(false); if (!string.IsNullOrEmpty(receiver.ErrorMessage)) { diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs index 26ad4a5c..af624c17 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs @@ -37,11 +37,6 @@ public partial class PackageManager /// protected readonly ILogger logger; - /// - /// The to use when communicating with the device. - /// - protected readonly IAdbClient client; - /// /// A function which returns a new instance of a class /// that implements the interface, @@ -72,7 +67,7 @@ public PackageManager(IAdbClient client, DeviceData device, bool thirdPartyOnly Device = device ?? throw new ArgumentNullException(nameof(device)); Packages = []; ThirdPartyOnly = thirdPartyOnly; - this.client = client ?? throw new ArgumentNullException(nameof(client)); + this.AdbClient = client ?? throw new ArgumentNullException(nameof(client)); this.syncServiceFactory = syncServiceFactory ?? Factories.SyncServiceFactory; @@ -88,7 +83,7 @@ public PackageManager(IAdbClient client, DeviceData device, bool thirdPartyOnly /// Gets a value indicating whether this package manager only lists third party applications, /// or also includes built-in applications. /// - public bool ThirdPartyOnly { get; private init; } + public bool ThirdPartyOnly { get; init; } /// /// Gets the list of packages currently installed on the device. They key is the name of the package; @@ -99,7 +94,12 @@ public PackageManager(IAdbClient client, DeviceData device, bool thirdPartyOnly /// /// Gets the device. /// - public DeviceData Device { get; private init; } + public DeviceData Device { get; init; } + + /// + /// The to use when communicating with the device. + /// + protected IAdbClient AdbClient { get; init; } /// /// Refreshes the packages. @@ -112,11 +112,11 @@ public virtual void RefreshPackages() if (ThirdPartyOnly) { - client.ExecuteShellCommand(Device, ListThirdPartyOnly, pmr); + AdbClient.ExecuteShellCommand(Device, ListThirdPartyOnly, pmr); } else { - client.ExecuteShellCommand(Device, ListFull, pmr); + AdbClient.ExecuteShellCommand(Device, ListFull, pmr); } } @@ -158,7 +158,7 @@ public virtual void InstallRemotePackage(string remoteFilePath, bool reinstall) string reinstallSwitch = reinstall ? "-r " : string.Empty; string cmd = $"pm install {reinstallSwitch}\"{remoteFilePath}\""; - client.ExecuteShellCommand(Device, cmd, receiver); + AdbClient.ExecuteShellCommand(Device, cmd, receiver); if (!string.IsNullOrEmpty(receiver.ErrorMessage)) { @@ -301,7 +301,7 @@ public virtual void InstallMultipleRemotePackage(string baseRemoteFilePath, ICol InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.Installing)); InstallOutputReceiver receiver = new(); - client.ExecuteShellCommand(Device, $"pm install-commit {session}", receiver); + AdbClient.ExecuteShellCommand(Device, $"pm install-commit {session}", receiver); if (!string.IsNullOrEmpty(receiver.ErrorMessage)) { @@ -342,7 +342,7 @@ public virtual void InstallMultipleRemotePackage(ICollection splitRemote InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.Installing)); InstallOutputReceiver receiver = new(); - client.ExecuteShellCommand(Device, $"pm install-commit {session}", receiver); + AdbClient.ExecuteShellCommand(Device, $"pm install-commit {session}", receiver); if (!string.IsNullOrEmpty(receiver.ErrorMessage)) { @@ -359,7 +359,7 @@ public virtual void UninstallPackage(string packageName) ValidateDevice(); InstallOutputReceiver receiver = new(); - client.ExecuteShellCommand(Device, $"pm uninstall {packageName}", receiver); + AdbClient.ExecuteShellCommand(Device, $"pm uninstall {packageName}", receiver); if (!string.IsNullOrEmpty(receiver.ErrorMessage)) { throw new PackageInstallationException(receiver.ErrorMessage); @@ -376,7 +376,7 @@ public virtual VersionInfo GetVersionInfo(string packageName) ValidateDevice(); VersionInfoReceiver receiver = new(); - client.ExecuteShellCommand(Device, $"dumpsys package {packageName}", receiver); + AdbClient.ExecuteShellCommand(Device, $"dumpsys package {packageName}", receiver); return receiver.VersionInfo; } @@ -415,7 +415,7 @@ protected virtual string SyncPackageToDevice(string localFilePath, Action logger = null) /// Gets the that represents the connection to the /// Android Debug Bridge. /// - public IAdbSocket Socket { get; private set; } + public IAdbSocket Socket { get; protected set; } /// /// Gets a value indicating whether this instance is running. /// /// if this instance is running; otherwise, . - public bool IsRunning { get; private set; } + public bool IsRunning { get; protected set; } /// public virtual void Start() diff --git a/AdvancedSharpAdbClient/Extensions/Attributes/SerializableAttribute.cs b/AdvancedSharpAdbClient/Extensions/Attributes/SerializableAttribute.cs index 2d81e84e..38818fdb 100644 --- a/AdvancedSharpAdbClient/Extensions/Attributes/SerializableAttribute.cs +++ b/AdvancedSharpAdbClient/Extensions/Attributes/SerializableAttribute.cs @@ -1,4 +1,4 @@ -#if NETSTANDARD && !NETSTANDARD2_0_OR_GREATER || NETCORE && !UAP10_0_15138_0 +#if (NETSTANDARD && !NETSTANDARD2_0_OR_GREATER) || (NETCORE && !UAP10_0_15138_0) // 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/Models/Element.cs b/AdvancedSharpAdbClient/Models/Element.cs index a795c90a..534c2204 100644 --- a/AdvancedSharpAdbClient/Models/Element.cs +++ b/AdvancedSharpAdbClient/Models/Element.cs @@ -124,12 +124,12 @@ IEnumerable FindElements() /// /// Gets or sets the current ADB client that manages the connection. /// - protected IAdbClient Client { get; set; } + public IAdbClient Client { get; init; } /// /// Gets the current device containing the element. /// - protected DeviceData Device { get; init; } + public DeviceData Device { get; init; } /// /// Gets the coordinates and size of the element. diff --git a/AdvancedSharpAdbClient/Models/Framebuffer.cs b/AdvancedSharpAdbClient/Models/Framebuffer.cs index 57e8fbd4..c660a176 100644 --- a/AdvancedSharpAdbClient/Models/Framebuffer.cs +++ b/AdvancedSharpAdbClient/Models/Framebuffer.cs @@ -23,8 +23,8 @@ public class Framebuffer(DeviceData device, EndPoint endPoint) : IDisposable /// Initializes a new instance of the class. /// /// The device for which to fetch the frame buffer. - /// A which manages the connection with adb. - public Framebuffer(DeviceData device, AdbClient client) : this(device, client?.EndPoint) { } + /// A which manages the connection with adb. + public Framebuffer(DeviceData device, IAdbClient client) : this(device, client?.EndPoint) { } /// /// Initializes a new instance of the class. diff --git a/AdvancedSharpAdbClient/Models/ShellStream.cs b/AdvancedSharpAdbClient/Models/ShellStream.cs index f929f006..4ec3dc45 100644 --- a/AdvancedSharpAdbClient/Models/ShellStream.cs +++ b/AdvancedSharpAdbClient/Models/ShellStream.cs @@ -349,7 +349,9 @@ 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 @@ -449,7 +451,9 @@ 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/SyncService.cs b/AdvancedSharpAdbClient/SyncService.cs index 9b59eb37..96d37ad2 100644 --- a/AdvancedSharpAdbClient/SyncService.cs +++ b/AdvancedSharpAdbClient/SyncService.cs @@ -84,7 +84,7 @@ public SyncService(IAdbSocket socket, DeviceData device) /// /// Gets the that enables the connection with the adb server. /// - public IAdbSocket Socket { get; private set; } + public IAdbSocket Socket { get; protected set; } /// public bool IsOpen => Socket != null && Socket.Connected; diff --git a/AdvancedSharpAdbClient/TcpSocket.Async.cs b/AdvancedSharpAdbClient/TcpSocket.Async.cs index a2098244..ee0339a0 100644 --- a/AdvancedSharpAdbClient/TcpSocket.Async.cs +++ b/AdvancedSharpAdbClient/TcpSocket.Async.cs @@ -21,23 +21,23 @@ public virtual async ValueTask ConnectAsync(EndPoint endPoint, CancellationToken throw new NotSupportedException("Only TCP endpoints are supported"); } - await socket.ConnectAsync(endPoint, cancellationToken).ConfigureAwait(false); - socket.Blocking = true; + await Socket.ConnectAsync(endPoint, cancellationToken).ConfigureAwait(false); + Socket.Blocking = true; this.endPoint = endPoint; } /// public virtual ValueTask ReconnectAsync(CancellationToken cancellationToken = default) { - if (socket.Connected) + if (Socket.Connected) { // Already connected - nothing to do. return ValueTask.CompletedTask; } else { - socket.Dispose(); - socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + Socket.Dispose(); + Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); return ConnectAsync(endPoint, cancellationToken); } } @@ -45,36 +45,36 @@ public virtual ValueTask ReconnectAsync(CancellationToken cancellationToken = de /// public virtual Task SendAsync(byte[] buffer, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) => - socket.SendAsync(buffer, size, socketFlags, cancellationToken); + Socket.SendAsync(buffer, size, socketFlags, cancellationToken); /// public virtual Task SendAsync(byte[] buffer, int offset, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) => - socket.SendAsync(buffer, offset, size, socketFlags, cancellationToken); + Socket.SendAsync(buffer, offset, size, socketFlags, cancellationToken); /// public virtual Task ReceiveAsync(byte[] buffer, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) => - socket.ReceiveAsync(buffer, size, socketFlags, cancellationToken); + Socket.ReceiveAsync(buffer, size, socketFlags, cancellationToken); /// public virtual Task ReceiveAsync(byte[] buffer, int offset, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) => - socket.ReceiveAsync(buffer, offset, size, socketFlags, cancellationToken); + Socket.ReceiveAsync(buffer, offset, size, socketFlags, cancellationToken); #if HAS_BUFFERS /// public ValueTask SendAsync(ReadOnlyMemory buffer, SocketFlags socketFlags, CancellationToken cancellationToken = default) => - socket.SendAsync(buffer, socketFlags, cancellationToken); + Socket.SendAsync(buffer, socketFlags, cancellationToken); /// public ValueTask ReceiveAsync(Memory buffer, SocketFlags socketFlags, CancellationToken cancellationToken = default) => - socket.ReceiveAsync(buffer, socketFlags, cancellationToken); + Socket.ReceiveAsync(buffer, socketFlags, cancellationToken); #else /// public virtual Task SendAsync(byte[] buffer, SocketFlags socketFlags, CancellationToken cancellationToken = default) => - socket.SendAsync(buffer, socketFlags, cancellationToken); + Socket.SendAsync(buffer, socketFlags, cancellationToken); /// public virtual Task ReceiveAsync(byte[] buffer, SocketFlags socketFlags, CancellationToken cancellationToken = default) => - socket.ReceiveAsync(buffer, socketFlags, cancellationToken); + Socket.ReceiveAsync(buffer, socketFlags, cancellationToken); #endif } } diff --git a/AdvancedSharpAdbClient/TcpSocket.cs b/AdvancedSharpAdbClient/TcpSocket.cs index f409948a..7d9c2a3e 100644 --- a/AdvancedSharpAdbClient/TcpSocket.cs +++ b/AdvancedSharpAdbClient/TcpSocket.cs @@ -10,15 +10,10 @@ namespace AdvancedSharpAdbClient { /// - /// Implements the interface using the standard class. + /// Implements the interface using the standard class. /// public partial class TcpSocket : ITcpSocket { - /// - /// The underlying socket that manages the connection. - /// - protected Socket socket; - /// /// The at which the socket is listening. /// @@ -27,16 +22,21 @@ public partial class TcpSocket : ITcpSocket /// /// Initializes a new instance of the class. /// - public TcpSocket() => socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + public TcpSocket() => Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + + /// + /// The underlying socket that manages the connection. + /// + public Socket Socket { get; protected set; } /// - public bool Connected => socket.Connected; + public bool Connected => Socket.Connected; /// public int ReceiveBufferSize { - get => socket.ReceiveBufferSize; - set => socket.ReceiveBufferSize = value; + get => Socket.ReceiveBufferSize; + set => Socket.ReceiveBufferSize = value; } /// @@ -47,23 +47,23 @@ public virtual void Connect(EndPoint endPoint) throw new NotSupportedException("Only TCP endpoints are supported"); } - socket.Connect(endPoint); - socket.Blocking = true; + Socket.Connect(endPoint); + Socket.Blocking = true; this.endPoint = endPoint; } /// public virtual void Reconnect() { - if (socket.Connected) + if (Socket.Connected) { // Already connected - nothing to do. return; } else { - socket.Dispose(); - socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + Socket.Dispose(); + Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); Connect(endPoint); } } @@ -73,7 +73,7 @@ protected virtual void Dispose(bool disposing) { if (disposing) { - socket.Dispose(); + Socket.Dispose(); } } @@ -85,43 +85,43 @@ public void Dispose() } /// - public virtual void Close() => socket.Close(); + public virtual void Close() => Socket.Close(); /// public virtual int Send(byte[] buffer, int size, SocketFlags socketFlags) => - socket.Send(buffer, size, socketFlags); + Socket.Send(buffer, size, socketFlags); /// public virtual int Send(byte[] buffer, int offset, int size, SocketFlags socketFlags) => - socket.Send(buffer, offset, size, socketFlags); + Socket.Send(buffer, offset, size, socketFlags); /// public virtual int Receive(byte[] buffer, int size, SocketFlags socketFlags) => - socket.Receive(buffer, size, socketFlags); + Socket.Receive(buffer, size, socketFlags); /// public virtual int Receive(byte[] buffer, int offset, int size, SocketFlags socketFlags) => - socket.Receive(buffer, offset, size, socketFlags); + Socket.Receive(buffer, offset, size, socketFlags); #if HAS_BUFFERS /// public virtual int Send(ReadOnlySpan buffer, SocketFlags socketFlags) => - socket.Send(buffer, socketFlags); + Socket.Send(buffer, socketFlags); /// public virtual int Receive(Span buffer, SocketFlags socketFlags) => - socket.Receive(buffer, socketFlags); + Socket.Receive(buffer, socketFlags); #else /// public virtual int Send(byte[] buffer, SocketFlags socketFlags) => - socket.Send(buffer, socketFlags); + Socket.Send(buffer, socketFlags); /// public virtual int Receive(byte[] buffer, SocketFlags socketFlags) => - socket.Receive(buffer, socketFlags); + Socket.Receive(buffer, socketFlags); #endif /// - public virtual Stream GetStream() => new NetworkStream(socket); + public virtual Stream GetStream() => new NetworkStream(Socket); } } From e688bff3b47bc05511f5a9ad2992ed7525e0110d Mon Sep 17 00:00:00 2001 From: wherewhere Date: Sun, 22 Oct 2023 02:21:44 +0800 Subject: [PATCH 47/66] Enable nullable --- .../DeviceCommands/PackageManagerTests.cs | 2 +- AdvancedSharpAdbClient/AdbClient.Async.cs | 41 ++-- AdvancedSharpAdbClient/AdbClient.cs | 73 ++++--- .../AdbCommandLineClient.Async.cs | 14 +- .../AdbCommandLineClient.cs | 13 +- AdvancedSharpAdbClient/AdbServer.Async.cs | 4 +- AdvancedSharpAdbClient/AdbServer.cs | 57 +++--- AdvancedSharpAdbClient/AdbSocket.Async.cs | 8 +- AdvancedSharpAdbClient/AdbSocket.cs | 26 +-- .../AdvancedSharpAdbClient.csproj | 3 +- .../DeviceCommands/DeviceExtensions.Async.cs | 12 +- .../DeviceCommands/DeviceExtensions.cs | 12 +- .../DeviceCommands/LinuxPath.cs | 7 +- .../DeviceCommands/Models/AndroidProcess.cs | 2 +- .../DeviceCommands/Models/VersionInfo.cs | 3 +- .../DeviceCommands/PackageManager.Async.cs | 5 +- .../DeviceCommands/PackageManager.cs | 8 +- .../Receivers/InfoOutputReceiver.cs | 16 +- .../Receivers/InstallOutputReceiver.cs | 4 +- .../Receivers/PackageManagerReceiver.cs | 2 +- .../Receivers/VersionInfoReceiver.cs | 4 +- AdvancedSharpAdbClient/DeviceMonitor.Async.cs | 6 +- AdvancedSharpAdbClient/DeviceMonitor.cs | 30 +-- .../Exceptions/AdbException.cs | 26 +-- .../Exceptions/CommandAbortingException.cs | 20 +- .../Exceptions/DeviceNotFoundException.cs | 4 +- .../Exceptions/ElementNotFoundException.cs | 8 +- .../Exceptions/InvalidKeyEventException.cs | 8 +- .../Exceptions/JavaException.cs | 34 ++-- .../PackageInstallationException.cs | 4 +- .../Exceptions/PermissionDeniedException.cs | 20 +- .../ShellCommandUnresponsiveException.cs | 6 +- .../Exceptions/UnknownOptionException.cs | 20 +- .../Extensions/AdbClientExtensions.Async.cs | 55 ++---- .../Extensions/AdbClientExtensions.cs | 55 ++---- .../Attributes/DoesNotReturnAttribute.cs | 16 -- .../Attributes/DoesNotReturnIfAttribute.cs | 25 --- .../Attributes/NullableAttributes.cs | 180 ++++++++++++++++++ .../Extensions/ExceptionExtensions.cs | 10 +- .../Extensions/Extensions.cs | 23 ++- .../Extensions/Factories.cs | 7 + .../Extensions/LoggerExtensions.cs | 26 +-- .../Extensions/StreamExtensions.cs | 4 +- .../Extensions/StringExtensions.cs | 6 +- .../Interfaces/IAdbClient.Async.cs | 8 +- .../Interfaces/IAdbClient.cs | 8 +- .../Interfaces/IDeviceMonitor.cs | 10 +- .../Interfaces/ISyncService.Async.cs | 4 +- .../Interfaces/ISyncService.cs | 6 +- .../Logs/AndroidLogEntry.cs | 6 +- .../Logs/Interfaces/ILogger.cs | 2 +- AdvancedSharpAdbClient/Logs/LogEntry.cs | 2 +- .../Logs/LogReader.Async.cs | 22 +-- AdvancedSharpAdbClient/Logs/LogReader.cs | 22 +-- AdvancedSharpAdbClient/Logs/LoggerProvider.cs | 4 +- AdvancedSharpAdbClient/Logs/NullLogger.cs | 2 +- AdvancedSharpAdbClient/Models/AdbResponse.cs | 3 +- .../Models/AdbServerStatus.cs | 6 +- AdvancedSharpAdbClient/Models/DeviceData.cs | 21 +- AdvancedSharpAdbClient/Models/DnsEndPoint.cs | 3 +- AdvancedSharpAdbClient/Models/Element.cs | 109 ++++++----- .../Models/FileStatistics.cs | 7 +- AdvancedSharpAdbClient/Models/ForwardData.cs | 14 +- AdvancedSharpAdbClient/Models/ForwardSpec.cs | 7 +- AdvancedSharpAdbClient/Models/Framebuffer.cs | 20 +- .../Models/FramebufferHeader.cs | 12 +- AdvancedSharpAdbClient/Models/HashCode.cs | 4 +- AdvancedSharpAdbClient/Models/Point.cs | 3 +- AdvancedSharpAdbClient/Models/Rectangle.cs | 3 +- AdvancedSharpAdbClient/Models/ShellStream.cs | 4 +- .../Receivers/ConsoleOutputReceiver.cs | 2 +- AdvancedSharpAdbClient/SyncService.Async.cs | 6 +- AdvancedSharpAdbClient/SyncService.cs | 24 ++- AdvancedSharpAdbClient/TcpSocket.Async.cs | 6 +- AdvancedSharpAdbClient/TcpSocket.cs | 16 +- 75 files changed, 702 insertions(+), 573 deletions(-) delete mode 100644 AdvancedSharpAdbClient/Extensions/Attributes/DoesNotReturnAttribute.cs delete mode 100644 AdvancedSharpAdbClient/Extensions/Attributes/DoesNotReturnIfAttribute.cs create mode 100644 AdvancedSharpAdbClient/Extensions/Attributes/NullableAttributes.cs diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs index 09e18160..7681f415 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs @@ -18,7 +18,7 @@ public void ConstructorNullTest() [Theory] [InlineData("package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d", "com.android.gallery3d", "/system/app/Gallery2/Gallery2.apk")] - [InlineData("package:mwc2015.be", "mwc2015.be", null)] + [InlineData("package:mwc2015.be", "mwc2015.be", "")] public void PackagesPropertyTest(string command, string packageName, string path) { DeviceData device = new() diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index 637ac538..4efdd51a 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -230,7 +230,7 @@ public async Task ExecuteServerCommandAsync(string target, string command, IAdbS // -- one of the integration test fetches output 1000 times and found no truncations. while (!cancellationToken.IsCancellationRequested) { - string line = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false); + string? line = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false); if (line == null) { break; } @@ -306,7 +306,7 @@ public async Task RunLogServiceAsync(DeviceData device, Action message while (!cancellationToken.IsCancellationRequested) { - LogEntry entry = null; + LogEntry? entry = null; try { @@ -552,7 +552,7 @@ await Extensions.WhenAll(splitAPKs.Select(async splitAPK => } /// - public async Task InstallCreateAsync(DeviceData device, string packageName, CancellationToken cancellationToken, params string[] arguments) + public async Task InstallCreateAsync(DeviceData device, string? packageName, CancellationToken cancellationToken, params string[] arguments) { EnsureDevice(device); @@ -578,7 +578,8 @@ public async Task InstallCreateAsync(DeviceData device, string packageNa AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); using StreamReader reader = new(socket.GetShellStream(), Encoding); - string result = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false); + string result = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false) + ?? throw new AdbException($"The {nameof(result)} of {nameof(InstallCreateAsync)} is null."); if (!result.Contains("Success")) { @@ -655,8 +656,8 @@ public async Task InstallCommitAsync(DeviceData device, string session, Cancella AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); using StreamReader reader = new(socket.GetShellStream(), Encoding); - string result = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false); - if (!result.Contains("Success")) + string? result = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false); + if (result?.Contains("Success") != true) { throw new AdbException(await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false)); } @@ -698,7 +699,7 @@ public async Task DumpScreenStringAsync(DeviceData device, CancellationT } /// - public async Task DumpScreenAsync(DeviceData device, CancellationToken cancellationToken = default) + public async Task DumpScreenAsync(DeviceData device, CancellationToken cancellationToken = default) { string xmlString = await DumpScreenStringAsync(device, cancellationToken).ConfigureAwait(false); XmlDocument doc = new(); @@ -712,7 +713,7 @@ public async Task DumpScreenAsync(DeviceData device, CancellationTo #if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER /// - public async Task DumpScreenWinRTAsync(DeviceData device, CancellationToken cancellationToken = default) + public async Task DumpScreenWinRTAsync(DeviceData device, CancellationToken cancellationToken = default) { string xmlString = await DumpScreenStringAsync(device, cancellationToken).ConfigureAwait(false); Windows.Data.Xml.Dom.XmlDocument doc = new(); @@ -819,7 +820,7 @@ public async Task IsAppRunningAsync(DeviceData device, string packageName, await socket.SendAdbRequestAsync($"shell:pidof {packageName}", cancellationToken).ConfigureAwait(false); AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); using StreamReader reader = new(socket.GetShellStream(), Encoding); - string result = await reader.ReadToEndAsync(cancellationToken).ContinueWith(x => x.Result.TrimStart().Split(' ', StringSplitOptions.RemoveEmptyEntries).FirstOrDefault()).ConfigureAwait(false); + string? result = await reader.ReadToEndAsync(cancellationToken).ContinueWith(x => x.Result.TrimStart().Split(' ', StringSplitOptions.RemoveEmptyEntries).FirstOrDefault()).ConfigureAwait(false); bool intParsed = int.TryParse(result, out int pid); return intParsed && pid > 0; } @@ -854,19 +855,19 @@ public async Task GetAppStatusAsync(DeviceData device, string package } /// - public async Task FindElementAsync(DeviceData device, string xpath = "hierarchy/node", CancellationToken cancellationToken = default) + public async Task FindElementAsync(DeviceData device, string xpath = "hierarchy/node", CancellationToken cancellationToken = default) { try { while (!cancellationToken.IsCancellationRequested) { - XmlDocument doc = await DumpScreenAsync(device, cancellationToken).ConfigureAwait(false); + XmlDocument? doc = await DumpScreenAsync(device, cancellationToken).ConfigureAwait(false); if (doc != null) { - XmlNode xmlNode = doc.SelectSingleNode(xpath); + XmlNode? xmlNode = doc.SelectSingleNode(xpath); if (xmlNode != null) { - Element element = Element.FromXmlNode(this, device, xmlNode); + Element? element = Element.FromXmlNode(this, device, xmlNode); if (element != null) { return element; @@ -899,17 +900,17 @@ public async Task> FindElementsAsync(DeviceData device, str { while (!cancellationToken.IsCancellationRequested) { - XmlDocument doc = await DumpScreenAsync(device, cancellationToken).ConfigureAwait(false); + XmlDocument? doc = await DumpScreenAsync(device, cancellationToken).ConfigureAwait(false); if (doc != null) { - XmlNodeList xmlNodes = doc.SelectNodes(xpath); + XmlNodeList? xmlNodes = doc.SelectNodes(xpath); if (xmlNodes != null) { IEnumerable FindElements() { for (int i = 0; i < xmlNodes.Count; i++) { - Element element = Element.FromXmlNode(this, device, xmlNodes[i]); + Element? element = Element.FromXmlNode(this, device, xmlNodes[i]); if (element != null) { yield return element; @@ -932,7 +933,7 @@ IEnumerable FindElements() throw new ShellCommandUnresponsiveException(e); } } - return null; + return Enumerable.Empty(); } #if NETCOREAPP3_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER @@ -941,7 +942,7 @@ public async IAsyncEnumerable FindAsyncElements(DeviceData device, stri { while (!cancellationToken.IsCancellationRequested) { - XmlDocument doc = null; + XmlDocument? doc = null; try { @@ -960,12 +961,12 @@ public async IAsyncEnumerable FindAsyncElements(DeviceData device, stri if (doc != null) { - XmlNodeList xmlNodes = doc.SelectNodes(xpath); + XmlNodeList? xmlNodes = doc.SelectNodes(xpath); if (xmlNodes != null) { for (int i = 0; i < xmlNodes.Count; i++) { - Element element = Element.FromXmlNode(this, device, xmlNodes[i]); + Element? element = Element.FromXmlNode(this, device, xmlNodes[i]); if (element != null) { yield return element; diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index 5db85a92..8a7dca93 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -59,7 +59,8 @@ public partial class AdbClient : IAdbClient /// /// Initializes a new instance of the class. /// - public AdbClient() : this(new IPEndPoint(IPAddress.Loopback, AdbServerPort), Factories.AdbSocketFactory) + public AdbClient() + : this(new IPEndPoint(IPAddress.Loopback, AdbServerPort), Factories.AdbSocketFactory) { } @@ -67,7 +68,8 @@ public AdbClient() : this(new IPEndPoint(IPAddress.Loopback, AdbServerPort), Fac /// Initializes a new instance of the class. /// /// The at which the adb server is listening. - public AdbClient(EndPoint endPoint) : this(endPoint, Factories.AdbSocketFactory) + public AdbClient(EndPoint endPoint) + : this(endPoint, Factories.AdbSocketFactory) { } @@ -76,31 +78,11 @@ public AdbClient(EndPoint endPoint) : this(endPoint, Factories.AdbSocketFactory) /// /// The host address at which the adb server is listening. /// The port at which the adb server is listening. - public AdbClient(string host, int port) : this(host, port, Factories.AdbSocketFactory) + public AdbClient(string host, int port) + : this(Extensions.CreateDnsEndPoint(host, port), Factories.AdbSocketFactory) { } - /// - /// Initializes a new instance of the class. - /// - /// The host address at which the adb server is listening. - /// The port at which the adb server is listening. - /// The to create . - public AdbClient(string host, int port, Func adbSocketFactory) - { - if (string.IsNullOrEmpty(host)) - { - throw new ArgumentNullException(nameof(host)); - } - - string[] values = host.Split(':'); - - this.adbSocketFactory = adbSocketFactory ?? throw new ArgumentNullException(nameof(adbSocketFactory)); - EndPoint = values.Length <= 0 - ? throw new ArgumentNullException(nameof(host)) - : new DnsEndPoint(values[0], values.Length > 1 && int.TryParse(values[1], out int _port) ? _port : port); - } - /// /// Initializes a new instance of the class. /// @@ -119,6 +101,17 @@ public AdbClient(EndPoint endPoint, Func adbSocketFactory) this.adbSocketFactory = adbSocketFactory ?? throw new ArgumentNullException(nameof(adbSocketFactory)); } + /// + /// Initializes a new instance of the class. + /// + /// The host address at which the adb server is listening. + /// The port at which the adb server is listening. + /// The to create . + public AdbClient(string host, int port, Func adbSocketFactory) + : this(Extensions.CreateDnsEndPoint(host, port), adbSocketFactory) + { + } + /// /// Get or set default encoding. /// @@ -373,7 +366,7 @@ public void ExecuteServerCommand(string target, string command, IAdbSocket socke // -- one of the integration test fetches output 1000 times and found no truncations. while (true) { - string line = reader.ReadLine(); + string? line = reader.ReadLine(); if (line == null) { break; } @@ -448,7 +441,7 @@ public void RunLogService(DeviceData device, Action messageSink, param while (true) { - LogEntry entry = null; + LogEntry? entry = null; try { @@ -696,7 +689,7 @@ public void InstallMultiple(DeviceData device, Stream baseAPK, IEnumerable - public string InstallCreate(DeviceData device, string packageName = null, params string[] arguments) + public string InstallCreate(DeviceData device, string? packageName = null, params string[] arguments) { EnsureDevice(device); @@ -722,7 +715,7 @@ public string InstallCreate(DeviceData device, string packageName = null, params AdbResponse response = socket.ReadAdbResponse(); using StreamReader reader = new(socket.GetShellStream(), Encoding); - string result = reader.ReadLine(); + string result = reader.ReadLine() ?? throw new AdbException($"The {nameof(result)} of {nameof(InstallCreate)} is null."); if (!result.Contains("Success")) { @@ -803,8 +796,8 @@ public void InstallCommit(DeviceData device, string session) AdbResponse response = socket.ReadAdbResponse(); using StreamReader reader = new(socket.GetShellStream(), Encoding); - string result = reader.ReadLine(); - if (!result.Contains("Success")) + string? result = reader.ReadLine(); + if (result?.Contains("Success") != true) { throw new AdbException(reader.ReadToEnd()); } @@ -847,7 +840,7 @@ public string DumpScreenString(DeviceData device) } /// - public XmlDocument DumpScreen(DeviceData device) + public XmlDocument? DumpScreen(DeviceData device) { XmlDocument doc = new(); string xmlString = DumpScreenString(device); @@ -861,7 +854,7 @@ public XmlDocument DumpScreen(DeviceData device) #if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER /// - public Windows.Data.Xml.Dom.XmlDocument DumpScreenWinRT(DeviceData device) + public Windows.Data.Xml.Dom.XmlDocument? DumpScreenWinRT(DeviceData device) { Windows.Data.Xml.Dom.XmlDocument doc = new(); string xmlString = DumpScreenString(device); @@ -968,7 +961,7 @@ public bool IsAppRunning(DeviceData device, string packageName) socket.SendAdbRequest($"shell:pidof {packageName}"); AdbResponse response = socket.ReadAdbResponse(); using StreamReader reader = new(socket.GetShellStream(), Encoding); - string result = reader.ReadToEnd().TrimStart().Split(' ', StringSplitOptions.RemoveEmptyEntries).FirstOrDefault(); + string? result = reader.ReadToEnd().TrimStart().Split(' ', StringSplitOptions.RemoveEmptyEntries).FirstOrDefault(); bool intParsed = int.TryParse(result, out int pid); return intParsed && pid > 0; } @@ -1005,20 +998,20 @@ public AppStatus GetAppStatus(DeviceData device, string packageName) } /// - public Element FindElement(DeviceData device, string xpath = "hierarchy/node", TimeSpan timeout = default) + public Element? FindElement(DeviceData device, string xpath = "hierarchy/node", TimeSpan timeout = default) { EnsureDevice(device); Stopwatch stopwatch = new(); stopwatch.Start(); while (timeout == TimeSpan.Zero || stopwatch.Elapsed < timeout) { - XmlDocument doc = DumpScreen(device); + XmlDocument? doc = DumpScreen(device); if (doc != null) { - XmlNode xmlNode = doc.SelectSingleNode(xpath); + XmlNode? xmlNode = doc.SelectSingleNode(xpath); if (xmlNode != null) { - Element element = Element.FromXmlNode(this, device, xmlNode); + Element? element = Element.FromXmlNode(this, device, xmlNode); if (element != null) { return element; @@ -1041,15 +1034,15 @@ public IEnumerable FindElements(DeviceData device, string xpath = "hier stopwatch.Start(); while (timeout == TimeSpan.Zero || stopwatch.Elapsed < timeout) { - XmlDocument doc = DumpScreen(device); + XmlDocument? doc = DumpScreen(device); if (doc != null) { - XmlNodeList xmlNodes = doc.SelectNodes(xpath); + XmlNodeList? xmlNodes = doc.SelectNodes(xpath); if (xmlNodes != null) { for (int i = 0; i < xmlNodes.Count; i++) { - Element element = Element.FromXmlNode(this, device, xmlNodes[i]); + Element? element = Element.FromXmlNode(this, device, xmlNodes[i]); if (element != null) { yield return element; diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs index 0a20df13..685062d0 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs @@ -104,7 +104,7 @@ public virtual async Task StartServerAsync(CancellationToken cancellationToken = /// Use this command only for adb commands that return immediately, such as /// adb version. This operation times out after 5 seconds. /// The process exited with an exit code other than 0. - protected virtual async Task RunAdbProcessAsync(string command, ICollection errorOutput, ICollection standardOutput, CancellationToken cancellationToken = default) + protected virtual async Task RunAdbProcessAsync(string command, ICollection? errorOutput, ICollection? standardOutput, CancellationToken cancellationToken = default) { int status = await RunAdbProcessInnerAsync(command, errorOutput, standardOutput, cancellationToken).ConfigureAwait(false); @@ -127,7 +127,7 @@ protected virtual async Task RunAdbProcessAsync(string command, ICollectionA which return the return code of the adb process. /// Use this command only for adb commands that return immediately, such as /// adb version. This operation times out after 5 seconds. - protected virtual async Task RunAdbProcessInnerAsync(string command, ICollection errorOutput, ICollection standardOutput, CancellationToken cancellationToken = default) + protected virtual async Task RunAdbProcessInnerAsync(string command, ICollection? errorOutput, ICollection? standardOutput, CancellationToken cancellationToken = default) { ExceptionExtensions.ThrowIfNull(command); @@ -143,7 +143,7 @@ protected virtual async Task RunAdbProcessInnerAsync(string command, IColle #if !HAS_PROCESS [DoesNotReturn] #endif - protected virtual async Task RunProcessAsync(string filename, string command, ICollection errorOutput, ICollection standardOutput, CancellationToken cancellationToken = default) + protected virtual async Task RunProcessAsync(string filename, string command, ICollection? errorOutput, ICollection? standardOutput, CancellationToken cancellationToken = default) { #if HAS_PROCESS ProcessStartInfo psi = new(filename, command) @@ -155,7 +155,8 @@ protected virtual async Task RunProcessAsync(string filename, string comman RedirectStandardOutput = true }; - using Process process = Process.Start(psi); + using Process process = Process.Start(psi) ?? throw new AdbException($"The adb process could not be started. The process returned null when starting {filename} {command}"); + string standardErrorString = await process.StandardError.ReadToEndAsync(cancellationToken).ConfigureAwait(false); string standardOutputString = await process.StandardOutput.ReadToEndAsync(cancellationToken).ConfigureAwait(false); @@ -187,9 +188,8 @@ protected virtual async Task RunProcessAsync(string filename, string comman // get the return code from the process return process.ExitCode; #else - TaskCompletionSource source = new(); - source.SetException(new PlatformNotSupportedException("This platform is not support System.Diagnostics.Process. You can start adb server by running `adb start-server` manually.")); - return await source.Task.ConfigureAwait(false); + await Task.CompletedTask; + throw new PlatformNotSupportedException("This platform is not support System.Diagnostics.Process. You can start adb server by running `adb start-server` manually."); #endif } } diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.cs index 4dfc904a..c7b0bde4 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.cs @@ -37,7 +37,7 @@ public partial class AdbCommandLineClient : IAdbCommandLineClient /// The path to the adb.exe executable. /// Don't check adb file name when . /// The logger to use when logging. - public AdbCommandLineClient(string adbPath, bool isForce = false, ILogger logger = null) + public AdbCommandLineClient(string adbPath, bool isForce = false, ILogger? logger = null) { if (StringExtensions.IsNullOrWhiteSpace(adbPath)) { @@ -172,7 +172,7 @@ protected virtual void EnsureIsValidAdbFile(string adbPath) /// /// The output of the adb.exe version command. /// A object that represents the version of the adb command line client. - protected static Version GetVersionFromOutput(IEnumerable output) + protected static Version? GetVersionFromOutput(IEnumerable output) { Regex regex = AdbVersionRegex(); foreach (string line in output) @@ -209,7 +209,7 @@ protected static Version GetVersionFromOutput(IEnumerable output) /// Use this command only for adb commands that return immediately, such as /// adb version. This operation times out after 5 seconds. /// The process exited with an exit code other than 0. - protected virtual void RunAdbProcess(string command, ICollection errorOutput, ICollection standardOutput) + protected virtual void RunAdbProcess(string command, ICollection? errorOutput, ICollection? standardOutput) { int status = RunAdbProcessInner(command, errorOutput, standardOutput); @@ -231,7 +231,7 @@ protected virtual void RunAdbProcess(string command, ICollection errorOu /// The return code of the adb process. /// Use this command only for adb commands that return immediately, such as /// adb version. This operation times out after 5 seconds. - protected virtual int RunAdbProcessInner(string command, ICollection errorOutput, ICollection standardOutput) + protected virtual int RunAdbProcessInner(string command, ICollection? errorOutput, ICollection? standardOutput) { ExceptionExtensions.ThrowIfNull(command); @@ -247,7 +247,7 @@ protected virtual int RunAdbProcessInner(string command, ICollection err #if !HAS_PROCESS [DoesNotReturn] #endif - protected virtual int RunProcess(string filename, string command, ICollection errorOutput, ICollection standardOutput) + protected virtual int RunProcess(string filename, string command, ICollection? errorOutput, ICollection? standardOutput) { #if HAS_PROCESS ProcessStartInfo psi = new(filename, command) @@ -259,7 +259,8 @@ protected virtual int RunProcess(string filename, string command, ICollection StartServerAsync(string adbPath, bo try { AdbServerStatus serverStatus = await GetStatusAsync(cancellationToken).ConfigureAwait(false); - Version commandLineVersion = null; + Version? commandLineVersion = null; IAdbCommandLineClient commandLineClient = adbCommandLineClientFactory(adbPath); CheckFileExists = commandLineClient.CheckFileExists; @@ -73,7 +73,7 @@ public virtual async Task StartServerAsync(string adbPath, bo /// public Task RestartServerAsync(CancellationToken cancellationToken = default) => - StartServerAsync(CachedAdbPath, true, cancellationToken); + StartServerAsync(CachedAdbPath!, true, cancellationToken); /// public virtual Task RestartServerAsync(string adbPath, CancellationToken cancellationToken = default) => diff --git a/AdvancedSharpAdbClient/AdbServer.cs b/AdvancedSharpAdbClient/AdbServer.cs index 8490a6ad..95032d93 100644 --- a/AdvancedSharpAdbClient/AdbServer.cs +++ b/AdvancedSharpAdbClient/AdbServer.cs @@ -58,7 +58,8 @@ public partial class AdbServer : IAdbServer /// /// Initializes a new instance of the class. /// - public AdbServer() : this(new IPEndPoint(IPAddress.Loopback, AdbClient.AdbServerPort), Factories.AdbSocketFactory, Factories.AdbCommandLineClientFactory) + public AdbServer() + : this(new IPEndPoint(IPAddress.Loopback, AdbClient.AdbServerPort), Factories.AdbSocketFactory, Factories.AdbCommandLineClientFactory) { } @@ -66,7 +67,8 @@ public AdbServer() : this(new IPEndPoint(IPAddress.Loopback, AdbClient.AdbServer /// Initializes a new instance of the class. /// /// A connection to an adb server. - public AdbServer(IAdbClient adbClient) : this(adbClient.EndPoint, Factories.AdbSocketFactory, Factories.AdbCommandLineClientFactory) + public AdbServer(IAdbClient adbClient) + : this(adbClient.EndPoint, Factories.AdbSocketFactory, Factories.AdbCommandLineClientFactory) { } @@ -74,7 +76,8 @@ public AdbServer(IAdbClient adbClient) : this(adbClient.EndPoint, Factories.AdbS /// Initializes a new instance of the class. /// /// The at which the adb server is listening. - public AdbServer(EndPoint endPoint) : this(endPoint, Factories.AdbSocketFactory, Factories.AdbCommandLineClientFactory) + public AdbServer(EndPoint endPoint) + : this(endPoint, Factories.AdbSocketFactory, Factories.AdbCommandLineClientFactory) { } @@ -83,7 +86,8 @@ public AdbServer(EndPoint endPoint) : this(endPoint, Factories.AdbSocketFactory, /// /// The host address at which the adb server is listening. /// The port at which the adb server is listening. - public AdbServer(string host, int port) : this(host, port, Factories.AdbSocketFactory, Factories.AdbCommandLineClientFactory) + public AdbServer(string host, int port) + : this(Extensions.CreateDnsEndPoint(host, port), Factories.AdbSocketFactory, Factories.AdbCommandLineClientFactory) { } @@ -93,51 +97,41 @@ public AdbServer(string host, int port) : this(host, port, Factories.AdbSocketFa /// A connection to an adb server. /// The to create . /// The to create . - public AdbServer(IAdbClient adbClient, Func adbSocketFactory, Func adbCommandLineClientFactory) : this(adbClient.EndPoint, adbSocketFactory, adbCommandLineClientFactory) + public AdbServer(IAdbClient adbClient, Func adbSocketFactory, Func adbCommandLineClientFactory) + : this(adbClient.EndPoint, adbSocketFactory, adbCommandLineClientFactory) { } /// /// Initializes a new instance of the class. /// - /// The host address at which the adb server is listening. - /// The port at which the adb server is listening. + /// The at which the adb server is listening. /// The to create . /// The to create . - public AdbServer(string host, int port, Func adbSocketFactory, Func adbCommandLineClientFactory) + public AdbServer(EndPoint endPoint, Func adbSocketFactory, Func adbCommandLineClientFactory) { - if (string.IsNullOrEmpty(host)) + ExceptionExtensions.ThrowIfNull(endPoint); + + if (endPoint is not (IPEndPoint or DnsEndPoint)) { - throw new ArgumentNullException(nameof(host)); + throw new NotSupportedException("Only TCP endpoints are supported"); } - string[] values = host.Split(':'); - + EndPoint = endPoint; this.adbSocketFactory = adbSocketFactory ?? throw new ArgumentNullException(nameof(adbSocketFactory)); this.adbCommandLineClientFactory = adbCommandLineClientFactory ?? throw new ArgumentNullException(nameof(adbCommandLineClientFactory)); - EndPoint = values.Length <= 0 - ? throw new ArgumentNullException(nameof(host)) - : new DnsEndPoint(values[0], values.Length > 1 && int.TryParse(values[1], out int _port) ? _port : port); } /// /// Initializes a new instance of the class. /// - /// The at which the adb server is listening. + /// The host address at which the adb server is listening. + /// The port at which the adb server is listening. /// The to create . /// The to create . - public AdbServer(EndPoint endPoint, Func adbSocketFactory, Func adbCommandLineClientFactory) + public AdbServer(string host, int port, Func adbSocketFactory, Func adbCommandLineClientFactory) + : this(Extensions.CreateDnsEndPoint(host, port), adbSocketFactory, adbCommandLineClientFactory) { - ExceptionExtensions.ThrowIfNull(endPoint); - - if (endPoint is not (IPEndPoint or DnsEndPoint)) - { - throw new NotSupportedException("Only TCP endpoints are supported"); - } - - EndPoint = endPoint; - this.adbSocketFactory = adbSocketFactory ?? throw new ArgumentNullException(nameof(adbSocketFactory)); - this.adbCommandLineClientFactory = adbCommandLineClientFactory ?? throw new ArgumentNullException(nameof(adbCommandLineClientFactory)); } /// @@ -145,7 +139,8 @@ public AdbServer(EndPoint endPoint, Func adbSocketFactory, /// /// The to create . /// The to create . - public AdbServer(Func adbSocketFactory, Func adbCommandLineClientFactory) : this(new IPEndPoint(IPAddress.Loopback, AdbClient.AdbServerPort), adbSocketFactory, adbCommandLineClientFactory) + public AdbServer(Func adbSocketFactory, Func adbCommandLineClientFactory) + : this(new IPEndPoint(IPAddress.Loopback, AdbClient.AdbServerPort), adbSocketFactory, adbCommandLineClientFactory) { } @@ -153,7 +148,7 @@ public AdbServer(Func adbSocketFactory, Func. Used when restarting /// the server to figure out where adb is located. /// - protected static string CachedAdbPath { get; set; } + protected static string? CachedAdbPath { get; set; } /// /// Gets or sets the default instance of the interface. @@ -182,7 +177,7 @@ public virtual StartServerResult StartServer(string adbPath, bool restartServerI try { AdbServerStatus serverStatus = GetStatus(); - Version commandLineVersion = null; + Version? commandLineVersion = null; IAdbCommandLineClient commandLineClient = adbCommandLineClientFactory(adbPath); CheckFileExists = commandLineClient.CheckFileExists; @@ -234,7 +229,7 @@ public virtual StartServerResult StartServer(string adbPath, bool restartServerI } /// - public virtual StartServerResult RestartServer() => StartServer(CachedAdbPath, true); + public virtual StartServerResult RestartServer() => StartServer(CachedAdbPath!, true); /// public virtual StartServerResult RestartServer(string adbPath) => diff --git a/AdvancedSharpAdbClient/AdbSocket.Async.cs b/AdvancedSharpAdbClient/AdbSocket.Async.cs index c81aed21..46414c31 100644 --- a/AdvancedSharpAdbClient/AdbSocket.Async.cs +++ b/AdvancedSharpAdbClient/AdbSocket.Async.cs @@ -158,7 +158,13 @@ public virtual async Task ReadStringAsync(CancellationToken cancellation { // The first 4 bytes contain the length of the string byte[] reply = new byte[4]; - _ = await ReadAsync(reply, cancellationToken).ConfigureAwait(false); + int read = await ReadAsync(reply, cancellationToken).ConfigureAwait(false); + + if (read == 0) + { + // There is no data to read + return string.Empty; + } // Convert the bytes to a hex string string lenHex = AdbClient.Encoding.GetString(reply); diff --git a/AdvancedSharpAdbClient/AdbSocket.cs b/AdvancedSharpAdbClient/AdbSocket.cs index 5dbeb836..c9bb1fd2 100644 --- a/AdvancedSharpAdbClient/AdbSocket.cs +++ b/AdvancedSharpAdbClient/AdbSocket.cs @@ -35,7 +35,7 @@ public partial class AdbSocket : IAdbSocket /// /// The at which the Android Debug Bridge is listening for clients. /// The logger to use when logging. - public AdbSocket(EndPoint endPoint, ILogger logger = null) + public AdbSocket(EndPoint endPoint, ILogger? logger = null) { Socket = new TcpSocket(); Socket.Connect(endPoint); @@ -49,23 +49,9 @@ public AdbSocket(EndPoint endPoint, ILogger logger = null) /// The host address at which the Android Debug Bridge is listening for clients. /// The port at which the Android Debug Bridge is listening for clients. /// The logger to use when logging. - public AdbSocket(string host, int port, ILogger logger = null) + public AdbSocket(string host, int port, ILogger? logger = null) + : this(Extensions.CreateDnsEndPoint(host, port), logger) { - if (string.IsNullOrEmpty(host)) - { - throw new ArgumentNullException(nameof(host)); - } - - string[] values = host.Split(':'); - - DnsEndPoint endPoint = values.Length <= 0 - ? throw new ArgumentNullException(nameof(host)) - : new DnsEndPoint(values[0], values.Length > 1 && int.TryParse(values[1], out int _port) ? _port : port); - - Socket = new TcpSocket(); - Socket.Connect(endPoint); - Socket.ReceiveBufferSize = ReceiveBufferSize; - this.logger = logger ?? LoggerProvider.CreateLogger(); } /// @@ -73,9 +59,9 @@ public AdbSocket(string host, int port, ILogger logger = null) /// /// The at which the Android Debug Bridge is listening for clients. /// The logger to use when logging. - public AdbSocket(ITcpSocket socket, ILogger logger = null) + public AdbSocket(ITcpSocket socket, ILogger? logger = null) { - this.Socket = socket; + Socket = socket; this.logger = logger ?? LoggerProvider.CreateLogger(); } @@ -252,7 +238,7 @@ public virtual string ReadString() if (read == 0) { // There is no data to read - return null; + return string.Empty; } // Convert the bytes to a hex string diff --git a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj index b8032abd..bde85b81 100644 --- a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj +++ b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj @@ -4,6 +4,7 @@ False True $(NoWarn);CS1685;CA2254 + Enable @@ -33,7 +34,7 @@ - + which represents the asynchronous operation. public static async Task PullAsync(this IAdbClient client, DeviceData device, string remotePath, Stream stream, - EventHandler syncProgressEventHandler = null, - IProgress progress = null, CancellationToken cancellationToken = default) + EventHandler? syncProgressEventHandler = null, + IProgress? progress = null, CancellationToken cancellationToken = default) { using ISyncService service = Factories.SyncServiceFactory(client, device); if (syncProgressEventHandler != null) @@ -104,8 +104,8 @@ public static async Task PullAsync(this IAdbClient client, DeviceData device, /// A which represents the asynchronous operation. public static async Task PushAsync(this IAdbClient client, DeviceData device, string remotePath, Stream stream, int permissions, DateTimeOffset timestamp, - EventHandler syncProgressEventHandler = null, - IProgress progress = null, CancellationToken cancellationToken = default) + EventHandler? syncProgressEventHandler = null, + IProgress? progress = null, CancellationToken cancellationToken = default) { using ISyncService service = Factories.SyncServiceFactory(client, device); if (syncProgressEventHandler != null) @@ -239,9 +239,9 @@ await client.ExecuteShellCommandAsync(device, @"SDK=""$(/system/bin/getprop ro.b { while (reader.Peek() > 0) { - string line = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false); + string? line = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false); - if (!line.All(char.IsDigit)) + if (line?.All(char.IsDigit) != true) { continue; } diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs index 16a1722d..751c6e04 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs @@ -73,8 +73,8 @@ public static IEnumerable List(this IAdbClient client, DeviceDat /// A that can be used to cancel the task. public static void Pull(this IAdbClient client, DeviceData device, string remotePath, Stream stream, - EventHandler syncProgressEventHandler = null, - IProgress progress = null, + EventHandler? syncProgressEventHandler = null, + IProgress? progress = null, in bool isCancelled = false) { using ISyncService service = Factories.SyncServiceFactory(client, device); @@ -99,8 +99,8 @@ public static void Pull(this IAdbClient client, DeviceData device, /// A that can be used to cancel the task. public static void Push(this IAdbClient client, DeviceData device, string remotePath, Stream stream, int permissions, DateTimeOffset timestamp, - EventHandler syncProgressEventHandler = null, - IProgress progress = null, + EventHandler? syncProgressEventHandler = null, + IProgress? progress = null, in bool isCancelled = false) { using ISyncService service = Factories.SyncServiceFactory(client, device); @@ -227,9 +227,9 @@ public static IEnumerable ListProcesses(this IAdbClient client, { while (reader.Peek() > 0) { - string line = reader.ReadLine(); + string? line = reader.ReadLine(); - if (!line.All(char.IsDigit)) + if (line?.All(char.IsDigit) != true) { continue; } diff --git a/AdvancedSharpAdbClient/DeviceCommands/LinuxPath.cs b/AdvancedSharpAdbClient/DeviceCommands/LinuxPath.cs index 75df540c..4a6a0066 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/LinuxPath.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/LinuxPath.cs @@ -3,6 +3,7 @@ // using System; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Text; @@ -110,7 +111,8 @@ public static string Combine(params string[] paths) /// The path parameter is longer /// than the system-defined maximum length. /// 1 - public static string GetDirectoryName(string path) + [return: NotNullIfNotNull(nameof(path))] + public static string? GetDirectoryName(string? path) { if (path != null) { @@ -149,7 +151,8 @@ public static string GetDirectoryName(string path) /// path contains one or more of the invalid characters /// defined in , or contains a wildcard character. /// 1 - public static string GetFileName(string path) + [return: NotNullIfNotNull(nameof(path))] + public static string? GetFileName(string path) { if (path != null) { diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs index 1efe67d9..14e0e1de 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs @@ -301,7 +301,7 @@ public AndroidProcess(string line, bool cmdLinePrefix = false) /// /// Gets or sets the name of the process, including arguments, if any. /// - public string Name { get; init; } + public string Name { get; init; } = string.Empty; /// /// Gets or sets the parent Process ID number. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/VersionInfo.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/VersionInfo.cs index d10bc51b..34bce4d9 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/VersionInfo.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/VersionInfo.cs @@ -5,6 +5,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace AdvancedSharpAdbClient.DeviceCommands { @@ -26,7 +27,7 @@ public readonly record struct VersionInfo(int VersionCode, string VersionName) : public string VersionName { get; init; } = VersionName; /// - public readonly int Compare(object x, object y) => + public readonly int Compare([NotNull] object? x, [NotNull] object? y) => x is VersionInfo left && y is VersionInfo right ? left.VersionCode.CompareTo(right.VersionCode) : throw new NotImplementedException(); diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs index 26009afa..71ac1bb0 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs @@ -3,6 +3,7 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // +using AdvancedSharpAdbClient.Exceptions; using System; using System.Collections.Generic; using System.Diagnostics; @@ -381,7 +382,7 @@ protected virtual async Task RemoveRemotePackageAsync(string remoteFilePath, Can /// The absolute package name of the base app. /// A which can be used to cancel the asynchronous operation. /// A which return the session ID. - protected virtual async Task CreateInstallSessionAsync(bool reinstall, string packageName = null, CancellationToken cancellationToken = default) + protected virtual async Task CreateInstallSessionAsync(bool reinstall, string? packageName = null, CancellationToken cancellationToken = default) { ValidateDevice(); @@ -397,7 +398,7 @@ protected virtual async Task CreateInstallSessionAsync(bool reinstall, s throw new PackageInstallationException(receiver.ErrorMessage); } - string result = receiver.SuccessMessage; + string result = receiver.SuccessMessage ?? throw new AdbException($"The {nameof(result)} of {nameof(CreateInstallSessionAsync)} is null."); int arr = result.IndexOf(']') - 1 - result.IndexOf('['); string session = result.Substring(result.IndexOf('[') + 1, arr); diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs index af624c17..abf0ad0b 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs @@ -47,7 +47,7 @@ public partial class PackageManager /// /// Occurs when there is a change in the status of the installing. /// - public event EventHandler InstallProgressChanged; + public event EventHandler? InstallProgressChanged; /// /// Initializes a new instance of the class. @@ -62,7 +62,7 @@ public partial class PackageManager /// A value indicating whether to skip the initial refresh of the package list or not. /// Used mainly by unit tests. /// The logger to use when logging. - public PackageManager(IAdbClient client, DeviceData device, bool thirdPartyOnly = false, Func syncServiceFactory = null, bool skipInit = false, ILogger logger = null) + public PackageManager(IAdbClient client, DeviceData device, bool thirdPartyOnly = false, Func? syncServiceFactory = null, bool skipInit = false, ILogger? logger = null) { Device = device ?? throw new ArgumentNullException(nameof(device)); Packages = []; @@ -468,7 +468,7 @@ protected virtual void RemoveRemotePackage(string remoteFilePath) /// Set to if re-install of app should be performed. /// The absolute package name of the base app. /// Session ID. - protected virtual string CreateInstallSession(bool reinstall, string packageName = null) + protected virtual string CreateInstallSession(bool reinstall, string? packageName = null) { ValidateDevice(); @@ -484,7 +484,7 @@ protected virtual string CreateInstallSession(bool reinstall, string packageName throw new PackageInstallationException(receiver.ErrorMessage); } - string result = receiver.SuccessMessage; + string result = receiver.SuccessMessage ?? throw new AdbException($"The {nameof(result)} of {nameof(CreateInstallSession)} is null."); int arr = result.IndexOf(']') - 1 - result.IndexOf('['); string session = result.Substring(result.IndexOf('[') + 1, arr); diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InfoOutputReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InfoOutputReceiver.cs index 1c72cf39..008b9671 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InfoOutputReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InfoOutputReceiver.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace AdvancedSharpAdbClient.DeviceCommands { @@ -20,13 +21,13 @@ public InfoOutputReceiver() { } /// /// Gets or sets a dictionary with the extracted properties and their corresponding values. /// - private Dictionary Properties { get; set; } = []; + private Dictionary Properties { get; set; } = []; /// /// Gets or sets the dictionary with all properties and their corresponding property parsers. /// A property parser extracts the property value out of a if possible. /// - private Dictionary> PropertyParsers { get; set; } = []; + private Dictionary> PropertyParsers { get; set; } = []; /// /// Gets the value of the property out of the Properties dictionary. @@ -34,7 +35,7 @@ public InfoOutputReceiver() { } /// /// The name of the property. /// The received value - public object GetPropertyValue(string propertyName) => Properties.TryGetValue(propertyName, out object property) ? property : null; + public object? GetPropertyValue(string propertyName) => Properties.TryGetValue(propertyName, out object? property) ? property : null; /// /// Gets the value of the property out of the Properties dictionary. @@ -43,7 +44,8 @@ public InfoOutputReceiver() { } /// The type of the property /// The name of the property. /// The received value. - public T GetPropertyValue(string propertyName) => Properties.TryGetValue(propertyName, out object property) && property is T value ? value : default; + [return: MaybeNull] + public T GetPropertyValue(string propertyName) => Properties.TryGetValue(propertyName, out object? property) && property is T value ? value : default; /// /// Adds a new parser to this receiver. @@ -52,7 +54,7 @@ public InfoOutputReceiver() { } /// /// The property corresponding with the parser. /// Function parsing one string and returning the property value if possible. - public void AddPropertyParser(string property, Func parser) => PropertyParsers[property] = parser; + public void AddPropertyParser(string property, Func parser) => PropertyParsers[property] = parser; /// /// Processes the new lines, and sets version information if the line represents package information data. @@ -67,9 +69,9 @@ protected override void ProcessNewLines(IEnumerable lines) continue; } - foreach (KeyValuePair> parser in PropertyParsers) + foreach (KeyValuePair> parser in PropertyParsers) { - object propertyValue = parser.Value(line); + object? propertyValue = parser.Value(line); if (propertyValue != null) { Properties[parser.Key] = propertyValue; diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs index ba306688..b42dd25b 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs @@ -50,12 +50,12 @@ public InstallOutputReceiver() { } /// /// Gets the error message if the install was unsuccessful. /// - public string ErrorMessage { get; private set; } + public string? ErrorMessage { get; private set; } /// /// Gets the success message if the install is successful. /// - public string SuccessMessage { get; private set; } + public string? SuccessMessage { get; private set; } /// /// Gets a value indicating whether the install was a success. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/PackageManagerReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/PackageManagerReceiver.cs index a12db1bc..979bc1c7 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/PackageManagerReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/PackageManagerReceiver.cs @@ -53,7 +53,7 @@ protected override void ProcessNewLines(IEnumerable lines) if (separator == -1) { - PackageManager.Packages[package] = null; + PackageManager.Packages[package] = string.Empty; } else { diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/VersionInfoReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/VersionInfoReceiver.cs index c1416fe8..731bca84 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/VersionInfoReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/VersionInfoReceiver.cs @@ -79,7 +79,7 @@ private void CheckPackagesSection(string line) /// /// The line to be parsed. /// The extracted version name. - internal object GetVersionName(string line) + internal string? GetVersionName(string line) { CheckPackagesSection(line); @@ -97,7 +97,7 @@ internal object GetVersionName(string line) /// /// The line to be parsed. /// The extracted version code. - internal object GetVersionCode(string line) + internal object? GetVersionCode(string line) { CheckPackagesSection(line); diff --git a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs index e807d5f1..184f2f92 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs @@ -5,6 +5,7 @@ using AdvancedSharpAdbClient.Exceptions; using System; +using System.Diagnostics.CodeAnalysis; using System.Net.Sockets; using System.Threading; @@ -27,9 +28,10 @@ public partial class DeviceMonitor /// /// The that monitors the and waits for device notifications. /// - protected Task monitorTask; + protected Task? monitorTask; /// + [MemberNotNull(nameof(monitorTask))] public virtual async Task StartAsync(CancellationToken cancellationToken = default) { if (monitorTask == null) @@ -74,7 +76,7 @@ protected virtual async Task DisposeAsyncCore() if (Socket != null) { Socket.Dispose(); - Socket = null; + Socket = null!; } firstDeviceListParsed.Dispose(); diff --git a/AdvancedSharpAdbClient/DeviceMonitor.cs b/AdvancedSharpAdbClient/DeviceMonitor.cs index cf1aab64..9f5afc1f 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Net; using System.Net.Sockets; @@ -72,29 +73,29 @@ public partial class DeviceMonitor : IDeviceMonitor /// /// The that monitors the and waits for device notifications. /// - protected Thread monitorThread; + protected Thread? monitorThread; #endif /// - public event EventHandler DeviceChanged; + public event EventHandler? DeviceChanged; /// - public event EventHandler DeviceNotified; + public event EventHandler? DeviceNotified; /// - public event EventHandler DeviceConnected; + public event EventHandler? DeviceConnected; /// - public event EventHandler DeviceListChanged; + public event EventHandler? DeviceListChanged; /// - public event EventHandler DeviceDisconnected; + public event EventHandler? DeviceDisconnected; /// /// Initializes a new instance of the class. /// /// The logger to use when logging. - public DeviceMonitor(ILogger logger = null) + public DeviceMonitor(ILogger? logger = null) : this(Factories.AdbSocketFactory(new IPEndPoint(IPAddress.Loopback, AdbClient.AdbServerPort)), logger) { } @@ -104,7 +105,7 @@ public DeviceMonitor(ILogger logger = null) /// /// The at which the adb server is listening. /// The logger to use when logging. - public DeviceMonitor(EndPoint endPoint, ILogger logger = null) + public DeviceMonitor(EndPoint endPoint, ILogger? logger = null) : this(Factories.AdbSocketFactory(endPoint), logger) { } @@ -114,7 +115,7 @@ public DeviceMonitor(EndPoint endPoint, ILogger logger = null) /// /// The that manages the connection with the adb server. /// The logger to use when logging. - public DeviceMonitor(IAdbSocket socket, ILogger logger = null) + public DeviceMonitor(IAdbSocket socket, ILogger? logger = null) { Socket = socket ?? throw new ArgumentNullException(nameof(socket)); devices = []; @@ -138,6 +139,13 @@ public DeviceMonitor(IAdbSocket socket, ILogger logger = null) public bool IsRunning { get; protected set; } /// + [MemberNotNull( +#if HAS_TASK + nameof(monitorTask) +#else + nameof(monitorThread) +#endif + )] public virtual void Start() { #if HAS_TASK @@ -195,7 +203,7 @@ protected virtual void Dispose(bool disposing) if (Socket != null) { Socket.Dispose(); - Socket = null; + Socket = null!; } firstDeviceListParsed.Dispose(); @@ -220,7 +228,7 @@ protected virtual void Dispose(bool disposing) if (Socket != null) { Socket.Dispose(); - Socket = null; + Socket = null!; } firstDeviceListParsed.Close(); diff --git a/AdvancedSharpAdbClient/Exceptions/AdbException.cs b/AdvancedSharpAdbClient/Exceptions/AdbException.cs index 38a0a9ac..6f3dc0b4 100644 --- a/AdvancedSharpAdbClient/Exceptions/AdbException.cs +++ b/AdvancedSharpAdbClient/Exceptions/AdbException.cs @@ -24,7 +24,7 @@ public AdbException() : base("An error occurred with ADB") /// Initializes a new instance of the class with the specified error message. /// /// The message that describes the error. - public AdbException(string message) : base(message) + public AdbException(string? message) : base(message) { } @@ -33,19 +33,28 @@ public AdbException(string message) : base(message) /// /// The message that describes the error on the client side. /// The raw error message that was sent by adb. - public AdbException(string message, string adbError) : base(message) => AdbError = adbError; + public AdbException(string? message, string? adbError) : base(message) => AdbError = adbError; /// /// Initializes a new instance of the class with the specified client error message and /// /// The message that describes the error on the client side. /// The that was sent by adb. - public AdbException(string message, AdbResponse response) : base(message) + public AdbException(string? message, AdbResponse response) : base(message) { AdbError = response.Message; Response = response; } + /// + /// Initializes a new instance of the class. + /// + /// The message. + /// The inner exception. + public AdbException(string? message, Exception? innerException) : base(message, innerException) + { + } + #if HAS_SERIALIZATION /// /// Initializes a new instance of the class. @@ -60,19 +69,10 @@ public AdbException(SerializationInfo serializationInfo, StreamingContext contex } #endif - /// - /// Initializes a new instance of the class. - /// - /// The message. - /// The inner exception. - public AdbException(string message, Exception innerException) : base(message, innerException) - { - } - /// /// Gets the error message that was sent by adb. /// - public string AdbError { get; init; } + public string? AdbError { get; init; } /// /// Gets the response that was sent by adb. diff --git a/AdvancedSharpAdbClient/Exceptions/CommandAbortingException.cs b/AdvancedSharpAdbClient/Exceptions/CommandAbortingException.cs index 8f4de81f..4f453ba8 100644 --- a/AdvancedSharpAdbClient/Exceptions/CommandAbortingException.cs +++ b/AdvancedSharpAdbClient/Exceptions/CommandAbortingException.cs @@ -23,7 +23,16 @@ public CommandAbortingException() : base("Permission to access the specified res /// Initializes a new instance of the class. /// /// The message. - public CommandAbortingException(string message) : base(message) + public CommandAbortingException(string? message) : base(message) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + /// The inner exception. + public CommandAbortingException(string? message, Exception? innerException) : base(message, innerException) { } @@ -40,14 +49,5 @@ public CommandAbortingException(SerializationInfo serializationInfo, StreamingCo { } #endif - - /// - /// Initializes a new instance of the class. - /// - /// The message. - /// The inner exception. - public CommandAbortingException(string message, Exception innerException) : base(message, innerException) - { - } } } diff --git a/AdvancedSharpAdbClient/Exceptions/DeviceNotFoundException.cs b/AdvancedSharpAdbClient/Exceptions/DeviceNotFoundException.cs index ca691438..44046194 100644 --- a/AdvancedSharpAdbClient/Exceptions/DeviceNotFoundException.cs +++ b/AdvancedSharpAdbClient/Exceptions/DeviceNotFoundException.cs @@ -23,7 +23,7 @@ public DeviceNotFoundException() : base("The device was not found.") /// Initializes a new instance of the class. /// /// The device. - public DeviceNotFoundException(string device) : base("The device '" + device + "' was not found.") + public DeviceNotFoundException(string? device) : base("The device '" + device + "' was not found.") { } @@ -32,7 +32,7 @@ public DeviceNotFoundException(string device) : base("The device '" + device + " /// /// The message. /// The inner exception. - public DeviceNotFoundException(string message, Exception innerException) : base(message, innerException) + public DeviceNotFoundException(string? message, Exception? innerException) : base(message, innerException) { } diff --git a/AdvancedSharpAdbClient/Exceptions/ElementNotFoundException.cs b/AdvancedSharpAdbClient/Exceptions/ElementNotFoundException.cs index 6da75c13..55247ee3 100644 --- a/AdvancedSharpAdbClient/Exceptions/ElementNotFoundException.cs +++ b/AdvancedSharpAdbClient/Exceptions/ElementNotFoundException.cs @@ -10,13 +10,7 @@ namespace AdvancedSharpAdbClient.Exceptions /// Represents an exception with Element. /// [Serializable] - public class ElementNotFoundException : Exception + public class ElementNotFoundException(string? message) : Exception(message) { - /// - /// Initializes a new instance of the class. - /// - public ElementNotFoundException(string message) : base(message) - { - } } } diff --git a/AdvancedSharpAdbClient/Exceptions/InvalidKeyEventException.cs b/AdvancedSharpAdbClient/Exceptions/InvalidKeyEventException.cs index 4ec903a2..63c73e2a 100644 --- a/AdvancedSharpAdbClient/Exceptions/InvalidKeyEventException.cs +++ b/AdvancedSharpAdbClient/Exceptions/InvalidKeyEventException.cs @@ -10,13 +10,7 @@ namespace AdvancedSharpAdbClient.Exceptions /// Represents an exception with key event. /// [Serializable] - public class InvalidKeyEventException : Exception + public class InvalidKeyEventException(string? message) : Exception(message) { - /// - /// Initializes a new instance of the class. - /// - public InvalidKeyEventException(string message) : base(message) - { - } } } diff --git a/AdvancedSharpAdbClient/Exceptions/JavaException.cs b/AdvancedSharpAdbClient/Exceptions/JavaException.cs index 81e6e36b..2da3e4d1 100644 --- a/AdvancedSharpAdbClient/Exceptions/JavaException.cs +++ b/AdvancedSharpAdbClient/Exceptions/JavaException.cs @@ -21,19 +21,19 @@ public partial class JavaException : Exception /// /// Gets the name of Java exception. /// - public string JavaName { get; init; } + public string? JavaName { get; init; } /// /// Gets a string representation of the immediate frames on the call stack of Java exception. /// - public string JavaStackTrace { get; init; } + public string? JavaStackTrace { get; init; } /// /// Initializes a new instance of the class. /// /// The name of Java exception. /// The stackTrace of Java exception. - public JavaException(string name, string stackTrace) : base(UnknownError) + public JavaException(string? name, string? stackTrace) : base(UnknownError) { JavaName = name; JavaStackTrace = stackTrace; @@ -45,7 +45,20 @@ public JavaException(string name, string stackTrace) : base(UnknownError) /// The name of Java exception. /// The message of Java exception. /// The stackTrace of Java exception. - public JavaException(string name, string message, string stackTrace) : base(message) + public JavaException(string? name, string? message, string? stackTrace) : base(message) + { + JavaName = name; + JavaStackTrace = stackTrace; + } + + /// + /// Initializes a new instance of the class. + /// + /// The name of Java exception. + /// The message of Java exception. + /// The stackTrace of Java exception. + /// The inner exception. + public JavaException(string? name, string? message, string? stackTrace, Exception? innerException) : base(message, innerException) { JavaName = name; JavaStackTrace = stackTrace; @@ -65,19 +78,6 @@ public JavaException(SerializationInfo serializationInfo, StreamingContext conte } #endif - /// - /// Initializes a new instance of the class. - /// - /// The name of Java exception. - /// The message of Java exception. - /// The stackTrace of Java exception. - /// The inner exception. - public JavaException(string name, string message, string stackTrace, Exception innerException) : base(message, innerException) - { - JavaName = name; - JavaStackTrace = stackTrace; - } - /// /// Creates a from it representation. /// diff --git a/AdvancedSharpAdbClient/Exceptions/PackageInstallationException.cs b/AdvancedSharpAdbClient/Exceptions/PackageInstallationException.cs index 6d6ce5ed..2878805c 100644 --- a/AdvancedSharpAdbClient/Exceptions/PackageInstallationException.cs +++ b/AdvancedSharpAdbClient/Exceptions/PackageInstallationException.cs @@ -23,7 +23,7 @@ public PackageInstallationException() /// Initializes a new instance of the class. /// /// The message. - public PackageInstallationException(string message) : base(message) + public PackageInstallationException(string? message) : base(message) { } @@ -32,7 +32,7 @@ public PackageInstallationException(string message) : base(message) /// /// The message. /// The inner. - public PackageInstallationException(string message, Exception inner) : base(message, inner) + public PackageInstallationException(string? message, Exception? inner) : base(message, inner) { } diff --git a/AdvancedSharpAdbClient/Exceptions/PermissionDeniedException.cs b/AdvancedSharpAdbClient/Exceptions/PermissionDeniedException.cs index 4a39bf6f..1fa9d0b0 100644 --- a/AdvancedSharpAdbClient/Exceptions/PermissionDeniedException.cs +++ b/AdvancedSharpAdbClient/Exceptions/PermissionDeniedException.cs @@ -23,7 +23,16 @@ public PermissionDeniedException() : base("Permission to access the specified re /// Initializes a new instance of the class. /// /// The message. - public PermissionDeniedException(string message) : base(message) + public PermissionDeniedException(string? message) : base(message) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + /// The inner exception. + public PermissionDeniedException(string? message, Exception? innerException) : base(message, innerException) { } @@ -40,14 +49,5 @@ public PermissionDeniedException(SerializationInfo serializationInfo, StreamingC { } #endif - - /// - /// Initializes a new instance of the class. - /// - /// The message. - /// The inner exception. - public PermissionDeniedException(string message, Exception innerException) : base(message, innerException) - { - } } } diff --git a/AdvancedSharpAdbClient/Exceptions/ShellCommandUnresponsiveException.cs b/AdvancedSharpAdbClient/Exceptions/ShellCommandUnresponsiveException.cs index 043fea9a..a5bba4c7 100644 --- a/AdvancedSharpAdbClient/Exceptions/ShellCommandUnresponsiveException.cs +++ b/AdvancedSharpAdbClient/Exceptions/ShellCommandUnresponsiveException.cs @@ -24,7 +24,7 @@ public ShellCommandUnresponsiveException() : base("The shell command has become /// specified error message. /// /// The message that describes the error. - public ShellCommandUnresponsiveException(string message) : base(message) + public ShellCommandUnresponsiveException(string? message) : base(message) { } @@ -33,7 +33,7 @@ public ShellCommandUnresponsiveException(string message) : base(message) /// reference to the inner exception that is the cause of this exception. /// /// The exception that is the cause of the current exception, or if no inner exception is specified. - public ShellCommandUnresponsiveException(Exception inner) : base("The shell command has become unresponsive", inner) + public ShellCommandUnresponsiveException(Exception? inner) : base("The shell command has become unresponsive", inner) { } @@ -44,7 +44,7 @@ public ShellCommandUnresponsiveException(Exception inner) : base("The shell comm /// /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, or if no inner exception is specified. - public ShellCommandUnresponsiveException(string message, Exception inner) : base(message, inner) + public ShellCommandUnresponsiveException(string? message, Exception? inner) : base(message, inner) { } diff --git a/AdvancedSharpAdbClient/Exceptions/UnknownOptionException.cs b/AdvancedSharpAdbClient/Exceptions/UnknownOptionException.cs index 0816fb5e..18a9c96f 100644 --- a/AdvancedSharpAdbClient/Exceptions/UnknownOptionException.cs +++ b/AdvancedSharpAdbClient/Exceptions/UnknownOptionException.cs @@ -23,7 +23,16 @@ public UnknownOptionException() : base("Unknown option.") /// Initializes a new instance of the class. /// /// The message. - public UnknownOptionException(string message) : base(message) + public UnknownOptionException(string? message) : base(message) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + /// The inner exception. + public UnknownOptionException(string? message, Exception? innerException) : base(message, innerException) { } @@ -40,14 +49,5 @@ public UnknownOptionException(SerializationInfo serializationInfo, StreamingCont { } #endif - - /// - /// Initializes a new instance of the class. - /// - /// The message. - /// The inner exception. - public UnknownOptionException(string message, Exception innerException) : base(message, innerException) - { - } } } diff --git a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs index 4de105f4..03decccc 100644 --- a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs +++ b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs @@ -31,7 +31,7 @@ public static partial class AdbClientExtensions /// If your requested to start forwarding to local port TCP:0, the port number of the TCP port /// which has been opened. In all other cases, 0. public static Task CreateForwardAsync(this IAdbClient client, DeviceData device, ForwardSpec local, ForwardSpec remote, bool allowRebind, CancellationToken cancellationToken = default) => - client.CreateForwardAsync(device, local?.ToString(), remote?.ToString(), allowRebind, cancellationToken); + client.CreateForwardAsync(device, local?.ToString()!, remote?.ToString()!, allowRebind, cancellationToken); /// /// Creates a port forwarding between a local and a remote port. @@ -79,7 +79,7 @@ public static Task CreateForwardAsync(this IAdbClient client, DeviceData de /// If your requested to start reverse to remote port TCP:0, the port number of the TCP port /// which has been opened. In all other cases, 0. public static Task CreateReverseForwardAsync(this IAdbClient client, DeviceData device, ForwardSpec remote, ForwardSpec local, bool allowRebind, CancellationToken cancellationToken = default) => - client.CreateReverseForwardAsync(device, remote?.ToString(), local?.ToString(), allowRebind, cancellationToken); + client.CreateReverseForwardAsync(device, remote?.ToString()!, local?.ToString()!, allowRebind, cancellationToken); /// /// Remove a reverse port forwarding between a remote and a local port. @@ -90,7 +90,7 @@ public static Task CreateReverseForwardAsync(this IAdbClient client, Device /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. public static Task RemoveReverseForwardAsync(this IAdbClient client, DeviceData device, ForwardSpec remote, CancellationToken cancellationToken = default) => - client.RemoveReverseForwardAsync(device, remote?.ToString(), cancellationToken); + client.RemoveReverseForwardAsync(device, remote?.ToString()!, cancellationToken); /// /// Executes a command on the adb server. @@ -221,19 +221,8 @@ public static Task PairAsync(this IAdbClient client, IPEndPoint endpoint /// The pairing code. /// A which can be used to cancel the asynchronous operation. /// The results from adb. - public static Task PairAsync(this IAdbClient client, string host, string code, CancellationToken cancellationToken = default) - { - if (string.IsNullOrEmpty(host)) - { - throw new ArgumentNullException(nameof(host)); - } - - string[] values = host.Split(':'); - - return values.Length <= 0 - ? throw new ArgumentNullException(nameof(host)) - : client.PairAsync(new DnsEndPoint(values[0], values.Length > 1 && int.TryParse(values[1], out int port) ? port : AdbClient.DefaultPort), code, cancellationToken); - } + public static Task PairAsync(this IAdbClient client, string host, string code, CancellationToken cancellationToken = default) => + client.PairAsync(Extensions.CreateDnsEndPoint(host, AdbClient.DefaultPort), code, cancellationToken); /// /// Pair with a device for secure TCP/IP communication. @@ -244,19 +233,8 @@ public static Task PairAsync(this IAdbClient client, string host, string /// The pairing code. /// A which can be used to cancel the asynchronous operation. /// The results from adb. - public static Task PairAsync(this IAdbClient client, string host, int port, string code, CancellationToken cancellationToken = default) - { - if (string.IsNullOrEmpty(host)) - { - throw new ArgumentNullException(nameof(host)); - } - - string[] values = host.Split(':'); - - return values.Length <= 0 - ? throw new ArgumentNullException(nameof(host)) - : client.PairAsync(new DnsEndPoint(values[0], values.Length > 1 && int.TryParse(values[1], out int _port) ? _port : port), code, cancellationToken); - } + public static Task PairAsync(this IAdbClient client, string host, int port, string code, CancellationToken cancellationToken = default) => + client.PairAsync(Extensions.CreateDnsEndPoint(host, port), code, cancellationToken); /// /// Connect to a device via TCP/IP. @@ -290,19 +268,8 @@ public static Task ConnectAsync(this IAdbClient client, IPEndPoint endpo /// The port of the remote device. /// A which can be used to cancel the asynchronous operation. /// A which return the results from adb. - public static Task ConnectAsync(this IAdbClient client, string host, int port = AdbClient.DefaultPort, CancellationToken cancellationToken = default) - { - if (string.IsNullOrEmpty(host)) - { - throw new ArgumentNullException(nameof(host)); - } - - string[] values = host.Split(':'); - - return values.Length <= 0 - ? throw new ArgumentNullException(nameof(host)) - : client.ConnectAsync(new DnsEndPoint(values[0], values.Length > 1 && int.TryParse(values[1], out int _port) ? _port : port), cancellationToken); - } + public static Task ConnectAsync(this IAdbClient client, string host, int port = AdbClient.DefaultPort, CancellationToken cancellationToken = default) => + client.ConnectAsync(Extensions.CreateDnsEndPoint(host, port), cancellationToken); /// /// Asynchronously installs an Android application on an device. @@ -346,7 +313,7 @@ public static Task InstallMultipleAsync(this IAdbClient client, DeviceData devic /// The package name of the baseAPK to install. /// The arguments to pass to adb install-create. /// A which return the session ID - public static Task InstallCreateAsync(this IAdbClient client, DeviceData device, string packageName = null, params string[] arguments) => + public static Task InstallCreateAsync(this IAdbClient client, DeviceData device, string? packageName = null, params string[] arguments) => client.InstallCreateAsync(device, packageName, default, arguments); /// @@ -360,7 +327,7 @@ public static Task InstallCreateAsync(this IAdbClient client, DeviceData public static async Task ClearInputAsync(this IAdbClient client, DeviceData device, int charCount, CancellationToken cancellationToken = default) { await client.SendKeyEventAsync(device, "KEYCODE_MOVE_END", cancellationToken).ConfigureAwait(false); - await client.SendKeyEventAsync(device, StringExtensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL", charCount)), cancellationToken).ConfigureAwait(false); + await client.SendKeyEventAsync(device, StringExtensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL", charCount)), cancellationToken).ConfigureAwait(false); } /// diff --git a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.cs b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.cs index 71a85734..f2d77896 100644 --- a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.cs +++ b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.cs @@ -27,7 +27,7 @@ public static partial class AdbClientExtensions /// If your requested to start forwarding to local port TCP:0, the port number of the TCP port /// which has been opened. In all other cases, 0. public static int CreateForward(this IAdbClient client, DeviceData device, ForwardSpec local, ForwardSpec remote, bool allowRebind) => - client.CreateForward(device, local?.ToString(), remote?.ToString(), allowRebind); + client.CreateForward(device, local?.ToString()!, remote?.ToString()!, allowRebind); /// /// Asks the ADB server to reverse forward local connections from @@ -42,7 +42,7 @@ public static int CreateForward(this IAdbClient client, DeviceData device, Forwa /// If your requested to start reverse to remote port TCP:0, the port number of the TCP port /// which has been opened. In all other cases, 0. public static int CreateReverseForward(this IAdbClient client, DeviceData device, ForwardSpec remote, ForwardSpec local, bool allowRebind) => - client.CreateReverseForward(device, remote?.ToString(), local?.ToString(), allowRebind); + client.CreateReverseForward(device, remote?.ToString()!, local?.ToString()!, allowRebind); /// /// Remove a reverse port forwarding between a remote and a local port. @@ -50,8 +50,8 @@ public static int CreateReverseForward(this IAdbClient client, DeviceData device /// An instance of a class that implements the interface. /// The device on which to remove the reverse port forwarding /// Specification of the remote that was forwarded - public static void RemoveReverseForward(this IAdbClient client, DeviceData device, string remote) => - client.RemoveReverseForward(device, remote); + public static void RemoveReverseForward(this IAdbClient client, DeviceData device, ForwardSpec remote) => + client.RemoveReverseForward(device, remote?.ToString()!); /// /// Executes a command on the adb server. @@ -181,19 +181,8 @@ public static string Pair(this IAdbClient client, IPEndPoint endpoint, string co /// The host address of the remote device. /// The pairing code. /// The results from adb. - public static string Pair(this IAdbClient client, string host, string code) - { - if (string.IsNullOrEmpty(host)) - { - throw new ArgumentNullException(nameof(host)); - } - - string[] values = host.Split(':'); - - return values.Length <= 0 - ? throw new ArgumentNullException(nameof(host)) - : client.Pair(new DnsEndPoint(values[0], values.Length > 1 && int.TryParse(values[1], out int port) ? port : AdbClient.DefaultPort), code); - } + public static string Pair(this IAdbClient client, string host, string code) => + client.Pair(Extensions.CreateDnsEndPoint(host, AdbClient.DefaultPort), code); /// /// Pair with a device for secure TCP/IP communication. @@ -203,19 +192,8 @@ public static string Pair(this IAdbClient client, string host, string code) /// The port of the remote device. /// The pairing code. /// The results from adb. - public static string Pair(this IAdbClient client, string host, int port, string code) - { - if (string.IsNullOrEmpty(host)) - { - throw new ArgumentNullException(nameof(host)); - } - - string[] values = host.Split(':'); - - return values.Length <= 0 - ? throw new ArgumentNullException(nameof(host)) - : client.Pair(new DnsEndPoint(values[0], values.Length > 1 && int.TryParse(values[1], out int _port) ? _port : port), code); - } + public static string Pair(this IAdbClient client, string host, int port, string code) => + client.Pair(Extensions.CreateDnsEndPoint(host, port), code); /// /// Connect to a device via TCP/IP. @@ -246,19 +224,8 @@ public static string Connect(this IAdbClient client, IPEndPoint endpoint) => /// The host address of the remote device. /// The port of the remote device. /// The results from adb. - public static string Connect(this IAdbClient client, string host, int port = AdbClient.DefaultPort) - { - if (string.IsNullOrEmpty(host)) - { - throw new ArgumentNullException(nameof(host)); - } - - string[] values = host.Split(':'); - - return values.Length <= 0 - ? throw new ArgumentNullException(nameof(host)) - : client.Connect(new DnsEndPoint(values[0], values.Length > 1 && int.TryParse(values[1], out int _port) ? _port : port)); - } + public static string Connect(this IAdbClient client, string host, int port = AdbClient.DefaultPort) => + client.Connect(Extensions.CreateDnsEndPoint(host, port)); /// /// Clear the input text. The input should be in focus. Use if the element isn't focused. @@ -269,7 +236,7 @@ public static string Connect(this IAdbClient client, string host, int port = Adb public static void ClearInput(this IAdbClient client, DeviceData device, int charCount) { client.SendKeyEvent(device, "KEYCODE_MOVE_END"); - client.SendKeyEvent(device, StringExtensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL", charCount))); + client.SendKeyEvent(device, StringExtensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL", charCount))); } /// diff --git a/AdvancedSharpAdbClient/Extensions/Attributes/DoesNotReturnAttribute.cs b/AdvancedSharpAdbClient/Extensions/Attributes/DoesNotReturnAttribute.cs deleted file mode 100644 index 574e30e1..00000000 --- a/AdvancedSharpAdbClient/Extensions/Attributes/DoesNotReturnAttribute.cs +++ /dev/null @@ -1,16 +0,0 @@ -#if !HAS_INDEXRANGE -namespace System.Diagnostics.CodeAnalysis -{ - /// - /// Specifies that a method that will never return under any circumstance. - /// - [AttributeUsage(AttributeTargets.Method, Inherited = false)] - internal sealed class DoesNotReturnAttribute : Attribute - { - /// - /// Initializes a new instance of the class. - /// - public DoesNotReturnAttribute() { } - } -} -#endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Extensions/Attributes/DoesNotReturnIfAttribute.cs b/AdvancedSharpAdbClient/Extensions/Attributes/DoesNotReturnIfAttribute.cs deleted file mode 100644 index 1f1a8345..00000000 --- a/AdvancedSharpAdbClient/Extensions/Attributes/DoesNotReturnIfAttribute.cs +++ /dev/null @@ -1,25 +0,0 @@ -#if !HAS_INDEXRANGE -// 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 method will not return if the associated parameter is passed the specified value. - /// - 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 \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Extensions/Attributes/NullableAttributes.cs b/AdvancedSharpAdbClient/Extensions/Attributes/NullableAttributes.cs new file mode 100644 index 00000000..adc36a56 --- /dev/null +++ b/AdvancedSharpAdbClient/Extensions/Attributes/NullableAttributes.cs @@ -0,0 +1,180 @@ +#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/Extensions/ExceptionExtensions.cs b/AdvancedSharpAdbClient/Extensions/ExceptionExtensions.cs index 794f6250..3c8d55b0 100644 --- a/AdvancedSharpAdbClient/Extensions/ExceptionExtensions.cs +++ b/AdvancedSharpAdbClient/Extensions/ExceptionExtensions.cs @@ -12,7 +12,7 @@ internal static class ExceptionExtensions /// /// The reference type argument to validate as non-null. /// The name of the parameter with which corresponds. - public static void ThrowIfNull(object argument, [CallerArgumentExpression(nameof(argument))] string paramName = null) + public static void ThrowIfNull([NotNull] object argument, [CallerArgumentExpression(nameof(argument))] string? paramName = null) { #if NET6_0_OR_GREATER ArgumentNullException.ThrowIfNull(argument, paramName); @@ -30,7 +30,7 @@ public static void ThrowIfNull(object argument, [CallerArgumentExpression(nameof /// The argument to validate as less or equal than . /// The value to compare with . /// The name of the parameter with which corresponds. - public static void ThrowIfGreaterThan(T value, T other, [CallerArgumentExpression(nameof(value))] string paramName = null) + public static void ThrowIfGreaterThan(T value, T other, [CallerArgumentExpression(nameof(value))] string? paramName = null) where T : IComparable { #if NET8_0_OR_GREATER @@ -49,7 +49,7 @@ public static void ThrowIfGreaterThan(T value, T other, [CallerArgumentExpres /// The argument to validate as greater than or equal than . /// The value to compare with . /// The name of the parameter with which corresponds. - public static void ThrowIfLessThan(T value, T other, [CallerArgumentExpression(nameof(value))] string paramName = null) + public static void ThrowIfLessThan(T value, T other, [CallerArgumentExpression(nameof(value))] string? paramName = null) where T : IComparable { #if NET8_0_OR_GREATER @@ -68,12 +68,12 @@ public static void ThrowIfLessThan(T value, T other, [CallerArgumentExpressio /// The argument to validate as non-negative. /// The name of the parameter with which corresponds. #if NET8_0_OR_GREATER - public static void ThrowIfNegative(T value, [CallerArgumentExpression(nameof(value))] string paramName = null) + public static void ThrowIfNegative(T value, [CallerArgumentExpression(nameof(value))] string? paramName = null) where T : INumberBase { ArgumentOutOfRangeException.ThrowIfNegative(value, paramName); #else - public static void ThrowIfNegative(int value, [CallerArgumentExpression(nameof(value))] string paramName = null) + public static void ThrowIfNegative(int value, [CallerArgumentExpression(nameof(value))] string? paramName = null) { if (value < 0) { diff --git a/AdvancedSharpAdbClient/Extensions/Extensions.cs b/AdvancedSharpAdbClient/Extensions/Extensions.cs index 114c7e1d..35fb616e 100644 --- a/AdvancedSharpAdbClient/Extensions/Extensions.cs +++ b/AdvancedSharpAdbClient/Extensions/Extensions.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net; using System.Runtime.InteropServices; using System.Text; using System.Threading; @@ -49,6 +50,26 @@ public static void AddRange(this ICollection source, IEnumerab } } + /// + /// Creates a from the specified host and port information. + /// + /// The host address. + /// The port. + /// The created from the specified host and port information. + public static DnsEndPoint CreateDnsEndPoint(string host, int port) + { + if (string.IsNullOrEmpty(host)) + { + throw new ArgumentNullException(nameof(host)); + } + + string[] values = host.Split(':'); + + return values.Length <= 0 + ? throw new ArgumentNullException(nameof(host)) + : new DnsEndPoint(values[0], values.Length > 1 && int.TryParse(values[1], out int _port) ? _port : port); + } + /// /// Converts the string representation of the name or numeric value of one or more /// enumerated constants to an equivalent enumerated object. A parameter specifies @@ -216,7 +237,7 @@ public static Task WhenAll(IEnumerable> tasks) /// 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 reader.ReadLineAsync(); #else diff --git a/AdvancedSharpAdbClient/Extensions/Factories.cs b/AdvancedSharpAdbClient/Extensions/Factories.cs index 33cc5889..538bea74 100644 --- a/AdvancedSharpAdbClient/Extensions/Factories.cs +++ b/AdvancedSharpAdbClient/Extensions/Factories.cs @@ -3,6 +3,7 @@ // using System; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Net; @@ -49,6 +50,12 @@ public static class Factories /// /// Resets all factories to their default values. /// + [MemberNotNull( + nameof(CheckFileExists), + nameof(AdbSocketFactory), + nameof(AdbClientFactory), + nameof(AdbCommandLineClientFactory), + nameof(SyncServiceFactory))] public static void Reset() { CheckFileExists = File.Exists; diff --git a/AdvancedSharpAdbClient/Extensions/LoggerExtensions.cs b/AdvancedSharpAdbClient/Extensions/LoggerExtensions.cs index ac88cf8f..6f7e31e4 100644 --- a/AdvancedSharpAdbClient/Extensions/LoggerExtensions.cs +++ b/AdvancedSharpAdbClient/Extensions/LoggerExtensions.cs @@ -22,7 +22,7 @@ public static class LoggerExtensions /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogDebug(exception, "Error while processing request from {Address}", address) - public static void LogDebug(this ILogger logger, Exception exception, string message, params object[] args) => + public static void LogDebug(this ILogger logger, Exception? exception, string? message, params object?[] args) => logger.Log(LogLevel.Debug, exception, message, args); /// @@ -32,7 +32,7 @@ public static void LogDebug(this ILogger logger, Exception exception, string mes /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogDebug("Processing request from {Address}", address) - public static void LogDebug(this ILogger logger, string message, params object[] args) => + public static void LogDebug(this ILogger logger, string? message, params object?[] args) => logger.Log(LogLevel.Debug, message, args); //------------------------------------------TRACE------------------------------------------// @@ -45,7 +45,7 @@ public static void LogDebug(this ILogger logger, string message, params object[] /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogTrace(exception, "Error while processing request from {Address}", address) - public static void LogTrace(this ILogger logger, Exception exception, string message, params object[] args) => + public static void LogTrace(this ILogger logger, Exception? exception, string? message, params object?[] args) => logger.Log(LogLevel.Trace, exception, message, args); /// @@ -55,7 +55,7 @@ public static void LogTrace(this ILogger logger, Exception exception, string mes /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogTrace("Processing request from {Address}", address) - public static void LogTrace(this ILogger logger, string message, params object[] args) => + public static void LogTrace(this ILogger logger, string? message, params object?[] args) => logger.Log(LogLevel.Trace, message, args); //------------------------------------------INFORMATION------------------------------------------// @@ -68,7 +68,7 @@ public static void LogTrace(this ILogger logger, string message, params object[] /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogInformation(exception, "Error while processing request from {Address}", address) - public static void LogInformation(this ILogger logger, Exception exception, string message, params object[] args) => + public static void LogInformation(this ILogger logger, Exception? exception, string? message, params object?[] args) => logger.Log(LogLevel.Information, exception, message, args); /// @@ -78,7 +78,7 @@ public static void LogInformation(this ILogger logger, Exception exception, stri /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogInformation("Processing request from {Address}", address) - public static void LogInformation(this ILogger logger, string message, params object[] args) => + public static void LogInformation(this ILogger logger, string? message, params object?[] args) => logger.Log(LogLevel.Information, message, args); //------------------------------------------WARNING------------------------------------------// @@ -91,7 +91,7 @@ public static void LogInformation(this ILogger logger, string message, params ob /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogWarning(exception, "Error while processing request from {Address}", address) - public static void LogWarning(this ILogger logger, Exception exception, string message, params object[] args) => + public static void LogWarning(this ILogger logger, Exception? exception, string? message, params object?[] args) => logger.Log(LogLevel.Warning, exception, message, args); /// @@ -101,7 +101,7 @@ public static void LogWarning(this ILogger logger, Exception exception, string m /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogWarning("Processing request from {Address}", address) - public static void LogWarning(this ILogger logger, string message, params object[] args) => + public static void LogWarning(this ILogger logger, string? message, params object?[] args) => logger.Log(LogLevel.Warning, message, args); //------------------------------------------ERROR------------------------------------------// @@ -114,7 +114,7 @@ public static void LogWarning(this ILogger logger, string message, params object /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogError(exception, "Error while processing request from {Address}", address) - public static void LogError(this ILogger logger, Exception exception, string message, params object[] args) => + public static void LogError(this ILogger logger, Exception? exception, string? message, params object?[] args) => logger.Log(LogLevel.Error, exception, message, args); /// @@ -124,7 +124,7 @@ public static void LogError(this ILogger logger, Exception exception, string mes /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogError("Processing request from {Address}", address) - public static void LogError(this ILogger logger, string message, params object[] args) => + public static void LogError(this ILogger logger, string? message, params object?[] args) => logger.Log(LogLevel.Error, message, args); //------------------------------------------CRITICAL------------------------------------------// @@ -137,7 +137,7 @@ public static void LogError(this ILogger logger, string message, params object[] /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogCritical(exception, "Error while processing request from {Address}", address) - public static void LogCritical(this ILogger logger, Exception exception, string message, params object[] args) => + public static void LogCritical(this ILogger logger, Exception? exception, string? message, params object?[] args) => logger.Log(LogLevel.Critical, exception, message, args); /// @@ -147,7 +147,7 @@ public static void LogCritical(this ILogger logger, Exception exception, string /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" /// An object array that contains zero or more objects to format. /// logger.LogCritical("Processing request from {Address}", address) - public static void LogCritical(this ILogger logger, string message, params object[] args) => + public static void LogCritical(this ILogger logger, string? message, params object?[] args) => logger.Log(LogLevel.Critical, message, args); /// @@ -157,7 +157,7 @@ public static void LogCritical(this ILogger logger, string message, params objec /// Entry will be written on this level. /// Format string of the log message. /// An object array that contains zero or more objects to format. - public static void Log(this ILogger logger, LogLevel logLevel, string message, params object[] args) => + public static void Log(this ILogger logger, LogLevel logLevel, string? message, params object?[] args) => logger.Log(logLevel, null, message, args); } } \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Extensions/StreamExtensions.cs b/AdvancedSharpAdbClient/Extensions/StreamExtensions.cs index 2f0fe1be..1323bb96 100644 --- a/AdvancedSharpAdbClient/Extensions/StreamExtensions.cs +++ b/AdvancedSharpAdbClient/Extensions/StreamExtensions.cs @@ -180,13 +180,13 @@ public static Task WriteAsync(this Stream stream, byte[] buffer, int offset, int // and convert to a TaskCancelledException - which is the exception we expect. CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.Register(stream.Close); - TaskCompletionSource taskCompletionSource = new(stream); + TaskCompletionSource taskCompletionSource = new(stream); IAsyncResult asyncResult = stream.BeginWrite(buffer, offset, count, iar => { // this is the callback - TaskCompletionSource taskCompletionSource = (TaskCompletionSource)iar.AsyncState; + TaskCompletionSource taskCompletionSource = (TaskCompletionSource)iar.AsyncState; Stream stream = (Stream)taskCompletionSource.Task.AsyncState; try diff --git a/AdvancedSharpAdbClient/Extensions/StringExtensions.cs b/AdvancedSharpAdbClient/Extensions/StringExtensions.cs index b2fec310..1a67370e 100644 --- a/AdvancedSharpAdbClient/Extensions/StringExtensions.cs +++ b/AdvancedSharpAdbClient/Extensions/StringExtensions.cs @@ -15,7 +15,7 @@ public static class StringExtensions /// The string to test. /// if the parameter is or /// , or if consists exclusively of white-space characters. - public static bool IsNullOrWhiteSpace(string value) + public static bool IsNullOrWhiteSpace(string? value) { #if NETFRAMEWORK && !NET40_OR_GREATER if (value == null) @@ -99,14 +99,14 @@ public static string[] Split(this string text, char separator, int count, String /// A collection that contains the strings to concatenate. /// A string that consists of the elements of delimited by the /// string.-or- if values has zero elements. - public static string Join(string separator, IEnumerable values) + public static string Join(string? separator, IEnumerable values) { #if NETFRAMEWORK && !NET40_OR_GREATER ExceptionExtensions.ThrowIfNull(values); separator ??= string.Empty; - using IEnumerator en = values.GetEnumerator(); + using IEnumerator en = values.GetEnumerator(); if (!en.MoveNext()) { return string.Empty; diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs b/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs index 59160ca6..bb3e8f7a 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs @@ -345,7 +345,7 @@ public partial interface IAdbClient /// A which can be used to cancel the asynchronous operation. /// The arguments to pass to adb install-create. /// A which return the session ID - Task InstallCreateAsync(DeviceData device, string packageName, CancellationToken cancellationToken, params string[] arguments); + Task InstallCreateAsync(DeviceData device, string? packageName, CancellationToken cancellationToken, params string[] arguments); /// /// Write an apk into the given install session. @@ -390,7 +390,7 @@ public partial interface IAdbClient /// The device for which to get the screen snapshot. /// A which can be used to cancel the asynchronous operation. /// A which return a containing current hierarchy. - Task DumpScreenAsync(DeviceData device, CancellationToken cancellationToken); + Task DumpScreenAsync(DeviceData device, CancellationToken cancellationToken); #if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER /// @@ -399,7 +399,7 @@ public partial interface IAdbClient /// The device for which to get the screen snapshot. /// A which can be used to cancel the asynchronous operation. /// A which return a containing current hierarchy. - Task DumpScreenWinRTAsync(DeviceData device, CancellationToken cancellationToken); + Task DumpScreenWinRTAsync(DeviceData device, CancellationToken cancellationToken); #endif /// @@ -480,7 +480,7 @@ public partial interface IAdbClient /// A which can be used to cancel the asynchronous operation. /// Only check once if . Or it will continue check until is . /// A which return the of . - Task FindElementAsync(DeviceData device, string xpath, CancellationToken cancellationToken); + Task FindElementAsync(DeviceData device, string xpath, CancellationToken cancellationToken); /// /// Get elements by xpath asynchronously. You can specify the waiting time in timeout. diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs b/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs index d875d63a..12b48fe4 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs @@ -351,7 +351,7 @@ public partial interface IAdbClient /// The package name of the baseAPK to install. /// The arguments to pass to adb install-create. /// The session ID of this install session. - string InstallCreate(DeviceData device, string packageName = null, params string[] arguments); + string InstallCreate(DeviceData device, string? packageName = null, params string[] arguments); /// /// Write an apk into the given install session. @@ -389,7 +389,7 @@ public partial interface IAdbClient /// /// The device for which to get the screen snapshot. /// A containing current hierarchy. - XmlDocument DumpScreen(DeviceData device); + XmlDocument? DumpScreen(DeviceData device); #if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER /// @@ -397,7 +397,7 @@ public partial interface IAdbClient /// /// The device for which to get the screen snapshot. /// A containing current hierarchy. - Windows.Data.Xml.Dom.XmlDocument DumpScreenWinRT(DeviceData device); + Windows.Data.Xml.Dom.XmlDocument? DumpScreenWinRT(DeviceData device); #endif /// @@ -467,7 +467,7 @@ public partial interface IAdbClient /// The timeout for waiting the element. /// Only check once if or . /// The of . - Element FindElement(DeviceData device, string xpath, TimeSpan timeout = default); + Element? FindElement(DeviceData device, string xpath, TimeSpan timeout = default); /// /// Get elements by xpath. You can specify the waiting time in timeout. diff --git a/AdvancedSharpAdbClient/Interfaces/IDeviceMonitor.cs b/AdvancedSharpAdbClient/Interfaces/IDeviceMonitor.cs index 1a84a083..1fde9832 100644 --- a/AdvancedSharpAdbClient/Interfaces/IDeviceMonitor.cs +++ b/AdvancedSharpAdbClient/Interfaces/IDeviceMonitor.cs @@ -18,27 +18,27 @@ public partial interface IDeviceMonitor : IDisposable /// /// Occurs when the status of one of the connected devices has changed. /// - event EventHandler DeviceChanged; + event EventHandler? DeviceChanged; /// /// Occurs when received a list of device from the Android Debug Bridge. /// - event EventHandler DeviceNotified; + event EventHandler? DeviceNotified; /// /// Occurs when a device has connected to the Android Debug Bridge. /// - event EventHandler DeviceConnected; + event EventHandler? DeviceConnected; /// /// Occurs when the list of the connected devices has changed. /// - event EventHandler DeviceListChanged; + event EventHandler? DeviceListChanged; /// /// Occurs when a device has disconnected from the Android Debug Bridge. /// - event EventHandler DeviceDisconnected; + event EventHandler? DeviceDisconnected; /// /// Gets the devices that are currently connected to the Android Debug Bridge. diff --git a/AdvancedSharpAdbClient/Interfaces/ISyncService.Async.cs b/AdvancedSharpAdbClient/Interfaces/ISyncService.Async.cs index 0c6c81a3..0e52b36d 100644 --- a/AdvancedSharpAdbClient/Interfaces/ISyncService.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/ISyncService.Async.cs @@ -22,7 +22,7 @@ public partial interface ISyncService /// An optional parameter which, when specified, returns progress notifications. The progress is reported as a value between 0 and 100, representing the percentage of the file which has been transferred. /// A that can be used to cancel the task. /// A which represents the asynchronous operation. - Task PushAsync(Stream stream, string remotePath, int permissions, DateTimeOffset timestamp, IProgress progress, CancellationToken cancellationToken); + Task PushAsync(Stream stream, string remotePath, int permissions, DateTimeOffset timestamp, IProgress? progress, CancellationToken cancellationToken); /// /// Pulls (downloads) a file from the remote device. @@ -32,7 +32,7 @@ public partial interface ISyncService /// An optional parameter which, when specified, returns progress notifications. The progress is reported as a value between 0 and 100, representing the percentage of the file which has been transferred. /// A that can be used to cancel the task. /// A which represents the asynchronous operation. - Task PullAsync(string remotePath, Stream stream, IProgress progress, CancellationToken cancellationToken); + Task PullAsync(string remotePath, Stream stream, IProgress? progress, CancellationToken cancellationToken); /// /// Returns information about a file on the device. diff --git a/AdvancedSharpAdbClient/Interfaces/ISyncService.cs b/AdvancedSharpAdbClient/Interfaces/ISyncService.cs index 6118e67f..88a47bc5 100644 --- a/AdvancedSharpAdbClient/Interfaces/ISyncService.cs +++ b/AdvancedSharpAdbClient/Interfaces/ISyncService.cs @@ -30,7 +30,7 @@ public partial interface ISyncService : IDisposable /// The time at which the file was last modified. /// An optional parameter which, when specified, returns progress notifications. The progress is reported as a value between 0 and 100, representing the percentage of the file which has been transferred. /// A that can be used to cancel the task. - void Push(Stream stream, string remotePath, int permissions, DateTimeOffset timestamp, IProgress progress, in bool isCancelled); + void Push(Stream stream, string remotePath, int permissions, DateTimeOffset timestamp, IProgress? progress, in bool isCancelled); /// /// Pulls (downloads) a file from the remote device. @@ -39,7 +39,7 @@ public partial interface ISyncService : IDisposable /// A that will receive the contents of the file. /// An optional parameter which, when specified, returns progress notifications. The progress is reported as a value between 0 and 100, representing the percentage of the file which has been transferred. /// A that can be used to cancel the task. - void Pull(string remotePath, Stream stream, IProgress progress, in bool isCancelled); + void Pull(string remotePath, Stream stream, IProgress? progress, in bool isCancelled); /// /// Returns information about a file on the device. @@ -63,6 +63,6 @@ public partial interface ISyncService : IDisposable /// /// Occurs when there is a change in the status of the sync. /// - event EventHandler SyncProgressChanged; + event EventHandler? SyncProgressChanged; } } diff --git a/AdvancedSharpAdbClient/Logs/AndroidLogEntry.cs b/AdvancedSharpAdbClient/Logs/AndroidLogEntry.cs index 8a5dfcf7..1531cf5a 100644 --- a/AdvancedSharpAdbClient/Logs/AndroidLogEntry.cs +++ b/AdvancedSharpAdbClient/Logs/AndroidLogEntry.cs @@ -39,12 +39,12 @@ public AndroidLogEntry() { } /// Gets or sets the log tag of the message. Used to identify the source of a log message. /// It usually identifies the class or activity where the log call occurred. /// - public string Tag { get; set; } + public string Tag { get; set; } = string.Empty; /// /// Gets or sets the message that has been logged. /// - public string Message { get; set; } + public string Message { get; set; } = string.Empty; /// public override string ToString() => @@ -56,6 +56,6 @@ public override string ToString() => /// The value to convert. /// A that represents in the system log. private static char FormatPriority(Priority value) => - PriorityFormatters?.TryGetValue(value, out char result) == true ? result : '?'; + PriorityFormatters.TryGetValue(value, out char result) == true ? result : '?'; } } diff --git a/AdvancedSharpAdbClient/Logs/Interfaces/ILogger.cs b/AdvancedSharpAdbClient/Logs/Interfaces/ILogger.cs index fa3460e8..cd305358 100644 --- a/AdvancedSharpAdbClient/Logs/Interfaces/ILogger.cs +++ b/AdvancedSharpAdbClient/Logs/Interfaces/ILogger.cs @@ -19,7 +19,7 @@ public interface ILogger /// The exception to log. /// Format string of the log message. /// An object array that contains zero or more objects to format. - void Log(LogLevel logLevel, Exception exception, string message, params object[] args); + void Log(LogLevel logLevel, Exception? exception, string? message, params object?[] args); } /// diff --git a/AdvancedSharpAdbClient/Logs/LogEntry.cs b/AdvancedSharpAdbClient/Logs/LogEntry.cs index d4aa810c..4c232140 100644 --- a/AdvancedSharpAdbClient/Logs/LogEntry.cs +++ b/AdvancedSharpAdbClient/Logs/LogEntry.cs @@ -48,6 +48,6 @@ public LogEntry() { } /// /// Gets or sets the entry's payload. /// - public byte[] Data { get; set; } + public byte[] Data { get; set; } = []; } } diff --git a/AdvancedSharpAdbClient/Logs/LogReader.Async.cs b/AdvancedSharpAdbClient/Logs/LogReader.Async.cs index f03f732b..2124bce2 100644 --- a/AdvancedSharpAdbClient/Logs/LogReader.Async.cs +++ b/AdvancedSharpAdbClient/Logs/LogReader.Async.cs @@ -18,7 +18,7 @@ public partial class LogReader /// /// A which can be used to cancel the asynchronous operation. /// A which return a new object. - public async Task ReadEntryAsync(CancellationToken cancellationToken = default) + public async Task ReadEntryAsync(CancellationToken cancellationToken = default) { // Read the log data in binary format. This format is defined at // https://android.googlesource.com/platform/system/core/+/master/include/log/logger.h @@ -35,11 +35,11 @@ public async Task ReadEntryAsync(CancellationToken cancellationToken = return null; } - ushort payloadLength = payloadLengthValue.Value; - ushort headerSize = headerSizeValue.Value; - int pid = pidValue.Value; - int tid = tidValue.Value; - int sec = secValue.Value; + ushort payloadLength = payloadLengthValue!.Value; + ushort headerSize = headerSizeValue!.Value; + int pid = pidValue!.Value; + int tid = tidValue!.Value; + int sec = secValue!.Value; int nsec = nsecValue.Value; // If the headerSize is not 0, we have on of the logger_entry_v* objects. @@ -87,7 +87,7 @@ public async Task ReadEntryAsync(CancellationToken cancellationToken = } } - byte[] data = await ReadBytesSafeAsync(payloadLength, cancellationToken).ConfigureAwait(false); + byte[]? data = await ReadBytesSafeAsync(payloadLength, cancellationToken).ConfigureAwait(false); if (data == null) { @@ -181,26 +181,26 @@ or LogId.Radio } private async Task ReadUInt16Async(CancellationToken cancellationToken = default) { - byte[] data = await ReadBytesSafeAsync(2, cancellationToken).ConfigureAwait(false); + byte[]? data = await ReadBytesSafeAsync(2, cancellationToken).ConfigureAwait(false); return data == null ? null : BitConverter.ToUInt16(data, 0); } private async Task ReadUInt32Async(CancellationToken cancellationToken = default) { - byte[] data = await ReadBytesSafeAsync(4, cancellationToken).ConfigureAwait(false); + byte[]? data = await ReadBytesSafeAsync(4, cancellationToken).ConfigureAwait(false); return data == null ? null : BitConverter.ToUInt32(data, 0); } private async Task ReadInt32Async(CancellationToken cancellationToken = default) { - byte[] data = await ReadBytesSafeAsync(4, cancellationToken).ConfigureAwait(false); + byte[]? data = await ReadBytesSafeAsync(4, cancellationToken).ConfigureAwait(false); return data == null ? null : BitConverter.ToInt32(data, 0); } - private async Task ReadBytesSafeAsync(int count, CancellationToken cancellationToken = default) + private async Task ReadBytesSafeAsync(int count, CancellationToken cancellationToken = default) { int totalRead = 0; int read = 0; diff --git a/AdvancedSharpAdbClient/Logs/LogReader.cs b/AdvancedSharpAdbClient/Logs/LogReader.cs index 7dcca3d1..88dc0a00 100644 --- a/AdvancedSharpAdbClient/Logs/LogReader.cs +++ b/AdvancedSharpAdbClient/Logs/LogReader.cs @@ -25,7 +25,7 @@ public partial class LogReader(Stream stream) /// Reads the next from the stream. /// /// A new object. - public virtual LogEntry ReadEntry() + public virtual LogEntry? ReadEntry() { // Read the log data in binary format. This format is defined at // https://android.googlesource.com/platform/system/core/+/master/include/log/logger.h @@ -42,11 +42,11 @@ public virtual LogEntry ReadEntry() return null; } - ushort payloadLength = payloadLengthValue.Value; - ushort headerSize = headerSizeValue.Value; - int pid = pidValue.Value; - int tid = tidValue.Value; - int sec = secValue.Value; + ushort payloadLength = payloadLengthValue!.Value; + ushort headerSize = headerSizeValue!.Value; + int pid = pidValue!.Value; + int tid = tidValue!.Value; + int sec = secValue!.Value; int nsec = nsecValue.Value; // If the headerSize is not 0, we have on of the logger_entry_v* objects. @@ -93,7 +93,7 @@ public virtual LogEntry ReadEntry() } } - byte[] data = ReadBytesSafe(payloadLength); + byte[]? data = ReadBytesSafe(payloadLength); if (data == null) { @@ -231,7 +231,7 @@ protected void ReadLogEntry(BinaryReader reader, ICollection parent) /// protected ushort? ReadUInt16() { - byte[] data = ReadBytesSafe(2); + byte[]? data = ReadBytesSafe(2); return data == null ? null : BitConverter.ToUInt16(data, 0); } @@ -241,7 +241,7 @@ protected void ReadLogEntry(BinaryReader reader, ICollection parent) /// protected uint? ReadUInt32() { - byte[] data = ReadBytesSafe(4); + byte[]? data = ReadBytesSafe(4); return data == null ? null : BitConverter.ToUInt32(data, 0); } @@ -251,7 +251,7 @@ protected void ReadLogEntry(BinaryReader reader, ICollection parent) /// protected int? ReadInt32() { - byte[] data = ReadBytesSafe(4); + byte[]? data = ReadBytesSafe(4); return data == null ? null : BitConverter.ToInt32(data, 0); } @@ -260,7 +260,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. - protected byte[] ReadBytesSafe(int count) + protected byte[]? ReadBytesSafe(int count) { int totalRead = 0; byte[] data = new byte[count]; diff --git a/AdvancedSharpAdbClient/Logs/LoggerProvider.cs b/AdvancedSharpAdbClient/Logs/LoggerProvider.cs index f2bc3026..2f8c10ba 100644 --- a/AdvancedSharpAdbClient/Logs/LoggerProvider.cs +++ b/AdvancedSharpAdbClient/Logs/LoggerProvider.cs @@ -9,13 +9,13 @@ namespace AdvancedSharpAdbClient.Logs /// public static class LoggerProvider { - private static ILoggerFactory _loggerFactory = null; + private static ILoggerFactory? _loggerFactory = null; /// /// Sets the current log provider based on logger factory. /// /// The logger factory. - public static void SetLogProvider(ILoggerFactory loggerFactory) => _loggerFactory = loggerFactory; + public static void SetLogProvider(ILoggerFactory? loggerFactory) => _loggerFactory = loggerFactory; /// /// Creates a new instance. diff --git a/AdvancedSharpAdbClient/Logs/NullLogger.cs b/AdvancedSharpAdbClient/Logs/NullLogger.cs index 229254cc..48fa3bc5 100644 --- a/AdvancedSharpAdbClient/Logs/NullLogger.cs +++ b/AdvancedSharpAdbClient/Logs/NullLogger.cs @@ -17,7 +17,7 @@ public class NullLogger : ILogger public static NullLogger Instance { get; } = new(); /// - public void Log(LogLevel logLevel, Exception exception, string message, params object[] args) { } + public void Log(LogLevel logLevel, Exception? exception, string? message, params object?[] args) { } } /// diff --git a/AdvancedSharpAdbClient/Models/AdbResponse.cs b/AdvancedSharpAdbClient/Models/AdbResponse.cs index e0f2f76a..91ebe18f 100644 --- a/AdvancedSharpAdbClient/Models/AdbResponse.cs +++ b/AdvancedSharpAdbClient/Models/AdbResponse.cs @@ -3,6 +3,7 @@ // using System; +using System.Diagnostics.CodeAnalysis; namespace AdvancedSharpAdbClient { @@ -74,7 +75,7 @@ namespace AdvancedSharpAdbClient /// /// The to compare with the current object. /// if the specified object is equal to the current object; otherwise, . - public override readonly bool Equals(object obj) => obj is AdbResponse other && Equals(other); + public override readonly bool Equals([NotNullWhen(true)] object? obj) => obj is AdbResponse other && Equals(other); /// /// Determines whether the specified is equal to the current object. diff --git a/AdvancedSharpAdbClient/Models/AdbServerStatus.cs b/AdvancedSharpAdbClient/Models/AdbServerStatus.cs index e9f444d2..e114e021 100644 --- a/AdvancedSharpAdbClient/Models/AdbServerStatus.cs +++ b/AdvancedSharpAdbClient/Models/AdbServerStatus.cs @@ -11,7 +11,7 @@ namespace AdvancedSharpAdbClient /// /// The value indicating whether the server is currently running. /// The version of the server when it is running. - public readonly record struct AdbServerStatus(bool IsRunning, Version Version) + public readonly record struct AdbServerStatus(bool IsRunning, Version? Version) { /// /// Gets a value indicating whether the server is currently running. @@ -21,14 +21,14 @@ public readonly record struct AdbServerStatus(bool IsRunning, Version Version) /// /// Gets the version of the server when it is running. /// - public Version Version { get; init; } = Version; + public Version? Version { get; init; } = Version; /// /// Deconstruct the struct. /// /// The value indicating whether the server is currently running. /// The version of the server when it is running. - public readonly void Deconstruct(out bool isRunning, out Version version) + public readonly void Deconstruct(out bool isRunning, out Version? version) { isRunning = IsRunning; version = Version; diff --git a/AdvancedSharpAdbClient/Models/DeviceData.cs b/AdvancedSharpAdbClient/Models/DeviceData.cs index 43b94b68..fdd0bce2 100644 --- a/AdvancedSharpAdbClient/Models/DeviceData.cs +++ b/AdvancedSharpAdbClient/Models/DeviceData.cs @@ -3,6 +3,7 @@ // using System; +using System.Diagnostics.CodeAnalysis; using System.Text.RegularExpressions; namespace AdvancedSharpAdbClient @@ -56,7 +57,7 @@ public DeviceData(string data) /// /// Gets or sets the device serial number. /// - public string Serial { get; init; } + public string Serial { get; init; } = string.Empty; /// /// Gets or sets the device state. @@ -66,37 +67,37 @@ public DeviceData(string data) /// /// Gets or sets the device model name. /// - public string Model { get; init; } + public string Model { get; init; } = string.Empty; /// /// Gets or sets the device product name. /// - public string Product { get; init; } + public string Product { get; init; } = string.Empty; /// /// Gets or sets the device name. /// - public string Name { get; init; } + public string Name { get; init; } = string.Empty; /// /// Gets or sets the features available on the device. /// - public string Features { get; init; } + public string Features { get; init; } = string.Empty; /// /// Gets or sets the USB port to which this device is connected. Usually available on Linux only. /// - public string Usb { get; init; } + public string Usb { get; init; } = string.Empty; /// /// Gets or sets the transport ID for this device. /// - public string TransportId { get; init; } + public string TransportId { get; init; } = string.Empty; /// /// Gets or sets the device info message. Currently only seen for NoPermissions state. /// - public string Message { get; init; } + public string Message { get; init; } = string.Empty; /// /// Creates a new instance of the class based on @@ -107,10 +108,10 @@ public DeviceData(string data) public static DeviceData CreateFromAdbData(string data) => new(data); /// - public override bool Equals(object obj) => Equals(obj as DeviceData); + public override bool Equals([NotNullWhen(true)] object? obj) => Equals(obj as DeviceData); /// - public bool Equals(DeviceData other) => + public bool Equals([NotNullWhen(true)] DeviceData? other) => other is not null && Serial == other.Serial && State == other.State diff --git a/AdvancedSharpAdbClient/Models/DnsEndPoint.cs b/AdvancedSharpAdbClient/Models/DnsEndPoint.cs index da375e03..583ad28e 100644 --- a/AdvancedSharpAdbClient/Models/DnsEndPoint.cs +++ b/AdvancedSharpAdbClient/Models/DnsEndPoint.cs @@ -1,5 +1,6 @@ #if NETFRAMEWORK && !NET40_OR_GREATER using System; +using System.Diagnostics.CodeAnalysis; using System.Net; using System.Net.Sockets; @@ -53,7 +54,7 @@ not AddressFamily.InterNetworkV6 and /// /// A instance to compare to the current instance. /// if the two instances are equal; otherwise, . - public override bool Equals(object comparand) + public override bool Equals([NotNullWhen(true)] object? comparand) { return comparand is DnsEndPoint dnsComparand && _family == dnsComparand._family && diff --git a/AdvancedSharpAdbClient/Models/Element.cs b/AdvancedSharpAdbClient/Models/Element.cs index 534c2204..8e3399c7 100644 --- a/AdvancedSharpAdbClient/Models/Element.cs +++ b/AdvancedSharpAdbClient/Models/Element.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Xml; @@ -24,7 +25,7 @@ public class Element : IEquatable /// The current device containing the element. /// The coordinates and size of the element. /// Gets or sets element attributes. - public Element(IAdbClient client, DeviceData device, Rectangle rectangle, Dictionary attributes = null) + public Element(IAdbClient client, DeviceData device, Rectangle rectangle, Dictionary? attributes = null) { Client = client; Device = device; @@ -38,32 +39,35 @@ public Element(IAdbClient client, DeviceData device, Rectangle rectangle, Dictio /// The current ADB client that manages the connection. /// The current device containing the element. /// The of the element. - public Element(IAdbClient client, DeviceData device, XmlNode xmlNode) + public Element(IAdbClient client, DeviceData device, XmlNode? xmlNode) { Client = client; Device = device; Node = xmlNode; - if (xmlNode.Attributes["bounds"]?.Value is string bounds) + if (xmlNode?.Attributes != null) { - string[] cords = bounds.Split(separator, StringSplitOptions.RemoveEmptyEntries); // x1, y1, x2, y2 - Bounds = Rectangle.FromLTRB(int.Parse(cords[0]), int.Parse(cords[1]), int.Parse(cords[2]), int.Parse(cords[3])); - } + if (xmlNode.Attributes["bounds"]?.Value is string bounds) + { + string[] cords = bounds.Split(separator, StringSplitOptions.RemoveEmptyEntries); // x1, y1, x2, y2 + Bounds = Rectangle.FromLTRB(int.Parse(cords[0]), int.Parse(cords[1]), int.Parse(cords[2]), int.Parse(cords[3])); + } - Attributes = new(xmlNode.Attributes.Count); - foreach (XmlAttribute at in xmlNode.Attributes) - { - Attributes[at.Name] = at.Value; + Attributes = new(xmlNode.Attributes.Count); + foreach (XmlAttribute at in xmlNode.Attributes.OfType()) + { + Attributes[at.Name] = at.Value; + } } IEnumerable FindElements() { - XmlNodeList childNodes = xmlNode.ChildNodes; + XmlNodeList? childNodes = xmlNode?.ChildNodes; if (childNodes != null) { - for (int i = 0; i < childNodes.Count; i++) + for (int i = 0; i < childNodes!.Count; i++) { - Element element = FromXmlNode(client, device, childNodes[i]); + Element? element = FromXmlNode(client, device, childNodes?[i]); if (element != null) { yield return element; @@ -83,23 +87,27 @@ IEnumerable FindElements() /// The of the element. public Element(IAdbClient client, DeviceData device, Windows.Data.Xml.Dom.IXmlNode xmlNode) { - XmlDocument doc = new(); - doc.LoadXml(xmlNode.GetXml()); - Client = client; Device = device; + + ExceptionExtensions.ThrowIfNull(xmlNode); + XmlDocument doc = new(); + doc.LoadXml(xmlNode.GetXml()); Node = doc.FirstChild; - if (xmlNode.Attributes?.GetNamedItem("bounds")?.NodeValue?.ToString() is string bounds) + if (xmlNode.Attributes != null) { - string[] cords = bounds.Split(separator, StringSplitOptions.RemoveEmptyEntries); // x1, y1, x2, y2 - Bounds = Rectangle.FromLTRB(int.Parse(cords[0]), int.Parse(cords[1]), int.Parse(cords[2]), int.Parse(cords[3])); - } - - Attributes = new(xmlNode.Attributes.Count); - foreach (Windows.Data.Xml.Dom.IXmlNode at in xmlNode.Attributes) - { - Attributes[at.NodeName] = at.NodeValue?.ToString(); + if (xmlNode.Attributes.GetNamedItem("bounds")?.NodeValue?.ToString() is string bounds) + { + string[] cords = bounds.Split(separator, StringSplitOptions.RemoveEmptyEntries); // x1, y1, x2, y2 + Bounds = Rectangle.FromLTRB(int.Parse(cords[0]), int.Parse(cords[1]), int.Parse(cords[2]), int.Parse(cords[3])); + } + + Attributes = new(xmlNode.Attributes.Count); + foreach (Windows.Data.Xml.Dom.IXmlNode at in xmlNode.Attributes) + { + Attributes[at.NodeName] = at.NodeValue.ToString(); + } } IEnumerable FindElements() @@ -109,7 +117,7 @@ IEnumerable FindElements() { foreach (Windows.Data.Xml.Dom.IXmlNode childNode in childNodes) { - Element element = FromIXmlNode(client, device, childNode); + Element? element = FromIXmlNode(client, device, childNode); if (element != null) { yield return element; @@ -139,17 +147,17 @@ IEnumerable FindElements() /// /// Gets the children of this element. /// - public IEnumerable Children { get; init; } + public IEnumerable? Children { get; init; } /// /// Gets the element attributes. /// - public Dictionary Attributes { get; init; } + public Dictionary? Attributes { get; init; } /// /// Gets the of this element. /// - public XmlNode Node { get; init; } + public XmlNode? Node { get; init; } /// /// Gets the coordinates of the the center of the element. @@ -159,22 +167,22 @@ IEnumerable FindElements() /// /// Gets the text of the element. /// - public string Text => Attributes.TryGetValue("text", out string text) ? text : string.Empty; + public string? Text => Attributes?.TryGetValue("text", out string? text) == true ? text : string.Empty; /// /// Gets the class name of the element. /// - public string Class => Attributes.TryGetValue("class", out string @class) ? @class : string.Empty; + public string? Class => Attributes?.TryGetValue("class", out string? @class) == true ? @class : string.Empty; /// /// Gets the package name of the element. /// - public string Package => Attributes.TryGetValue("package", out string package) ? package : string.Empty; + public string? Package => Attributes?.TryGetValue("package", out string? package) == true ? package : string.Empty; /// /// Gets the resource ID of the element. /// - public string ResourceID => Attributes.TryGetValue("resource-id", out string resource_id) ? resource_id : string.Empty; + public string? ResourceID => Attributes?.TryGetValue("resource-id", out string? resource_id) == true ? resource_id : string.Empty; /// /// Gets the element at the specified index. @@ -182,7 +190,7 @@ IEnumerable FindElements() /// The zero-based index of the element to get or set. /// The element at the specified index. /// The index method is index by . - public Element this[int index] => Children.ElementAt(index); + public Element? this[int index] => Children?.ElementAt(index); /// /// Creates a new with the specified . @@ -191,8 +199,8 @@ IEnumerable FindElements() /// The current device containing the element. /// The of the element. /// The new that this method creates. - public static Element FromXmlNode(IAdbClient client, DeviceData device, XmlNode xmlNode) => - xmlNode.Attributes["bounds"] != null ? new Element(client, device, xmlNode) : null; + public static Element? FromXmlNode(IAdbClient client, DeviceData device, XmlNode? xmlNode) => + xmlNode?.Attributes?["bounds"] != null ? new Element(client, device, xmlNode) : null; #if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER /// @@ -202,14 +210,14 @@ public static Element FromXmlNode(IAdbClient client, DeviceData device, XmlNode /// The current device containing the element. /// The of the element. /// The new that this method creates. - public static Element FromIXmlNode(IAdbClient client, DeviceData device, Windows.Data.Xml.Dom.IXmlNode xmlNode) => + public static Element? FromIXmlNode(IAdbClient client, DeviceData device, Windows.Data.Xml.Dom.IXmlNode xmlNode) => xmlNode.Attributes?.GetNamedItem("bounds") != null ? new Element(client, device, xmlNode) : null; #endif /// /// Gets the count of in this element. /// - public virtual int GetChildCount() => Children.Count() + Children.Select(x => x.GetChildCount()).Sum(); + public virtual int GetChildCount() => Children == null ? 0 : Children.Count() + Children.Select(x => x.GetChildCount()).Sum(); /// /// Clicks on this coordinates. @@ -229,7 +237,8 @@ public void SendText(string text) /// /// Clear the input text. Use if the element is focused. /// - public void ClearInput() => ClearInput(Text.Length); + [MemberNotNull(nameof(Text))] + public void ClearInput() => ClearInput(Text!.Length); /// /// Clear the input text. Use if the element is focused. @@ -266,8 +275,9 @@ public async Task SendTextAsync(string text, CancellationToken cancellationToken /// /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. + [MemberNotNull(nameof(Text))] public Task ClearInputAsync(CancellationToken cancellationToken = default) => - ClearInputAsync(Text.Length, cancellationToken); + ClearInputAsync(Text!.Length, cancellationToken); /// /// Clear the input text. Use if the element is focused. @@ -287,8 +297,10 @@ public async Task ClearInputAsync(int charCount, CancellationToken cancellationT /// /// The predicate to use to match the descendant nodes. /// The descendant that was found, or . - public Element FindDescendant(Func predicate) + public Element? FindDescendant(Func predicate) { + if (Children == null) { return null; } + foreach (Element child in Children) { if (predicate(child)) @@ -296,7 +308,7 @@ public Element FindDescendant(Func predicate) return child; } - Element descendant = child.FindDescendant(predicate); + Element? descendant = child.FindDescendant(predicate); if (descendant != null) { @@ -312,7 +324,7 @@ public Element FindDescendant(Func predicate) /// /// The predicate to use to match the descendant nodes. /// The descendant (or self) that was found, or . - public Element FindDescendantOrSelf(Func predicate) => + public Element? FindDescendantOrSelf(Func predicate) => predicate(this) ? this : FindDescendant(predicate); /// @@ -328,6 +340,7 @@ public Element FindDescendantOrSelf(Func predicate) => /// All the descendant instance from . public IEnumerable FindDescendants() { + if (Children == null) { yield break; } foreach (Element child in Children) { yield return child; @@ -340,17 +353,17 @@ public IEnumerable FindDescendants() } /// - public override bool Equals(object obj) => Equals(obj as Element); + public override bool Equals([NotNullWhen(true)] object? obj) => Equals(obj as Element); /// - public bool Equals(Element other) => + public bool Equals([NotNullWhen(true)] Element? other) => other is not null && Client == other.Client && Device == other.Device - && Node == null + && (Node == null ? Bounds == other.Bounds && Attributes == other.Attributes - : Node == other.Node; + : Node == other.Node); /// public override int GetHashCode() => @@ -359,7 +372,7 @@ public override int GetHashCode() => : HashCode.Combine(Client, Device, Node); /// - public override string ToString() => + public override string? ToString() => string.IsNullOrEmpty(Text) ? string.IsNullOrEmpty(Class) ? base.ToString() : Class : Text; } } diff --git a/AdvancedSharpAdbClient/Models/FileStatistics.cs b/AdvancedSharpAdbClient/Models/FileStatistics.cs index 8822925e..be4a58eb 100644 --- a/AdvancedSharpAdbClient/Models/FileStatistics.cs +++ b/AdvancedSharpAdbClient/Models/FileStatistics.cs @@ -3,6 +3,7 @@ // using System; +using System.Diagnostics.CodeAnalysis; namespace AdvancedSharpAdbClient { @@ -19,7 +20,7 @@ public FileStatistics() { } /// /// Gets or sets the path of the file. /// - public string Path { get; set; } + public string Path { get; set; } = string.Empty; /// /// Gets or sets the attributes of the file. @@ -43,10 +44,10 @@ public FileStatistics() { } public override string ToString() => Path; /// - public override bool Equals(object obj) => Equals(obj as FileStatistics); + public override bool Equals([NotNullWhen(true)] object? obj) => Equals(obj as FileStatistics); /// - public bool Equals(FileStatistics other) => + public bool Equals([NotNullWhen(true)] FileStatistics? other) => other is not null && Path == other.Path && FileType == other.FileType diff --git a/AdvancedSharpAdbClient/Models/ForwardData.cs b/AdvancedSharpAdbClient/Models/ForwardData.cs index 6fadec06..238db1be 100644 --- a/AdvancedSharpAdbClient/Models/ForwardData.cs +++ b/AdvancedSharpAdbClient/Models/ForwardData.cs @@ -3,6 +3,7 @@ // using System; +using System.Diagnostics.CodeAnalysis; namespace AdvancedSharpAdbClient { @@ -44,12 +45,12 @@ public ForwardData(string value) /// /// Gets or sets the serial number of the device for which the port forwarding is configured. /// - public string SerialNumber { get; init; } + public string SerialNumber { get; init; } = string.Empty; /// /// Gets or sets a that represents the local (PC) endpoint. /// - public string Local { get; init; } + public string Local { get; init; } = string.Empty; /// /// Gets a that represents the local (PC) endpoint. @@ -59,7 +60,7 @@ public ForwardData(string value) /// /// Gets or sets a that represents the remote (device) endpoint. /// - public string Remote { get; init; } + public string Remote { get; init; } = string.Empty; /// /// Gets a that represents the remote (device) endpoint. @@ -71,13 +72,14 @@ public ForwardData(string value) /// /// The value to parse. /// A object that represents the port forwarding information contained in . - public static ForwardData FromString(string value) => value == null ? null : new ForwardData(value); + [return: NotNullIfNotNull(nameof(value))] + public static ForwardData? FromString(string? value) => value == null ? null : new ForwardData(value); /// - public override bool Equals(object obj) => Equals(obj as ForwardData); + public override bool Equals([NotNullWhen(true)] object? obj) => Equals(obj as ForwardData); /// - public bool Equals(ForwardData other) => + public bool Equals([NotNullWhen(true)] ForwardData? other) => other is not null && SerialNumber == other.SerialNumber && Local == other.Local diff --git a/AdvancedSharpAdbClient/Models/ForwardSpec.cs b/AdvancedSharpAdbClient/Models/ForwardSpec.cs index 39233ccd..80839731 100644 --- a/AdvancedSharpAdbClient/Models/ForwardSpec.cs +++ b/AdvancedSharpAdbClient/Models/ForwardSpec.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; namespace AdvancedSharpAdbClient @@ -103,7 +104,7 @@ or ForwardProtocol.LocalReserved /// or , /// the Unix domain socket name of the socket being forwarded. /// - public string SocketName { get; init; } + public string? SocketName { get; init; } /// /// Gets or sets, when the is , @@ -139,10 +140,10 @@ or ForwardProtocol.LocalReserved public override int GetHashCode() => HashCode.Combine(Protocol, Port, ProcessId, SocketName); /// - public override bool Equals(object obj) => Equals(obj as ForwardSpec); + public override bool Equals([NotNullWhen(true)] object? obj) => Equals(obj as ForwardSpec); /// - public bool Equals(ForwardSpec other) => + public bool Equals([NotNullWhen(true)] ForwardSpec? other) => other != null && other.Protocol == Protocol && Protocol switch diff --git a/AdvancedSharpAdbClient/Models/Framebuffer.cs b/AdvancedSharpAdbClient/Models/Framebuffer.cs index c660a176..2e7d557b 100644 --- a/AdvancedSharpAdbClient/Models/Framebuffer.cs +++ b/AdvancedSharpAdbClient/Models/Framebuffer.cs @@ -3,6 +3,7 @@ // using System; +using System.Diagnostics.CodeAnalysis; using System.Net; using System.Threading; @@ -24,7 +25,7 @@ public class Framebuffer(DeviceData device, EndPoint endPoint) : IDisposable /// /// The device for which to fetch the frame buffer. /// A which manages the connection with adb. - public Framebuffer(DeviceData device, IAdbClient client) : this(device, client?.EndPoint) { } + public Framebuffer(DeviceData device, IAdbClient client) : this(device, client?.EndPoint!) { } /// /// Initializes a new instance of the class. @@ -52,13 +53,14 @@ public Framebuffer(DeviceData device) : this(device, AdbClient.DefaultEndPoint) /// Gets the framebuffer data. You need to parse the to interpret this data (such as the color encoding). /// This property is set after you call . /// - public byte[] Data { get; private set; } + public byte[]? Data { get; private set; } /// /// Refreshes the framebuffer: fetches the latest framebuffer data from the device. Access the /// and properties to get the updated framebuffer data. /// /// Refreshes the header of framebuffer when . + [MemberNotNull(nameof(Data))] public virtual void Refresh(bool reset = false) { EnsureNotDisposed(); @@ -164,7 +166,7 @@ public virtual async Task RefreshAsync(bool reset = false, CancellationToken can #if NET [SupportedOSPlatform("windows")] #endif - public virtual Bitmap ToImage() + public virtual Bitmap? ToImage() { EnsureNotDisposed(); return Data == null ? throw new InvalidOperationException($"Call {nameof(Refresh)} first") : Header.ToImage(Data); @@ -174,13 +176,13 @@ public virtual Bitmap ToImage() #if NET [SupportedOSPlatform("windows")] #endif - public static explicit operator Image(Framebuffer value) => value.ToImage(); + public static explicit operator Image?(Framebuffer value) => value.ToImage(); /// #if NET [SupportedOSPlatform("windows")] #endif - public static explicit operator Bitmap(Framebuffer value) => value.ToImage(); + public static explicit operator Bitmap?(Framebuffer value) => value.ToImage(); #endif #if WINDOWS_UWP @@ -189,7 +191,7 @@ public virtual Bitmap ToImage() /// /// A which can be used to cancel the asynchronous task. /// An which represents the framebuffer data. - public virtual Task ToBitmap(CancellationToken cancellationToken = default) + public virtual Task ToBitmap(CancellationToken cancellationToken = default) { EnsureNotDisposed(); return Data == null ? throw new InvalidOperationException($"Call {nameof(RefreshAsync)} first") : Header.ToBitmap(Data, cancellationToken); @@ -201,7 +203,7 @@ public virtual Task ToBitmap(CancellationToken cancellationToke /// The target to invoke the code on. /// A which can be used to cancel the asynchronous task. /// An which represents the framebuffer data. - public virtual Task ToBitmap(CoreDispatcher dispatcher, CancellationToken cancellationToken = default) + public virtual Task ToBitmap(CoreDispatcher dispatcher, CancellationToken cancellationToken = default) { EnsureNotDisposed(); return Data == null ? throw new InvalidOperationException($"Call {nameof(RefreshAsync)} first") : Header.ToBitmap(Data, dispatcher, cancellationToken); @@ -213,7 +215,7 @@ public virtual Task ToBitmap(CoreDispatcher dispatcher, Cancell /// The target to invoke the code on. /// A which can be used to cancel the asynchronous task. /// An which represents the framebuffer data. - public virtual Task ToBitmap(DispatcherQueue dispatcher, CancellationToken cancellationToken = default) + public virtual Task ToBitmap(DispatcherQueue dispatcher, CancellationToken cancellationToken = default) { EnsureNotDisposed(); return Data == null ? throw new InvalidOperationException($"Call {nameof(RefreshAsync)} first") : Header.ToBitmap(Data, dispatcher, cancellationToken); @@ -231,7 +233,7 @@ protected virtual void Dispose(bool disposing) ArrayPool.Shared.Return(Data, clearArray: false); } #endif - headerData = null; + headerData = null!; headerInitialized = false; disposed = true; } diff --git a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs index a06fc391..2f7ee0c5 100644 --- a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs +++ b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs @@ -142,7 +142,7 @@ public FramebufferHeader(byte[] data) #if NET [SupportedOSPlatform("windows")] #endif - public readonly Bitmap ToImage(byte[] buffer) + public readonly Bitmap? ToImage(byte[] buffer) { ExceptionExtensions.ThrowIfNull(buffer); @@ -291,7 +291,7 @@ private readonly PixelFormat StandardizePixelFormat(byte[] buffer) /// A which can be used to cancel the asynchronous task. /// A that represents the image contained in the frame buffer, or /// if the framebuffer does not contain any data. This can happen when DRM is enabled on the device. - public readonly Task ToBitmap(byte[] buffer, CoreDispatcher dispatcher, CancellationToken cancellationToken = default) + public readonly Task ToBitmap(byte[] buffer, CoreDispatcher dispatcher, CancellationToken cancellationToken = default) { FramebufferHeader self = this; @@ -301,7 +301,7 @@ public readonly Task ToBitmap(byte[] buffer, CoreDispatcher dis } else { - TaskCompletionSource taskCompletionSource = new(); + TaskCompletionSource taskCompletionSource = new(); _ = dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () => { @@ -328,7 +328,7 @@ public readonly Task ToBitmap(byte[] buffer, CoreDispatcher dis /// A that represents the image contained in the frame buffer, or /// if the framebuffer does not contain any data. This can happen when DRM is enabled on the device. [ContractVersion(typeof(UniversalApiContract), 327680u)] - public readonly Task ToBitmap(byte[] buffer, DispatcherQueue dispatcher, CancellationToken cancellationToken = default) + public readonly Task ToBitmap(byte[] buffer, DispatcherQueue dispatcher, CancellationToken cancellationToken = default) { FramebufferHeader self = this; @@ -338,7 +338,7 @@ public readonly Task ToBitmap(byte[] buffer, DispatcherQueue di } else { - TaskCompletionSource taskCompletionSource = new(); + TaskCompletionSource taskCompletionSource = new(); if (!dispatcher.TryEnqueue(async () => { @@ -366,7 +366,7 @@ public readonly Task ToBitmap(byte[] buffer, DispatcherQueue di /// A which can be used to cancel the asynchronous task. /// A that represents the image contained in the frame buffer, or /// if the framebuffer does not contain any data. This can happen when DRM is enabled on the device. - public readonly async Task ToBitmap(byte[] buffer, CancellationToken cancellationToken = default) + public readonly async Task ToBitmap(byte[] buffer, CancellationToken cancellationToken = default) { if (buffer == null) { diff --git a/AdvancedSharpAdbClient/Models/HashCode.cs b/AdvancedSharpAdbClient/Models/HashCode.cs index d11ab852..fcf091eb 100644 --- a/AdvancedSharpAdbClient/Models/HashCode.cs +++ b/AdvancedSharpAdbClient/Models/HashCode.cs @@ -427,7 +427,7 @@ public void Add(T value) /// The type of the value to add to the hash code. /// The value to add to the hash code. /// The to use to calculate the hash code. This value can be a null reference (Nothing in Visual Basic), which will use the default equality comparer for . - public void Add(T value, IEqualityComparer comparer) + public void Add(T value, IEqualityComparer? comparer) { Add(value is null ? 0 : (comparer?.GetHashCode(value) ?? value.GetHashCode())); } @@ -561,7 +561,7 @@ public readonly int ToHashCode() [Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes.", error: true)] [EditorBrowsable(EditorBrowsableState.Never)] [DoesNotReturn] - public override readonly bool Equals(object obj) => throw new NotSupportedException("HashCode is a mutable struct and should not be compared with other HashCodes."); + public override readonly bool Equals(object? obj) => throw new NotSupportedException("HashCode is a mutable struct and should not be compared with other HashCodes."); /// public static bool operator ==(HashCode left, HashCode right) => left.Equals(right); diff --git a/AdvancedSharpAdbClient/Models/Point.cs b/AdvancedSharpAdbClient/Models/Point.cs index 6e516112..4f33c5a2 100644 --- a/AdvancedSharpAdbClient/Models/Point.cs +++ b/AdvancedSharpAdbClient/Models/Point.cs @@ -3,6 +3,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; namespace AdvancedSharpAdbClient { @@ -90,7 +91,7 @@ public int Y /// /// The to test for equality. /// if is a and has the same coordinates as this point instance. - public override readonly bool Equals(object obj) => obj is Point point && Equals(point); + public override readonly bool Equals([NotNullWhen(true)] object? obj) => obj is Point point && Equals(point); /// /// Specifies whether this contains the same coordinates as the specified diff --git a/AdvancedSharpAdbClient/Models/Rectangle.cs b/AdvancedSharpAdbClient/Models/Rectangle.cs index adf65eba..66919e9f 100644 --- a/AdvancedSharpAdbClient/Models/Rectangle.cs +++ b/AdvancedSharpAdbClient/Models/Rectangle.cs @@ -3,6 +3,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; namespace AdvancedSharpAdbClient { @@ -105,7 +106,7 @@ public Point Location /// This method returns if is a structure /// and its , , , and properties are equal to /// the corresponding properties of this structure; otherwise, . - public override readonly bool Equals(object obj) => obj is Rectangle rectangle && Equals(rectangle); + public override readonly bool Equals([NotNullWhen(true)] object? obj) => obj is Rectangle rectangle && Equals(rectangle); /// public readonly bool Equals(Rectangle other) => this == other; diff --git a/AdvancedSharpAdbClient/Models/ShellStream.cs b/AdvancedSharpAdbClient/Models/ShellStream.cs index 4ec3dc45..4d2a9ecd 100644 --- a/AdvancedSharpAdbClient/Models/ShellStream.cs +++ b/AdvancedSharpAdbClient/Models/ShellStream.cs @@ -480,7 +480,7 @@ protected override void Dispose(bool disposing) if (closeStream && Inner != null) { Inner.Dispose(); - Inner = null; + Inner = null!; } } @@ -494,7 +494,7 @@ public override async ValueTask DisposeAsync() if (closeStream && Inner != null) { await Inner.DisposeAsync(); - Inner = null; + Inner = null!; } await base.DisposeAsync(); GC.SuppressFinalize(this); diff --git a/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs b/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs index 97082d73..ff547197 100644 --- a/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs +++ b/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs @@ -16,7 +16,7 @@ namespace AdvancedSharpAdbClient /// fetch the console output that was received, used the method. /// /// The logger to use when logging. - public partial class ConsoleOutputReceiver(ILogger logger = null) : MultiLineReceiver + public partial class ConsoleOutputReceiver(ILogger? logger = null) : MultiLineReceiver { /// /// The default to use when parsing the output. diff --git a/AdvancedSharpAdbClient/SyncService.Async.cs b/AdvancedSharpAdbClient/SyncService.Async.cs index d0db5934..4c9c87cf 100644 --- a/AdvancedSharpAdbClient/SyncService.Async.cs +++ b/AdvancedSharpAdbClient/SyncService.Async.cs @@ -35,7 +35,7 @@ public virtual Task ReopenAsync(IAdbSocket socket, CancellationToken cancellatio if (Socket != null) { Socket.Dispose(); - Socket = null; + Socket = null!; } Socket = socket; return OpenAsync(cancellationToken); @@ -50,7 +50,7 @@ public virtual Task ReopenAsync(IAdbSocket socket, CancellationToken cancellatio public Task ReopenAsync(IAdbClient client, CancellationToken cancellationToken = default) => ReopenAsync(Factories.AdbSocketFactory(client.EndPoint), cancellationToken); /// - public virtual async Task PushAsync(Stream stream, string remotePath, int permissions, DateTimeOffset timestamp, IProgress progress = null, CancellationToken cancellationToken = default) + public virtual async Task PushAsync(Stream stream, string remotePath, int permissions, DateTimeOffset timestamp, IProgress? progress = null, CancellationToken cancellationToken = default) { ExceptionExtensions.ThrowIfNull(stream); @@ -151,7 +151,7 @@ public virtual async Task PushAsync(Stream stream, string remotePath, int permis } /// - public virtual async Task PullAsync(string remoteFilePath, Stream stream, IProgress progress = null, CancellationToken cancellationToken = default) + public virtual async Task PullAsync(string remoteFilePath, Stream stream, IProgress? progress = null, CancellationToken cancellationToken = default) { ExceptionExtensions.ThrowIfNull(remoteFilePath); diff --git a/AdvancedSharpAdbClient/SyncService.cs b/AdvancedSharpAdbClient/SyncService.cs index 96d37ad2..2b5068ed 100644 --- a/AdvancedSharpAdbClient/SyncService.cs +++ b/AdvancedSharpAdbClient/SyncService.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Net; namespace AdvancedSharpAdbClient { @@ -47,14 +48,25 @@ public partial class SyncService : ISyncService protected const int MaxPathLength = 1024; /// - public event EventHandler SyncProgressChanged; + public event EventHandler? SyncProgressChanged; /// /// Initializes a new instance of the class. /// /// A connection to an adb server. /// The device on which to interact with the files. - public SyncService(IAdbClient client, DeviceData device) : this(Factories.AdbSocketFactory(client.EndPoint), device) + public SyncService(IAdbClient client, DeviceData device) + : this(Factories.AdbSocketFactory(client.EndPoint), device) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The at which the adb server is listening. + /// The device on which to interact with the files. + public SyncService(EndPoint endPoint, DeviceData device) + : this(Factories.AdbSocketFactory(endPoint), device) { } @@ -108,7 +120,7 @@ public virtual void Reopen(IAdbSocket socket) if (Socket != null) { Socket.Dispose(); - Socket = null; + Socket = null!; } Socket = socket; Open(); @@ -121,7 +133,7 @@ public virtual void Reopen(IAdbSocket socket) public void Reopen(IAdbClient client) => Reopen(Factories.AdbSocketFactory(client.EndPoint)); /// - public virtual void Push(Stream stream, string remotePath, int permissions, DateTimeOffset timestamp, IProgress progress = null, in bool isCancelled = false) + public virtual void Push(Stream stream, string remotePath, int permissions, DateTimeOffset timestamp, IProgress? progress = null, in bool isCancelled = false) { ExceptionExtensions.ThrowIfNull(stream); @@ -218,7 +230,7 @@ public virtual void Push(Stream stream, string remotePath, int permissions, Date } /// - public virtual void Pull(string remoteFilePath, Stream stream, IProgress progress = null, in bool isCancelled = false) + public virtual void Pull(string remoteFilePath, Stream stream, IProgress? progress = null, in bool isCancelled = false) { ExceptionExtensions.ThrowIfNull(remoteFilePath); @@ -347,7 +359,7 @@ protected virtual void Dispose(bool disposing) if (Socket != null) { Socket.Dispose(); - Socket = null; + Socket = null!; } } } diff --git a/AdvancedSharpAdbClient/TcpSocket.Async.cs b/AdvancedSharpAdbClient/TcpSocket.Async.cs index ee0339a0..0c08a570 100644 --- a/AdvancedSharpAdbClient/TcpSocket.Async.cs +++ b/AdvancedSharpAdbClient/TcpSocket.Async.cs @@ -4,6 +4,7 @@ // using System; +using System.Diagnostics.CodeAnalysis; using System.Net; using System.Net.Sockets; using System.Threading; @@ -14,6 +15,7 @@ public partial class TcpSocket { #if NET6_0_OR_GREATER /// + [MemberNotNull(nameof(EndPoint))] public virtual async ValueTask ConnectAsync(EndPoint endPoint, CancellationToken cancellationToken = default) { if (endPoint is not (IPEndPoint or DnsEndPoint)) @@ -21,9 +23,9 @@ public virtual async ValueTask ConnectAsync(EndPoint endPoint, CancellationToken throw new NotSupportedException("Only TCP endpoints are supported"); } + EndPoint = endPoint; await Socket.ConnectAsync(endPoint, cancellationToken).ConfigureAwait(false); Socket.Blocking = true; - this.endPoint = endPoint; } /// @@ -38,7 +40,7 @@ public virtual ValueTask ReconnectAsync(CancellationToken cancellationToken = de { Socket.Dispose(); Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - return ConnectAsync(endPoint, cancellationToken); + return ConnectAsync(EndPoint!, cancellationToken); } } #endif diff --git a/AdvancedSharpAdbClient/TcpSocket.cs b/AdvancedSharpAdbClient/TcpSocket.cs index 7d9c2a3e..f6573d2e 100644 --- a/AdvancedSharpAdbClient/TcpSocket.cs +++ b/AdvancedSharpAdbClient/TcpSocket.cs @@ -3,6 +3,7 @@ // using System; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Net; using System.Net.Sockets; @@ -14,11 +15,6 @@ namespace AdvancedSharpAdbClient /// public partial class TcpSocket : ITcpSocket { - /// - /// The at which the socket is listening. - /// - protected EndPoint endPoint; - /// /// Initializes a new instance of the class. /// @@ -29,6 +25,11 @@ public partial class TcpSocket : ITcpSocket /// public Socket Socket { get; protected set; } + /// + /// The at which the socket is listening. + /// + public EndPoint? EndPoint { get; protected set; } + /// public bool Connected => Socket.Connected; @@ -40,6 +41,7 @@ public int ReceiveBufferSize } /// + [MemberNotNull(nameof(EndPoint))] public virtual void Connect(EndPoint endPoint) { if (endPoint is not (IPEndPoint or DnsEndPoint)) @@ -47,9 +49,9 @@ public virtual void Connect(EndPoint endPoint) throw new NotSupportedException("Only TCP endpoints are supported"); } + EndPoint = endPoint; Socket.Connect(endPoint); Socket.Blocking = true; - this.endPoint = endPoint; } /// @@ -64,7 +66,7 @@ public virtual void Reconnect() { Socket.Dispose(); Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - Connect(endPoint); + Connect(EndPoint!); } } From 31580454ae9c500d98d2c583cb52a70b02cc4468 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Tue, 24 Oct 2023 22:17:57 +0800 Subject: [PATCH 48/66] Remove unused methods of Extensions Fix test on Linux --- .../AdbClientTests.Async.cs | 2 +- .../AdbClientTests.cs | 2 +- .../Exceptions/JavaExceptionTests.cs | 6 ++-- AdvancedSharpAdbClient/DeviceMonitor.Async.cs | 2 +- AdvancedSharpAdbClient/DeviceMonitor.cs | 2 +- .../Extensions/Extensions.cs | 28 ++++--------------- 6 files changed, 13 insertions(+), 29 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs index 6426a416..b7cc2aae 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs @@ -1039,7 +1039,7 @@ at com.android.server.input.InputManagerService.injectInputEventInternal(InputMa at com.android.server.input.InputManagerService.injectInputEvent(InputManagerService.java:651) at android.hardware.input.IInputManager$Stub.onTransact(IInputManager.java:430) at android.os.Binder.execTransactInternal(Binder.java:1165) - at android.os.Binder.execTransact(Binder.java:1134)", exception.JavaStackTrace); + at android.os.Binder.execTransact(Binder.java:1134)", exception.JavaStackTrace, ignoreLineEndingDifferences: true); } /// diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs index c1595d5f..01bc606f 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs @@ -1148,7 +1148,7 @@ at com.android.server.input.InputManagerService.injectInputEventInternal(InputMa at com.android.server.input.InputManagerService.injectInputEvent(InputManagerService.java:651) at android.hardware.input.IInputManager$Stub.onTransact(IInputManager.java:430) at android.os.Binder.execTransactInternal(Binder.java:1165) - at android.os.Binder.execTransact(Binder.java:1134)", exception.JavaStackTrace); + at android.os.Binder.execTransact(Binder.java:1134)", exception.JavaStackTrace, ignoreLineEndingDifferences: true); } /// diff --git a/AdvancedSharpAdbClient.Tests/Exceptions/JavaExceptionTests.cs b/AdvancedSharpAdbClient.Tests/Exceptions/JavaExceptionTests.cs index 04e0c17f..371be72a 100644 --- a/AdvancedSharpAdbClient.Tests/Exceptions/JavaExceptionTests.cs +++ b/AdvancedSharpAdbClient.Tests/Exceptions/JavaExceptionTests.cs @@ -58,7 +58,7 @@ at android.os.Binder.onTransact(Binder.java:824) at android.content.pm.IPackageManager$Stub.onTransact(IPackageManager.java:4644) at com.android.server.pm.PackageManagerService.onTransact(PackageManagerService.java:4513) at android.os.Binder.execTransactInternal(Binder.java:1170) - at android.os.Binder.execTransact(Binder.java:1134)", javaException.JavaStackTrace); + at android.os.Binder.execTransact(Binder.java:1134)", javaException.JavaStackTrace, ignoreLineEndingDifferences: true); } [Fact] @@ -78,7 +78,7 @@ at android.os.Binder.onTransact(Binder.java:824) at android.content.pm.IPackageManager$Stub.onTransact(IPackageManager.java:4644) at com.android.server.pm.PackageManagerService.onTransact(PackageManagerService.java:4513) at android.os.Binder.execTransactInternal(Binder.java:1170) - at android.os.Binder.execTransact(Binder.java:1134)".Split(Environment.NewLine); + at android.os.Binder.execTransact(Binder.java:1134)".Split(Extensions.NewLineSeparator); JavaException javaException = JavaException.Parse(lines); @@ -97,7 +97,7 @@ at android.os.Binder.onTransact(Binder.java:824) at android.content.pm.IPackageManager$Stub.onTransact(IPackageManager.java:4644) at com.android.server.pm.PackageManagerService.onTransact(PackageManagerService.java:4513) at android.os.Binder.execTransactInternal(Binder.java:1170) - at android.os.Binder.execTransact(Binder.java:1134)", javaException.JavaStackTrace); + at android.os.Binder.execTransact(Binder.java:1134)", javaException.JavaStackTrace, ignoreLineEndingDifferences: true); } } } diff --git a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs index 184f2f92..4f4af966 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs @@ -38,7 +38,7 @@ public virtual async Task StartAsync(CancellationToken cancellationToken = defau { _ = firstDeviceListParsed.Reset(); - monitorTask = Extensions.Run(() => DeviceMonitorLoopAsync(monitorTaskCancellationTokenSource.Token), cancellationToken); + 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); diff --git a/AdvancedSharpAdbClient/DeviceMonitor.cs b/AdvancedSharpAdbClient/DeviceMonitor.cs index 9f5afc1f..a982a45b 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.cs @@ -153,7 +153,7 @@ public virtual void Start() { _ = firstDeviceListParsed.Reset(); - monitorTask = Extensions.Run(() => DeviceMonitorLoopAsync(monitorTaskCancellationTokenSource.Token)); + monitorTask = DeviceMonitorLoopAsync(monitorTaskCancellationTokenSource.Token); // Wait for the worker thread to have read the first list of devices. _ = firstDeviceListParsed.WaitOne(); diff --git a/AdvancedSharpAdbClient/Extensions/Extensions.cs b/AdvancedSharpAdbClient/Extensions/Extensions.cs index 35fb616e..90e265d5 100644 --- a/AdvancedSharpAdbClient/Extensions/Extensions.cs +++ b/AdvancedSharpAdbClient/Extensions/Extensions.cs @@ -167,22 +167,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 task returned by . - /// - /// The work to execute asynchronously. - /// A cancellation token that can be used to cancel the work if it has not yet started. - /// A task that represents a proxy for the task returned by . - /// The parameter was . - /// For information on handling exceptions thrown by task operations, see Exception Handling. - public static Task Run(Action function, CancellationToken cancellationToken = default) => -#if NETFRAMEWORK && !NET45_OR_GREATER - TaskEx -#else - Task -#endif - .Run(function, 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. @@ -238,10 +222,10 @@ public static Task WhenAll(IEnumerable> tasks) /// 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) => -#if !NET35 - reader.ReadLineAsync(); -#else +#if NET35 Run(reader.ReadLine, cancellationToken); +#else + reader.ReadLineAsync(); #endif /// @@ -253,10 +237,10 @@ public static Task WhenAll(IEnumerable> tasks) /// parameter contains a string with the characters from the current position to /// the end of the stream. public static Task ReadToEndAsync(this TextReader reader, CancellationToken cancellationToken) => -#if !NET35 - reader.ReadToEndAsync(); -#else +#if NET35 Run(reader.ReadToEnd, cancellationToken); +#else + reader.ReadToEndAsync(); #endif #endif #endif From f4a88ade5a33fb3261edde0ba12c3d994c2c5e6c Mon Sep 17 00:00:00 2001 From: wherewhere Date: Sun, 29 Oct 2023 17:17:28 +0800 Subject: [PATCH 49/66] Build .NETFX on Github Action --- .github/workflows/build-and-test.yml | 2 +- AdvancedSharpAdbClient.Tests/SocketBasedTests.cs | 2 +- AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj | 7 +++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 8590e011..55c253f5 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -45,7 +45,7 @@ jobs: pack-and-publish: name: pack-and-publish needs: build-and-test - runs-on: ubuntu-latest + runs-on: windows-latest steps: - name: Checkout diff --git a/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs b/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs index fbdcfb87..f92622a1 100644 --- a/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs +++ b/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs @@ -35,7 +35,7 @@ protected SocketBasedTests(bool integrationTest, bool doDispose) #else // In release mode (e.g. on the build server), // never run integration tests. - DummyAdbSocket socket = new DummyAdbSocket(); + DummyAdbSocket socket = new(); Factories.AdbSocketFactory = (endPoint) => socket; IntegrationTest = false; #endif diff --git a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj index bde85b81..eec44c2b 100644 --- a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj +++ b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj @@ -11,14 +11,13 @@ $(NoWarn);NU1603;NU1605;NU1902;NU1903 net6.0;net8.0;netcoreapp2.1;netcoreapp3.1;netstandard1.3;netstandard2.0;netstandard2.1 - $(TargetFrameworks);net6.0-windows10.0.17763.0;net8.0-windows10.0.17763.0 - $(TargetFrameworks);net2.0-client;net3.5-client;net4.0-client;net4.5.2;net4.6.2;net4.8.1;netcore5.0;uap10.0;uap10.0.15138.0 + $(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);netcore5.0;uap10.0;uap10.0.15138.0 net8.0;netcoreapp3.1;netstandard1.3;netstandard2.0;netstandard2.1 - $(TargetFrameworks);net8.0-windows10.0.17763.0 - $(TargetFrameworks);net2.0-client;net3.5-client;net4.5.2;net4.8.1 + $(TargetFrameworks);net2.0-client;net3.5-client;net4.5.2;net4.8.1;net8.0-windows10.0.17763.0 From 9dad0ef5b1128fabcd8019750daa6eb470a81d9c Mon Sep 17 00:00:00 2001 From: wherewhere Date: Sun, 29 Oct 2023 20:06:04 +0800 Subject: [PATCH 50/66] Add ThreadSwitcher --- .github/workflows/build-and-test.yml | 4 +- AdvancedSharpAdbClient/DeviceMonitor.Async.cs | 2 + .../Attributes/BrowsableAttribute.cs | 61 ++++++++ .../Extensions/Extensions.cs | 16 ++ .../Extensions/SocketExtensions.cs | 4 +- .../Extensions/ThreadExtensions.cs | 147 ++++++++++++++++++ .../Models/FramebufferHeader.cs | 57 +------ 7 files changed, 239 insertions(+), 52 deletions(-) create mode 100644 AdvancedSharpAdbClient/Extensions/Attributes/BrowsableAttribute.cs create mode 100644 AdvancedSharpAdbClient/Extensions/ThreadExtensions.cs diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 55c253f5..4700dc6b 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -67,7 +67,9 @@ jobs: - name: Publish if: ${{ github.event_name == 'push' && github.ref_name == 'main' }} - run: dotnet nuget push ./nugets/*nupkg --source https://nuget.pkg.github.com/yungd1plomat/index.json --skip-duplicate --api-key ${{ secrets.NUGET_KEY }} + run: dotnet nuget push nugets/**.nupkg --source $env:NUGET_SOURCE --skip-duplicate --api-key ${{ secrets.NUGET_KEY }} + env: + NUGET_SOURCE: https://nuget.pkg.github.com/yungd1plomat/index.json - name: Upload uses: actions/upload-artifact@v3 diff --git a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs index 4f4af966..c9bc84f4 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs @@ -110,6 +110,8 @@ protected virtual async Task DeviceMonitorLoopAsync(CancellationToken cancellati { IsRunning = true; + await ThreadExtensions.ResumeBackgroundAsync(); + // Set up the connection to track the list of devices. await InitializeSocketAsync(cancellationToken).ConfigureAwait(false); diff --git a/AdvancedSharpAdbClient/Extensions/Attributes/BrowsableAttribute.cs b/AdvancedSharpAdbClient/Extensions/Attributes/BrowsableAttribute.cs new file mode 100644 index 00000000..e2959119 --- /dev/null +++ b/AdvancedSharpAdbClient/Extensions/Attributes/BrowsableAttribute.cs @@ -0,0 +1,61 @@ +#if NETSTANDARD && !NETSTANDARD2_0_OR_GREATER || NETCORE && !UAP10_0_15138_0 +// 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; + +namespace System.ComponentModel +{ + /// + /// Specifies whether a property or event should be displayed in a property + /// browsing window. + /// + [AttributeUsage(AttributeTargets.All)] + internal sealed class BrowsableAttribute : Attribute + { + /// + /// Specifies that a property or event can be modified at design time. + /// This field is read-only. + /// + public static readonly BrowsableAttribute Yes = new(true); + + /// + /// Specifies that a property or event cannot be modified at design time.' + /// This field is read-only. + /// + public static readonly BrowsableAttribute No = new(false); + + /// + /// Specifies the default value for the , which is . + /// This field is read-only. + /// + public static readonly BrowsableAttribute Default = Yes; + + /// + /// Initializes a new instance of the class. + /// + public BrowsableAttribute(bool browsable) + { + Browsable = browsable; + } + + /// + /// Gets a value indicating whether an object is browsable. + /// + public bool Browsable { get; } + + /// + public override bool Equals([NotNullWhen(true)] object? obj) => + obj is BrowsableAttribute other && other.Browsable == Browsable; + + /// + public override int GetHashCode() => Browsable.GetHashCode(); + + /// + /// Determines if this attribute is the default. + /// + /// if the attribute is the default value for this attribute class; otherwise, . + public bool IsDefaultAttribute() => Equals(Default); + } +} +#endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Extensions/Extensions.cs b/AdvancedSharpAdbClient/Extensions/Extensions.cs index 90e265d5..25b45854 100644 --- a/AdvancedSharpAdbClient/Extensions/Extensions.cs +++ b/AdvancedSharpAdbClient/Extensions/Extensions.cs @@ -167,6 +167,22 @@ 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 task returned by . + /// + /// The work to execute asynchronously. + /// A cancellation token that can be used to cancel the work if it has not yet started. + /// A task that represents a proxy for the task returned by . + /// The parameter was . + /// For information on handling exceptions thrown by task operations, see Exception Handling. + public static Task Run(Action function, CancellationToken cancellationToken = default) => +#if NETFRAMEWORK && !NET45_OR_GREATER + TaskEx +#else + Task +#endif + .Run(function, 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. diff --git a/AdvancedSharpAdbClient/Extensions/SocketExtensions.cs b/AdvancedSharpAdbClient/Extensions/SocketExtensions.cs index 390a8092..76dce262 100644 --- a/AdvancedSharpAdbClient/Extensions/SocketExtensions.cs +++ b/AdvancedSharpAdbClient/Extensions/SocketExtensions.cs @@ -191,7 +191,7 @@ public static Task SendAsync(this Socket socket, byte[] buffer, int offset, /// Closes the connection and releases all associated resources. /// /// The to release. - public static void Close(this Socket socket) => socket.Dispose(); + internal static void Close(this Socket socket) => socket.Dispose(); #endif #if NETFRAMEWORK && !NET40_OR_GREATER @@ -199,7 +199,7 @@ public static Task SendAsync(this Socket socket, byte[] buffer, int offset, /// Releases all resources used by the current instance of the class. /// /// The to release. - public static void Dispose(this Socket socket) + internal static void Dispose(this Socket socket) { socket.Close(); GC.SuppressFinalize(socket); diff --git a/AdvancedSharpAdbClient/Extensions/ThreadExtensions.cs b/AdvancedSharpAdbClient/Extensions/ThreadExtensions.cs new file mode 100644 index 00000000..77cbc596 --- /dev/null +++ b/AdvancedSharpAdbClient/Extensions/ThreadExtensions.cs @@ -0,0 +1,147 @@ +#if HAS_TASK +// +// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. +// + +using System; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace AdvancedSharpAdbClient +{ +#if WINDOWS_UWP + /// + /// A helper type for switch thread by . This type is not intended to be used directly from your code. + /// + /// A whose foreground thread to switch execution to. + /// Specifies the priority for event dispatch. + [Browsable(false)] + public readonly struct DispatcherThreadSwitcher(CoreDispatcher dispatcher, CoreDispatcherPriority priority) : INotifyCompletion + { + /// + /// Gets a value that indicates whether the asynchronous operation has completed. + /// + public bool IsCompleted => dispatcher.HasThreadAccess; + + /// + /// Ends the await on the completed task. + /// + public void GetResult() { } + + /// + /// Gets an awaiter used to await this . + /// + /// An awaiter instance. + public DispatcherThreadSwitcher GetAwaiter() => this; + + /// + public void OnCompleted(Action continuation) + { + if (IsCompleted) + { + continuation(); + } + else + { + _ = dispatcher.RunAsync(priority, () => continuation()); + } + } + } + + /// + /// A helper type for switch thread by . This type is not intended to be used directly from your code. + /// + /// A whose foreground thread to switch execution to. + /// Specifies the priority for event dispatch. + [Browsable(false)] + public readonly struct DispatcherQueueThreadSwitcher(DispatcherQueue dispatcher, DispatcherQueuePriority priority) : INotifyCompletion + { + /// + /// Gets a value that indicates whether the asynchronous operation has completed. + /// + public bool IsCompleted => dispatcher.HasThreadAccess; + + /// + /// Ends the await on the completed task. + /// + public void GetResult() { } + + /// + /// Gets an awaiter used to await this . + /// + /// An awaiter instance. + public DispatcherQueueThreadSwitcher GetAwaiter() => this; + + /// + public void OnCompleted(Action continuation) + { + if (IsCompleted) + { + continuation(); + } + else + { + _ = dispatcher.TryEnqueue(priority, () => continuation()); + } + } + } +#endif + + /// + /// A helper type for switch thread by . This type is not intended to be used directly from your code. + /// + [Browsable(false)] + public readonly struct TaskThreadSwitcher : INotifyCompletion + { + /// + /// Gets a value that indicates whether the asynchronous operation has completed. + /// + public bool IsCompleted => SynchronizationContext.Current == null; + + /// + /// Ends the await on the completed task. + /// + public void GetResult() { } + + /// + /// Gets an awaiter used to await this . + /// + /// An awaiter instance. + public TaskThreadSwitcher GetAwaiter() => this; + + /// + public void OnCompleted(Action continuation) => _ = Extensions.Run(continuation); + } + + /// + /// The extensions for switching threads. + /// + public static class ThreadExtensions + { +#if WINDOWS_UWP + /// + /// A helper function—for use within a coroutine—that you can to switch execution to a specific foreground thread. + /// + /// A whose foreground thread to switch execution to. + /// Specifies the priority for event dispatch. + /// An object that you can . + public static DispatcherQueueThreadSwitcher ResumeForegroundAsync(this DispatcherQueue dispatcher, DispatcherQueuePriority priority = DispatcherQueuePriority.Normal) => new(dispatcher, priority); + + /// + /// A helper function—for use within a coroutine—that you can to switch execution to a specific foreground thread. + /// + /// A whose foreground thread to switch execution to. + /// Specifies the priority for event dispatch. + /// An object that you can . + public static DispatcherThreadSwitcher ResumeForegroundAsync(this CoreDispatcher dispatcher, CoreDispatcherPriority priority = CoreDispatcherPriority.Normal) => new (dispatcher, priority); +#endif + + /// + /// A helper function—for use within a coroutine—that returns control to the caller, and then immediately resumes execution on a thread pool thread. + /// + /// An object that you can . + public static TaskThreadSwitcher ResumeBackgroundAsync() => new(); + } +} +#endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs index 2f7ee0c5..d664a1db 100644 --- a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs +++ b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs @@ -291,32 +291,13 @@ private readonly PixelFormat StandardizePixelFormat(byte[] buffer) /// A which can be used to cancel the asynchronous task. /// A that represents the image contained in the frame buffer, or /// if the framebuffer does not contain any data. This can happen when DRM is enabled on the device. - public readonly Task ToBitmap(byte[] buffer, CoreDispatcher dispatcher, CancellationToken cancellationToken = default) + public readonly async Task ToBitmap(byte[] buffer, CoreDispatcher dispatcher, CancellationToken cancellationToken = default) { - FramebufferHeader self = this; - - if (dispatcher.HasThreadAccess) + if (dispatcher?.HasThreadAccess == false) { - return ToBitmap(buffer, cancellationToken); - } - else - { - TaskCompletionSource taskCompletionSource = new(); - - _ = dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () => - { - try - { - taskCompletionSource.SetResult(await self.ToBitmap(buffer, cancellationToken)); - } - catch (Exception e) - { - taskCompletionSource.SetException(e); - } - }); - - return taskCompletionSource.Task; + await dispatcher.ResumeForegroundAsync(); } + return await ToBitmap(buffer, cancellationToken).ConfigureAwait(false); } /// @@ -328,35 +309,13 @@ private readonly PixelFormat StandardizePixelFormat(byte[] buffer) /// A that represents the image contained in the frame buffer, or /// if the framebuffer does not contain any data. This can happen when DRM is enabled on the device. [ContractVersion(typeof(UniversalApiContract), 327680u)] - public readonly Task ToBitmap(byte[] buffer, DispatcherQueue dispatcher, CancellationToken cancellationToken = default) + public readonly async Task ToBitmap(byte[] buffer, DispatcherQueue dispatcher, CancellationToken cancellationToken = default) { - FramebufferHeader self = this; - - if (ApiInformation.IsMethodPresent("Windows.System.DispatcherQueue", "HasThreadAccess") && dispatcher.HasThreadAccess) + if (dispatcher?.HasThreadAccess == false) { - return ToBitmap(buffer, cancellationToken); - } - else - { - TaskCompletionSource taskCompletionSource = new(); - - if (!dispatcher.TryEnqueue(async () => - { - try - { - taskCompletionSource.SetResult(await self.ToBitmap(buffer, cancellationToken)); - } - catch (Exception e) - { - taskCompletionSource.SetException(e); - } - })) - { - taskCompletionSource.SetException(new InvalidOperationException("Failed to enqueue the operation")); - } - - return taskCompletionSource.Task; + await dispatcher.ResumeForegroundAsync(); } + return await ToBitmap(buffer, cancellationToken).ConfigureAwait(false); } /// From 2475deb879f6459676f182f84b364caa0014d986 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Sun, 29 Oct 2023 22:01:59 +0800 Subject: [PATCH 51/66] Change namespace Models to .Models Receivers to .Receivers Extensions to .Polyfills --- .../AdbClientTests.Async.cs | 4 +- .../AdbClientTests.cs | 4 +- .../AdbCommandLineClientTests.Async.cs | 3 +- .../AdbCommandLineClientTests.cs | 3 +- .../AdbServerTests.Async.cs | 3 +- .../AdbServerTests.cs | 3 +- .../AdbSocketTests.Async.cs | 3 +- .../AdbSocketTests.cs | 3 +- .../AdvancedSharpAdbClient.Tests.csproj | 50 +-------------- .../DeviceExtensionsTests.Async.cs | 3 +- .../DeviceCommands/DeviceExtensionsTests.cs | 3 +- .../Models/AndroidProcessTests.cs | 2 +- .../DeviceCommands/Models/VersionInfoTests.cs | 2 +- .../PackageManagerTests.Async.cs | 3 +- .../DeviceCommands/PackageManagerTests.cs | 3 +- .../EnvironmentVariablesReceiverTests.cs | 2 +- .../Receivers/GetPropReceiverTests.cs | 5 +- .../Receivers/InstallOutputReceiverTests.cs | 2 +- .../Receivers/PackageManagerReceiverTests.cs | 5 +- .../Receivers/ProcessOutputReceiverTests.cs | 2 +- .../Receivers/VersionInfoReceiverTests.cs | 12 ++-- .../DeviceMonitorTests.cs | 3 +- .../Dummys/DummyAdbClient.cs | 3 +- .../Dummys/DummyAdbSocket.cs | 3 +- .../Dummys/TracingAdbSocket.cs | 3 +- ...ionTests.cs => DateTimeExtensionsTests.cs} | 4 +- .../Extensions/EnumerableExtensionsTests.cs | 36 +++++++++++ .../Extensions/ExceptionExtensionsTests.cs | 2 +- .../Extensions/ExtensionsTests.cs | 25 -------- .../Models/AdbResponseTests.cs | 2 +- .../Models/AdbServerStatusTests.cs | 2 +- .../Models/DeviceDataTests.cs | 2 +- .../Models/ForwardDataTests.cs | 2 +- .../Models/ForwardSpecTests.cs | 2 +- .../Models/FramebufferHeaderTests.cs | 2 +- .../Models/FramebufferTests.cs | 2 +- .../Models/ShellStreamTests.cs | 2 +- .../Properties/GlobalUsings.cs | 14 +++++ .../Receivers/ConsoleOutputReceiverTests.cs | 5 +- .../SocketBasedTests.cs | 3 +- AdvancedSharpAdbClient/AdbClient.Async.cs | 2 - AdvancedSharpAdbClient/AdbClient.cs | 4 +- .../AdbCommandLineClient.Async.cs | 1 - .../AdbCommandLineClient.cs | 2 - AdvancedSharpAdbClient/AdbServer.Async.cs | 1 - AdvancedSharpAdbClient/AdbServer.cs | 1 - AdvancedSharpAdbClient/AdbSocket.Async.cs | 1 - AdvancedSharpAdbClient/AdbSocket.cs | 2 - .../DeviceCommands/Models/AndroidProcess.cs | 2 +- .../Models/Enums/AndroidProcessState.cs | 2 +- .../Enums/PackageInstallProgressState.cs | 2 +- .../Models/Enums/PerProcessFlags.cs | 2 +- .../Models/InstallProgressEventArgs.cs | 2 +- .../DeviceCommands/Models/NamespaceDoc.cs | 17 ++++++ .../DeviceCommands/Models/VersionInfo.cs | 2 +- .../DeviceCommands/NamespaceDoc.cs | 4 +- .../DeviceCommands/PackageManager.Async.cs | 1 - .../DeviceCommands/PackageManager.cs | 2 - .../Receivers/EnvironmentVariablesReceiver.cs | 2 +- .../Receivers/GetPropReceiver.cs | 2 +- .../Receivers/InfoOutputReceiver.cs | 2 +- .../Receivers/InstallOutputReceiver.cs | 2 +- .../DeviceCommands/Receivers/NamespaceDoc.cs | 17 ++++++ .../Receivers/PackageManagerReceiver.cs | 2 +- .../Receivers/ProcessOutputReceiver.cs | 2 +- .../Receivers/VersionInfoReceiver.cs | 2 +- AdvancedSharpAdbClient/DeviceMonitor.Async.cs | 1 - AdvancedSharpAdbClient/DeviceMonitor.cs | 2 - .../Exceptions/NamespaceDoc.cs | 4 +- .../Extensions/AdbClientExtensions.Async.cs | 2 - .../Extensions/AdbClientExtensions.cs | 1 - .../Attributes/BrowsableAttribute.cs | 61 ------------------- .../Extensions/Extensions.cs | 33 ---------- .../Extensions/LoggerExtensions.cs | 1 - .../Interfaces/IAdbClient.Async.cs | 2 - .../Interfaces/IAdbClient.cs | 2 - .../Interfaces/IAdbServer.Async.cs | 1 - .../Interfaces/IAdbServer.cs | 2 - .../Interfaces/IAdbSocket.cs | 1 - .../Logs/LogReader.Async.cs | 1 - AdvancedSharpAdbClient/Logs/LogReader.cs | 1 - AdvancedSharpAdbClient/Logs/NamespaceDoc.cs | 4 +- AdvancedSharpAdbClient/Models/AdbResponse.cs | 2 +- .../Models/AdbServerStatus.cs | 2 +- AdvancedSharpAdbClient/Models/ColorData.cs | 2 +- AdvancedSharpAdbClient/Models/DeviceData.cs | 2 +- .../Models/DeviceDataEventArgs.cs | 2 +- AdvancedSharpAdbClient/Models/Element.cs | 2 +- .../Models/Enums/AppStatus.cs | 2 +- .../Models/Enums/DeviceState.cs | 2 +- .../Models/Enums/ForwardProtocol.cs | 2 +- .../Models/Enums/StartServerResult.cs | 2 +- .../Models/Enums/SyncCommand.cs | 2 +- .../Models/Enums/TransportType.cs | 2 +- .../Models/Enums/UnixFileType.cs | 2 +- .../Models/FileStatistics.cs | 2 +- AdvancedSharpAdbClient/Models/ForwardData.cs | 2 +- AdvancedSharpAdbClient/Models/ForwardSpec.cs | 2 +- AdvancedSharpAdbClient/Models/Framebuffer.cs | 2 +- .../Models/FramebufferHeader.cs | 2 +- AdvancedSharpAdbClient/Models/NamespaceDoc.cs | 17 ++++++ AdvancedSharpAdbClient/Models/ShellStream.cs | 2 +- .../Models/SyncProgressChangedEventArgs.cs | 2 +- AdvancedSharpAdbClient/NamespaceDoc.cs | 2 + .../CallerArgumentExpressionAttribute.cs | 0 .../ExcludeFromCodeCoverageAttribute.cs | 0 .../Attributes/IsExternalInit.cs | 0 .../Attributes/NullableAttributes.cs | 0 .../Attributes/SerializableAttribute.cs | 0 .../Attributes/StackTraceHiddenAttribute.cs | 0 .../{Models => Polyfills}/DnsEndPoint.cs | 2 +- .../Extensions/DateTimeExtensions.cs | 2 +- .../Extensions/EnumerableExtensions.cs | 45 ++++++++++++++ .../Extensions/ExceptionExtensions.cs | 9 ++- .../Extensions/SocketExtensions.cs | 2 +- .../Extensions/StreamExtensions.cs | 2 +- .../Extensions/StringExtensions.cs | 2 +- .../Extensions/ThreadExtensions.cs | 8 +-- .../{Models => Polyfills}/HashCode.cs | 2 +- .../{ => Polyfills}/Interfaces/IProgress.cs | 2 +- .../Polyfills/NamespaceDoc.cs | 17 ++++++ .../{Models => Polyfills}/Point.cs | 2 +- .../{Models => Polyfills}/Rectangle.cs | 2 +- .../Properties/GlobalUsings.cs | 13 +++- .../Receivers/ConsoleOutputReceiver.cs | 4 +- .../Receivers/IShellOutputReceiver.cs | 2 +- .../Receivers/MultilineReceiver.cs | 2 +- .../Receivers/NamespaceDoc.cs | 18 ++++++ AdvancedSharpAdbClient/SyncService.Async.cs | 1 - AdvancedSharpAdbClient/SyncService.cs | 1 - 130 files changed, 307 insertions(+), 327 deletions(-) rename AdvancedSharpAdbClient.Tests/Extensions/{DateTimeExtensionTests.cs => DateTimeExtensionsTests.cs} (94%) create mode 100644 AdvancedSharpAdbClient.Tests/Extensions/EnumerableExtensionsTests.cs create mode 100644 AdvancedSharpAdbClient.Tests/Properties/GlobalUsings.cs create mode 100644 AdvancedSharpAdbClient/DeviceCommands/Models/NamespaceDoc.cs create mode 100644 AdvancedSharpAdbClient/DeviceCommands/Receivers/NamespaceDoc.cs delete mode 100644 AdvancedSharpAdbClient/Extensions/Attributes/BrowsableAttribute.cs create mode 100644 AdvancedSharpAdbClient/Models/NamespaceDoc.cs rename AdvancedSharpAdbClient/{Extensions => Polyfills}/Attributes/CallerArgumentExpressionAttribute.cs (100%) rename AdvancedSharpAdbClient/{Extensions => Polyfills}/Attributes/ExcludeFromCodeCoverageAttribute.cs (100%) rename AdvancedSharpAdbClient/{Extensions => Polyfills}/Attributes/IsExternalInit.cs (100%) rename AdvancedSharpAdbClient/{Extensions => Polyfills}/Attributes/NullableAttributes.cs (100%) rename AdvancedSharpAdbClient/{Extensions => Polyfills}/Attributes/SerializableAttribute.cs (100%) rename AdvancedSharpAdbClient/{Extensions => Polyfills}/Attributes/StackTraceHiddenAttribute.cs (100%) rename AdvancedSharpAdbClient/{Models => Polyfills}/DnsEndPoint.cs (99%) rename AdvancedSharpAdbClient/{ => Polyfills}/Extensions/DateTimeExtensions.cs (99%) create mode 100644 AdvancedSharpAdbClient/Polyfills/Extensions/EnumerableExtensions.cs rename AdvancedSharpAdbClient/{ => Polyfills}/Extensions/ExceptionExtensions.cs (93%) rename AdvancedSharpAdbClient/{ => Polyfills}/Extensions/SocketExtensions.cs (99%) rename AdvancedSharpAdbClient/{ => Polyfills}/Extensions/StreamExtensions.cs (99%) rename AdvancedSharpAdbClient/{ => Polyfills}/Extensions/StringExtensions.cs (99%) rename AdvancedSharpAdbClient/{ => Polyfills}/Extensions/ThreadExtensions.cs (96%) rename AdvancedSharpAdbClient/{Models => Polyfills}/HashCode.cs (99%) rename AdvancedSharpAdbClient/{ => Polyfills}/Interfaces/IProgress.cs (92%) create mode 100644 AdvancedSharpAdbClient/Polyfills/NamespaceDoc.cs rename AdvancedSharpAdbClient/{Models => Polyfills}/Point.cs (99%) rename AdvancedSharpAdbClient/{Models => Polyfills}/Rectangle.cs (99%) create mode 100644 AdvancedSharpAdbClient/Receivers/NamespaceDoc.cs diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs index b7cc2aae..a21db936 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs @@ -1,6 +1,4 @@ -using AdvancedSharpAdbClient.Exceptions; -using AdvancedSharpAdbClient.Logs; -using System; +using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs index 01bc606f..644b2197 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs @@ -1,6 +1,4 @@ -using AdvancedSharpAdbClient.Exceptions; -using AdvancedSharpAdbClient.Logs; -using System; +using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; diff --git a/AdvancedSharpAdbClient.Tests/AdbCommandLineClientTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbCommandLineClientTests.Async.cs index 29a55dec..06629c38 100644 --- a/AdvancedSharpAdbClient.Tests/AdbCommandLineClientTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbCommandLineClientTests.Async.cs @@ -1,5 +1,4 @@ -using AdvancedSharpAdbClient.Exceptions; -using System; +using System; using Xunit; namespace AdvancedSharpAdbClient.Tests diff --git a/AdvancedSharpAdbClient.Tests/AdbCommandLineClientTests.cs b/AdvancedSharpAdbClient.Tests/AdbCommandLineClientTests.cs index 532f06ea..00d8358c 100644 --- a/AdvancedSharpAdbClient.Tests/AdbCommandLineClientTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbCommandLineClientTests.cs @@ -1,5 +1,4 @@ -using AdvancedSharpAdbClient.Exceptions; -using System; +using System; using Xunit; namespace AdvancedSharpAdbClient.Tests diff --git a/AdvancedSharpAdbClient.Tests/AdbServerTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbServerTests.Async.cs index c4c60d0b..4f253a5c 100644 --- a/AdvancedSharpAdbClient.Tests/AdbServerTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbServerTests.Async.cs @@ -1,5 +1,4 @@ -using AdvancedSharpAdbClient.Exceptions; -using NSubstitute; +using NSubstitute; using NSubstitute.ExceptionExtensions; using System; using System.Net.Sockets; diff --git a/AdvancedSharpAdbClient.Tests/AdbServerTests.cs b/AdvancedSharpAdbClient.Tests/AdbServerTests.cs index ddb218de..6a6fa61d 100644 --- a/AdvancedSharpAdbClient.Tests/AdbServerTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbServerTests.cs @@ -1,5 +1,4 @@ -using AdvancedSharpAdbClient.Exceptions; -using NSubstitute; +using NSubstitute; using System; using System.Net; using System.Net.Sockets; diff --git a/AdvancedSharpAdbClient.Tests/AdbSocketTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbSocketTests.Async.cs index 7c27e58d..8d2a6697 100644 --- a/AdvancedSharpAdbClient.Tests/AdbSocketTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbSocketTests.Async.cs @@ -1,5 +1,4 @@ -using AdvancedSharpAdbClient.Exceptions; -using System; +using System; using System.IO; using System.Text; using System.Threading; diff --git a/AdvancedSharpAdbClient.Tests/AdbSocketTests.cs b/AdvancedSharpAdbClient.Tests/AdbSocketTests.cs index 54c47c13..b98f2ec4 100644 --- a/AdvancedSharpAdbClient.Tests/AdbSocketTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbSocketTests.cs @@ -1,5 +1,4 @@ -using AdvancedSharpAdbClient.Exceptions; -using System; +using System; using System.IO; using System.Text; using Xunit; diff --git a/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj b/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj index 3aacef3a..3ac4d63b 100644 --- a/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj +++ b/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj @@ -24,55 +24,7 @@ - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - + PreserveNewest diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs index db7567f4..60b4c63e 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs @@ -1,5 +1,4 @@ -using AdvancedSharpAdbClient.Tests; -using NSubstitute; +using NSubstitute; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.cs index d82c9360..29b923f6 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.cs @@ -1,5 +1,4 @@ -using AdvancedSharpAdbClient.Tests; -using NSubstitute; +using NSubstitute; using System.Collections.Generic; using System.Linq; using Xunit; diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/Models/AndroidProcessTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/Models/AndroidProcessTests.cs index 5749e36a..99b8e753 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/Models/AndroidProcessTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/Models/AndroidProcessTests.cs @@ -7,7 +7,7 @@ using System; using Xunit; -namespace AdvancedSharpAdbClient.DeviceCommands.Tests +namespace AdvancedSharpAdbClient.Models.DeviceCommands.Tests { /// /// Tests the class. diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/Models/VersionInfoTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/Models/VersionInfoTests.cs index cbf51678..3d93c4fa 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/Models/VersionInfoTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/Models/VersionInfoTests.cs @@ -1,6 +1,6 @@ using Xunit; -namespace AdvancedSharpAdbClient.DeviceCommands.Tests +namespace AdvancedSharpAdbClient.Models.DeviceCommands.Tests { /// /// Tests the class. diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs index 91f1564f..687129cd 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs @@ -1,5 +1,4 @@ -using AdvancedSharpAdbClient.Tests; -using System.IO; +using System.IO; using Xunit; namespace AdvancedSharpAdbClient.DeviceCommands.Tests diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs index 7681f415..326c6111 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs @@ -1,5 +1,4 @@ -using AdvancedSharpAdbClient.Tests; -using NSubstitute; +using NSubstitute; using System; using System.IO; using Xunit; diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/EnvironmentVariablesReceiverTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/EnvironmentVariablesReceiverTests.cs index f37383d1..88bd4b29 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/EnvironmentVariablesReceiverTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/EnvironmentVariablesReceiverTests.cs @@ -1,6 +1,6 @@ using Xunit; -namespace AdvancedSharpAdbClient.DeviceCommands.Tests +namespace AdvancedSharpAdbClient.Receivers.DeviceCommands.Tests { /// /// Tests the class. diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/GetPropReceiverTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/GetPropReceiverTests.cs index 7600e3eb..da7f71b9 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/GetPropReceiverTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/GetPropReceiverTests.cs @@ -1,8 +1,7 @@ -using AdvancedSharpAdbClient.Tests; -using System.Collections.Generic; +using System.Collections.Generic; using Xunit; -namespace AdvancedSharpAdbClient.DeviceCommands.Tests +namespace AdvancedSharpAdbClient.Receivers.DeviceCommands.Tests { public class GetPropReceiverTests { diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/InstallOutputReceiverTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/InstallOutputReceiverTests.cs index e615b048..a0871234 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/InstallOutputReceiverTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/InstallOutputReceiverTests.cs @@ -1,6 +1,6 @@ using Xunit; -namespace AdvancedSharpAdbClient.DeviceCommands.Tests +namespace AdvancedSharpAdbClient.Receivers.DeviceCommands.Tests { public class InstallOutputReceiverTests { diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/PackageManagerReceiverTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/PackageManagerReceiverTests.cs index 3473a896..58023d12 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/PackageManagerReceiverTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/PackageManagerReceiverTests.cs @@ -1,7 +1,6 @@ -using AdvancedSharpAdbClient.Tests; -using Xunit; +using Xunit; -namespace AdvancedSharpAdbClient.DeviceCommands.Tests +namespace AdvancedSharpAdbClient.Receivers.DeviceCommands.Tests { public class PackageManagerReceiverTests { diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/ProcessOutputReceiverTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/ProcessOutputReceiverTests.cs index 78c82b4c..80fb796d 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/ProcessOutputReceiverTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/ProcessOutputReceiverTests.cs @@ -1,6 +1,6 @@ using Xunit; -namespace AdvancedSharpAdbClient.DeviceCommands.Tests +namespace AdvancedSharpAdbClient.Receivers.DeviceCommands.Tests { /// /// Tests the class. diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/VersionInfoReceiverTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/VersionInfoReceiverTests.cs index 630ee6f4..879856d1 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/VersionInfoReceiverTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/VersionInfoReceiverTests.cs @@ -8,7 +8,7 @@ using System.IO; using Xunit; -namespace AdvancedSharpAdbClient.DeviceCommands.Tests +namespace AdvancedSharpAdbClient.Receivers.DeviceCommands.Tests { /// /// Tests the class. @@ -31,11 +31,11 @@ public void GetVersionTest() Assert.Null(receiver.GetVersionCode(string.Empty)); Assert.Null(receiver.GetVersionCode(" versionCode=10210targetSdk=18")); - Assert.Equal("4.7.1", (string)receiver.GetVersionName(" versionName=4.7.1")); - Assert.Null((string)receiver.GetVersionName(null)); - Assert.Null((string)receiver.GetVersionName(" test")); - Assert.Null((string)receiver.GetVersionName(" versionName")); - Assert.Equal(string.Empty, (string)receiver.GetVersionName(" versionName=")); + Assert.Equal("4.7.1", receiver.GetVersionName(" versionName=4.7.1")); + Assert.Null(receiver.GetVersionName(null)); + Assert.Null(receiver.GetVersionName(" test")); + Assert.Null(receiver.GetVersionName(" versionName")); + Assert.Equal(string.Empty, receiver.GetVersionName(" versionName=")); string dumpsys = string.Join(Environment.NewLine, File.ReadAllLines(@"Assets/dumpsys_package.txt")); receiver = new VersionInfoReceiver(); diff --git a/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs b/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs index 972eba33..42ec5a56 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs @@ -1,5 +1,4 @@ -using AdvancedSharpAdbClient.Logs; -using System; +using System; using System.Linq; using System.Threading; using Xunit; diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs index c9e31e10..04344201 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs @@ -1,5 +1,4 @@ -using AdvancedSharpAdbClient.Logs; -using System; +using System; using System.Collections.Generic; using System.Drawing; using System.IO; diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs index 168af890..fb8dada9 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs @@ -1,5 +1,4 @@ -using AdvancedSharpAdbClient.Exceptions; -using System; +using System; using System.Collections.Generic; using System.IO; using System.Net.Sockets; diff --git a/AdvancedSharpAdbClient.Tests/Dummys/TracingAdbSocket.cs b/AdvancedSharpAdbClient.Tests/Dummys/TracingAdbSocket.cs index 8742595b..595ac507 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/TracingAdbSocket.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/TracingAdbSocket.cs @@ -1,5 +1,4 @@ -using AdvancedSharpAdbClient.Exceptions; -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; diff --git a/AdvancedSharpAdbClient.Tests/Extensions/DateTimeExtensionTests.cs b/AdvancedSharpAdbClient.Tests/Extensions/DateTimeExtensionsTests.cs similarity index 94% rename from AdvancedSharpAdbClient.Tests/Extensions/DateTimeExtensionTests.cs rename to AdvancedSharpAdbClient.Tests/Extensions/DateTimeExtensionsTests.cs index 9e7d5923..e74c7fc2 100644 --- a/AdvancedSharpAdbClient.Tests/Extensions/DateTimeExtensionTests.cs +++ b/AdvancedSharpAdbClient.Tests/Extensions/DateTimeExtensionsTests.cs @@ -1,12 +1,12 @@ using System; using Xunit; -namespace AdvancedSharpAdbClient.Tests +namespace AdvancedSharpAdbClient.Polyfills.Tests { /// /// Tests the class. /// - public class DateTimeExtensionTests + public class DateTimeExtensionsTests { /// /// Tests the method. diff --git a/AdvancedSharpAdbClient.Tests/Extensions/EnumerableExtensionsTests.cs b/AdvancedSharpAdbClient.Tests/Extensions/EnumerableExtensionsTests.cs new file mode 100644 index 00000000..86068dfe --- /dev/null +++ b/AdvancedSharpAdbClient.Tests/Extensions/EnumerableExtensionsTests.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; +using Xunit; + +namespace AdvancedSharpAdbClient.Polyfills.Tests +{ + /// + /// Tests the class. + /// + public class EnumerableExtensionsTests + { + /// + /// Tests the method. + /// + [Fact] + public void AddRangeTest() + { + int[] numbs = [6, 7, 8, 9, 10]; + + List list = [1, 2, 3, 4, 5]; + list.AddRange(numbs); + Assert.Equal(10, list.Count); + Assert.Equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], list); + + HashSet hashSet = [1, 2, 3, 4, 5]; + hashSet.AddRange(numbs); + Assert.Equal(10, hashSet.Count); + Assert.Equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], hashSet); + + Collection collection = [1, 2, 3, 4, 5]; + collection.AddRange(numbs); + Assert.Equal(10, collection.Count); + Assert.Equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], collection); + } + } +} diff --git a/AdvancedSharpAdbClient.Tests/Extensions/ExceptionExtensionsTests.cs b/AdvancedSharpAdbClient.Tests/Extensions/ExceptionExtensionsTests.cs index 8de7a169..4aa180e5 100644 --- a/AdvancedSharpAdbClient.Tests/Extensions/ExceptionExtensionsTests.cs +++ b/AdvancedSharpAdbClient.Tests/Extensions/ExceptionExtensionsTests.cs @@ -1,7 +1,7 @@ using System; using Xunit; -namespace AdvancedSharpAdbClient.Tests +namespace AdvancedSharpAdbClient.Polyfills.Tests { /// /// Tests the class. diff --git a/AdvancedSharpAdbClient.Tests/Extensions/ExtensionsTests.cs b/AdvancedSharpAdbClient.Tests/Extensions/ExtensionsTests.cs index a1442504..cc86cdfc 100644 --- a/AdvancedSharpAdbClient.Tests/Extensions/ExtensionsTests.cs +++ b/AdvancedSharpAdbClient.Tests/Extensions/ExtensionsTests.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Linq; using System.Threading.Tasks; using Xunit; @@ -11,30 +10,6 @@ namespace AdvancedSharpAdbClient.Tests /// public class ExtensionsTests { - /// - /// Tests the method. - /// - [Fact] - public void AddRangeTest() - { - int[] numbs = [6, 7, 8, 9, 10]; - - List list = [1, 2, 3, 4, 5]; - list.AddRange(numbs); - Assert.Equal(10, list.Count); - Assert.Equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], list); - - HashSet hashSet = [1, 2, 3, 4, 5]; - hashSet.AddRange(numbs); - Assert.Equal(10, hashSet.Count); - Assert.Equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], hashSet); - - Collection collection = [1, 2, 3, 4, 5]; - collection.AddRange(numbs); - Assert.Equal(10, collection.Count); - Assert.Equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], collection); - } - [Fact] public async void TaskToArrayTest() { diff --git a/AdvancedSharpAdbClient.Tests/Models/AdbResponseTests.cs b/AdvancedSharpAdbClient.Tests/Models/AdbResponseTests.cs index 7443b278..e896cc6b 100644 --- a/AdvancedSharpAdbClient.Tests/Models/AdbResponseTests.cs +++ b/AdvancedSharpAdbClient.Tests/Models/AdbResponseTests.cs @@ -1,6 +1,6 @@ using Xunit; -namespace AdvancedSharpAdbClient.Tests +namespace AdvancedSharpAdbClient.Models.Tests { /// /// Tests the class. diff --git a/AdvancedSharpAdbClient.Tests/Models/AdbServerStatusTests.cs b/AdvancedSharpAdbClient.Tests/Models/AdbServerStatusTests.cs index bd9f8f7c..624ee170 100644 --- a/AdvancedSharpAdbClient.Tests/Models/AdbServerStatusTests.cs +++ b/AdvancedSharpAdbClient.Tests/Models/AdbServerStatusTests.cs @@ -1,7 +1,7 @@ using System; using Xunit; -namespace AdvancedSharpAdbClient.Tests +namespace AdvancedSharpAdbClient.Models.Tests { /// /// Tests the class. diff --git a/AdvancedSharpAdbClient.Tests/Models/DeviceDataTests.cs b/AdvancedSharpAdbClient.Tests/Models/DeviceDataTests.cs index 6b413da8..cf001fa1 100644 --- a/AdvancedSharpAdbClient.Tests/Models/DeviceDataTests.cs +++ b/AdvancedSharpAdbClient.Tests/Models/DeviceDataTests.cs @@ -1,7 +1,7 @@ using System; using Xunit; -namespace AdvancedSharpAdbClient.Tests +namespace AdvancedSharpAdbClient.Models.Tests { /// /// Tests the class. diff --git a/AdvancedSharpAdbClient.Tests/Models/ForwardDataTests.cs b/AdvancedSharpAdbClient.Tests/Models/ForwardDataTests.cs index ae75dcf9..30958db1 100644 --- a/AdvancedSharpAdbClient.Tests/Models/ForwardDataTests.cs +++ b/AdvancedSharpAdbClient.Tests/Models/ForwardDataTests.cs @@ -1,6 +1,6 @@ using Xunit; -namespace AdvancedSharpAdbClient.Tests +namespace AdvancedSharpAdbClient.Models.Tests { /// /// Tests the class. diff --git a/AdvancedSharpAdbClient.Tests/Models/ForwardSpecTests.cs b/AdvancedSharpAdbClient.Tests/Models/ForwardSpecTests.cs index b3659a74..276729b2 100644 --- a/AdvancedSharpAdbClient.Tests/Models/ForwardSpecTests.cs +++ b/AdvancedSharpAdbClient.Tests/Models/ForwardSpecTests.cs @@ -1,7 +1,7 @@ using System; using Xunit; -namespace AdvancedSharpAdbClient.Tests +namespace AdvancedSharpAdbClient.Models.Tests { /// /// Tests the class. diff --git a/AdvancedSharpAdbClient.Tests/Models/FramebufferHeaderTests.cs b/AdvancedSharpAdbClient.Tests/Models/FramebufferHeaderTests.cs index 4373c24e..39c5f027 100644 --- a/AdvancedSharpAdbClient.Tests/Models/FramebufferHeaderTests.cs +++ b/AdvancedSharpAdbClient.Tests/Models/FramebufferHeaderTests.cs @@ -4,7 +4,7 @@ using System.Runtime.Versioning; using Xunit; -namespace AdvancedSharpAdbClient.Tests +namespace AdvancedSharpAdbClient.Models.Tests { /// /// Tests the struct. diff --git a/AdvancedSharpAdbClient.Tests/Models/FramebufferTests.cs b/AdvancedSharpAdbClient.Tests/Models/FramebufferTests.cs index b113192b..1c90c8f8 100644 --- a/AdvancedSharpAdbClient.Tests/Models/FramebufferTests.cs +++ b/AdvancedSharpAdbClient.Tests/Models/FramebufferTests.cs @@ -5,7 +5,7 @@ using System.Net; using Xunit; -namespace AdvancedSharpAdbClient.Tests.Models +namespace AdvancedSharpAdbClient.Models.Tests { /// /// Tests the class. diff --git a/AdvancedSharpAdbClient.Tests/Models/ShellStreamTests.cs b/AdvancedSharpAdbClient.Tests/Models/ShellStreamTests.cs index 0afc4938..112bc28f 100644 --- a/AdvancedSharpAdbClient.Tests/Models/ShellStreamTests.cs +++ b/AdvancedSharpAdbClient.Tests/Models/ShellStreamTests.cs @@ -3,7 +3,7 @@ using System.Text; using Xunit; -namespace AdvancedSharpAdbClient.Tests +namespace AdvancedSharpAdbClient.Models.Tests { public class ShellStreamTests { diff --git a/AdvancedSharpAdbClient.Tests/Properties/GlobalUsings.cs b/AdvancedSharpAdbClient.Tests/Properties/GlobalUsings.cs new file mode 100644 index 00000000..bf4ebe71 --- /dev/null +++ b/AdvancedSharpAdbClient.Tests/Properties/GlobalUsings.cs @@ -0,0 +1,14 @@ +#region AdvancedSharpAdbClient +global using AdvancedSharpAdbClient.DeviceCommands; +global using AdvancedSharpAdbClient.Exceptions; +global using AdvancedSharpAdbClient.Logs; +global using AdvancedSharpAdbClient.Models; +global using AdvancedSharpAdbClient.Models.DeviceCommands; +global using AdvancedSharpAdbClient.Polyfills; +global using AdvancedSharpAdbClient.Receivers; +global using AdvancedSharpAdbClient.Receivers.DeviceCommands; +#endregion + +#region AdvancedSharpAdbClient.Tests +global using AdvancedSharpAdbClient.Tests; +#endregion \ No newline at end of file diff --git a/AdvancedSharpAdbClient.Tests/Receivers/ConsoleOutputReceiverTests.cs b/AdvancedSharpAdbClient.Tests/Receivers/ConsoleOutputReceiverTests.cs index a7e804de..46da400c 100644 --- a/AdvancedSharpAdbClient.Tests/Receivers/ConsoleOutputReceiverTests.cs +++ b/AdvancedSharpAdbClient.Tests/Receivers/ConsoleOutputReceiverTests.cs @@ -1,9 +1,8 @@ -using AdvancedSharpAdbClient.Exceptions; -using System; +using System; using System.IO; using Xunit; -namespace AdvancedSharpAdbClient.Tests +namespace AdvancedSharpAdbClient.Receivers.Tests { /// /// Tests the class. diff --git a/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs b/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs index f92622a1..c57f01fb 100644 --- a/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs +++ b/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs @@ -1,5 +1,4 @@ -using AdvancedSharpAdbClient.Tests; -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index 4efdd51a..e358902c 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -3,8 +3,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; -using AdvancedSharpAdbClient.Logs; using System; using System.Collections.Generic; using System.Diagnostics; diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index 8a7dca93..9efd2b22 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -2,10 +2,9 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; -using AdvancedSharpAdbClient.Logs; using System; using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; @@ -1152,6 +1151,7 @@ protected static void EnsureDevice(DeviceData device) /// See as the class. /// [Obsolete("AdvancedAdbClient is too long to remember. Please use AdbClient instead.")] + [EditorBrowsable(EditorBrowsableState.Never)] public class AdvancedAdbClient : AdbClient { } diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs index 685062d0..1b117ef9 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs @@ -3,7 +3,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; using System; using System.Collections.Generic; using System.ComponentModel; diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.cs index c7b0bde4..504100e1 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.cs @@ -2,8 +2,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; -using AdvancedSharpAdbClient.Logs; using System; using System.Collections.Generic; using System.ComponentModel; diff --git a/AdvancedSharpAdbClient/AdbServer.Async.cs b/AdvancedSharpAdbClient/AdbServer.Async.cs index 3c5a314a..bfbf35bd 100644 --- a/AdvancedSharpAdbClient/AdbServer.Async.cs +++ b/AdvancedSharpAdbClient/AdbServer.Async.cs @@ -3,7 +3,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; using System; using System.Globalization; using System.Net.Sockets; diff --git a/AdvancedSharpAdbClient/AdbServer.cs b/AdvancedSharpAdbClient/AdbServer.cs index 95032d93..1ac66757 100644 --- a/AdvancedSharpAdbClient/AdbServer.cs +++ b/AdvancedSharpAdbClient/AdbServer.cs @@ -2,7 +2,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; using System; using System.Globalization; using System.Net; diff --git a/AdvancedSharpAdbClient/AdbSocket.Async.cs b/AdvancedSharpAdbClient/AdbSocket.Async.cs index 46414c31..b28c484e 100644 --- a/AdvancedSharpAdbClient/AdbSocket.Async.cs +++ b/AdvancedSharpAdbClient/AdbSocket.Async.cs @@ -3,7 +3,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; using System; using System.Globalization; using System.IO; diff --git a/AdvancedSharpAdbClient/AdbSocket.cs b/AdvancedSharpAdbClient/AdbSocket.cs index c9bb1fd2..9b2136ba 100644 --- a/AdvancedSharpAdbClient/AdbSocket.cs +++ b/AdvancedSharpAdbClient/AdbSocket.cs @@ -2,8 +2,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; -using AdvancedSharpAdbClient.Logs; using System; using System.Globalization; using System.IO; diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs index 14e0e1de..683dd4fd 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs @@ -4,7 +4,7 @@ using System; -namespace AdvancedSharpAdbClient.DeviceCommands +namespace AdvancedSharpAdbClient.Models.DeviceCommands { /// /// Represents a process running on an Android device. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/Enums/AndroidProcessState.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/Enums/AndroidProcessState.cs index 7d5eaf50..5fefe092 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/Enums/AndroidProcessState.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/Enums/AndroidProcessState.cs @@ -2,7 +2,7 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -namespace AdvancedSharpAdbClient.DeviceCommands +namespace AdvancedSharpAdbClient.Models.DeviceCommands { /// /// Represents the state of a process running on an Android device. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/Enums/PackageInstallProgressState.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/Enums/PackageInstallProgressState.cs index 9ac2ccf4..7834380d 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/Enums/PackageInstallProgressState.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/Enums/PackageInstallProgressState.cs @@ -2,7 +2,7 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -namespace AdvancedSharpAdbClient.DeviceCommands +namespace AdvancedSharpAdbClient.Models.DeviceCommands { /// /// Represents the state of the installation. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/Enums/PerProcessFlags.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/Enums/PerProcessFlags.cs index 429321cc..474d2f08 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/Enums/PerProcessFlags.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/Enums/PerProcessFlags.cs @@ -4,7 +4,7 @@ using System; -namespace AdvancedSharpAdbClient.DeviceCommands +namespace AdvancedSharpAdbClient.Models.DeviceCommands { /// /// Per process flags. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/InstallProgressEventArgs.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/InstallProgressEventArgs.cs index 002df30d..c4c533e3 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/InstallProgressEventArgs.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/InstallProgressEventArgs.cs @@ -4,7 +4,7 @@ using System; -namespace AdvancedSharpAdbClient.DeviceCommands +namespace AdvancedSharpAdbClient.Models.DeviceCommands { /// /// Represents the state of the installation for . diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/NamespaceDoc.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/NamespaceDoc.cs new file mode 100644 index 00000000..91d43186 --- /dev/null +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/NamespaceDoc.cs @@ -0,0 +1,17 @@ +// +// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. +// + +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace AdvancedSharpAdbClient.Models.DeviceCommands +{ + /// + /// The classes in this namespace provide models for . + /// + /// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. + [CompilerGenerated] + [EditorBrowsable(EditorBrowsableState.Never)] + internal class NamespaceDoc : Models.NamespaceDoc { } +} diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/VersionInfo.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/VersionInfo.cs index 34bce4d9..a52af84b 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/VersionInfo.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/VersionInfo.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -namespace AdvancedSharpAdbClient.DeviceCommands +namespace AdvancedSharpAdbClient.Models.DeviceCommands { /// /// Represents a version of an Android application. diff --git a/AdvancedSharpAdbClient/DeviceCommands/NamespaceDoc.cs b/AdvancedSharpAdbClient/DeviceCommands/NamespaceDoc.cs index 4a19a9c9..e29ebbdb 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/NamespaceDoc.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/NamespaceDoc.cs @@ -2,6 +2,7 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // +using System.ComponentModel; using System.Runtime.CompilerServices; namespace AdvancedSharpAdbClient.DeviceCommands @@ -11,5 +12,6 @@ namespace AdvancedSharpAdbClient.DeviceCommands /// /// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. [CompilerGenerated] - internal class NamespaceDoc { } + [EditorBrowsable(EditorBrowsableState.Never)] + internal class NamespaceDoc : AdvancedSharpAdbClient.NamespaceDoc { } } diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs index 71ac1bb0..04fb2a45 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs @@ -3,7 +3,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; using System; using System.Collections.Generic; using System.Diagnostics; diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs index abf0ad0b..6e4373bf 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs @@ -2,8 +2,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; -using AdvancedSharpAdbClient.Logs; using System; using System.Collections.Generic; using System.Diagnostics; diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/EnvironmentVariablesReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/EnvironmentVariablesReceiver.cs index 009c5b0e..40d72da0 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/EnvironmentVariablesReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/EnvironmentVariablesReceiver.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Text.RegularExpressions; -namespace AdvancedSharpAdbClient.DeviceCommands +namespace AdvancedSharpAdbClient.Receivers.DeviceCommands { /// /// Processes the output of the printenv command, which dumps all environment variables of an Android device. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs index 78399fb8..a7caf189 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Text.RegularExpressions; -namespace AdvancedSharpAdbClient.DeviceCommands +namespace AdvancedSharpAdbClient.Receivers.DeviceCommands { /// /// Parses the output of the getprop command, which lists all properties of an Android device. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InfoOutputReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InfoOutputReceiver.cs index 008b9671..f99485af 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InfoOutputReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InfoOutputReceiver.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -namespace AdvancedSharpAdbClient.DeviceCommands +namespace AdvancedSharpAdbClient.Receivers.DeviceCommands { /// /// Processes command line output of a adb shell command. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs index b42dd25b..29e91c98 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Text.RegularExpressions; -namespace AdvancedSharpAdbClient.DeviceCommands +namespace AdvancedSharpAdbClient.Receivers.DeviceCommands { /// /// Processes output of the pm install command. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/NamespaceDoc.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/NamespaceDoc.cs new file mode 100644 index 00000000..8c2de411 --- /dev/null +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/NamespaceDoc.cs @@ -0,0 +1,17 @@ +// +// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. +// + +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace AdvancedSharpAdbClient.Receivers.DeviceCommands +{ + /// + /// The classes in this namespace provide receivers for . + /// + /// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. + [CompilerGenerated] + [EditorBrowsable(EditorBrowsableState.Never)] + internal class NamespaceDoc : Receivers.NamespaceDoc { } +} diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/PackageManagerReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/PackageManagerReceiver.cs index 979bc1c7..9ea82c3b 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/PackageManagerReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/PackageManagerReceiver.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; -namespace AdvancedSharpAdbClient.DeviceCommands +namespace AdvancedSharpAdbClient.Receivers.DeviceCommands { /// /// Parses the output of the various pm commands. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs index 9d4e2a47..bd76bbdb 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; -namespace AdvancedSharpAdbClient.DeviceCommands +namespace AdvancedSharpAdbClient.Receivers.DeviceCommands { /// /// Parses the output of a cat /proc/[pid]/stat command. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/VersionInfoReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/VersionInfoReceiver.cs index 731bca84..c9d4d37c 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/VersionInfoReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/VersionInfoReceiver.cs @@ -5,7 +5,7 @@ using System; using System.Text.RegularExpressions; -namespace AdvancedSharpAdbClient.DeviceCommands +namespace AdvancedSharpAdbClient.Receivers.DeviceCommands { /// /// Processes command line output of the dumpsys package command. diff --git a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs index c9bc84f4..56a2d3cd 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs @@ -3,7 +3,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; using System; using System.Diagnostics.CodeAnalysis; using System.Net.Sockets; diff --git a/AdvancedSharpAdbClient/DeviceMonitor.cs b/AdvancedSharpAdbClient/DeviceMonitor.cs index a982a45b..b9318815 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.cs @@ -2,8 +2,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; -using AdvancedSharpAdbClient.Logs; using System; using System.Collections.Generic; using System.Collections.ObjectModel; diff --git a/AdvancedSharpAdbClient/Exceptions/NamespaceDoc.cs b/AdvancedSharpAdbClient/Exceptions/NamespaceDoc.cs index dfa29824..bf3b8d99 100644 --- a/AdvancedSharpAdbClient/Exceptions/NamespaceDoc.cs +++ b/AdvancedSharpAdbClient/Exceptions/NamespaceDoc.cs @@ -3,6 +3,7 @@ // using System; +using System.ComponentModel; using System.Runtime.CompilerServices; namespace AdvancedSharpAdbClient.Exceptions @@ -12,5 +13,6 @@ namespace AdvancedSharpAdbClient.Exceptions /// /// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. [CompilerGenerated] - internal class NamespaceDoc { } + [EditorBrowsable(EditorBrowsableState.Never)] + internal class NamespaceDoc : AdvancedSharpAdbClient.NamespaceDoc { } } diff --git a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs index 03decccc..1c81e7fc 100644 --- a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs +++ b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs @@ -3,8 +3,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; -using AdvancedSharpAdbClient.Logs; using System; using System.Collections.Generic; using System.IO; diff --git a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.cs b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.cs index f2d77896..41467308 100644 --- a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.cs +++ b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.cs @@ -2,7 +2,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; using System; using System.Linq; using System.Net; diff --git a/AdvancedSharpAdbClient/Extensions/Attributes/BrowsableAttribute.cs b/AdvancedSharpAdbClient/Extensions/Attributes/BrowsableAttribute.cs deleted file mode 100644 index e2959119..00000000 --- a/AdvancedSharpAdbClient/Extensions/Attributes/BrowsableAttribute.cs +++ /dev/null @@ -1,61 +0,0 @@ -#if NETSTANDARD && !NETSTANDARD2_0_OR_GREATER || NETCORE && !UAP10_0_15138_0 -// 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; - -namespace System.ComponentModel -{ - /// - /// Specifies whether a property or event should be displayed in a property - /// browsing window. - /// - [AttributeUsage(AttributeTargets.All)] - internal sealed class BrowsableAttribute : Attribute - { - /// - /// Specifies that a property or event can be modified at design time. - /// This field is read-only. - /// - public static readonly BrowsableAttribute Yes = new(true); - - /// - /// Specifies that a property or event cannot be modified at design time.' - /// This field is read-only. - /// - public static readonly BrowsableAttribute No = new(false); - - /// - /// Specifies the default value for the , which is . - /// This field is read-only. - /// - public static readonly BrowsableAttribute Default = Yes; - - /// - /// Initializes a new instance of the class. - /// - public BrowsableAttribute(bool browsable) - { - Browsable = browsable; - } - - /// - /// Gets a value indicating whether an object is browsable. - /// - public bool Browsable { get; } - - /// - public override bool Equals([NotNullWhen(true)] object? obj) => - obj is BrowsableAttribute other && other.Browsable == Browsable; - - /// - public override int GetHashCode() => Browsable.GetHashCode(); - - /// - /// Determines if this attribute is the default. - /// - /// if the attribute is the default value for this attribute class; otherwise, . - public bool IsDefaultAttribute() => Equals(Default); - } -} -#endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Extensions/Extensions.cs b/AdvancedSharpAdbClient/Extensions/Extensions.cs index 25b45854..3d373dbf 100644 --- a/AdvancedSharpAdbClient/Extensions/Extensions.cs +++ b/AdvancedSharpAdbClient/Extensions/Extensions.cs @@ -17,39 +17,6 @@ internal static class Extensions { public static char[] NewLineSeparator { get; } = ['\r', '\n']; - /// - /// Adds the elements of the specified collection to the end of the . - /// - /// The type of the elements of . - /// The to be added. - /// The collection whose elements should be added to the end of the . - /// The collection itself cannot be , but it can contain elements that are - /// , if type is a reference type. - /// or is null. - public static void AddRange(this ICollection source, IEnumerable collection) - { - ExceptionExtensions.ThrowIfNull(source); - ExceptionExtensions.ThrowIfNull(collection); - - if (source is List list) - { - list.AddRange(collection); - } -#if !NETFRAMEWORK || NET40_OR_GREATER - else if (source is ISet set) - { - set.UnionWith(collection); - } -#endif - else - { - foreach (TSource item in collection) - { - source.Add(item); - } - } - } - /// /// Creates a from the specified host and port information. /// diff --git a/AdvancedSharpAdbClient/Extensions/LoggerExtensions.cs b/AdvancedSharpAdbClient/Extensions/LoggerExtensions.cs index 6f7e31e4..14566e73 100644 --- a/AdvancedSharpAdbClient/Extensions/LoggerExtensions.cs +++ b/AdvancedSharpAdbClient/Extensions/LoggerExtensions.cs @@ -2,7 +2,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Logs; using System; namespace AdvancedSharpAdbClient diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs b/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs index bb3e8f7a..5e6e5440 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs @@ -3,8 +3,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; -using AdvancedSharpAdbClient.Logs; using System; using System.Collections.Generic; using System.IO; diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs b/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs index 12b48fe4..9fafa91b 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs @@ -2,8 +2,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; -using AdvancedSharpAdbClient.Logs; using System; using System.Collections.Generic; using System.IO; diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbServer.Async.cs b/AdvancedSharpAdbClient/Interfaces/IAdbServer.Async.cs index abc63422..49fa3840 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbServer.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbServer.Async.cs @@ -3,7 +3,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; using System.Threading; namespace AdvancedSharpAdbClient diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbServer.cs b/AdvancedSharpAdbClient/Interfaces/IAdbServer.cs index 3e3ffe71..3ad593ae 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbServer.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbServer.cs @@ -2,8 +2,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; - namespace AdvancedSharpAdbClient { /// diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbSocket.cs b/AdvancedSharpAdbClient/Interfaces/IAdbSocket.cs index c397b5f6..988ec151 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbSocket.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbSocket.cs @@ -2,7 +2,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; using System; using System.IO; using System.Net.Sockets; diff --git a/AdvancedSharpAdbClient/Logs/LogReader.Async.cs b/AdvancedSharpAdbClient/Logs/LogReader.Async.cs index 2124bce2..d95fb309 100644 --- a/AdvancedSharpAdbClient/Logs/LogReader.Async.cs +++ b/AdvancedSharpAdbClient/Logs/LogReader.Async.cs @@ -3,7 +3,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; using System; using System.IO; using System.Text; diff --git a/AdvancedSharpAdbClient/Logs/LogReader.cs b/AdvancedSharpAdbClient/Logs/LogReader.cs index 88dc0a00..b75fa581 100644 --- a/AdvancedSharpAdbClient/Logs/LogReader.cs +++ b/AdvancedSharpAdbClient/Logs/LogReader.cs @@ -2,7 +2,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; using System; using System.Collections.Generic; using System.IO; diff --git a/AdvancedSharpAdbClient/Logs/NamespaceDoc.cs b/AdvancedSharpAdbClient/Logs/NamespaceDoc.cs index 52b00fbf..10619948 100644 --- a/AdvancedSharpAdbClient/Logs/NamespaceDoc.cs +++ b/AdvancedSharpAdbClient/Logs/NamespaceDoc.cs @@ -2,6 +2,7 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // +using System.ComponentModel; using System.Runtime.CompilerServices; namespace AdvancedSharpAdbClient.Logs @@ -11,5 +12,6 @@ namespace AdvancedSharpAdbClient.Logs /// /// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. [CompilerGenerated] - internal class NamespaceDoc { } + [EditorBrowsable(EditorBrowsableState.Never)] + internal class NamespaceDoc : AdvancedSharpAdbClient.NamespaceDoc { } } diff --git a/AdvancedSharpAdbClient/Models/AdbResponse.cs b/AdvancedSharpAdbClient/Models/AdbResponse.cs index 91ebe18f..43975418 100644 --- a/AdvancedSharpAdbClient/Models/AdbResponse.cs +++ b/AdvancedSharpAdbClient/Models/AdbResponse.cs @@ -5,7 +5,7 @@ using System; using System.Diagnostics.CodeAnalysis; -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Models { /// /// The response returned by ADB server. diff --git a/AdvancedSharpAdbClient/Models/AdbServerStatus.cs b/AdvancedSharpAdbClient/Models/AdbServerStatus.cs index e114e021..dbb2030b 100644 --- a/AdvancedSharpAdbClient/Models/AdbServerStatus.cs +++ b/AdvancedSharpAdbClient/Models/AdbServerStatus.cs @@ -4,7 +4,7 @@ using System; -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Models { /// /// Represents the status of the adb server. diff --git a/AdvancedSharpAdbClient/Models/ColorData.cs b/AdvancedSharpAdbClient/Models/ColorData.cs index 2938cc7c..8d8f4245 100644 --- a/AdvancedSharpAdbClient/Models/ColorData.cs +++ b/AdvancedSharpAdbClient/Models/ColorData.cs @@ -2,7 +2,7 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Models { /// /// Contains information about a color element. Information about each pixel on the screen diff --git a/AdvancedSharpAdbClient/Models/DeviceData.cs b/AdvancedSharpAdbClient/Models/DeviceData.cs index fdd0bce2..931a9d37 100644 --- a/AdvancedSharpAdbClient/Models/DeviceData.cs +++ b/AdvancedSharpAdbClient/Models/DeviceData.cs @@ -6,7 +6,7 @@ using System.Diagnostics.CodeAnalysis; using System.Text.RegularExpressions; -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Models { /// /// Represents a device that is connected to the Android Debug Bridge. diff --git a/AdvancedSharpAdbClient/Models/DeviceDataEventArgs.cs b/AdvancedSharpAdbClient/Models/DeviceDataEventArgs.cs index 7ca8681f..9ae1c49e 100644 --- a/AdvancedSharpAdbClient/Models/DeviceDataEventArgs.cs +++ b/AdvancedSharpAdbClient/Models/DeviceDataEventArgs.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Models { /// /// The event arguments that are passed when a device event occurs. diff --git a/AdvancedSharpAdbClient/Models/Element.cs b/AdvancedSharpAdbClient/Models/Element.cs index 8e3399c7..fe1280f7 100644 --- a/AdvancedSharpAdbClient/Models/Element.cs +++ b/AdvancedSharpAdbClient/Models/Element.cs @@ -9,7 +9,7 @@ using System.Threading; using System.Xml; -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Models { /// /// Implement of screen element, likes Selenium. diff --git a/AdvancedSharpAdbClient/Models/Enums/AppStatus.cs b/AdvancedSharpAdbClient/Models/Enums/AppStatus.cs index f69dad38..280d5122 100644 --- a/AdvancedSharpAdbClient/Models/Enums/AppStatus.cs +++ b/AdvancedSharpAdbClient/Models/Enums/AppStatus.cs @@ -2,7 +2,7 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Models { /// /// The status of an application if it is stopped or running in foreground or background. diff --git a/AdvancedSharpAdbClient/Models/Enums/DeviceState.cs b/AdvancedSharpAdbClient/Models/Enums/DeviceState.cs index 2b44c453..d563ff6f 100644 --- a/AdvancedSharpAdbClient/Models/Enums/DeviceState.cs +++ b/AdvancedSharpAdbClient/Models/Enums/DeviceState.cs @@ -2,7 +2,7 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Models { /// /// Defines the state of an Android device connected to the Android Debug Bridge. diff --git a/AdvancedSharpAdbClient/Models/Enums/ForwardProtocol.cs b/AdvancedSharpAdbClient/Models/Enums/ForwardProtocol.cs index 27cc2b00..e2ebd3af 100644 --- a/AdvancedSharpAdbClient/Models/Enums/ForwardProtocol.cs +++ b/AdvancedSharpAdbClient/Models/Enums/ForwardProtocol.cs @@ -2,7 +2,7 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Models { /// /// Represents a protocol which is being forwarded over adb. diff --git a/AdvancedSharpAdbClient/Models/Enums/StartServerResult.cs b/AdvancedSharpAdbClient/Models/Enums/StartServerResult.cs index 75192dc0..1d4c2796 100644 --- a/AdvancedSharpAdbClient/Models/Enums/StartServerResult.cs +++ b/AdvancedSharpAdbClient/Models/Enums/StartServerResult.cs @@ -2,7 +2,7 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Models { /// /// Gives information about a operation. diff --git a/AdvancedSharpAdbClient/Models/Enums/SyncCommand.cs b/AdvancedSharpAdbClient/Models/Enums/SyncCommand.cs index a5dc29bf..60188e2c 100644 --- a/AdvancedSharpAdbClient/Models/Enums/SyncCommand.cs +++ b/AdvancedSharpAdbClient/Models/Enums/SyncCommand.cs @@ -2,7 +2,7 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Models { /// /// Defines a command that can be sent to, or a response that can be received from the sync service. diff --git a/AdvancedSharpAdbClient/Models/Enums/TransportType.cs b/AdvancedSharpAdbClient/Models/Enums/TransportType.cs index 283ae50e..113938ad 100644 --- a/AdvancedSharpAdbClient/Models/Enums/TransportType.cs +++ b/AdvancedSharpAdbClient/Models/Enums/TransportType.cs @@ -2,7 +2,7 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Models { /// /// Specifies the transport type used between the device and the Android Debug Bridge server. diff --git a/AdvancedSharpAdbClient/Models/Enums/UnixFileType.cs b/AdvancedSharpAdbClient/Models/Enums/UnixFileType.cs index ec47bc17..05fecae9 100644 --- a/AdvancedSharpAdbClient/Models/Enums/UnixFileType.cs +++ b/AdvancedSharpAdbClient/Models/Enums/UnixFileType.cs @@ -4,7 +4,7 @@ using System; -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Models { /// /// Describes the properties of a file on an Android device. diff --git a/AdvancedSharpAdbClient/Models/FileStatistics.cs b/AdvancedSharpAdbClient/Models/FileStatistics.cs index be4a58eb..a567f963 100644 --- a/AdvancedSharpAdbClient/Models/FileStatistics.cs +++ b/AdvancedSharpAdbClient/Models/FileStatistics.cs @@ -5,7 +5,7 @@ using System; using System.Diagnostics.CodeAnalysis; -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Models { /// /// Contains information about a file on the remote device. diff --git a/AdvancedSharpAdbClient/Models/ForwardData.cs b/AdvancedSharpAdbClient/Models/ForwardData.cs index 238db1be..eb2e4093 100644 --- a/AdvancedSharpAdbClient/Models/ForwardData.cs +++ b/AdvancedSharpAdbClient/Models/ForwardData.cs @@ -5,7 +5,7 @@ using System; using System.Diagnostics.CodeAnalysis; -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Models { /// /// Contains information about port forwarding configured by the Android Debug Bridge. diff --git a/AdvancedSharpAdbClient/Models/ForwardSpec.cs b/AdvancedSharpAdbClient/Models/ForwardSpec.cs index 80839731..d44d8cbb 100644 --- a/AdvancedSharpAdbClient/Models/ForwardSpec.cs +++ b/AdvancedSharpAdbClient/Models/ForwardSpec.cs @@ -7,7 +7,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Models { /// /// Represents an adb forward specification as used by the various adb port forwarding functions. diff --git a/AdvancedSharpAdbClient/Models/Framebuffer.cs b/AdvancedSharpAdbClient/Models/Framebuffer.cs index 2e7d557b..d0e32809 100644 --- a/AdvancedSharpAdbClient/Models/Framebuffer.cs +++ b/AdvancedSharpAdbClient/Models/Framebuffer.cs @@ -7,7 +7,7 @@ using System.Net; using System.Threading; -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Models { /// /// Provides access to the framebuffer (that is, a copy of the image being displayed on the device screen). diff --git a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs index d664a1db..13bc553a 100644 --- a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs +++ b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs @@ -11,7 +11,7 @@ using System.Threading; #endif -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Models { /// /// Whenever the framebuffer: service is invoked, the adb server responds with the contents diff --git a/AdvancedSharpAdbClient/Models/NamespaceDoc.cs b/AdvancedSharpAdbClient/Models/NamespaceDoc.cs new file mode 100644 index 00000000..a60004f7 --- /dev/null +++ b/AdvancedSharpAdbClient/Models/NamespaceDoc.cs @@ -0,0 +1,17 @@ +// +// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. +// + +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace AdvancedSharpAdbClient.Models +{ + /// + /// The classes in this namespace provide models for . + /// + /// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. + [CompilerGenerated] + [EditorBrowsable(EditorBrowsableState.Never)] + internal class NamespaceDoc : AdvancedSharpAdbClient.NamespaceDoc { } +} diff --git a/AdvancedSharpAdbClient/Models/ShellStream.cs b/AdvancedSharpAdbClient/Models/ShellStream.cs index 4d2a9ecd..e3a89064 100644 --- a/AdvancedSharpAdbClient/Models/ShellStream.cs +++ b/AdvancedSharpAdbClient/Models/ShellStream.cs @@ -7,7 +7,7 @@ using System.IO; using System.Threading; -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Models { /// Represents a that wraps around an inner that contains /// output from an Android shell command. In the shell output, the LF character is replaced by a diff --git a/AdvancedSharpAdbClient/Models/SyncProgressChangedEventArgs.cs b/AdvancedSharpAdbClient/Models/SyncProgressChangedEventArgs.cs index d054258b..d869a58b 100644 --- a/AdvancedSharpAdbClient/Models/SyncProgressChangedEventArgs.cs +++ b/AdvancedSharpAdbClient/Models/SyncProgressChangedEventArgs.cs @@ -4,7 +4,7 @@ using System; -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Models { /// /// Provides data for the event. diff --git a/AdvancedSharpAdbClient/NamespaceDoc.cs b/AdvancedSharpAdbClient/NamespaceDoc.cs index bd3aab03..3151bd5e 100644 --- a/AdvancedSharpAdbClient/NamespaceDoc.cs +++ b/AdvancedSharpAdbClient/NamespaceDoc.cs @@ -2,6 +2,7 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // +using System.ComponentModel; using System.Runtime.CompilerServices; namespace AdvancedSharpAdbClient @@ -56,5 +57,6 @@ namespace AdvancedSharpAdbClient /// /// [CompilerGenerated] + [EditorBrowsable(EditorBrowsableState.Never)] internal class NamespaceDoc { } } diff --git a/AdvancedSharpAdbClient/Extensions/Attributes/CallerArgumentExpressionAttribute.cs b/AdvancedSharpAdbClient/Polyfills/Attributes/CallerArgumentExpressionAttribute.cs similarity index 100% rename from AdvancedSharpAdbClient/Extensions/Attributes/CallerArgumentExpressionAttribute.cs rename to AdvancedSharpAdbClient/Polyfills/Attributes/CallerArgumentExpressionAttribute.cs diff --git a/AdvancedSharpAdbClient/Extensions/Attributes/ExcludeFromCodeCoverageAttribute.cs b/AdvancedSharpAdbClient/Polyfills/Attributes/ExcludeFromCodeCoverageAttribute.cs similarity index 100% rename from AdvancedSharpAdbClient/Extensions/Attributes/ExcludeFromCodeCoverageAttribute.cs rename to AdvancedSharpAdbClient/Polyfills/Attributes/ExcludeFromCodeCoverageAttribute.cs diff --git a/AdvancedSharpAdbClient/Extensions/Attributes/IsExternalInit.cs b/AdvancedSharpAdbClient/Polyfills/Attributes/IsExternalInit.cs similarity index 100% rename from AdvancedSharpAdbClient/Extensions/Attributes/IsExternalInit.cs rename to AdvancedSharpAdbClient/Polyfills/Attributes/IsExternalInit.cs diff --git a/AdvancedSharpAdbClient/Extensions/Attributes/NullableAttributes.cs b/AdvancedSharpAdbClient/Polyfills/Attributes/NullableAttributes.cs similarity index 100% rename from AdvancedSharpAdbClient/Extensions/Attributes/NullableAttributes.cs rename to AdvancedSharpAdbClient/Polyfills/Attributes/NullableAttributes.cs diff --git a/AdvancedSharpAdbClient/Extensions/Attributes/SerializableAttribute.cs b/AdvancedSharpAdbClient/Polyfills/Attributes/SerializableAttribute.cs similarity index 100% rename from AdvancedSharpAdbClient/Extensions/Attributes/SerializableAttribute.cs rename to AdvancedSharpAdbClient/Polyfills/Attributes/SerializableAttribute.cs diff --git a/AdvancedSharpAdbClient/Extensions/Attributes/StackTraceHiddenAttribute.cs b/AdvancedSharpAdbClient/Polyfills/Attributes/StackTraceHiddenAttribute.cs similarity index 100% rename from AdvancedSharpAdbClient/Extensions/Attributes/StackTraceHiddenAttribute.cs rename to AdvancedSharpAdbClient/Polyfills/Attributes/StackTraceHiddenAttribute.cs diff --git a/AdvancedSharpAdbClient/Models/DnsEndPoint.cs b/AdvancedSharpAdbClient/Polyfills/DnsEndPoint.cs similarity index 99% rename from AdvancedSharpAdbClient/Models/DnsEndPoint.cs rename to AdvancedSharpAdbClient/Polyfills/DnsEndPoint.cs index 583ad28e..295846e9 100644 --- a/AdvancedSharpAdbClient/Models/DnsEndPoint.cs +++ b/AdvancedSharpAdbClient/Polyfills/DnsEndPoint.cs @@ -4,7 +4,7 @@ using System.Net; using System.Net.Sockets; -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Polyfills { /// /// Represents a network endpoint as a host name or a string representation of an IP address and a port number. diff --git a/AdvancedSharpAdbClient/Extensions/DateTimeExtensions.cs b/AdvancedSharpAdbClient/Polyfills/Extensions/DateTimeExtensions.cs similarity index 99% rename from AdvancedSharpAdbClient/Extensions/DateTimeExtensions.cs rename to AdvancedSharpAdbClient/Polyfills/Extensions/DateTimeExtensions.cs index bf2b2702..33e0d8a5 100644 --- a/AdvancedSharpAdbClient/Extensions/DateTimeExtensions.cs +++ b/AdvancedSharpAdbClient/Polyfills/Extensions/DateTimeExtensions.cs @@ -4,7 +4,7 @@ using System; -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Polyfills { /// /// Provides helper methods for working with Unix-based date formats. diff --git a/AdvancedSharpAdbClient/Polyfills/Extensions/EnumerableExtensions.cs b/AdvancedSharpAdbClient/Polyfills/Extensions/EnumerableExtensions.cs new file mode 100644 index 00000000..7ff6a497 --- /dev/null +++ b/AdvancedSharpAdbClient/Polyfills/Extensions/EnumerableExtensions.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AdvancedSharpAdbClient.Polyfills +{ + /// + /// Provides extension methods for the class. + /// + public static class EnumerableExtensions + { + /// + /// Adds the elements of the specified collection to the end of the . + /// + /// The type of the elements of . + /// The to be added. + /// The collection whose elements should be added to the end of the . + /// The collection itself cannot be , but it can contain elements that are + /// , if type is a reference type. + /// or is null. + public static void AddRange(this ICollection source, IEnumerable collection) + { + ExceptionExtensions.ThrowIfNull(source); + ExceptionExtensions.ThrowIfNull(collection); + + if (source is List list) + { + list.AddRange(collection); + } +#if !NETFRAMEWORK || NET40_OR_GREATER + else if (source is ISet set) + { + set.UnionWith(collection); + } +#endif + else + { + foreach (TSource item in collection) + { + source.Add(item); + } + } + } + } +} diff --git a/AdvancedSharpAdbClient/Extensions/ExceptionExtensions.cs b/AdvancedSharpAdbClient/Polyfills/Extensions/ExceptionExtensions.cs similarity index 93% rename from AdvancedSharpAdbClient/Extensions/ExceptionExtensions.cs rename to AdvancedSharpAdbClient/Polyfills/Extensions/ExceptionExtensions.cs index 3c8d55b0..7085e5c4 100644 --- a/AdvancedSharpAdbClient/Extensions/ExceptionExtensions.cs +++ b/AdvancedSharpAdbClient/Polyfills/Extensions/ExceptionExtensions.cs @@ -3,9 +3,12 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Polyfills { - internal static class ExceptionExtensions + /// + /// Provides extension methods for the class. + /// + public static class ExceptionExtensions { /// /// Throws an if is null. @@ -73,7 +76,7 @@ public static void ThrowIfNegative(T value, [CallerArgumentExpression(nameof( { ArgumentOutOfRangeException.ThrowIfNegative(value, paramName); #else - public static void ThrowIfNegative(int value, [CallerArgumentExpression(nameof(value))] string? paramName = null) + public static void ThrowIfNegative(double value, [CallerArgumentExpression(nameof(value))] string? paramName = null) { if (value < 0) { diff --git a/AdvancedSharpAdbClient/Extensions/SocketExtensions.cs b/AdvancedSharpAdbClient/Polyfills/Extensions/SocketExtensions.cs similarity index 99% rename from AdvancedSharpAdbClient/Extensions/SocketExtensions.cs rename to AdvancedSharpAdbClient/Polyfills/Extensions/SocketExtensions.cs index 76dce262..a7f2bed0 100644 --- a/AdvancedSharpAdbClient/Extensions/SocketExtensions.cs +++ b/AdvancedSharpAdbClient/Polyfills/Extensions/SocketExtensions.cs @@ -6,7 +6,7 @@ using System.Net.Sockets; using System.Threading; -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Polyfills { /// /// Provides extension methods for the class. diff --git a/AdvancedSharpAdbClient/Extensions/StreamExtensions.cs b/AdvancedSharpAdbClient/Polyfills/Extensions/StreamExtensions.cs similarity index 99% rename from AdvancedSharpAdbClient/Extensions/StreamExtensions.cs rename to AdvancedSharpAdbClient/Polyfills/Extensions/StreamExtensions.cs index 1323bb96..6ffae039 100644 --- a/AdvancedSharpAdbClient/Extensions/StreamExtensions.cs +++ b/AdvancedSharpAdbClient/Polyfills/Extensions/StreamExtensions.cs @@ -6,7 +6,7 @@ using System.IO; using System.Threading; -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Polyfills { /// /// Provides extension methods for the class. diff --git a/AdvancedSharpAdbClient/Extensions/StringExtensions.cs b/AdvancedSharpAdbClient/Polyfills/Extensions/StringExtensions.cs similarity index 99% rename from AdvancedSharpAdbClient/Extensions/StringExtensions.cs rename to AdvancedSharpAdbClient/Polyfills/Extensions/StringExtensions.cs index 1a67370e..7870641f 100644 --- a/AdvancedSharpAdbClient/Extensions/StringExtensions.cs +++ b/AdvancedSharpAdbClient/Polyfills/Extensions/StringExtensions.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Text; -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Polyfills { /// /// Provides extension methods for the class. diff --git a/AdvancedSharpAdbClient/Extensions/ThreadExtensions.cs b/AdvancedSharpAdbClient/Polyfills/Extensions/ThreadExtensions.cs similarity index 96% rename from AdvancedSharpAdbClient/Extensions/ThreadExtensions.cs rename to AdvancedSharpAdbClient/Polyfills/Extensions/ThreadExtensions.cs index 77cbc596..adfcc1fc 100644 --- a/AdvancedSharpAdbClient/Extensions/ThreadExtensions.cs +++ b/AdvancedSharpAdbClient/Polyfills/Extensions/ThreadExtensions.cs @@ -8,7 +8,7 @@ using System.Runtime.CompilerServices; using System.Threading; -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Polyfills { #if WINDOWS_UWP /// @@ -16,7 +16,7 @@ namespace AdvancedSharpAdbClient /// /// A whose foreground thread to switch execution to. /// Specifies the priority for event dispatch. - [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] public readonly struct DispatcherThreadSwitcher(CoreDispatcher dispatcher, CoreDispatcherPriority priority) : INotifyCompletion { /// @@ -54,7 +54,7 @@ public void OnCompleted(Action continuation) /// /// A whose foreground thread to switch execution to. /// Specifies the priority for event dispatch. - [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] public readonly struct DispatcherQueueThreadSwitcher(DispatcherQueue dispatcher, DispatcherQueuePriority priority) : INotifyCompletion { /// @@ -91,7 +91,7 @@ public void OnCompleted(Action continuation) /// /// A helper type for switch thread by . This type is not intended to be used directly from your code. /// - [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] public readonly struct TaskThreadSwitcher : INotifyCompletion { /// diff --git a/AdvancedSharpAdbClient/Models/HashCode.cs b/AdvancedSharpAdbClient/Polyfills/HashCode.cs similarity index 99% rename from AdvancedSharpAdbClient/Models/HashCode.cs rename to AdvancedSharpAdbClient/Polyfills/HashCode.cs index fcf091eb..a88fa196 100644 --- a/AdvancedSharpAdbClient/Models/HashCode.cs +++ b/AdvancedSharpAdbClient/Polyfills/HashCode.cs @@ -47,7 +47,7 @@ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Polyfills { // xxHash32 is used for the hash code. // https://github.com/Cyan4973/xxHash diff --git a/AdvancedSharpAdbClient/Interfaces/IProgress.cs b/AdvancedSharpAdbClient/Polyfills/Interfaces/IProgress.cs similarity index 92% rename from AdvancedSharpAdbClient/Interfaces/IProgress.cs rename to AdvancedSharpAdbClient/Polyfills/Interfaces/IProgress.cs index 015e9510..5c88a752 100644 --- a/AdvancedSharpAdbClient/Interfaces/IProgress.cs +++ b/AdvancedSharpAdbClient/Polyfills/Interfaces/IProgress.cs @@ -2,7 +2,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Polyfills { /// Defines a provider for progress updates. /// The type of progress update value. diff --git a/AdvancedSharpAdbClient/Polyfills/NamespaceDoc.cs b/AdvancedSharpAdbClient/Polyfills/NamespaceDoc.cs new file mode 100644 index 00000000..d992eddf --- /dev/null +++ b/AdvancedSharpAdbClient/Polyfills/NamespaceDoc.cs @@ -0,0 +1,17 @@ +// +// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. +// + +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace AdvancedSharpAdbClient.Polyfills +{ + /// + /// The classes in this namespace provide polyfills. + /// + /// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. + [CompilerGenerated] + [EditorBrowsable(EditorBrowsableState.Never)] + internal class NamespaceDoc : AdvancedSharpAdbClient.NamespaceDoc { } +} diff --git a/AdvancedSharpAdbClient/Models/Point.cs b/AdvancedSharpAdbClient/Polyfills/Point.cs similarity index 99% rename from AdvancedSharpAdbClient/Models/Point.cs rename to AdvancedSharpAdbClient/Polyfills/Point.cs index 4f33c5a2..9f6ac9e0 100644 --- a/AdvancedSharpAdbClient/Models/Point.cs +++ b/AdvancedSharpAdbClient/Polyfills/Point.cs @@ -5,7 +5,7 @@ using System; using System.Diagnostics.CodeAnalysis; -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Polyfills { /// /// Represents an ordered pair of x and y coordinates that define a point in a two-dimensional plane. diff --git a/AdvancedSharpAdbClient/Models/Rectangle.cs b/AdvancedSharpAdbClient/Polyfills/Rectangle.cs similarity index 99% rename from AdvancedSharpAdbClient/Models/Rectangle.cs rename to AdvancedSharpAdbClient/Polyfills/Rectangle.cs index 66919e9f..520e97a4 100644 --- a/AdvancedSharpAdbClient/Models/Rectangle.cs +++ b/AdvancedSharpAdbClient/Polyfills/Rectangle.cs @@ -5,7 +5,7 @@ using System; using System.Diagnostics.CodeAnalysis; -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Polyfills { /// /// Stores the location and size of a rectangular region. diff --git a/AdvancedSharpAdbClient/Properties/GlobalUsings.cs b/AdvancedSharpAdbClient/Properties/GlobalUsings.cs index 7b52be14..9c9cc3a2 100644 --- a/AdvancedSharpAdbClient/Properties/GlobalUsings.cs +++ b/AdvancedSharpAdbClient/Properties/GlobalUsings.cs @@ -1,4 +1,15 @@ -#if NET +#region AdvancedSharpAdbClient +global using AdvancedSharpAdbClient.DeviceCommands; +global using AdvancedSharpAdbClient.Exceptions; +global using AdvancedSharpAdbClient.Logs; +global using AdvancedSharpAdbClient.Models; +global using AdvancedSharpAdbClient.Models.DeviceCommands; +global using AdvancedSharpAdbClient.Polyfills; +global using AdvancedSharpAdbClient.Receivers; +global using AdvancedSharpAdbClient.Receivers.DeviceCommands; +#endregion + +#if NET global using System.Runtime.Versioning; #endif diff --git a/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs b/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs index ff547197..8d857f41 100644 --- a/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs +++ b/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs @@ -2,14 +2,12 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; -using AdvancedSharpAdbClient.Logs; using System.Collections.Generic; using System.IO; using System.Text; using System.Text.RegularExpressions; -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Receivers { /// /// Receives console output, and makes the console output available as a . To diff --git a/AdvancedSharpAdbClient/Receivers/IShellOutputReceiver.cs b/AdvancedSharpAdbClient/Receivers/IShellOutputReceiver.cs index 4444d39a..bd0f16a4 100644 --- a/AdvancedSharpAdbClient/Receivers/IShellOutputReceiver.cs +++ b/AdvancedSharpAdbClient/Receivers/IShellOutputReceiver.cs @@ -4,7 +4,7 @@ using System.Text; -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Receivers { /// /// This interface contains various receivers that are able to parse Android console output. You can use diff --git a/AdvancedSharpAdbClient/Receivers/MultilineReceiver.cs b/AdvancedSharpAdbClient/Receivers/MultilineReceiver.cs index 15ccd798..f98c2765 100644 --- a/AdvancedSharpAdbClient/Receivers/MultilineReceiver.cs +++ b/AdvancedSharpAdbClient/Receivers/MultilineReceiver.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Receivers { /// /// A multiline receiver to receive and process shell output with multi lines. diff --git a/AdvancedSharpAdbClient/Receivers/NamespaceDoc.cs b/AdvancedSharpAdbClient/Receivers/NamespaceDoc.cs new file mode 100644 index 00000000..e332cc55 --- /dev/null +++ b/AdvancedSharpAdbClient/Receivers/NamespaceDoc.cs @@ -0,0 +1,18 @@ +// +// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. +// + +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Text; + +namespace AdvancedSharpAdbClient.Receivers +{ + /// + /// The classes in this namespace provide receivers for . + /// + /// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. + [CompilerGenerated] + [EditorBrowsable(EditorBrowsableState.Never)] + internal class NamespaceDoc : AdvancedSharpAdbClient.NamespaceDoc { } +} diff --git a/AdvancedSharpAdbClient/SyncService.Async.cs b/AdvancedSharpAdbClient/SyncService.Async.cs index 4c9c87cf..6e4acbfc 100644 --- a/AdvancedSharpAdbClient/SyncService.Async.cs +++ b/AdvancedSharpAdbClient/SyncService.Async.cs @@ -3,7 +3,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; using System; using System.Collections.Generic; using System.IO; diff --git a/AdvancedSharpAdbClient/SyncService.cs b/AdvancedSharpAdbClient/SyncService.cs index 2b5068ed..10e8e2f3 100644 --- a/AdvancedSharpAdbClient/SyncService.cs +++ b/AdvancedSharpAdbClient/SyncService.cs @@ -2,7 +2,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using AdvancedSharpAdbClient.Exceptions; using System; using System.Collections.Generic; using System.IO; From 2530877c3bb1b656b75749f81aa3d31d1134e7c3 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Mon, 30 Oct 2023 16:12:47 +0800 Subject: [PATCH 52/66] Split Extensions Version update to 3.0.x --- .../Extensions/EnumerableExtensionsTests.cs | 15 ++++ .../Extensions/ExtensionsTests.cs | 23 ------ AdvancedSharpAdbClient/AdbClient.cs | 3 +- .../DeviceCommands/DeviceExtensions.cs | 3 +- .../Extensions/Extensions.cs | 71 +++---------------- .../Extensions/SyncCommandConverter.cs | 2 +- .../Interfaces/IAdbClient.cs | 4 +- AdvancedSharpAdbClient/Models/DeviceData.cs | 2 +- .../Polyfills/Extensions/EnumExtensions.cs | 55 ++++++++++++++ .../Extensions/EnumerableExtensions.cs | 20 ++++++ .../Polyfills/Extensions/SocketExtensions.cs | 28 ++------ .../Extensions/StringBuilderExtensions.cs | 27 +++++++ .../Polyfills/Extensions/StringExtensions.cs | 6 +- Directory.Build.props | 2 +- 14 files changed, 145 insertions(+), 116 deletions(-) delete mode 100644 AdvancedSharpAdbClient.Tests/Extensions/ExtensionsTests.cs create mode 100644 AdvancedSharpAdbClient/Polyfills/Extensions/EnumExtensions.cs create mode 100644 AdvancedSharpAdbClient/Polyfills/Extensions/StringBuilderExtensions.cs diff --git a/AdvancedSharpAdbClient.Tests/Extensions/EnumerableExtensionsTests.cs b/AdvancedSharpAdbClient.Tests/Extensions/EnumerableExtensionsTests.cs index 86068dfe..2242e4b0 100644 --- a/AdvancedSharpAdbClient.Tests/Extensions/EnumerableExtensionsTests.cs +++ b/AdvancedSharpAdbClient.Tests/Extensions/EnumerableExtensionsTests.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Linq; +using System.Threading.Tasks; using Xunit; namespace AdvancedSharpAdbClient.Polyfills.Tests @@ -32,5 +34,18 @@ public void AddRangeTest() Assert.Equal(10, collection.Count); Assert.Equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], collection); } + + /// + /// Tests the method. + /// + [Fact] + 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)); + Assert.Equal(array, await taskArray.ToArrayAsync()); + Assert.Equal(array, await arrayTask.ToArrayAsync()); + } } } diff --git a/AdvancedSharpAdbClient.Tests/Extensions/ExtensionsTests.cs b/AdvancedSharpAdbClient.Tests/Extensions/ExtensionsTests.cs deleted file mode 100644 index cc86cdfc..00000000 --- a/AdvancedSharpAdbClient.Tests/Extensions/ExtensionsTests.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Xunit; - -namespace AdvancedSharpAdbClient.Tests -{ - /// - /// Tests the class. - /// - public class ExtensionsTests - { - [Fact] - 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)); - Assert.Equal(array, await taskArray.ToArrayAsync()); - Assert.Equal(array, await arrayTask.ToArrayAsync()); - } - } -} diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index 9efd2b22..97afd509 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -48,6 +48,7 @@ public partial class AdbClient : IAdbClient /// Gets a new instance of the class. /// [Obsolete("This function has been removed since SharpAdbClient. Here is a placeholder which function is gets a new instance instead of gets or sets the default instance.")] + [EditorBrowsable(EditorBrowsableState.Never)] public static IAdbClient Instance => new AdbClient(); /// @@ -1150,7 +1151,7 @@ protected static void EnsureDevice(DeviceData device) /// /// See as the class. /// - [Obsolete("AdvancedAdbClient is too long to remember. Please use AdbClient instead.")] + [Obsolete($"{nameof(AdvancedAdbClient)} is too long to remember. Please use {nameof(AdbClient)} instead.")] [EditorBrowsable(EditorBrowsableState.Never)] public class AdvancedAdbClient : AdbClient { diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs index 751c6e04..32d321d7 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs @@ -257,8 +257,7 @@ public static IEnumerable ListProcesses(this IAdbClient client, if (i > 0 && (i % 25 == 0 || i == pids.Count - 1)) { client.ExecuteShellCommand(device, catBuilder.ToString(), processOutputReceiver); - catBuilder.Clear(); - _ = catBuilder.Append("cat "); + _ = catBuilder.Clear().Append("cat "); } } diff --git a/AdvancedSharpAdbClient/Extensions/Extensions.cs b/AdvancedSharpAdbClient/Extensions/Extensions.cs index 3d373dbf..ee3b7ef4 100644 --- a/AdvancedSharpAdbClient/Extensions/Extensions.cs +++ b/AdvancedSharpAdbClient/Extensions/Extensions.cs @@ -5,8 +5,8 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Net; +using System.Net.Sockets; using System.Runtime.InteropServices; using System.Text; using System.Threading; @@ -37,57 +37,23 @@ public static DnsEndPoint CreateDnsEndPoint(string host, int port) : new DnsEndPoint(values[0], values.Length > 1 && int.TryParse(values[1], out int _port) ? _port : port); } +#if !HAS_PROCESS /// - /// Converts the string representation of the name or numeric value of one or more - /// enumerated constants to an equivalent enumerated object. A parameter specifies - /// whether the operation is case-sensitive. The return value indicates whether the - /// conversion succeeded. + /// Closes the connection and releases all associated resources. /// - /// The enumeration type to which to convert . - /// The string representation of the enumeration name or underlying value to convert. - /// to ignore case; to consider case. - /// When this method returns, contains an object of type whose - /// value is represented by if the parse operation succeeds. If the parse operation fails, - /// contains the default value of the underlying type of . This parameter is passed uninitialized. - /// if the value parameter was converted successfully; otherwise, . - /// is not an enumeration type. - public static bool TryParse(string value, bool ignoreCase, out TEnum result) where TEnum : struct - { -#if NETFRAMEWORK && !NET40_OR_GREATER - string strTypeFixed = value.Replace(' ', '_'); - if (Enum.IsDefined(typeof(TEnum), strTypeFixed)) - { - result = (TEnum)Enum.Parse(typeof(TEnum), strTypeFixed, ignoreCase); - return true; - } - else - { - foreach (string str in Enum.GetNames(typeof(TEnum))) - { - if (str.Equals(strTypeFixed, StringComparison.OrdinalIgnoreCase)) - { - result = (TEnum)Enum.Parse(typeof(TEnum), str, ignoreCase); - return true; - } - } - result = default; - return false; - } -#else - return Enum.TryParse(value, ignoreCase, out result); + /// The to release. + public static void Close(this Socket socket) => socket.Dispose(); #endif - } #if NETFRAMEWORK && !NET40_OR_GREATER /// - /// Removes all characters from the current instance. + /// Releases all resources used by the current instance of the class. /// - /// The to removes all characters. - /// An object whose is 0 (zero). - public static StringBuilder Clear(this StringBuilder builder) + /// The to release. + public static void Dispose(this Socket socket) { - builder.Length = 0; - return builder; + socket.Close(); + GC.SuppressFinalize(socket); } /// @@ -102,23 +68,6 @@ public static void Dispose(this WaitHandle waitHandle) #endif #if HAS_TASK - /// - /// Creates an array from a . - /// - /// The type of the elements of . - /// An to create an array from. - /// An array that contains the elements from the input sequence. - public static Task ToArrayAsync(this Task> source) => - source.ContinueWith(x => x.Result.ToArray()); - - /// - /// Creates an array from a . - /// - /// The type of the elements of . - /// An to create an array from. - /// An array that contains the elements from the input sequence. - public static Task ToArrayAsync(this IEnumerable> source) => WhenAll(source); - /// /// Creates a task that completes after a specified number of milliseconds. /// diff --git a/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs b/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs index 73d8b250..7163a090 100644 --- a/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs +++ b/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs @@ -59,7 +59,7 @@ public static SyncCommand GetCommand(byte[] value) string commandText = AdbClient.Encoding.GetString(value); - return Extensions.TryParse(commandText, true, out SyncCommand command) ? command : throw new ArgumentOutOfRangeException(nameof(value), $"{commandText} is not a valid sync command"); + return EnumExtensions.TryParse(commandText, true, out SyncCommand command) ? command : throw new ArgumentOutOfRangeException(nameof(value), $"{commandText} is not a valid sync command"); } } } diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs b/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs index 9fafa91b..52ba3b3c 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.IO; using System.Net; using System.Text; @@ -509,7 +510,8 @@ public partial interface IAdbClient /// /// See as the interface. /// - [Obsolete("IAdvancedAdbClient is too long to remember. Please use IAdbClient instead.")] + [Obsolete($"{nameof(IAdbCommandLineClient)} is too long to remember. Please use {nameof(IAdbClient)} instead.")] + [EditorBrowsable(EditorBrowsableState.Never)] public interface IAdvancedAdbClient : IAdbClient { } diff --git a/AdvancedSharpAdbClient/Models/DeviceData.cs b/AdvancedSharpAdbClient/Models/DeviceData.cs index 931a9d37..ffd40e37 100644 --- a/AdvancedSharpAdbClient/Models/DeviceData.cs +++ b/AdvancedSharpAdbClient/Models/DeviceData.cs @@ -165,7 +165,7 @@ internal static DeviceState GetStateFromString(string state) else { // Else, we try to match a value of the DeviceState enumeration. - if (!Extensions.TryParse(state, true, out value)) + if (!EnumExtensions.TryParse(state, true, out value)) { value = DeviceState.Unknown; } diff --git a/AdvancedSharpAdbClient/Polyfills/Extensions/EnumExtensions.cs b/AdvancedSharpAdbClient/Polyfills/Extensions/EnumExtensions.cs new file mode 100644 index 00000000..e9718122 --- /dev/null +++ b/AdvancedSharpAdbClient/Polyfills/Extensions/EnumExtensions.cs @@ -0,0 +1,55 @@ +// +// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. +// + +using System; + +namespace AdvancedSharpAdbClient.Polyfills +{ + /// + /// Provides extension methods for the class. + /// + public static class EnumExtensions + { + /// + /// Converts the string representation of the name or numeric value of one or more + /// enumerated constants to an equivalent enumerated object. A parameter specifies + /// whether the operation is case-sensitive. The return value indicates whether the + /// conversion succeeded. + /// + /// The enumeration type to which to convert . + /// The string representation of the enumeration name or underlying value to convert. + /// to ignore case; to consider case. + /// When this method returns, contains an object of type whose + /// value is represented by if the parse operation succeeds. If the parse operation fails, + /// contains the default value of the underlying type of . This parameter is passed uninitialized. + /// if the value parameter was converted successfully; otherwise, . + /// is not an enumeration type. + public static bool TryParse(string value, bool ignoreCase, out TEnum result) where TEnum : struct + { +#if NETFRAMEWORK && !NET40_OR_GREATER + string strTypeFixed = value.Replace(' ', '_'); + if (Enum.IsDefined(typeof(TEnum), strTypeFixed)) + { + result = (TEnum)Enum.Parse(typeof(TEnum), strTypeFixed, ignoreCase); + return true; + } + else + { + foreach (string str in Enum.GetNames(typeof(TEnum))) + { + if (str.Equals(strTypeFixed, StringComparison.OrdinalIgnoreCase)) + { + result = (TEnum)Enum.Parse(typeof(TEnum), str, ignoreCase); + return true; + } + } + result = default; + return false; + } +#else + return Enum.TryParse(value, ignoreCase, out result); +#endif + } + } +} diff --git a/AdvancedSharpAdbClient/Polyfills/Extensions/EnumerableExtensions.cs b/AdvancedSharpAdbClient/Polyfills/Extensions/EnumerableExtensions.cs index 7ff6a497..4ee7fabf 100644 --- a/AdvancedSharpAdbClient/Polyfills/Extensions/EnumerableExtensions.cs +++ b/AdvancedSharpAdbClient/Polyfills/Extensions/EnumerableExtensions.cs @@ -41,5 +41,25 @@ public static void AddRange(this ICollection source, IEnumerab } } } + +#if HAS_TASK + /// + /// Creates an array from a . + /// + /// The type of the elements of . + /// An to create an array from. + /// An array that contains the elements from the input sequence. + public static Task ToArrayAsync(this Task> source) => + source.ContinueWith(x => x.Result.ToArray()); + + /// + /// Creates an array from a . + /// + /// The type of the elements of . + /// An 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); +#endif } } diff --git a/AdvancedSharpAdbClient/Polyfills/Extensions/SocketExtensions.cs b/AdvancedSharpAdbClient/Polyfills/Extensions/SocketExtensions.cs index a7f2bed0..1dbf03b0 100644 --- a/AdvancedSharpAdbClient/Polyfills/Extensions/SocketExtensions.cs +++ b/AdvancedSharpAdbClient/Polyfills/Extensions/SocketExtensions.cs @@ -1,4 +1,5 @@ -// +#if HAS_TASK +// // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // @@ -13,7 +14,6 @@ namespace AdvancedSharpAdbClient.Polyfills /// public static class SocketExtensions { -#if HAS_TASK #if !HAS_BUFFERS /// /// Asynchronously receives data from a connected socket. @@ -184,26 +184,6 @@ public static Task SendAsync(this Socket socket, byte[] buffer, int offset, return Extensions.Run(() => socket.Send(buffer, offset, size, socketFlags), cancellationToken); #endif } -#endif - -#if !HAS_PROCESS - /// - /// Closes the connection and releases all associated resources. - /// - /// The to release. - internal static void Close(this Socket socket) => socket.Dispose(); -#endif - -#if NETFRAMEWORK && !NET40_OR_GREATER - /// - /// Releases all resources used by the current instance of the class. - /// - /// The to release. - internal static void Dispose(this Socket socket) - { - socket.Close(); - GC.SuppressFinalize(socket); - } -#endif } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Polyfills/Extensions/StringBuilderExtensions.cs b/AdvancedSharpAdbClient/Polyfills/Extensions/StringBuilderExtensions.cs new file mode 100644 index 00000000..d3480120 --- /dev/null +++ b/AdvancedSharpAdbClient/Polyfills/Extensions/StringBuilderExtensions.cs @@ -0,0 +1,27 @@ +#if NETFRAMEWORK && !NET40_OR_GREATER +// +// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. +// + +using System.Text; + +namespace AdvancedSharpAdbClient.Polyfills +{ + /// + /// Provides extension methods for the class. + /// + public static class StringBuilderExtensions + { + /// + /// Removes all characters from the current instance. + /// + /// The to removes all characters. + /// An object whose is 0 (zero). + public static StringBuilder Clear(this StringBuilder builder) + { + builder.Length = 0; + return builder; + } + } +} +#endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Polyfills/Extensions/StringExtensions.cs b/AdvancedSharpAdbClient/Polyfills/Extensions/StringExtensions.cs index 7870641f..443982ae 100644 --- a/AdvancedSharpAdbClient/Polyfills/Extensions/StringExtensions.cs +++ b/AdvancedSharpAdbClient/Polyfills/Extensions/StringExtensions.cs @@ -1,4 +1,8 @@ -using System; +// +// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. +// + +using System; using System.Collections.Generic; using System.Text; diff --git a/Directory.Build.props b/Directory.Build.props index 5772398d..2a2d56ad 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -24,7 +24,7 @@ https://github.com/yungd1plomat/AdvancedSharpAdbClient snupkg .NET client for adb, Android Debug Bridge (AdvancedSharpAdbClient) - 2.6.9 + 3.0.9 From 235789fa8c5ba3bb8128e0b5cb28fc05aa850fe6 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Tue, 31 Oct 2023 00:51:18 +0800 Subject: [PATCH 53/66] Use Task.Yield instead of ThreadSwitcher --- .../Receivers/ProcessOutputReceiver.cs | 3 +- AdvancedSharpAdbClient/DeviceMonitor.Async.cs | 2 +- .../Extensions/Extensions.cs | 36 +++-- .../Models/FramebufferHeader.cs | 61 ++++++-- .../Polyfills/Extensions/ThreadExtensions.cs | 147 ------------------ .../Properties/GlobalUsings.cs | 2 + 6 files changed, 75 insertions(+), 176 deletions(-) delete mode 100644 AdvancedSharpAdbClient/Polyfills/Extensions/ThreadExtensions.cs diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs index bd76bbdb..eb459f7f 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs @@ -2,7 +2,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using System; using System.Collections.Generic; namespace AdvancedSharpAdbClient.Receivers.DeviceCommands @@ -37,7 +36,7 @@ protected override void ProcessNewLines(IEnumerable lines) { Processes.Add(new AndroidProcess(line, cmdLinePrefix: true)); } - catch (Exception) + catch { // Swallow } diff --git a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs index 56a2d3cd..9ea52783 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs @@ -109,7 +109,7 @@ protected virtual async Task DeviceMonitorLoopAsync(CancellationToken cancellati { IsRunning = true; - await ThreadExtensions.ResumeBackgroundAsync(); + await Extensions.Yield(); // Set up the connection to track the list of devices. await InitializeSocketAsync(cancellationToken).ConfigureAwait(false); diff --git a/AdvancedSharpAdbClient/Extensions/Extensions.cs b/AdvancedSharpAdbClient/Extensions/Extensions.cs index ee3b7ef4..9cec30b3 100644 --- a/AdvancedSharpAdbClient/Extensions/Extensions.cs +++ b/AdvancedSharpAdbClient/Extensions/Extensions.cs @@ -7,10 +7,14 @@ using System.IO; using System.Net; using System.Net.Sockets; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Text; using System.Threading; +#if NET40 +using Microsoft.Runtime.CompilerServices; +#endif + namespace AdvancedSharpAdbClient { internal static class Extensions @@ -83,22 +87,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 task returned by . - /// - /// The work to execute asynchronously. - /// A cancellation token that can be used to cancel the work if it has not yet started. - /// A task that represents a proxy for the task returned by . - /// The parameter was . - /// For information on handling exceptions thrown by task operations, see Exception Handling. - public static Task Run(Action function, CancellationToken cancellationToken = default) => -#if NETFRAMEWORK && !NET45_OR_GREATER - TaskEx -#else - Task -#endif - .Run(function, 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. @@ -144,6 +132,20 @@ 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(); + #if !NET7_0_OR_GREATER /// /// Reads a line of characters asynchronously and returns the data as a string. diff --git a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs index 13bc553a..a2df3dfc 100644 --- a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs +++ b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs @@ -291,13 +291,33 @@ private readonly PixelFormat StandardizePixelFormat(byte[] buffer) /// A which can be used to cancel the asynchronous task. /// A that represents the image contained in the frame buffer, or /// if the framebuffer does not contain any data. This can happen when DRM is enabled on the device. - public readonly async Task ToBitmap(byte[] buffer, CoreDispatcher dispatcher, CancellationToken cancellationToken = default) + public readonly Task ToBitmap(byte[] buffer, CoreDispatcher dispatcher, CancellationToken cancellationToken = default) { - if (dispatcher?.HasThreadAccess == false) + FramebufferHeader self = this; + + if (dispatcher.HasThreadAccess) { - await dispatcher.ResumeForegroundAsync(); + return ToBitmap(buffer, cancellationToken); + } + else + { + TaskCompletionSource taskCompletionSource = new(); + + _ = dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () => + { + try + { + WriteableBitmap? result = await self.ToBitmap(buffer, cancellationToken).ConfigureAwait(false); + taskCompletionSource.SetResult(result); + } + catch (Exception e) + { + taskCompletionSource.SetException(e); + } + }); + + return taskCompletionSource.Task; } - return await ToBitmap(buffer, cancellationToken).ConfigureAwait(false); } /// @@ -309,13 +329,36 @@ private readonly PixelFormat StandardizePixelFormat(byte[] buffer) /// A that represents the image contained in the frame buffer, or /// if the framebuffer does not contain any data. This can happen when DRM is enabled on the device. [ContractVersion(typeof(UniversalApiContract), 327680u)] - public readonly async Task ToBitmap(byte[] buffer, DispatcherQueue dispatcher, CancellationToken cancellationToken = default) + public readonly Task ToBitmap(byte[] buffer, DispatcherQueue dispatcher, CancellationToken cancellationToken = default) { - if (dispatcher?.HasThreadAccess == false) + FramebufferHeader self = this; + + if (ApiInformation.IsMethodPresent("Windows.System.DispatcherQueue", "HasThreadAccess") && dispatcher.HasThreadAccess) { - await dispatcher.ResumeForegroundAsync(); + return ToBitmap(buffer, cancellationToken); + } + else + { + TaskCompletionSource taskCompletionSource = new(); + + if (!dispatcher.TryEnqueue(async () => + { + try + { + WriteableBitmap? result = await self.ToBitmap(buffer, cancellationToken).ConfigureAwait(false); + taskCompletionSource.SetResult(result); + } + catch (Exception e) + { + taskCompletionSource.SetException(e); + } + })) + { + taskCompletionSource.SetException(new InvalidOperationException("Failed to enqueue the operation")); + } + + return taskCompletionSource.Task; } - return await ToBitmap(buffer, cancellationToken).ConfigureAwait(false); } /// @@ -349,7 +392,7 @@ private readonly PixelFormat StandardizePixelFormat(byte[] buffer) await WriteableImage.SetSourceAsync(randomAccessStream).AsTask(cancellationToken); return WriteableImage; } - catch (Exception) + catch when (!cancellationToken.IsCancellationRequested) { using InMemoryRandomAccessStream random = new(); BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, random).AsTask(cancellationToken); diff --git a/AdvancedSharpAdbClient/Polyfills/Extensions/ThreadExtensions.cs b/AdvancedSharpAdbClient/Polyfills/Extensions/ThreadExtensions.cs deleted file mode 100644 index adfcc1fc..00000000 --- a/AdvancedSharpAdbClient/Polyfills/Extensions/ThreadExtensions.cs +++ /dev/null @@ -1,147 +0,0 @@ -#if HAS_TASK -// -// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. -// - -using System; -using System.ComponentModel; -using System.Runtime.CompilerServices; -using System.Threading; - -namespace AdvancedSharpAdbClient.Polyfills -{ -#if WINDOWS_UWP - /// - /// A helper type for switch thread by . This type is not intended to be used directly from your code. - /// - /// A whose foreground thread to switch execution to. - /// Specifies the priority for event dispatch. - [EditorBrowsable(EditorBrowsableState.Never)] - public readonly struct DispatcherThreadSwitcher(CoreDispatcher dispatcher, CoreDispatcherPriority priority) : INotifyCompletion - { - /// - /// Gets a value that indicates whether the asynchronous operation has completed. - /// - public bool IsCompleted => dispatcher.HasThreadAccess; - - /// - /// Ends the await on the completed task. - /// - public void GetResult() { } - - /// - /// Gets an awaiter used to await this . - /// - /// An awaiter instance. - public DispatcherThreadSwitcher GetAwaiter() => this; - - /// - public void OnCompleted(Action continuation) - { - if (IsCompleted) - { - continuation(); - } - else - { - _ = dispatcher.RunAsync(priority, () => continuation()); - } - } - } - - /// - /// A helper type for switch thread by . This type is not intended to be used directly from your code. - /// - /// A whose foreground thread to switch execution to. - /// Specifies the priority for event dispatch. - [EditorBrowsable(EditorBrowsableState.Never)] - public readonly struct DispatcherQueueThreadSwitcher(DispatcherQueue dispatcher, DispatcherQueuePriority priority) : INotifyCompletion - { - /// - /// Gets a value that indicates whether the asynchronous operation has completed. - /// - public bool IsCompleted => dispatcher.HasThreadAccess; - - /// - /// Ends the await on the completed task. - /// - public void GetResult() { } - - /// - /// Gets an awaiter used to await this . - /// - /// An awaiter instance. - public DispatcherQueueThreadSwitcher GetAwaiter() => this; - - /// - public void OnCompleted(Action continuation) - { - if (IsCompleted) - { - continuation(); - } - else - { - _ = dispatcher.TryEnqueue(priority, () => continuation()); - } - } - } -#endif - - /// - /// A helper type for switch thread by . This type is not intended to be used directly from your code. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public readonly struct TaskThreadSwitcher : INotifyCompletion - { - /// - /// Gets a value that indicates whether the asynchronous operation has completed. - /// - public bool IsCompleted => SynchronizationContext.Current == null; - - /// - /// Ends the await on the completed task. - /// - public void GetResult() { } - - /// - /// Gets an awaiter used to await this . - /// - /// An awaiter instance. - public TaskThreadSwitcher GetAwaiter() => this; - - /// - public void OnCompleted(Action continuation) => _ = Extensions.Run(continuation); - } - - /// - /// The extensions for switching threads. - /// - public static class ThreadExtensions - { -#if WINDOWS_UWP - /// - /// A helper function—for use within a coroutine—that you can to switch execution to a specific foreground thread. - /// - /// A whose foreground thread to switch execution to. - /// Specifies the priority for event dispatch. - /// An object that you can . - public static DispatcherQueueThreadSwitcher ResumeForegroundAsync(this DispatcherQueue dispatcher, DispatcherQueuePriority priority = DispatcherQueuePriority.Normal) => new(dispatcher, priority); - - /// - /// A helper function—for use within a coroutine—that you can to switch execution to a specific foreground thread. - /// - /// A whose foreground thread to switch execution to. - /// Specifies the priority for event dispatch. - /// An object that you can . - public static DispatcherThreadSwitcher ResumeForegroundAsync(this CoreDispatcher dispatcher, CoreDispatcherPriority priority = CoreDispatcherPriority.Normal) => new (dispatcher, priority); -#endif - - /// - /// A helper function—for use within a coroutine—that returns control to the caller, and then immediately resumes execution on a thread pool thread. - /// - /// An object that you can . - public static TaskThreadSwitcher ResumeBackgroundAsync() => new(); - } -} -#endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Properties/GlobalUsings.cs b/AdvancedSharpAdbClient/Properties/GlobalUsings.cs index 9c9cc3a2..c4d2f724 100644 --- a/AdvancedSharpAdbClient/Properties/GlobalUsings.cs +++ b/AdvancedSharpAdbClient/Properties/GlobalUsings.cs @@ -30,6 +30,8 @@ global using TimeSpan = System.TimeSpan; #if HAS_DRAWING global using Point = System.Drawing.Point; +#else +global using Point = AdvancedSharpAdbClient.Polyfills.Point; #endif #endif From 25bec112a4681e183596958e096189dcf7f118d9 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Tue, 31 Oct 2023 16:05:11 +0800 Subject: [PATCH 54/66] Improve async methods --- .../DeviceMonitorTests.Async.cs | 17 ++-- AdvancedSharpAdbClient/AdbSocket.Async.cs | 8 +- AdvancedSharpAdbClient/DeviceMonitor.Async.cs | 1 - .../Extensions/Extensions.cs | 92 +++++++++++++------ .../Interfaces/IAdbSocket.Async.cs | 8 ++ .../Interfaces/ITcpSocket.Async.cs | 16 ++++ AdvancedSharpAdbClient/TcpSocket.Async.cs | 34 +++++++ 7 files changed, 136 insertions(+), 40 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.Async.cs b/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.Async.cs index de5c5372..bd455b16 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceMonitorTests.Async.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Threading; +using System.Threading.Tasks; using Xunit; namespace AdvancedSharpAdbClient.Tests @@ -39,11 +40,11 @@ await RunTestAsync( // Device disconnects ManualResetEvent eventWaiter = sink.CreateEventSignal(); - RunTest( + _ = await RunTestAsync( NoResponses, [string.Empty], NoRequests, - () => _ = eventWaiter.WaitOne(1000)); + () => Task.Run(() => eventWaiter.WaitOne(1000))); Assert.Empty(monitor.Devices); Assert.Empty(sink.ConnectedEvents); @@ -87,11 +88,11 @@ await RunTestAsync( // Device disconnects ManualResetEvent eventWaiter = sink.CreateEventSignal(); - _ = RunTest( + _ = await RunTestAsync( NoResponses, ["169.254.109.177:5555\tdevice\n"], NoRequests, - () => eventWaiter.WaitOne(1000)); + () => Task.Run(() => eventWaiter.WaitOne(1000))); Assert.Single(monitor.Devices); Assert.Single(sink.ConnectedEvents); @@ -165,11 +166,11 @@ await RunTestAsync( // Device disconnects ManualResetEvent eventWaiter = sink.CreateEventSignal(); - _ = RunTest( + _ = await RunTestAsync( NoResponses, ["169.254.109.177:5555\tdevice\n"], NoRequests, - () => eventWaiter.WaitOne(1000)); + () => Task.Run(() => eventWaiter.WaitOne(1000))); Assert.Single(monitor.Devices); Assert.Equal(DeviceState.Online, monitor.Devices[0].State); @@ -215,11 +216,11 @@ await RunTestAsync( // Something happens but device does not change ManualResetEvent eventWaiter = sink.CreateEventSignal(); - _ = RunTest( + _ = await RunTestAsync( NoResponses, ["169.254.109.177:5555\toffline\n"], NoRequests, - () => eventWaiter.WaitOne(1000)); + () => Task.Run(() => eventWaiter.WaitOne(1000))); Assert.Single(monitor.Devices); Assert.Equal(DeviceState.Offline, monitor.Devices.ElementAt(0).State); diff --git a/AdvancedSharpAdbClient/AdbSocket.Async.cs b/AdvancedSharpAdbClient/AdbSocket.Async.cs index b28c484e..4e52dd9a 100644 --- a/AdvancedSharpAdbClient/AdbSocket.Async.cs +++ b/AdvancedSharpAdbClient/AdbSocket.Async.cs @@ -13,10 +13,14 @@ namespace AdvancedSharpAdbClient { public partial class AdbSocket { -#if NET6_0_OR_GREATER /// - public virtual ValueTask ReconnectAsync(CancellationToken cancellationToken = default) => Socket.ReconnectAsync(cancellationToken); + public virtual +#if NET6_0_OR_GREATER + ValueTask +#else + Task #endif + ReconnectAsync(CancellationToken cancellationToken = default) => Socket.ReconnectAsync(cancellationToken); /// public virtual async Task SendAsync(byte[] data, int length, CancellationToken cancellationToken = default) diff --git a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs index 9ea52783..7b08b37c 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs @@ -108,7 +108,6 @@ public async Task DisposeAsync() protected virtual async Task DeviceMonitorLoopAsync(CancellationToken cancellationToken = default) { IsRunning = true; - await Extensions.Yield(); // Set up the connection to track the list of devices. diff --git a/AdvancedSharpAdbClient/Extensions/Extensions.cs b/AdvancedSharpAdbClient/Extensions/Extensions.cs index 9cec30b3..00bbcd25 100644 --- a/AdvancedSharpAdbClient/Extensions/Extensions.cs +++ b/AdvancedSharpAdbClient/Extensions/Extensions.cs @@ -41,37 +41,27 @@ public static DnsEndPoint CreateDnsEndPoint(string host, int port) : new DnsEndPoint(values[0], values.Length > 1 && int.TryParse(values[1], out int _port) ? _port : port); } -#if !HAS_PROCESS +#if HAS_TASK +#if NETFRAMEWORK && !NET46_OR_GREATER /// - /// Closes the connection and releases all associated resources. + /// Singleton cached task that's been completed successfully. /// - /// The to release. - public static void Close(this Socket socket) => socket.Dispose(); + internal static readonly Task s_cachedCompleted = +#if NET45_OR_GREATER + Task. +#else + TaskEx. #endif - -#if NETFRAMEWORK && !NET40_OR_GREATER - /// - /// Releases all resources used by the current instance of the class. - /// - /// The to release. - public static void Dispose(this Socket socket) - { - socket.Close(); - GC.SuppressFinalize(socket); - } + FromResult(null); /// - /// Releases all resources used by the current instance of the class. + /// Gets a task that's already been completed successfully. /// - /// The to release. - public static void Dispose(this WaitHandle waitHandle) - { - waitHandle.Close(); - GC.SuppressFinalize(waitHandle); - } + public static Task CompletedTask => s_cachedCompleted; +#else + public static Task CompletedTask => Task.CompletedTask; #endif -#if HAS_TASK /// /// Creates a task that completes after a specified number of milliseconds. /// @@ -155,12 +145,16 @@ public static YieldAwaitable Yield() => /// 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 async Task ReadLineAsync(this TextReader reader, CancellationToken cancellationToken) + { + using CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.Register(reader.Close); #if NET35 - Run(reader.ReadLine, cancellationToken); + await Yield(); + return reader.ReadLine(); #else - reader.ReadLineAsync(); + return await reader.ReadLineAsync(); #endif + } /// /// Reads all characters from the current position to the end of the stream asynchronously and returns them as one string. @@ -170,13 +164,53 @@ public static YieldAwaitable Yield() => /// 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 Task ReadToEndAsync(this TextReader reader, CancellationToken cancellationToken) => + public static async Task ReadToEndAsync(this TextReader reader, CancellationToken cancellationToken) + { + using CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.Register(reader.Close); #if NET35 - Run(reader.ReadToEnd, cancellationToken); + await Yield(); + return reader.ReadToEnd(); #else - reader.ReadToEndAsync(); + return await reader.ReadToEndAsync(); +#endif + } #endif #endif + +#if !HAS_PROCESS + /// + /// Closes the connection and releases all associated resources. + /// + /// The to release. + public static void Close(this Socket socket) => socket.Dispose(); + + /// + /// Closes the and releases any system resources associated with the . + /// + /// The to release. + public static void Close(this TextReader reader) => reader.Dispose(); +#endif + +#if NETFRAMEWORK && !NET40_OR_GREATER + /// + /// Releases all resources used by the current instance of the class. + /// + /// The to release. + public static void Dispose(this Socket socket) + { + socket.Close(); + GC.SuppressFinalize(socket); + } + + /// + /// Releases all resources used by the current instance of the class. + /// + /// The to release. + public static void Dispose(this WaitHandle waitHandle) + { + waitHandle.Close(); + GC.SuppressFinalize(waitHandle); + } #endif public static bool IsWindowsPlatform() => diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbSocket.Async.cs b/AdvancedSharpAdbClient/Interfaces/IAdbSocket.Async.cs index ad667dee..6ab0421f 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbSocket.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbSocket.Async.cs @@ -20,6 +20,14 @@ public partial interface IAdbSocket /// A which can be used to cancel the asynchronous task. /// A that represents the asynchronous operation. ValueTask ReconnectAsync(CancellationToken cancellationToken); +#else + /// + /// Reconnects the to the same endpoint it was initially connected to. + /// Use this when the socket was disconnected by adb and you have restarted adb. + /// + /// A which can be used to cancel the asynchronous task. + /// A that represents the asynchronous operation. + Task ReconnectAsync(CancellationToken cancellationToken); #endif /// diff --git a/AdvancedSharpAdbClient/Interfaces/ITcpSocket.Async.cs b/AdvancedSharpAdbClient/Interfaces/ITcpSocket.Async.cs index dd7dbc37..da6bc6fd 100644 --- a/AdvancedSharpAdbClient/Interfaces/ITcpSocket.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/ITcpSocket.Async.cs @@ -28,6 +28,22 @@ public partial interface ITcpSocket /// A which can be used to cancel the asynchronous task. /// A that represents the asynchronous operation. ValueTask ReconnectAsync(CancellationToken cancellationToken); +#else + /// + /// Begins an asynchronous request for a connection to a remote host. + /// + /// An that represents the remote device. + /// A which can be used to cancel the asynchronous task. + /// A that represents the asynchronous operation. + Task ConnectAsync(EndPoint endPoint, CancellationToken cancellationToken); + + /// + /// Re-establishes the connection to a remote host. Assumes you have resolved the reason that caused the + /// socket to disconnect. + /// + /// A which can be used to cancel the asynchronous task. + /// A that represents the asynchronous operation. + Task ReconnectAsync(CancellationToken cancellationToken); #endif /// diff --git a/AdvancedSharpAdbClient/TcpSocket.Async.cs b/AdvancedSharpAdbClient/TcpSocket.Async.cs index 0c08a570..415494a0 100644 --- a/AdvancedSharpAdbClient/TcpSocket.Async.cs +++ b/AdvancedSharpAdbClient/TcpSocket.Async.cs @@ -43,6 +43,40 @@ public virtual ValueTask ReconnectAsync(CancellationToken cancellationToken = de return ConnectAsync(EndPoint!, cancellationToken); } } +#else + /// + [MemberNotNull(nameof(EndPoint))] + public virtual async Task ConnectAsync(EndPoint endPoint, CancellationToken cancellationToken = default) + { + if (endPoint is not (IPEndPoint or DnsEndPoint)) + { + throw new NotSupportedException("Only TCP endpoints are supported"); + } + + using (CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.Register(Socket.Close)) + { + EndPoint = endPoint; + await Extensions.Yield(); + Socket.Connect(endPoint); + } + Socket.Blocking = true; + } + + /// + public virtual Task ReconnectAsync(CancellationToken cancellationToken = default) + { + if (Socket.Connected) + { + // Already connected - nothing to do. + return Extensions.CompletedTask; + } + else + { + Socket.Dispose(); + Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + return ConnectAsync(EndPoint!, cancellationToken); + } + } #endif /// From fe8b37992120f384d502502fe059402143541515 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Tue, 31 Oct 2023 23:27:48 +0800 Subject: [PATCH 55/66] Add Task.Yield on Task dummy --- .../PackageManagerTests.Async.cs | 78 +++++----- .../DeviceCommands/PackageManagerTests.cs | 74 +++++---- .../Dummys/DummyAdbClient.cs | 10 +- .../Dummys/DummyAdbCommandLineClient.cs | 10 +- .../Dummys/DummyAdbServer.cs | 19 +-- .../Dummys/DummyAdbSocket.cs | 83 +++++------ .../Dummys/DummySyncService.cs | 53 +++++-- .../Dummys/DummyTcpSocket.cs | 17 ++- .../DeviceCommands/PackageManager.Async.cs | 141 +++++++++++------- .../DeviceCommands/PackageManager.cs | 107 +++++++------ 10 files changed, 340 insertions(+), 252 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs index 687129cd..19a14fdc 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs @@ -25,10 +25,11 @@ public async void InstallRemotePackageAsyncTest() Assert.Equal(2, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install \"/data/test.apk\"", adbClient.ReceivedCommands[1]); + adbClient.ReceivedCommands.Clear(); await manager.InstallRemotePackageAsync("/data/test.apk", true); - Assert.Equal(3, adbClient.ReceivedCommands.Count); - Assert.Equal("shell:pm install -r \"/data/test.apk\"", adbClient.ReceivedCommands[2]); + Assert.Single(adbClient.ReceivedCommands); + Assert.Equal("shell:pm install -r \"/data/test.apk\"", adbClient.ReceivedCommands[0]); } [Fact] @@ -53,6 +54,7 @@ public async void InstallPackageAsyncTest() PackageManager manager = new(adbClient, device); await manager.InstallPackageAsync("Assets/test.txt", false); + Assert.Equal(3, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[1]); Assert.Equal("shell:rm \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[2]); @@ -74,8 +76,8 @@ public async void InstallMultipleRemotePackageAsyncTest() adbClient.Commands["shell:pm install-create -p com.google.android.gms"] = "Success: created install session [936013062]"; adbClient.Commands["shell:pm install-create -r -p com.google.android.gms"] = "Success: created install session [936013062]"; adbClient.Commands["shell:pm install-write 936013062 base.apk \"/data/base.apk\""] = "Success"; - adbClient.Commands["shell:pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\""] = "Success"; - adbClient.Commands["shell:pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\""] = "Success"; + adbClient.Commands["shell:pm install-write 936013062 split0.apk \"/data/split-dpi.apk\""] = "Success"; + adbClient.Commands["shell:pm install-write 936013062 split1.apk \"/data/split-abi.apk\""] = "Success"; adbClient.Commands["shell:pm install-commit 936013062"] = "Success"; DeviceData device = new() @@ -89,34 +91,37 @@ public async void InstallMultipleRemotePackageAsyncTest() Assert.Equal(6, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create", adbClient.ReceivedCommands[1]); Assert.Equal("shell:pm install-write 936013062 base.apk \"/data/base.apk\"", adbClient.ReceivedCommands[2]); - Assert.Equal("shell:pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[3]); - Assert.Equal("shell:pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[4]); + Assert.Contains("shell:pm install-write 936013062 split0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[3..5]); + Assert.Contains("shell:pm install-write 936013062 split1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[3..5]); Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[5]); + adbClient.ReceivedCommands.Clear(); await manager.InstallMultipleRemotePackageAsync("/data/base.apk", ["/data/split-dpi.apk", "/data/split-abi.apk"], true); - Assert.Equal(11, adbClient.ReceivedCommands.Count); - Assert.Equal("shell:pm install-create -r", adbClient.ReceivedCommands[6]); - Assert.Equal("shell:pm install-write 936013062 base.apk \"/data/base.apk\"", adbClient.ReceivedCommands[7]); - Assert.Equal("shell:pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[8]); - Assert.Equal("shell:pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[9]); - Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[10]); + Assert.Equal(5, adbClient.ReceivedCommands.Count); + Assert.Equal("shell:pm install-create -r", adbClient.ReceivedCommands[0]); + Assert.Equal("shell:pm install-write 936013062 base.apk \"/data/base.apk\"", adbClient.ReceivedCommands[1]); + Assert.Contains("shell:pm install-write 936013062 split0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[2..4]); + Assert.Contains("shell:pm install-write 936013062 split1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[2..4]); + Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[4]); + adbClient.ReceivedCommands.Clear(); await manager.InstallMultipleRemotePackageAsync(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms", false); - Assert.Equal(15, adbClient.ReceivedCommands.Count); - Assert.Equal("shell:pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[11]); - Assert.Equal("shell:pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[12]); - Assert.Equal("shell:pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[13]); - Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[14]); + Assert.Equal(4, adbClient.ReceivedCommands.Count); + Assert.Equal("shell:pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[0]); + Assert.Contains("shell:pm install-write 936013062 split0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[1..3]); + Assert.Contains("shell:pm install-write 936013062 split1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[1..3]); + Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[3]); + adbClient.ReceivedCommands.Clear(); await manager.InstallMultipleRemotePackageAsync(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms", true); - Assert.Equal(19, adbClient.ReceivedCommands.Count); - Assert.Equal("shell:pm install-create -r -p com.google.android.gms", adbClient.ReceivedCommands[15]); - Assert.Equal("shell:pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[16]); - Assert.Equal("shell:pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[17]); - Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[18]); + Assert.Equal(4, adbClient.ReceivedCommands.Count); + Assert.Equal("shell:pm install-create -r -p com.google.android.gms", adbClient.ReceivedCommands[0]); + Assert.Contains("shell:pm install-write 936013062 split0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[1..3]); + Assert.Contains("shell:pm install-write 936013062 split1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[1..3]); + Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[3]); } [Fact] @@ -134,8 +139,8 @@ public async void InstallMultiplePackageAsyncTest() adbClient.Commands["shell:pm install-create"] = "Success: created install session [936013062]"; adbClient.Commands["shell:pm install-create -p com.google.android.gms"] = "Success: created install session [936013062]"; adbClient.Commands["shell:pm install-write 936013062 base.apk \"/data/local/tmp/test.txt\""] = "Success"; - adbClient.Commands["shell:pm install-write 936013062 splitapp0.apk \"/data/local/tmp/gapps.txt\""] = "Success"; - adbClient.Commands["shell:pm install-write 936013062 splitapp1.apk \"/data/local/tmp/logcat.bin\""] = "Success"; + adbClient.Commands["shell:pm install-write 936013062 split0.apk \"/data/local/tmp/gapps.txt\""] = "Success"; + adbClient.Commands["shell:pm install-write 936013062 split1.apk \"/data/local/tmp/logcat.bin\""] = "Success"; adbClient.Commands["shell:pm install-commit 936013062"] = "Success"; adbClient.Commands["shell:rm \"/data/local/tmp/test.txt\""] = string.Empty; adbClient.Commands["shell:rm \"/data/local/tmp/gapps.txt\""] = string.Empty; @@ -148,14 +153,15 @@ public async void InstallMultiplePackageAsyncTest() PackageManager manager = new(adbClient, device); await manager.InstallMultiplePackageAsync("Assets/test.txt", ["Assets/gapps.txt", "Assets/logcat.bin"], false); + Assert.Equal(9, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create", adbClient.ReceivedCommands[1]); Assert.Equal("shell:pm install-write 936013062 base.apk \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[2]); - Assert.Equal("shell:pm install-write 936013062 splitapp0.apk \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[3]); - Assert.Equal("shell:pm install-write 936013062 splitapp1.apk \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[4]); + Assert.Contains("shell:pm install-write 936013062 split0.apk \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[3..5]); + Assert.Contains("shell:pm install-write 936013062 split1.apk \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[3..5]); Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[5]); - Assert.Equal("shell:rm \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[6]); - Assert.Equal("shell:rm \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[7]); + Assert.Contains("shell:rm \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[6..8]); + Assert.Contains("shell:rm \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[6..8]); Assert.Equal("shell:rm \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[8]); Assert.Equal(3, syncService.UploadedFiles.Count); @@ -164,14 +170,16 @@ public async void InstallMultiplePackageAsyncTest() Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/logcat.bin")); syncService.UploadedFiles.Clear(); + adbClient.ReceivedCommands.Clear(); await manager.InstallMultiplePackageAsync(["Assets/gapps.txt", "Assets/logcat.bin"], "com.google.android.gms", false); - Assert.Equal(15, adbClient.ReceivedCommands.Count); - Assert.Equal("shell:pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[9]); - Assert.Equal("shell:pm install-write 936013062 splitapp0.apk \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[10]); - Assert.Equal("shell:pm install-write 936013062 splitapp1.apk \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[11]); - Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[12]); - Assert.Equal("shell:rm \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[6]); - Assert.Equal("shell:rm \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[7]); + + Assert.Equal(6, adbClient.ReceivedCommands.Count); + Assert.Equal("shell:pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[0]); + Assert.Contains("shell:pm install-write 936013062 split0.apk \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[1..3]); + Assert.Contains("shell:pm install-write 936013062 split1.apk \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[1..3]); + Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[3]); + Assert.Contains("shell:rm \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[4..6]); + Assert.Contains("shell:rm \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[4..6]); Assert.Equal(2, syncService.UploadedFiles.Count); Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/gapps.txt")); diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs index 326c6111..75b6a16d 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs @@ -53,10 +53,11 @@ public void InstallRemotePackageTest() Assert.Equal(2, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install \"/data/test.apk\"", adbClient.ReceivedCommands[1]); + adbClient.ReceivedCommands.Clear(); manager.InstallRemotePackage("/data/test.apk", true); - Assert.Equal(3, adbClient.ReceivedCommands.Count); - Assert.Equal("shell:pm install -r \"/data/test.apk\"", adbClient.ReceivedCommands[2]); + Assert.Single(adbClient.ReceivedCommands); + Assert.Equal("shell:pm install -r \"/data/test.apk\"", adbClient.ReceivedCommands[0]); } [Fact] @@ -81,6 +82,7 @@ public void InstallPackageTest() PackageManager manager = new(adbClient, device); manager.InstallPackage("Assets/test.txt", false); + Assert.Equal(3, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[1]); Assert.Equal("shell:rm \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[2]); @@ -102,8 +104,8 @@ public void InstallMultipleRemotePackageTest() adbClient.Commands["shell:pm install-create -p com.google.android.gms"] = "Success: created install session [936013062]"; adbClient.Commands["shell:pm install-create -r -p com.google.android.gms"] = "Success: created install session [936013062]"; adbClient.Commands["shell:pm install-write 936013062 base.apk \"/data/base.apk\""] = "Success"; - adbClient.Commands["shell:pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\""] = "Success"; - adbClient.Commands["shell:pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\""] = "Success"; + adbClient.Commands["shell:pm install-write 936013062 split0.apk \"/data/split-dpi.apk\""] = "Success"; + adbClient.Commands["shell:pm install-write 936013062 split1.apk \"/data/split-abi.apk\""] = "Success"; adbClient.Commands["shell:pm install-commit 936013062"] = "Success"; DeviceData device = new() @@ -117,34 +119,37 @@ public void InstallMultipleRemotePackageTest() Assert.Equal(6, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create", adbClient.ReceivedCommands[1]); Assert.Equal("shell:pm install-write 936013062 base.apk \"/data/base.apk\"", adbClient.ReceivedCommands[2]); - Assert.Equal("shell:pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[3]); - Assert.Equal("shell:pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[4]); + Assert.Equal("shell:pm install-write 936013062 split0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[3]); + Assert.Equal("shell:pm install-write 936013062 split1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[4]); Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[5]); + adbClient.ReceivedCommands.Clear(); manager.InstallMultipleRemotePackage("/data/base.apk", ["/data/split-dpi.apk", "/data/split-abi.apk"], true); - Assert.Equal(11, adbClient.ReceivedCommands.Count); - Assert.Equal("shell:pm install-create -r", adbClient.ReceivedCommands[6]); - Assert.Equal("shell:pm install-write 936013062 base.apk \"/data/base.apk\"", adbClient.ReceivedCommands[7]); - Assert.Equal("shell:pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[8]); - Assert.Equal("shell:pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[9]); - Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[10]); + Assert.Equal(5, adbClient.ReceivedCommands.Count); + Assert.Equal("shell:pm install-create -r", adbClient.ReceivedCommands[0]); + Assert.Equal("shell:pm install-write 936013062 base.apk \"/data/base.apk\"", adbClient.ReceivedCommands[1]); + Assert.Equal("shell:pm install-write 936013062 split0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[2]); + Assert.Equal("shell:pm install-write 936013062 split1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[3]); + Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[4]); + adbClient.ReceivedCommands.Clear(); manager.InstallMultipleRemotePackage(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms", false); - Assert.Equal(15, adbClient.ReceivedCommands.Count); - Assert.Equal("shell:pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[11]); - Assert.Equal("shell:pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[12]); - Assert.Equal("shell:pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[13]); - Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[14]); + Assert.Equal(4, adbClient.ReceivedCommands.Count); + Assert.Equal("shell:pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[0]); + Assert.Equal("shell:pm install-write 936013062 split0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[1]); + Assert.Equal("shell:pm install-write 936013062 split1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[2]); + Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[3]); + adbClient.ReceivedCommands.Clear(); manager.InstallMultipleRemotePackage(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms", true); - Assert.Equal(19, adbClient.ReceivedCommands.Count); - Assert.Equal("shell:pm install-create -r -p com.google.android.gms", adbClient.ReceivedCommands[15]); - Assert.Equal("shell:pm install-write 936013062 splitapp0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[16]); - Assert.Equal("shell:pm install-write 936013062 splitapp1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[17]); - Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[18]); + Assert.Equal(4, adbClient.ReceivedCommands.Count); + Assert.Equal("shell:pm install-create -r -p com.google.android.gms", adbClient.ReceivedCommands[0]); + Assert.Equal("shell:pm install-write 936013062 split0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[1]); + Assert.Equal("shell:pm install-write 936013062 split1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[2]); + Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[3]); } [Fact] @@ -162,8 +167,8 @@ public void InstallMultiplePackageTest() adbClient.Commands["shell:pm install-create"] = "Success: created install session [936013062]"; adbClient.Commands["shell:pm install-create -p com.google.android.gms"] = "Success: created install session [936013062]"; adbClient.Commands["shell:pm install-write 936013062 base.apk \"/data/local/tmp/test.txt\""] = "Success"; - adbClient.Commands["shell:pm install-write 936013062 splitapp0.apk \"/data/local/tmp/gapps.txt\""] = "Success"; - adbClient.Commands["shell:pm install-write 936013062 splitapp1.apk \"/data/local/tmp/logcat.bin\""] = "Success"; + adbClient.Commands["shell:pm install-write 936013062 split0.apk \"/data/local/tmp/gapps.txt\""] = "Success"; + adbClient.Commands["shell:pm install-write 936013062 split1.apk \"/data/local/tmp/logcat.bin\""] = "Success"; adbClient.Commands["shell:pm install-commit 936013062"] = "Success"; adbClient.Commands["shell:rm \"/data/local/tmp/test.txt\""] = string.Empty; adbClient.Commands["shell:rm \"/data/local/tmp/gapps.txt\""] = string.Empty; @@ -176,11 +181,12 @@ public void InstallMultiplePackageTest() PackageManager manager = new(adbClient, device); manager.InstallMultiplePackage("Assets/test.txt", ["Assets/gapps.txt", "Assets/logcat.bin"], false); + Assert.Equal(9, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create", adbClient.ReceivedCommands[1]); Assert.Equal("shell:pm install-write 936013062 base.apk \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[2]); - Assert.Equal("shell:pm install-write 936013062 splitapp0.apk \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[3]); - Assert.Equal("shell:pm install-write 936013062 splitapp1.apk \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[4]); + Assert.Equal("shell:pm install-write 936013062 split0.apk \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[3]); + Assert.Equal("shell:pm install-write 936013062 split1.apk \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[4]); Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[5]); Assert.Equal("shell:rm \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[6]); Assert.Equal("shell:rm \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[7]); @@ -192,14 +198,16 @@ public void InstallMultiplePackageTest() Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/logcat.bin")); syncService.UploadedFiles.Clear(); + adbClient.ReceivedCommands.Clear(); manager.InstallMultiplePackage(["Assets/gapps.txt", "Assets/logcat.bin"], "com.google.android.gms", false); - Assert.Equal(15, adbClient.ReceivedCommands.Count); - Assert.Equal("shell:pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[9]); - Assert.Equal("shell:pm install-write 936013062 splitapp0.apk \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[10]); - Assert.Equal("shell:pm install-write 936013062 splitapp1.apk \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[11]); - Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[12]); - Assert.Equal("shell:rm \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[6]); - Assert.Equal("shell:rm \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[7]); + + Assert.Equal(6, adbClient.ReceivedCommands.Count); + Assert.Equal("shell:pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[0]); + Assert.Equal("shell:pm install-write 936013062 split0.apk \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[1]); + Assert.Equal("shell:pm install-write 936013062 split1.apk \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[2]); + Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[3]); + Assert.Equal("shell:rm \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[4]); + Assert.Equal("shell:rm \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[5]); Assert.Equal(2, syncService.UploadedFiles.Count); Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/gapps.txt")); diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs index 04344201..366ca221 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs @@ -10,6 +10,9 @@ namespace AdvancedSharpAdbClient.Tests { + /// + /// A mock implementation of the class. + /// internal class DummyAdbClient : IAdbClient { public Dictionary Commands { get; } = []; @@ -81,8 +84,10 @@ public void ExecuteServerCommand(string target, string command, IShellOutputRece public void ExecuteServerCommand(string target, string command, IAdbSocket socket, IShellOutputReceiver receiver, Encoding encoding) => ExecuteServerCommand(target, command, receiver, encoding); - public Task ExecuteServerCommandAsync(string target, string command, Encoding encoding, CancellationToken cancellationToken = default) + public async Task ExecuteServerCommandAsync(string target, string command, Encoding encoding, CancellationToken cancellationToken = default) { + await Task.Yield(); + StringBuilder requestBuilder = new(); if (!StringExtensions.IsNullOrWhiteSpace(target)) { @@ -92,7 +97,6 @@ public Task ExecuteServerCommandAsync(string target, string command, Encoding en string request = requestBuilder.ToString(); ReceivedCommands.Add(request); - return Task.CompletedTask; } public Task ExecuteServerCommandAsync(string target, string command, IAdbSocket socket, Encoding encoding, CancellationToken cancellationToken) => @@ -118,7 +122,7 @@ public async Task ExecuteServerCommandAsync(string target, string command, IShel while (reader.Peek() != -1) { - receiver.AddOutput(await reader.ReadLineAsync(cancellationToken)); + receiver.AddOutput(await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false)); } receiver.Flush(); diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbCommandLineClient.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbCommandLineClient.cs index f6837a06..7987d578 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbCommandLineClient.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbCommandLineClient.cs @@ -7,7 +7,7 @@ namespace AdvancedSharpAdbClient.Tests { /// - /// + /// A mock implementation of the class. /// internal class DummyAdbCommandLineClient : AdbCommandLineClient { @@ -50,12 +50,10 @@ protected override int RunProcess(string filename, string command, ICollection RunProcessAsync(string filename, string command, ICollection errorOutput, ICollection standardOutput, CancellationToken cancellationToken = default) + protected override async Task RunProcessAsync(string filename, string command, ICollection errorOutput, ICollection standardOutput, CancellationToken cancellationToken = default) { - int result = RunProcess(filename, command, errorOutput, standardOutput); - TaskCompletionSource tcs = new(); - tcs.SetResult(result); - return tcs.Task; + await Task.Yield(); + return RunProcess(filename, command, errorOutput, standardOutput); } private static string ServerName => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "adb.exe" : "adb"; diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbServer.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbServer.cs index 6af5a5f6..3ea33ef6 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbServer.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbServer.cs @@ -31,11 +31,10 @@ internal class DummyAdbServer : IAdbServer public AdbServerStatus GetStatus() => Status; /// - public Task GetStatusAsync(CancellationToken cancellationToken = default) + public async Task GetStatusAsync(CancellationToken cancellationToken = default) { - TaskCompletionSource tcs = new(); - tcs.SetResult(Status); - return tcs.Task; + await Task.Yield(); + return Status; } /// @@ -70,22 +69,20 @@ public StartServerResult StartServer(string adbPath, bool restartServerIfNewer) } /// - public Task StartServerAsync(string adbPath, bool restartServerIfNewer, CancellationToken cancellationToken = default) + public async Task StartServerAsync(string adbPath, bool restartServerIfNewer, CancellationToken cancellationToken = default) { - StartServerResult result = StartServer(adbPath, restartServerIfNewer); - TaskCompletionSource tcs = new(); - tcs.SetResult(result); - return tcs.Task; + await Task.Yield(); + return StartServer(adbPath, restartServerIfNewer); } /// public void StopServer() => Status = Status with { IsRunning = false }; /// - public Task StopServerAsync(CancellationToken cancellationToken) + public async Task StopServerAsync(CancellationToken cancellationToken) { + await Task.Yield(); StopServer(); - return Task.CompletedTask; } } } diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs index fb8dada9..9f9dd95f 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs @@ -8,6 +8,9 @@ namespace AdvancedSharpAdbClient.Tests { + /// + /// A mock implementation of the class. + /// internal class DummyAdbSocket : IDummyAdbSocket { /// @@ -98,7 +101,7 @@ public string ReadString() { while (ResponseMessages.Count == 0) { - Thread.Sleep(1000); + Thread.Sleep(100); } } @@ -165,69 +168,69 @@ public void SetDevice(DeviceData device) public SyncCommand ReadSyncResponse() => SyncResponses.Dequeue(); - public Task SendAsync(byte[] data, int length, CancellationToken cancellationToken = default) + public async Task SendAsync(byte[] data, int length, CancellationToken cancellationToken = default) { + await Task.Yield(); Send(data, length); - return Task.CompletedTask; } - public Task SendAsync(byte[] data, int offset, int length, CancellationToken cancellationToken = default) + public async Task SendAsync(byte[] data, int offset, int length, CancellationToken cancellationToken = default) { + await Task.Yield(); Send(data, offset, length); - return Task.CompletedTask; } - public ValueTask SendAsync(ReadOnlyMemory data, CancellationToken cancellationToken = default) + public async ValueTask SendAsync(ReadOnlyMemory data, CancellationToken cancellationToken = default) { + await Task.Yield(); Send(data.Span); - return ValueTask.CompletedTask; } - public Task SendSyncRequestAsync(SyncCommand command, string path, int permissions, CancellationToken cancellationToken = default) + public async Task SendSyncRequestAsync(SyncCommand command, string path, int permissions, CancellationToken cancellationToken = default) { + await Task.Yield(); SendSyncRequest(command, path, permissions); - return Task.CompletedTask; } - public Task SendSyncRequestAsync(SyncCommand command, string path, CancellationToken cancellationToken = default) + public async Task SendSyncRequestAsync(SyncCommand command, string path, CancellationToken cancellationToken = default) { + await Task.Yield(); SendSyncRequest(command, path); - return Task.CompletedTask; } - public Task SendSyncRequestAsync(SyncCommand command, int length, CancellationToken cancellationToken = default) + public async Task SendSyncRequestAsync(SyncCommand command, int length, CancellationToken cancellationToken = default) { + await Task.Yield(); SendSyncRequest(command, length); - return Task.CompletedTask; } - public Task SendAdbRequestAsync(string request, CancellationToken cancellationToken = default) + public async Task SendAdbRequestAsync(string request, CancellationToken cancellationToken = default) { + await Task.Yield(); SendAdbRequest(request); - return Task.CompletedTask; } - public Task ReadAsync(byte[] data, int length, CancellationToken cancellationToken = default) + public async Task ReadAsync(byte[] data, int length, CancellationToken cancellationToken = default) { - int result = Read(data, length); - TaskCompletionSource tcs = new(); - tcs.SetResult(result); - return tcs.Task; + await Task.Yield(); + return Read(data, length); } - public ValueTask ReadAsync(Memory data, CancellationToken cancellationToken) + public async ValueTask ReadAsync(Memory data, CancellationToken cancellationToken) { - int result = Read(data.Span); - return new ValueTask(result); + await Task.Yield(); + return Read(data.Span); } public async Task ReadStringAsync(CancellationToken cancellationToken = default) { + await Task.Yield(); + if (WaitForNewData) { while (ResponseMessages.Count == 0) { - await Task.Delay(TimeSpan.FromMilliseconds(100), cancellationToken); + await Task.Delay(TimeSpan.FromMilliseconds(100), cancellationToken).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); } } @@ -245,34 +248,28 @@ public async Task ReadStringAsync(CancellationToken cancellationToken = } } - public Task ReadSyncStringAsync(CancellationToken cancellationToken = default) + public async Task ReadSyncStringAsync(CancellationToken cancellationToken = default) { - string response = ReadSyncString(); - TaskCompletionSource tcs = new(); - tcs.SetResult(response); - return tcs.Task; + await Task.Yield(); + return ReadSyncString(); } - public Task ReadSyncResponseAsync(CancellationToken cancellationToken = default) + public async Task ReadSyncResponseAsync(CancellationToken cancellationToken = default) { - SyncCommand response = ReadSyncResponse(); - TaskCompletionSource tcs = new(); - tcs.SetResult(response); - return tcs.Task; + await Task.Yield(); + return ReadSyncResponse(); } - public Task ReadAdbResponseAsync(CancellationToken cancellationToken = default) + public async Task ReadAdbResponseAsync(CancellationToken cancellationToken = default) { - AdbResponse response = ReadAdbResponse(); - TaskCompletionSource tcs = new(); - tcs.SetResult(response); - return tcs.Task; + await Task.Yield(); + return ReadAdbResponse(); } - public Task SetDeviceAsync(DeviceData device, CancellationToken cancellationToken = default) + public async Task SetDeviceAsync(DeviceData device, CancellationToken cancellationToken = default) { + await Task.Yield(); SetDevice(device); - return Task.CompletedTask; } public void Dispose() => IsConnected = false; @@ -281,10 +278,10 @@ public Task SetDeviceAsync(DeviceData device, CancellationToken cancellationToke public void Reconnect() => DidReconnect = true; - public ValueTask ReconnectAsync(CancellationToken cancellationToken = default) + public async ValueTask ReconnectAsync(CancellationToken cancellationToken = default) { + await Task.Yield(); DidReconnect = true; - return ValueTask.CompletedTask; } } } diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs index 1274c0be..a38f9ac6 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs @@ -6,6 +6,9 @@ namespace AdvancedSharpAdbClient.Tests { + /// + /// A mock implementation of the class. + /// internal class DummySyncService : ISyncService { public Dictionary UploadedFiles { get; } = []; @@ -18,34 +21,54 @@ internal class DummySyncService : ISyncService public void Open() => IsOpen = true; - public Task OpenAsync(CancellationToken cancellationToken) + public async Task OpenAsync(CancellationToken cancellationToken) { + await Task.Yield(); IsOpen = true; - return Task.CompletedTask; } - public void Pull(string remotePath, Stream stream, IProgress progress = null, in bool isCancelled = false) => - SyncProgressChanged?.Invoke(this, new SyncProgressChangedEventArgs(100, 100)); + public void Pull(string remotePath, Stream stream, IProgress progress = null, in bool isCancelled = false) + { + for (int i = 0; i <= 100; i++) + { + Thread.Yield(); + SyncProgressChanged?.Invoke(this, new SyncProgressChangedEventArgs(i, 100)); + } + } - public Task PullAsync(string remotePath, Stream stream, IProgress progress, CancellationToken cancellationToken = default) + public async Task PullAsync(string remotePath, Stream stream, IProgress progress, CancellationToken cancellationToken = default) { - SyncProgressChanged?.Invoke(this, new SyncProgressChangedEventArgs(100, 100)); - return Task.CompletedTask; + for (int i = 0; i <= 100; i++) + { + await Task.Yield(); + SyncProgressChanged?.Invoke(this, new SyncProgressChangedEventArgs(i, 100)); + } } public void Push(Stream stream, string remotePath, int permissions, DateTimeOffset timestamp, IProgress progress = null, in bool isCancelled = false) { - SyncProgressChanged?.Invoke(this, new SyncProgressChangedEventArgs(0, 100)); - UploadedFiles[remotePath] = stream; - SyncProgressChanged?.Invoke(this, new SyncProgressChangedEventArgs(100, 100)); + for (int i = 0; i <= 100; i++) + { + Thread.Yield(); + if (i == 100) + { + UploadedFiles[remotePath] = stream; + } + SyncProgressChanged?.Invoke(this, new SyncProgressChangedEventArgs(i, 100)); + } } - public Task PushAsync(Stream stream, string remotePath, int permissions, DateTimeOffset timestamp, IProgress progress, CancellationToken cancellationToken = default) + public async Task PushAsync(Stream stream, string remotePath, int permissions, DateTimeOffset timestamp, IProgress progress, CancellationToken cancellationToken = default) { - SyncProgressChanged?.Invoke(this, new SyncProgressChangedEventArgs(0, 100)); - UploadedFiles[remotePath] = stream; - SyncProgressChanged?.Invoke(this, new SyncProgressChangedEventArgs(100, 100)); - return Task.CompletedTask; + for (int i = 0; i <= 100; i++) + { + await Task.Yield(); + if (i == 100) + { + UploadedFiles[remotePath] = stream; + } + SyncProgressChanged?.Invoke(this, new SyncProgressChangedEventArgs(i, 100)); + } } #region Not Implemented diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyTcpSocket.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyTcpSocket.cs index 4f06eb19..ee28c764 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyTcpSocket.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyTcpSocket.cs @@ -7,6 +7,9 @@ namespace AdvancedSharpAdbClient.Tests { + /// + /// A mock implementation of the class. + /// internal class DummyTcpSocket : ITcpSocket { /// @@ -27,18 +30,18 @@ internal class DummyTcpSocket : ITcpSocket public void Connect(EndPoint endPoint) => Connected = true; - public ValueTask ConnectAsync(EndPoint endPoint, CancellationToken cancellationToken) + public async ValueTask ConnectAsync(EndPoint endPoint, CancellationToken cancellationToken) { + await Task.Yield(); Connected = true; - return ValueTask.CompletedTask; } public void Reconnect() => Connected = true; - public ValueTask ReconnectAsync(CancellationToken cancellationToken) + public async ValueTask ReconnectAsync(CancellationToken cancellationToken) { + await Task.Yield(); Connected = true; - return ValueTask.CompletedTask; } public void Dispose() => Connected = false; @@ -77,19 +80,19 @@ public int Send(ReadOnlySpan buffer, SocketFlags socketFlags) public async Task SendAsync(byte[] buffer, int size, SocketFlags socketFlags, CancellationToken cancellationToken) { - await OutputStream.WriteAsync(buffer.AsMemory(0, size), cancellationToken); + await OutputStream.WriteAsync(buffer.AsMemory(0, size), cancellationToken).ConfigureAwait(false); return size; } public async Task SendAsync(byte[] buffer, int offset, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) { - await OutputStream.WriteAsync(buffer.AsMemory(offset, size), cancellationToken); + await OutputStream.WriteAsync(buffer.AsMemory(offset, size), cancellationToken).ConfigureAwait(false); return size; } public async ValueTask SendAsync(ReadOnlyMemory buffer, SocketFlags socketFlags, CancellationToken cancellationToken = default) { - await OutputStream.WriteAsync(buffer, cancellationToken); + await OutputStream.WriteAsync(buffer, cancellationToken).ConfigureAwait(false); return buffer.Length; } diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs index 04fb2a45..15a253c2 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs @@ -8,6 +8,7 @@ using System.Diagnostics; using System.IO; using System.Linq; +using System.Text; using System.Threading; namespace AdvancedSharpAdbClient.DeviceCommands @@ -68,10 +69,17 @@ public virtual async Task InstallRemotePackageAsync(string remoteFilePath, bool ValidateDevice(); - InstallOutputReceiver receiver = new(); - string reinstallSwitch = reinstall ? "-r " : string.Empty; + StringBuilder requestBuilder = new StringBuilder().Append("pm install"); + + if (reinstall) + { + _ = requestBuilder.Append(" -r"); + } - string cmd = $"pm install {reinstallSwitch}\"{remoteFilePath}\""; + _ = requestBuilder.AppendFormat(" \"{0}\"", remoteFilePath); + + string cmd = requestBuilder.ToString(); + InstallOutputReceiver receiver = new(); await AdbClient.ExecuteShellCommandAsync(Device, cmd, receiver, cancellationToken).ConfigureAwait(false); if (!string.IsNullOrEmpty(receiver.ErrorMessage)) @@ -100,31 +108,45 @@ void OnMainSyncProgressChanged(object sender, SyncProgressChangedEventArgs args) Dictionary progress = new(splitPackageFilePaths.Count); void OnSplitSyncProgressChanged(object sender, SyncProgressChangedEventArgs args) { - int count = 1; - if (sender is string path) - { - progress[path] = args.ProgressPercentage; - } - else if (sender is true) + lock (progress) { - count++; + int count = 1; + if (sender is string path) + { + progress[path] = args.ProgressPercentage; + } + else if (sender is true) + { + count++; + } + double present = progress.Values.Select(x => x / splitPackageFilePaths.Count / 2).Sum(); + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitPackageFilePaths.Count + 1, present)); } - double present = progress.Values.Select(x => x / splitPackageFilePaths.Count / 2).Sum(); - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitPackageFilePaths.Count + 1, present)); } string[] splitRemoteFilePaths = await splitPackageFilePaths.Select(x => SyncPackageToDeviceAsync(x, OnSplitSyncProgressChanged, cancellationToken)).ToArrayAsync().ConfigureAwait(false); + if (splitRemoteFilePaths.Length < splitPackageFilePaths.Count) + { + throw new PackageInstallationException($"{nameof(SyncPackageToDeviceAsync)} failed. {splitPackageFilePaths.Count} should process but only {splitRemoteFilePaths.Length} processed."); + } + await InstallMultipleRemotePackageAsync(baseRemoteFilePath, splitRemoteFilePaths, reinstall, cancellationToken); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFilePaths.Length + 1, PackageInstallProgressState.PostInstall)); int count = 0; await Extensions.WhenAll(splitRemoteFilePaths.Select(async x => { + count++; await RemoveRemotePackageAsync(x, cancellationToken).ConfigureAwait(false); - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(++count, splitRemoteFilePaths.Length + 1, PackageInstallProgressState.PostInstall)); + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitRemoteFilePaths.Length + 1, PackageInstallProgressState.PostInstall)); })).ConfigureAwait(false); + if (count < splitRemoteFilePaths.Length) + { + throw new PackageInstallationException($"{nameof(RemoveRemotePackageAsync)} failed. {splitRemoteFilePaths.Length} should process but only {count} processed."); + } + await RemoveRemotePackageAsync(baseRemoteFilePath, cancellationToken).ConfigureAwait(false); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(++count, splitRemoteFilePaths.Length + 1, PackageInstallProgressState.PostInstall)); @@ -146,31 +168,45 @@ public virtual async Task InstallMultiplePackageAsync(ICollection splitP Dictionary progress = new(splitPackageFilePaths.Count); void OnSyncProgressChanged(object sender, SyncProgressChangedEventArgs args) { - int count = 1; - if (sender is string path) + lock (progress) { - progress[path] = args.ProgressPercentage; - } - else if (sender is true) - { - count++; + int count = 1; + if (sender is string path) + { + progress[path] = args.ProgressPercentage; + } + else if (sender is true) + { + count++; + } + double present = progress.Values.Select(x => x / splitPackageFilePaths.Count).Sum(); + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitPackageFilePaths.Count, present)); } - double present = progress.Values.Select(x => x / splitPackageFilePaths.Count).Sum(); - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitPackageFilePaths.Count, present)); } string[] splitRemoteFilePaths = await splitPackageFilePaths.Select(x => SyncPackageToDeviceAsync(x, OnSyncProgressChanged, cancellationToken)).ToArrayAsync().ConfigureAwait(false); + if (splitRemoteFilePaths.Length < splitPackageFilePaths.Count) + { + throw new PackageInstallationException($"{nameof(SyncPackageToDeviceAsync)} failed. {splitPackageFilePaths.Count} should process but only {splitRemoteFilePaths.Length} processed."); + } + await InstallMultipleRemotePackageAsync(splitRemoteFilePaths, packageName, reinstall, cancellationToken); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFilePaths.Length, PackageInstallProgressState.PostInstall)); int count = 0; await Extensions.WhenAll(splitRemoteFilePaths.Select(async x => { + count++; await RemoveRemotePackageAsync(x, cancellationToken).ConfigureAwait(false); - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(++count, splitRemoteFilePaths.Length, PackageInstallProgressState.PostInstall)); + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitRemoteFilePaths.Length, PackageInstallProgressState.PostInstall)); })).ConfigureAwait(false); + if (count < splitRemoteFilePaths.Length) + { + throw new PackageInstallationException($"{nameof(RemoveRemotePackageAsync)} failed. {splitRemoteFilePaths.Length} should process but only {count} processed."); + } + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.Finished)); } @@ -196,20 +232,18 @@ public virtual async Task InstallMultipleRemotePackageAsync(string baseRemoteFil InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(1, splitRemoteFilePaths.Count + 1, PackageInstallProgressState.WriteSession)); - int i = 0, count = 0; + int count = 0; await Extensions.WhenAll(splitRemoteFilePaths.Select(async (splitRemoteFilePath) => { - try - { - await WriteInstallSessionAsync(session, $"splitapp{i++}", splitRemoteFilePath, cancellationToken).ConfigureAwait(false); - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(++count, splitRemoteFilePaths.Count + 1, PackageInstallProgressState.WriteSession)); - } - catch (Exception ex) - { - Debug.WriteLine(ex.Message); - } + await WriteInstallSessionAsync(session, $"split{count++}", splitRemoteFilePath, cancellationToken).ConfigureAwait(false); + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitRemoteFilePaths.Count + 1, PackageInstallProgressState.WriteSession)); })).ConfigureAwait(false); + if (count < splitRemoteFilePaths.Count) + { + throw new PackageInstallationException($"{nameof(WriteInstallSessionAsync)} failed. {splitRemoteFilePaths.Count} should process but only {count} processed."); + } + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.Installing)); InstallOutputReceiver receiver = new(); @@ -239,20 +273,18 @@ public virtual async Task InstallMultipleRemotePackageAsync(ICollection InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFilePaths.Count, PackageInstallProgressState.WriteSession)); - int i = 0, count = 0; + int count = 0; await Extensions.WhenAll(splitRemoteFilePaths.Select(async (splitRemoteFilePath) => { - try - { - await WriteInstallSessionAsync(session, $"splitapp{i++}", splitRemoteFilePath, cancellationToken).ConfigureAwait(false); - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(++count, splitRemoteFilePaths.Count, PackageInstallProgressState.WriteSession)); - } - catch (Exception ex) - { - Debug.WriteLine(ex.Message); - } + await WriteInstallSessionAsync(session, $"split{count++}", splitRemoteFilePath, cancellationToken).ConfigureAwait(false); + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitRemoteFilePaths.Count, PackageInstallProgressState.WriteSession)); })).ConfigureAwait(false); + if (count < splitRemoteFilePaths.Count) + { + throw new PackageInstallationException($"{nameof(WriteInstallSessionAsync)} failed. {splitRemoteFilePaths.Count} should process but only {count} processed."); + } + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.Installing)); InstallOutputReceiver receiver = new(); @@ -305,9 +337,9 @@ public virtual async Task GetVersionInfoAsync(string packageName, C /// A which can be used to cancel the asynchronous operation. /// A which return the destination path on device for file. /// If fatal error occurred when pushing file. - protected virtual async Task SyncPackageToDeviceAsync(string localFilePath, Action progress, CancellationToken cancellationToken = default) + protected virtual async Task SyncPackageToDeviceAsync(string localFilePath, Action? progress, CancellationToken cancellationToken = default) { - progress(localFilePath, new SyncProgressChangedEventArgs(0, 0)); + progress?.Invoke(localFilePath, new SyncProgressChangedEventArgs(0, 0)); ValidateDevice(); @@ -349,7 +381,7 @@ protected virtual async Task SyncPackageToDeviceAsync(string localFilePa } finally { - progress(true, new SyncProgressChangedEventArgs(0, 0)); + progress?.Invoke(true, new SyncProgressChangedEventArgs(0, 0)); } } @@ -385,11 +417,20 @@ protected virtual async Task CreateInstallSessionAsync(bool reinstall, s { ValidateDevice(); - InstallOutputReceiver receiver = new(); - string reinstallSwitch = reinstall ? " -r" : string.Empty; - string addon = StringExtensions.IsNullOrWhiteSpace(packageName) ? string.Empty : $" -p {packageName}"; + StringBuilder requestBuilder = new StringBuilder().Append("pm install-create"); - string cmd = $"pm install-create{reinstallSwitch}{addon}"; + if (reinstall) + { + _ = requestBuilder.Append(" -r"); + } + + if (!StringExtensions.IsNullOrWhiteSpace(packageName)) + { + requestBuilder.Append($" -p {packageName}"); + } + + string cmd = requestBuilder.ToString(); + InstallOutputReceiver receiver = new(); await AdbClient.ExecuteShellCommandAsync(Device, cmd, receiver, cancellationToken).ConfigureAwait(false); if (string.IsNullOrEmpty(receiver.SuccessMessage)) diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs index 6e4373bf..8a11fe78 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.IO; using System.Linq; +using System.Text; namespace AdvancedSharpAdbClient.DeviceCommands { @@ -65,7 +66,7 @@ public PackageManager(IAdbClient client, DeviceData device, bool thirdPartyOnly Device = device ?? throw new ArgumentNullException(nameof(device)); Packages = []; ThirdPartyOnly = thirdPartyOnly; - this.AdbClient = client ?? throw new ArgumentNullException(nameof(client)); + AdbClient = client ?? throw new ArgumentNullException(nameof(client)); this.syncServiceFactory = syncServiceFactory ?? Factories.SyncServiceFactory; @@ -152,10 +153,17 @@ public virtual void InstallRemotePackage(string remoteFilePath, bool reinstall) ValidateDevice(); - InstallOutputReceiver receiver = new(); - string reinstallSwitch = reinstall ? "-r " : string.Empty; + StringBuilder requestBuilder = new StringBuilder().Append("pm install"); + + if (reinstall) + { + _ = requestBuilder.Append(" -r"); + } + + _ = requestBuilder.AppendFormat(" \"{0}\"", remoteFilePath); - string cmd = $"pm install {reinstallSwitch}\"{remoteFilePath}\""; + string cmd = requestBuilder.ToString(); + InstallOutputReceiver receiver = new(); AdbClient.ExecuteShellCommand(Device, cmd, receiver); if (!string.IsNullOrEmpty(receiver.ErrorMessage)) @@ -182,17 +190,20 @@ void OnMainSyncProgressChanged(object sender, SyncProgressChangedEventArgs args) Dictionary progress = new(splitPackageFilePaths.Count); void OnSplitSyncProgressChanged(object sender, SyncProgressChangedEventArgs args) { - int count = 1; - if (sender is string path) - { - progress[path] = args.ProgressPercentage; - } - else if (sender is true) + lock (progress) { - count++; + int count = 1; + if (sender is string path) + { + progress[path] = args.ProgressPercentage; + } + else if (sender is true) + { + count++; + } + double present = progress.Values.Select(x => x / splitPackageFilePaths.Count / 2).Sum(); + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitPackageFilePaths.Count + 1, present)); } - double present = progress.Values.Select(x => x / splitPackageFilePaths.Count / 2).Sum(); - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitPackageFilePaths.Count + 1, present)); } string[] splitRemoteFilePaths = new string[splitPackageFilePaths.Count]; @@ -230,17 +241,20 @@ public virtual void InstallMultiplePackage(IList splitPackageFilePaths, Dictionary progress = new(splitPackageFilePaths.Count); void OnSyncProgressChanged(object sender, SyncProgressChangedEventArgs args) { - int count = 1; - if (sender is string path) - { - progress[path] = args.ProgressPercentage; - } - else if (sender is true) + lock (progress) { - count++; + int count = 1; + if (sender is string path) + { + progress[path] = args.ProgressPercentage; + } + else if (sender is true) + { + count++; + } + double present = progress.Values.Select(x => x / splitPackageFilePaths.Count).Sum(); + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitPackageFilePaths.Count, present)); } - double present = progress.Values.Select(x => x / splitPackageFilePaths.Count).Sum(); - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitPackageFilePaths.Count, present)); } string[] splitRemoteFilePaths = new string[splitPackageFilePaths.Count]; @@ -282,18 +296,11 @@ public virtual void InstallMultipleRemotePackage(string baseRemoteFilePath, ICol InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(1, splitRemoteFilePaths.Count + 1, PackageInstallProgressState.WriteSession)); - int i = 0; + int count = 0; foreach (string splitRemoteFilePath in splitRemoteFilePaths) { - try - { - WriteInstallSession(session, $"splitapp{i++}", splitRemoteFilePath); - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(i, splitRemoteFilePaths.Count + 1, PackageInstallProgressState.WriteSession)); - } - catch (Exception ex) - { - Debug.WriteLine(ex.Message); - } + WriteInstallSession(session, $"split{count++}", splitRemoteFilePath); + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitRemoteFilePaths.Count + 1, PackageInstallProgressState.WriteSession)); } InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.Installing)); @@ -323,18 +330,11 @@ public virtual void InstallMultipleRemotePackage(ICollection splitRemote InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFilePaths.Count, PackageInstallProgressState.WriteSession)); - int i = 0; + int count = 0; foreach (string splitRemoteFilePath in splitRemoteFilePaths) { - try - { - WriteInstallSession(session, $"splitapp{i++}", splitRemoteFilePath); - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(i, splitRemoteFilePaths.Count, PackageInstallProgressState.WriteSession)); - } - catch (Exception ex) - { - Debug.WriteLine(ex.Message); - } + WriteInstallSession(session, $"split{count++}", splitRemoteFilePath); + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitRemoteFilePaths.Count, PackageInstallProgressState.WriteSession)); } InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.Installing)); @@ -396,9 +396,9 @@ protected void ValidateDevice() /// An optional parameter which, when specified, returns progress notifications. /// Destination path on device for file. /// If fatal error occurred when pushing file. - protected virtual string SyncPackageToDevice(string localFilePath, Action progress) + protected virtual string SyncPackageToDevice(string localFilePath, Action? progress) { - progress(localFilePath, new SyncProgressChangedEventArgs(0, 0)); + progress?.Invoke(localFilePath, new SyncProgressChangedEventArgs(0, 0)); ValidateDevice(); @@ -437,7 +437,7 @@ protected virtual string SyncPackageToDevice(string localFilePath, Action Date: Wed, 1 Nov 2023 01:28:51 +0800 Subject: [PATCH 56/66] Add uninstall in AdbClient Use params on PackageManager --- .github/workflows/build-and-test.yml | 2 +- .../AdbClientTests.Async.cs | 23 +++ .../AdbClientTests.cs | 23 +++ .../DeviceExtensionsTests.Async.cs | 4 +- .../DeviceCommands/DeviceExtensionsTests.cs | 4 +- .../PackageManagerTests.Async.cs | 36 ++-- .../DeviceCommands/PackageManagerTests.cs | 36 ++-- .../Receivers/PackageManagerReceiverTests.cs | 2 +- .../Dummys/DummyAdbClient.cs | 4 + .../Dummys/DummySyncService.cs | 6 +- AdvancedSharpAdbClient/AdbClient.Async.cs | 43 ++++- AdvancedSharpAdbClient/AdbClient.cs | 55 ++++-- .../DeviceCommands/DeviceExtensions.Async.cs | 6 +- .../DeviceCommands/DeviceExtensions.cs | 6 +- .../DeviceCommands/PackageManager.Async.cs | 158 ++++++++++++++---- .../DeviceCommands/PackageManager.cs | 134 +++++++++------ .../Extensions/AdbClientExtensions.Async.cs | 11 ++ .../Interfaces/IAdbClient.Async.cs | 10 ++ .../Interfaces/IAdbClient.cs | 8 + README.md | 7 +- 20 files changed, 425 insertions(+), 153 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 4700dc6b..c0cb4187 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -40,7 +40,7 @@ jobs: run: dotnet build --no-restore -p:FullTargets=false - name: Test - run: dotnet test --no-restore -p:FullTargets=false + run: dotnet test --no-restore --blame-hang-timeout 1m -p:FullTargets=false pack-and-publish: name: pack-and-publish diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs index a21db936..91d82c01 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs @@ -785,6 +785,29 @@ await RunTestAsync( () => TestClient.InstallCommitAsync(Device, "936013062")); } + /// + /// Tests the method. + /// + [Fact] + public async void UninstallAsyncTest() + { + string[] requests = + [ + "host:transport:169.254.109.177:5555", + "exec:cmd package 'uninstall' com.android.gallery3d" + ]; + + byte[] streamData = Encoding.ASCII.GetBytes("Success\r\n"); + using MemoryStream shellStream = new(streamData); + + await RunTestAsync( + OkResponses(2), + NoResponseMessages, + requests, + [shellStream], + () => TestClient.UninstallAsync(Device, "com.android.gallery3d")); + } + /// /// Tests the method. /// diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs index 644b2197..26f76987 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs @@ -893,6 +893,29 @@ public void InstallCommitTest() () => TestClient.InstallCommit(Device, "936013062")); } + /// + /// Tests the method. + /// + [Fact] + public void UninstallTest() + { + string[] requests = + [ + "host:transport:169.254.109.177:5555", + "exec:cmd package 'uninstall' com.android.gallery3d" + ]; + + byte[] streamData = Encoding.ASCII.GetBytes("Success\r\n"); + using MemoryStream shellStream = new(streamData); + + RunTest( + OkResponses(2), + NoResponseMessages, + requests, + [shellStream], + () => TestClient.Uninstall(Device, "com.android.gallery3d")); + } + /// /// Tests the method. /// diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs index 60b4c63e..55c2c2bf 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs @@ -329,11 +329,11 @@ public async void ListProcessesAsyncTest() 3 acpi asound"; - adbClient.Commands["shell:cat /proc/1/stat /proc/2/stat /proc/3/stat "] = + adbClient.Commands["shell:cat /proc/1/stat /proc/2/stat /proc/3/stat"] = @"1 (init) S 0 0 0 0 -1 1077944576 2680 83280 0 179 0 67 16 39 20 0 1 0 2 17735680 143 18446744073709551615 134512640 135145076 4288071392 4288070744 134658736 0 0 0 65536 18446744071580117077 0 0 17 1 0 0 0 0 0 135152736 135165080 142131200 4288073690 4288073696 4288073696 4288073714 0 2 (kthreadd) S 0 0 0 0 -1 2129984 0 0 0 0 0 0 0 0 20 0 1 0 2 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 18446744071579254310 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 (ksoftirqd/0) S 2 0 0 0 -1 69238848 0 0 0 0 0 23 0 0 20 0 1 0 7 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 18446744071579284070 0 0 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0"; - adbClient.Commands["shell:cat /proc/1/cmdline /proc/1/stat /proc/2/cmdline /proc/2/stat /proc/3/cmdline /proc/3/stat "] = + adbClient.Commands["shell:cat /proc/1/cmdline /proc/1/stat /proc/2/cmdline /proc/2/stat /proc/3/cmdline /proc/3/stat"] = @" 1 (init) S 0 0 0 0 -1 1077944576 2680 83280 0 179 0 67 16 39 20 0 1 0 2 17735680 143 18446744073709551615 134512640 135145076 4288071392 4288070744 134658736 0 0 0 65536 18446744071580117077 0 0 17 1 0 0 0 0 0 135152736 135165080 142131200 4288073690 4288073696 4288073696 4288073714 0 diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.cs index 29b923f6..489c249c 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.cs @@ -329,11 +329,11 @@ public void ListProcessesTest() 3 acpi asound"; - adbClient.Commands["shell:cat /proc/1/stat /proc/2/stat /proc/3/stat "] = + adbClient.Commands["shell:cat /proc/1/stat /proc/2/stat /proc/3/stat"] = @"1 (init) S 0 0 0 0 -1 1077944576 2680 83280 0 179 0 67 16 39 20 0 1 0 2 17735680 143 18446744073709551615 134512640 135145076 4288071392 4288070744 134658736 0 0 0 65536 18446744071580117077 0 0 17 1 0 0 0 0 0 135152736 135165080 142131200 4288073690 4288073696 4288073696 4288073714 0 2 (kthreadd) S 0 0 0 0 -1 2129984 0 0 0 0 0 0 0 0 20 0 1 0 2 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 18446744071579254310 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 (ksoftirqd/0) S 2 0 0 0 -1 69238848 0 0 0 0 0 23 0 0 20 0 1 0 7 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 18446744071579284070 0 0 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0"; - adbClient.Commands["shell:cat /proc/1/cmdline /proc/1/stat /proc/2/cmdline /proc/2/stat /proc/3/cmdline /proc/3/stat "] = + adbClient.Commands["shell:cat /proc/1/cmdline /proc/1/stat /proc/2/cmdline /proc/2/stat /proc/3/cmdline /proc/3/stat"] = @" 1 (init) S 0 0 0 0 -1 1077944576 2680 83280 0 179 0 67 16 39 20 0 1 0 2 17735680 143 18446744073709551615 134512640 135145076 4288071392 4288070744 134658736 0 0 0 65536 18446744071580117077 0 0 17 1 0 0 0 0 0 135152736 135165080 142131200 4288073690 4288073696 4288073696 4288073714 0 diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs index 19a14fdc..7f527469 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs @@ -12,7 +12,7 @@ public async void InstallRemotePackageAsyncTest() adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; adbClient.Commands["shell:pm install \"/data/test.apk\""] = "Success"; - adbClient.Commands["shell:pm install -r \"/data/test.apk\""] = "Success"; + adbClient.Commands["shell:pm install -r -t \"/data/test.apk\""] = "Success"; DeviceData device = new() { @@ -20,16 +20,16 @@ public async void InstallRemotePackageAsyncTest() }; PackageManager manager = new(adbClient, device); - await manager.InstallRemotePackageAsync("/data/test.apk", false); + await manager.InstallRemotePackageAsync("/data/test.apk"); Assert.Equal(2, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install \"/data/test.apk\"", adbClient.ReceivedCommands[1]); adbClient.ReceivedCommands.Clear(); - await manager.InstallRemotePackageAsync("/data/test.apk", true); + await manager.InstallRemotePackageAsync("/data/test.apk", "-r", "-t"); Assert.Single(adbClient.ReceivedCommands); - Assert.Equal("shell:pm install -r \"/data/test.apk\"", adbClient.ReceivedCommands[0]); + Assert.Equal("shell:pm install -r -t \"/data/test.apk\"", adbClient.ReceivedCommands[0]); } [Fact] @@ -53,8 +53,8 @@ public async void InstallPackageAsyncTest() }; PackageManager manager = new(adbClient, device); - await manager.InstallPackageAsync("Assets/test.txt", false); - + await manager.InstallPackageAsync("Assets/test.txt"); + Assert.Equal(3, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[1]); Assert.Equal("shell:rm \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[2]); @@ -72,9 +72,9 @@ public async void InstallMultipleRemotePackageAsyncTest() adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; adbClient.Commands["shell:pm install-create"] = "Success: created install session [936013062]"; - adbClient.Commands["shell:pm install-create -r"] = "Success: created install session [936013062]"; + adbClient.Commands["shell:pm install-create -r -t"] = "Success: created install session [936013062]"; adbClient.Commands["shell:pm install-create -p com.google.android.gms"] = "Success: created install session [936013062]"; - adbClient.Commands["shell:pm install-create -r -p com.google.android.gms"] = "Success: created install session [936013062]"; + adbClient.Commands["shell:pm install-create -p com.google.android.gms -r -t"] = "Success: created install session [936013062]"; adbClient.Commands["shell:pm install-write 936013062 base.apk \"/data/base.apk\""] = "Success"; adbClient.Commands["shell:pm install-write 936013062 split0.apk \"/data/split-dpi.apk\""] = "Success"; adbClient.Commands["shell:pm install-write 936013062 split1.apk \"/data/split-abi.apk\""] = "Success"; @@ -86,7 +86,7 @@ public async void InstallMultipleRemotePackageAsyncTest() }; PackageManager manager = new(adbClient, device); - await manager.InstallMultipleRemotePackageAsync("/data/base.apk", ["/data/split-dpi.apk", "/data/split-abi.apk"], false); + await manager.InstallMultipleRemotePackageAsync("/data/base.apk", ["/data/split-dpi.apk", "/data/split-abi.apk"]); Assert.Equal(6, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create", adbClient.ReceivedCommands[1]); @@ -96,17 +96,17 @@ public async void InstallMultipleRemotePackageAsyncTest() Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[5]); adbClient.ReceivedCommands.Clear(); - await manager.InstallMultipleRemotePackageAsync("/data/base.apk", ["/data/split-dpi.apk", "/data/split-abi.apk"], true); + await manager.InstallMultipleRemotePackageAsync("/data/base.apk", ["/data/split-dpi.apk", "/data/split-abi.apk"], "-r", "-t"); Assert.Equal(5, adbClient.ReceivedCommands.Count); - Assert.Equal("shell:pm install-create -r", adbClient.ReceivedCommands[0]); + Assert.Equal("shell:pm install-create -r -t", adbClient.ReceivedCommands[0]); Assert.Equal("shell:pm install-write 936013062 base.apk \"/data/base.apk\"", adbClient.ReceivedCommands[1]); Assert.Contains("shell:pm install-write 936013062 split0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[2..4]); Assert.Contains("shell:pm install-write 936013062 split1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[2..4]); Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[4]); adbClient.ReceivedCommands.Clear(); - await manager.InstallMultipleRemotePackageAsync(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms", false); + await manager.InstallMultipleRemotePackageAsync(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms"); Assert.Equal(4, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[0]); @@ -115,10 +115,10 @@ public async void InstallMultipleRemotePackageAsyncTest() Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[3]); adbClient.ReceivedCommands.Clear(); - await manager.InstallMultipleRemotePackageAsync(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms", true); + await manager.InstallMultipleRemotePackageAsync(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms", "-r", "-t"); Assert.Equal(4, adbClient.ReceivedCommands.Count); - Assert.Equal("shell:pm install-create -r -p com.google.android.gms", adbClient.ReceivedCommands[0]); + Assert.Equal("shell:pm install-create -p com.google.android.gms -r -t", adbClient.ReceivedCommands[0]); Assert.Contains("shell:pm install-write 936013062 split0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[1..3]); Assert.Contains("shell:pm install-write 936013062 split1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[1..3]); Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[3]); @@ -152,8 +152,8 @@ public async void InstallMultiplePackageAsyncTest() }; PackageManager manager = new(adbClient, device); - await manager.InstallMultiplePackageAsync("Assets/test.txt", ["Assets/gapps.txt", "Assets/logcat.bin"], false); - + await manager.InstallMultiplePackageAsync("Assets/test.txt", ["Assets/gapps.txt", "Assets/logcat.bin"]); + Assert.Equal(9, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create", adbClient.ReceivedCommands[1]); Assert.Equal("shell:pm install-write 936013062 base.apk \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[2]); @@ -171,8 +171,8 @@ public async void InstallMultiplePackageAsyncTest() syncService.UploadedFiles.Clear(); adbClient.ReceivedCommands.Clear(); - await manager.InstallMultiplePackageAsync(["Assets/gapps.txt", "Assets/logcat.bin"], "com.google.android.gms", false); - + await manager.InstallMultiplePackageAsync(["Assets/gapps.txt", "Assets/logcat.bin"], "com.google.android.gms"); + Assert.Equal(6, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[0]); Assert.Contains("shell:pm install-write 936013062 split0.apk \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[1..3]); diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs index 75b6a16d..11041da1 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs @@ -40,7 +40,7 @@ public void InstallRemotePackageTest() adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; adbClient.Commands["shell:pm install \"/data/test.apk\""] = "Success"; - adbClient.Commands["shell:pm install -r \"/data/test.apk\""] = "Success"; + adbClient.Commands["shell:pm install -r -t \"/data/test.apk\""] = "Success"; DeviceData device = new() { @@ -48,16 +48,16 @@ public void InstallRemotePackageTest() }; PackageManager manager = new(adbClient, device); - manager.InstallRemotePackage("/data/test.apk", false); + manager.InstallRemotePackage("/data/test.apk"); Assert.Equal(2, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install \"/data/test.apk\"", adbClient.ReceivedCommands[1]); adbClient.ReceivedCommands.Clear(); - manager.InstallRemotePackage("/data/test.apk", true); + manager.InstallRemotePackage("/data/test.apk", "-r", "-t"); Assert.Single(adbClient.ReceivedCommands); - Assert.Equal("shell:pm install -r \"/data/test.apk\"", adbClient.ReceivedCommands[0]); + Assert.Equal("shell:pm install -r -t \"/data/test.apk\"", adbClient.ReceivedCommands[0]); } [Fact] @@ -81,8 +81,8 @@ public void InstallPackageTest() }; PackageManager manager = new(adbClient, device); - manager.InstallPackage("Assets/test.txt", false); - + manager.InstallPackage("Assets/test.txt"); + Assert.Equal(3, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[1]); Assert.Equal("shell:rm \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[2]); @@ -100,9 +100,9 @@ public void InstallMultipleRemotePackageTest() adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; adbClient.Commands["shell:pm install-create"] = "Success: created install session [936013062]"; - adbClient.Commands["shell:pm install-create -r"] = "Success: created install session [936013062]"; + adbClient.Commands["shell:pm install-create -r -t"] = "Success: created install session [936013062]"; adbClient.Commands["shell:pm install-create -p com.google.android.gms"] = "Success: created install session [936013062]"; - adbClient.Commands["shell:pm install-create -r -p com.google.android.gms"] = "Success: created install session [936013062]"; + adbClient.Commands["shell:pm install-create -p com.google.android.gms -r -t"] = "Success: created install session [936013062]"; adbClient.Commands["shell:pm install-write 936013062 base.apk \"/data/base.apk\""] = "Success"; adbClient.Commands["shell:pm install-write 936013062 split0.apk \"/data/split-dpi.apk\""] = "Success"; adbClient.Commands["shell:pm install-write 936013062 split1.apk \"/data/split-abi.apk\""] = "Success"; @@ -114,7 +114,7 @@ public void InstallMultipleRemotePackageTest() }; PackageManager manager = new(adbClient, device); - manager.InstallMultipleRemotePackage("/data/base.apk", ["/data/split-dpi.apk", "/data/split-abi.apk"], false); + manager.InstallMultipleRemotePackage("/data/base.apk", ["/data/split-dpi.apk", "/data/split-abi.apk"]); Assert.Equal(6, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create", adbClient.ReceivedCommands[1]); @@ -124,17 +124,17 @@ public void InstallMultipleRemotePackageTest() Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[5]); adbClient.ReceivedCommands.Clear(); - manager.InstallMultipleRemotePackage("/data/base.apk", ["/data/split-dpi.apk", "/data/split-abi.apk"], true); + manager.InstallMultipleRemotePackage("/data/base.apk", ["/data/split-dpi.apk", "/data/split-abi.apk"], "-r", "-t"); Assert.Equal(5, adbClient.ReceivedCommands.Count); - Assert.Equal("shell:pm install-create -r", adbClient.ReceivedCommands[0]); + Assert.Equal("shell:pm install-create -r -t", adbClient.ReceivedCommands[0]); Assert.Equal("shell:pm install-write 936013062 base.apk \"/data/base.apk\"", adbClient.ReceivedCommands[1]); Assert.Equal("shell:pm install-write 936013062 split0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[2]); Assert.Equal("shell:pm install-write 936013062 split1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[3]); Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[4]); adbClient.ReceivedCommands.Clear(); - manager.InstallMultipleRemotePackage(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms", false); + manager.InstallMultipleRemotePackage(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms"); Assert.Equal(4, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[0]); @@ -143,10 +143,10 @@ public void InstallMultipleRemotePackageTest() Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[3]); adbClient.ReceivedCommands.Clear(); - manager.InstallMultipleRemotePackage(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms", true); + manager.InstallMultipleRemotePackage(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms", "-r", "-t"); Assert.Equal(4, adbClient.ReceivedCommands.Count); - Assert.Equal("shell:pm install-create -r -p com.google.android.gms", adbClient.ReceivedCommands[0]); + Assert.Equal("shell:pm install-create -p com.google.android.gms -r -t", adbClient.ReceivedCommands[0]); Assert.Equal("shell:pm install-write 936013062 split0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[1]); Assert.Equal("shell:pm install-write 936013062 split1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[2]); Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[3]); @@ -180,8 +180,8 @@ public void InstallMultiplePackageTest() }; PackageManager manager = new(adbClient, device); - manager.InstallMultiplePackage("Assets/test.txt", ["Assets/gapps.txt", "Assets/logcat.bin"], false); - + manager.InstallMultiplePackage("Assets/test.txt", ["Assets/gapps.txt", "Assets/logcat.bin"]); + Assert.Equal(9, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create", adbClient.ReceivedCommands[1]); Assert.Equal("shell:pm install-write 936013062 base.apk \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[2]); @@ -199,8 +199,8 @@ public void InstallMultiplePackageTest() syncService.UploadedFiles.Clear(); adbClient.ReceivedCommands.Clear(); - manager.InstallMultiplePackage(["Assets/gapps.txt", "Assets/logcat.bin"], "com.google.android.gms", false); - + manager.InstallMultiplePackage(["Assets/gapps.txt", "Assets/logcat.bin"], "com.google.android.gms"); + Assert.Equal(6, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[0]); Assert.Equal("shell:pm install-write 936013062 split0.apk \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[1]); diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/PackageManagerReceiverTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/PackageManagerReceiverTests.cs index 58023d12..1e2086fa 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/PackageManagerReceiverTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/PackageManagerReceiverTests.cs @@ -15,7 +15,7 @@ public void ParseThirdPartyPackage() DummyAdbClient client = new(); - PackageManager manager = new(client, device, thirdPartyOnly: false, syncServiceFactory: null, skipInit: true); + PackageManager manager = new(client, device, syncServiceFactory: null, skipInit: true); PackageManagerReceiver receiver = new(manager); // Act diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs index 366ca221..bd6717d8 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs @@ -309,6 +309,10 @@ public Task ExecuteServerCommandAsync(string target, string command, IAdbSocket Task IAdbClient.SwipeAsync(DeviceData device, int x1, int y1, int x2, int y2, long speed, CancellationToken cancellationToken) => throw new NotImplementedException(); + void IAdbClient.Uninstall(DeviceData device, string packageName, params string[] arguments) => throw new NotImplementedException(); + + Task IAdbClient.UninstallAsync(DeviceData device, string packageName, CancellationToken cancellationToken, params string[] arguments) => throw new NotImplementedException(); + void IAdbClient.Unroot(DeviceData device) => throw new NotImplementedException(); Task IAdbClient.UnrootAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs index a38f9ac6..7690991b 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs @@ -31,16 +31,15 @@ public void Pull(string remotePath, Stream stream, IProgress progress = nul { for (int i = 0; i <= 100; i++) { - Thread.Yield(); SyncProgressChanged?.Invoke(this, new SyncProgressChangedEventArgs(i, 100)); } } public async Task PullAsync(string remotePath, Stream stream, IProgress progress, CancellationToken cancellationToken = default) { + await Task.Yield(); for (int i = 0; i <= 100; i++) { - await Task.Yield(); SyncProgressChanged?.Invoke(this, new SyncProgressChangedEventArgs(i, 100)); } } @@ -49,7 +48,6 @@ public void Push(Stream stream, string remotePath, int permissions, DateTimeOffs { for (int i = 0; i <= 100; i++) { - Thread.Yield(); if (i == 100) { UploadedFiles[remotePath] = stream; @@ -60,9 +58,9 @@ public void Push(Stream stream, string remotePath, int permissions, DateTimeOffs public async Task PushAsync(Stream stream, string remotePath, int permissions, DateTimeOffset timestamp, IProgress progress, CancellationToken cancellationToken = default) { + await Task.Yield(); for (int i = 0; i <= 100; i++) { - await Task.Yield(); if (i == 100) { UploadedFiles[remotePath] = stream; diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index e358902c..17d7f2d2 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -290,7 +290,7 @@ public async Task RunLogServiceAsync(DeviceData device, Action message foreach (LogId logName in logNames) { - _ = request.Append($" -b {logName.ToString().ToLowerInvariant()}"); + _ = request.AppendFormat(" -b {0}", logName.ToString().ToLowerInvariant()); } await socket.SendAdbRequestAsync(request.ToString(), cancellationToken).ConfigureAwait(false); @@ -440,7 +440,7 @@ public async Task InstallAsync(DeviceData device, Stream apk, CancellationToken { foreach (string argument in arguments) { - _ = requestBuilder.Append($" {argument}"); + _ = requestBuilder.AppendFormat(" {0}", argument); } } @@ -558,14 +558,14 @@ public async Task InstallCreateAsync(DeviceData device, string? packageN if (!StringExtensions.IsNullOrWhiteSpace(packageName)) { - requestBuilder.Append($" -p {packageName}"); + requestBuilder.AppendFormat(" -p {0}", packageName); } if (arguments != null) { foreach (string argument in arguments) { - _ = requestBuilder.Append($" {argument}"); + _ = requestBuilder.AppendFormat(" {0}", argument); } } @@ -609,8 +609,8 @@ public async Task InstallWriteAsync(DeviceData device, Stream apk, string apkNam new StringBuilder().Append($"exec:cmd package 'install-write'") // add size parameter [required for streaming installs] // do last to override any user specified value - .Append($" -S {apk.Length}") - .Append($" {session} {apkName}.apk"); + .AppendFormat(" -S {0}", apk.Length) + .AppendFormat(" {0} {1}.apk", session, apkName); using IAdbSocket socket = adbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); @@ -661,6 +661,37 @@ public async Task InstallCommitAsync(DeviceData device, string session, Cancella } } + /// + public async Task UninstallAsync(DeviceData device, string packageName, CancellationToken cancellationToken, params string[] arguments) + { + EnsureDevice(device); + + StringBuilder requestBuilder = new StringBuilder().Append("exec:cmd package 'uninstall'"); + + if (arguments != null) + { + foreach (string argument in arguments) + { + _ = requestBuilder.AppendFormat(" {0}", argument); + } + } + + _ = requestBuilder.AppendFormat(" {0}", packageName); + + using IAdbSocket socket = adbSocketFactory(EndPoint); + await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); + + await socket.SendAdbRequestAsync(requestBuilder.ToString(), cancellationToken).ConfigureAwait(false); + AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + + using StreamReader reader = new(socket.GetShellStream(), Encoding); + string? result = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false); + if (result?.Contains("Success") != true) + { + throw new AdbException(await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false)); + } + } + /// public async Task> GetFeatureSetAsync(DeviceData device, CancellationToken cancellationToken = default) { diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index 97afd509..804206b6 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -430,7 +430,7 @@ public void RunLogService(DeviceData device, Action messageSink, param foreach (LogId logName in logNames) { - _ = request.Append($" -b {logName.ToString().ToLowerInvariant()}"); + _ = request.AppendFormat(" -b {0}", logName.ToString().ToLowerInvariant()); } socket.SendAdbRequest(request.ToString()); @@ -579,13 +579,13 @@ public void Install(DeviceData device, Stream apk, params string[] arguments) { foreach (string argument in arguments) { - _ = requestBuilder.Append($" {argument}"); + _ = requestBuilder.AppendFormat(" {0}", argument); } } // add size parameter [required for streaming installs] // do last to override any user specified value - _ = requestBuilder.Append($" -S {apk.Length}"); + _ = requestBuilder.AppendFormat(" -S {0}", apk.Length); using IAdbSocket socket = adbSocketFactory(EndPoint); socket.SetDevice(device); @@ -697,14 +697,14 @@ public string InstallCreate(DeviceData device, string? packageName = null, param if (!StringExtensions.IsNullOrWhiteSpace(packageName)) { - requestBuilder.Append($" -p {packageName}"); + requestBuilder.AppendFormat(" -p {0}", packageName); } if (arguments != null) { foreach (string argument in arguments) { - _ = requestBuilder.Append($" {argument}"); + _ = requestBuilder.AppendFormat(" {0}", argument); } } @@ -743,14 +743,12 @@ public void InstallWrite(DeviceData device, Stream apk, string apkName, string s ExceptionExtensions.ThrowIfNull(apkName); - StringBuilder requestBuilder = new(); - requestBuilder.Append($"exec:cmd package 'install-write'"); - - // add size parameter [required for streaming installs] - // do last to override any user specified value - requestBuilder.Append($" -S {apk.Length}"); - - requestBuilder.Append($" {session} {apkName}.apk"); + StringBuilder requestBuilder = + new StringBuilder().Append($"exec:cmd package 'install-write'") + // add size parameter [required for streaming installs] + // do last to override any user specified value + .AppendFormat(" -S {0}", apk.Length) + .AppendFormat(" {0} {1}.apk", session, apkName); using IAdbSocket socket = adbSocketFactory(EndPoint); socket.SetDevice(device); @@ -803,6 +801,37 @@ public void InstallCommit(DeviceData device, string session) } } + /// + public void Uninstall(DeviceData device, string packageName, params string[] arguments) + { + EnsureDevice(device); + + StringBuilder requestBuilder = new StringBuilder().Append("exec:cmd package 'uninstall'"); + + if (arguments != null) + { + foreach (string argument in arguments) + { + _ = requestBuilder.AppendFormat(" {0}", argument); + } + } + + _ = requestBuilder.AppendFormat(" {0}", packageName); + + using IAdbSocket socket = adbSocketFactory(EndPoint); + socket.SetDevice(device); + + socket.SendAdbRequest(requestBuilder.ToString()); + AdbResponse response = socket.ReadAdbResponse(); + + using StreamReader reader = new(socket.GetShellStream(), Encoding); + string? result = reader.ReadLine(); + if (result?.Contains("Success") != true) + { + throw new AdbException(reader.ReadToEnd()); + } + } + /// public IEnumerable GetFeatureSet(DeviceData device) { diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs index c5b3a8f7..0ba679ae 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs @@ -260,16 +260,16 @@ await client.ExecuteShellCommandAsync(device, @"SDK=""$(/system/bin/getprop ro.b StringBuilder catBuilder = new(); ProcessOutputReceiver processOutputReceiver = new(); - _ = catBuilder.Append("cat "); + _ = catBuilder.Append("cat"); for (int i = 0; i < pids.Count; i++) { - _ = catBuilder.Append($"/proc/{pids[i]}/cmdline /proc/{pids[i]}/stat "); + _ = catBuilder.AppendFormat(" /proc/{0}/cmdline /proc/{1}/stat", pids[i], pids[i]); if (i > 0 && (i % 25 == 0 || i == pids.Count - 1)) { await client.ExecuteShellCommandAsync(device, catBuilder.ToString(), processOutputReceiver, cancellationToken).ConfigureAwait(false); - _ = catBuilder.Clear().Append("cat "); + _ = catBuilder.Clear().Append("cat"); } } diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs index 32d321d7..1a7d2b72 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs @@ -248,16 +248,16 @@ public static IEnumerable ListProcesses(this IAdbClient client, StringBuilder catBuilder = new(); ProcessOutputReceiver processOutputReceiver = new(); - _ = catBuilder.Append("cat "); + _ = catBuilder.Append("cat"); for (int i = 0; i < pids.Count; i++) { - _ = catBuilder.Append($"/proc/{pids[i]}/cmdline /proc/{pids[i]}/stat "); + _ = catBuilder.AppendFormat(" /proc/{0}/cmdline /proc/{1}/stat", pids[i], pids[i]); if (i > 0 && (i % 25 == 0 || i == pids.Count - 1)) { client.ExecuteShellCommand(device, catBuilder.ToString(), processOutputReceiver); - _ = catBuilder.Clear().Append("cat "); + _ = catBuilder.Clear().Append("cat"); } } diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs index 15a253c2..18469a07 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Text; @@ -24,27 +23,44 @@ public virtual Task RefreshPackagesAsync(CancellationToken cancellationToken = d { ValidateDevice(); - PackageManagerReceiver pmr = new(this); + StringBuilder requestBuilder = new StringBuilder().Append(ListFull); - return ThirdPartyOnly - ? AdbClient.ExecuteShellCommandAsync(Device, ListThirdPartyOnly, pmr, cancellationToken) - : AdbClient.ExecuteShellCommandAsync(Device, ListFull, pmr, cancellationToken); + if (Arguments != null) + { + foreach (string argument in Arguments) + { + _ = requestBuilder.AppendFormat(" {0}", argument); + } + } + + string cmd = requestBuilder.ToString(); + PackageManagerReceiver pmr = new(this); + return AdbClient.ExecuteShellCommandAsync(Device, cmd, pmr, cancellationToken); } /// /// Installs an Android application on device. /// /// The absolute file system path to file on local host to install. - /// if re-install of app should be performed; otherwise, . + /// The arguments to pass to adb install. + /// A which represents the asynchronous operation. + public Task InstallPackageAsync(string packageFilePath, params string[] arguments) => + InstallPackageAsync(packageFilePath, default, arguments); + + /// + /// Installs an Android application on device. + /// + /// The absolute file system path to file on local host to install. /// A which can be used to cancel the asynchronous operation. + /// The arguments to pass to adb install. /// A which represents the asynchronous operation. - public virtual async Task InstallPackageAsync(string packageFilePath, bool reinstall, CancellationToken cancellationToken = default) + public virtual async Task InstallPackageAsync(string packageFilePath, CancellationToken cancellationToken, params string[] arguments) { ValidateDevice(); string remoteFilePath = await SyncPackageToDeviceAsync(packageFilePath, OnSyncProgressChanged, cancellationToken).ConfigureAwait(false); - await InstallRemotePackageAsync(remoteFilePath, reinstall, cancellationToken).ConfigureAwait(false); + await InstallRemotePackageAsync(remoteFilePath, cancellationToken, arguments).ConfigureAwait(false); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, 1, PackageInstallProgressState.PostInstall)); await RemoveRemotePackageAsync(remoteFilePath, cancellationToken).ConfigureAwait(false); @@ -60,10 +76,19 @@ void OnSyncProgressChanged(object sender, SyncProgressChangedEventArgs args) => /// Installs the application package that was pushed to a temporary location on the device. /// /// absolute file path to package file on device. - /// Set to if re-install of app should be performed. + /// The arguments to pass to pm install. + /// A which represents the asynchronous operation. + public Task InstallRemotePackageAsync(string remoteFilePath, params string[] arguments) => + InstallRemotePackageAsync(remoteFilePath, default, arguments); + + /// + /// Installs the application package that was pushed to a temporary location on the device. + /// + /// absolute file path to package file on device. /// A which can be used to cancel the asynchronous operation. + /// The arguments to pass to pm install. /// A which represents the asynchronous operation. - public virtual async Task InstallRemotePackageAsync(string remoteFilePath, bool reinstall, CancellationToken cancellationToken = default) + public virtual async Task InstallRemotePackageAsync(string remoteFilePath, CancellationToken cancellationToken, params string[] arguments) { InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.Installing)); @@ -71,9 +96,12 @@ public virtual async Task InstallRemotePackageAsync(string remoteFilePath, bool StringBuilder requestBuilder = new StringBuilder().Append("pm install"); - if (reinstall) + if (arguments != null) { - _ = requestBuilder.Append(" -r"); + foreach (string argument in arguments) + { + _ = requestBuilder.AppendFormat(" {0}", argument); + } } _ = requestBuilder.AppendFormat(" \"{0}\"", remoteFilePath); @@ -93,10 +121,20 @@ public virtual async Task InstallRemotePackageAsync(string remoteFilePath, bool /// /// The absolute base app file system path to file on local host to install. /// The absolute split app file system paths to file on local host to install. - /// Set to if re-install of app should be performed. + /// The arguments to pass to pm install-create. + /// A which represents the asynchronous operation. + public Task InstallMultiplePackageAsync(string basePackageFilePath, ICollection splitPackageFilePaths, params string[] arguments) => + InstallMultiplePackageAsync(basePackageFilePath, splitPackageFilePaths, default, arguments); + + /// + /// Installs Android multiple application on device. + /// + /// The absolute base app file system path to file on local host to install. + /// The absolute split app file system paths to file on local host to install. /// A which can be used to cancel the asynchronous operation. + /// The arguments to pass to pm install-create. /// A which represents the asynchronous operation. - public virtual async Task InstallMultiplePackageAsync(string basePackageFilePath, ICollection splitPackageFilePaths, bool reinstall, CancellationToken cancellationToken = default) + public virtual async Task InstallMultiplePackageAsync(string basePackageFilePath, ICollection splitPackageFilePaths, CancellationToken cancellationToken, params string[] arguments) { ValidateDevice(); @@ -131,7 +169,7 @@ void OnSplitSyncProgressChanged(object sender, SyncProgressChangedEventArgs args throw new PackageInstallationException($"{nameof(SyncPackageToDeviceAsync)} failed. {splitPackageFilePaths.Count} should process but only {splitRemoteFilePaths.Length} processed."); } - await InstallMultipleRemotePackageAsync(baseRemoteFilePath, splitRemoteFilePaths, reinstall, cancellationToken); + await InstallMultipleRemotePackageAsync(baseRemoteFilePath, splitRemoteFilePaths, cancellationToken, arguments); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFilePaths.Length + 1, PackageInstallProgressState.PostInstall)); int count = 0; @@ -158,10 +196,20 @@ await Extensions.WhenAll(splitRemoteFilePaths.Select(async x => /// /// The absolute split app file system paths to file on local host to install. /// The absolute package name of the base app. - /// Set to if re-install of app should be performed. + /// The arguments to pass to pm install-create. + /// A which represents the asynchronous operation. + public Task InstallMultiplePackageAsync(ICollection splitPackageFilePaths, string packageName, params string[] arguments) => + InstallMultiplePackageAsync(splitPackageFilePaths, packageName, default, arguments); + + /// + /// Installs Android multiple application on device. + /// + /// The absolute split app file system paths to file on local host to install. + /// The absolute package name of the base app. /// A which can be used to cancel the asynchronous operation. + /// The arguments to pass to pm install-create. /// A which represents the asynchronous operation. - public virtual async Task InstallMultiplePackageAsync(ICollection splitPackageFilePaths, string packageName, bool reinstall, CancellationToken cancellationToken = default) + public virtual async Task InstallMultiplePackageAsync(ICollection splitPackageFilePaths, string packageName, CancellationToken cancellationToken, params string[] arguments) { ValidateDevice(); @@ -191,7 +239,7 @@ void OnSyncProgressChanged(object sender, SyncProgressChangedEventArgs args) throw new PackageInstallationException($"{nameof(SyncPackageToDeviceAsync)} failed. {splitPackageFilePaths.Count} should process but only {splitRemoteFilePaths.Length} processed."); } - await InstallMultipleRemotePackageAsync(splitRemoteFilePaths, packageName, reinstall, cancellationToken); + await InstallMultipleRemotePackageAsync(splitRemoteFilePaths, packageName, cancellationToken, arguments); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFilePaths.Length, PackageInstallProgressState.PostInstall)); int count = 0; @@ -215,16 +263,26 @@ await Extensions.WhenAll(splitRemoteFilePaths.Select(async x => /// /// The absolute base app file path to package file on device. /// The absolute split app file paths to package file on device. - /// Set to if re-install of app should be performed. + /// The arguments to pass to pm install-create. + /// A which represents the asynchronous operation. + public Task InstallMultipleRemotePackageAsync(string baseRemoteFilePath, ICollection splitRemoteFilePaths, params string[] arguments) => + InstallMultipleRemotePackageAsync(baseRemoteFilePath, splitRemoteFilePaths, default, arguments); + + /// + /// Installs the multiple application package that was pushed to a temporary location on the device. + /// + /// The absolute base app file path to package file on device. + /// The absolute split app file paths to package file on device. /// A which can be used to cancel the asynchronous operation. + /// The arguments to pass to pm install-create. /// A which represents the asynchronous operation. - public virtual async Task InstallMultipleRemotePackageAsync(string baseRemoteFilePath, ICollection splitRemoteFilePaths, bool reinstall, CancellationToken cancellationToken = default) + public virtual async Task InstallMultipleRemotePackageAsync(string baseRemoteFilePath, ICollection splitRemoteFilePaths, CancellationToken cancellationToken, params string[] arguments) { InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.CreateSession)); ValidateDevice(); - string session = await CreateInstallSessionAsync(reinstall, cancellationToken: cancellationToken).ConfigureAwait(false); + string session = await CreateInstallSessionAsync(cancellationToken: cancellationToken, arguments: arguments).ConfigureAwait(false); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFilePaths.Count + 1, PackageInstallProgressState.WriteSession)); @@ -260,16 +318,26 @@ await Extensions.WhenAll(splitRemoteFilePaths.Select(async (splitRemoteFilePath) /// /// The absolute split app file paths to package file on device. /// The absolute package name of the base app. - /// Set to if re-install of app should be performed. + /// The arguments to pass to pm install-create. + /// A which represents the asynchronous operation. + public Task InstallMultipleRemotePackageAsync(ICollection splitRemoteFilePaths, string packageName, params string[] arguments) => + InstallMultipleRemotePackageAsync(splitRemoteFilePaths, packageName, default, arguments); + + /// + /// Installs the multiple application package that was pushed to a temporary location on the device. + /// + /// The absolute split app file paths to package file on device. + /// The absolute package name of the base app. /// A which can be used to cancel the asynchronous operation. + /// The arguments to pass to pm install-create. /// A which represents the asynchronous operation. - public virtual async Task InstallMultipleRemotePackageAsync(ICollection splitRemoteFilePaths, string packageName, bool reinstall, CancellationToken cancellationToken = default) + public virtual async Task InstallMultipleRemotePackageAsync(ICollection splitRemoteFilePaths, string packageName, CancellationToken cancellationToken, params string[] arguments) { InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.CreateSession)); ValidateDevice(); - string session = await CreateInstallSessionAsync(reinstall, packageName, cancellationToken).ConfigureAwait(false); + string session = await CreateInstallSessionAsync(packageName, cancellationToken, arguments).ConfigureAwait(false); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFilePaths.Count, PackageInstallProgressState.WriteSession)); @@ -296,18 +364,41 @@ await Extensions.WhenAll(splitRemoteFilePaths.Select(async (splitRemoteFilePath) } } + /// + /// Uninstalls a package from the device. + /// + /// The name of the package to uninstall. + /// The arguments to pass to pm uninstall. + /// A which represents the asynchronous operation. + public Task UninstallPackageAsync(string packageName, params string[] arguments) => + UninstallPackageAsync(packageName, default, arguments); + /// /// Uninstalls a package from the device. /// /// The name of the package to uninstall. /// A which can be used to cancel the asynchronous operation. + /// The arguments to pass to pm uninstall. /// A which represents the asynchronous operation. - public virtual async Task UninstallPackageAsync(string packageName, CancellationToken cancellationToken = default) + public virtual async Task UninstallPackageAsync(string packageName, CancellationToken cancellationToken, params string[] arguments) { ValidateDevice(); + StringBuilder requestBuilder = new StringBuilder().Append("pm uninstall"); + + if (arguments != null) + { + foreach (string argument in arguments) + { + _ = requestBuilder.AppendFormat(" {0}", argument); + } + } + + _ = requestBuilder.AppendFormat(" {0}", packageName); + + string cmd = requestBuilder.ToString(); InstallOutputReceiver receiver = new(); - await AdbClient.ExecuteShellCommandAsync(Device, $"pm uninstall {packageName}", receiver, cancellationToken).ConfigureAwait(false); + await AdbClient.ExecuteShellCommandAsync(Device, cmd, receiver, cancellationToken).ConfigureAwait(false); if (!string.IsNullOrEmpty(receiver.ErrorMessage)) { throw new PackageInstallationException(receiver.ErrorMessage); @@ -409,24 +500,27 @@ protected virtual async Task RemoveRemotePackageAsync(string remoteFilePath, Can /// /// Like "install", but starts an install session. /// - /// Set to if re-install of app should be performed. /// The absolute package name of the base app. /// A which can be used to cancel the asynchronous operation. + /// The arguments to pass to pm install-create. /// A which return the session ID. - protected virtual async Task CreateInstallSessionAsync(bool reinstall, string? packageName = null, CancellationToken cancellationToken = default) + protected virtual async Task CreateInstallSessionAsync(string? packageName = null, CancellationToken cancellationToken = default, params string[] arguments) { ValidateDevice(); StringBuilder requestBuilder = new StringBuilder().Append("pm install-create"); - if (reinstall) + if (!StringExtensions.IsNullOrWhiteSpace(packageName)) { - _ = requestBuilder.Append(" -r"); + _ = requestBuilder.AppendFormat(" -p {0}", packageName); } - if (!StringExtensions.IsNullOrWhiteSpace(packageName)) + if (arguments != null) { - requestBuilder.Append($" -p {packageName}"); + foreach (string argument in arguments) + { + _ = requestBuilder.AppendFormat(" {0}", argument); + } } string cmd = requestBuilder.ToString(); diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs index 8a11fe78..3acbc9c5 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Text; @@ -26,11 +25,6 @@ public partial class PackageManager /// protected const string ListFull = "pm list packages -f"; - /// - /// The command that list all third party packages installed on the device. - /// - protected const string ListThirdPartyOnly = "pm list packages -f -3"; - /// /// The logger to use when logging messages. /// @@ -53,20 +47,19 @@ public partial class PackageManager /// /// The to use to communicate with the Android Debug Bridge. /// The device on which to look for packages. - /// to only indicate third party applications; - /// to also include built-in applications. /// 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. /// A value indicating whether to skip the initial refresh of the package list or not. /// Used mainly by unit tests. /// The logger to use when logging. - public PackageManager(IAdbClient client, DeviceData device, bool thirdPartyOnly = false, Func? syncServiceFactory = null, bool skipInit = false, ILogger? logger = null) + /// The arguments to pass to pm list packages. + public PackageManager(IAdbClient client, DeviceData device, Func? syncServiceFactory = null, bool skipInit = false, ILogger? logger = null, params string[] arguments) { Device = device ?? throw new ArgumentNullException(nameof(device)); Packages = []; - ThirdPartyOnly = thirdPartyOnly; AdbClient = client ?? throw new ArgumentNullException(nameof(client)); + Arguments = arguments; this.syncServiceFactory = syncServiceFactory ?? Factories.SyncServiceFactory; @@ -79,10 +72,34 @@ public PackageManager(IAdbClient client, DeviceData device, bool thirdPartyOnly } /// - /// Gets a value indicating whether this package manager only lists third party applications, - /// or also includes built-in applications. + /// Gets or sets a value to pass to pm list packages when list packages. /// - public bool ThirdPartyOnly { get; init; } + /// + /// + /// + /// -a: all known packages (but excluding APEXes) + /// + /// + /// -d: filter to only show disabled packages + /// + /// + /// -e: filter to only show enabled packages + /// + /// + /// -s: filter to only show system packages + /// + /// + /// -3: filter to only show third party packages + /// + /// + /// -i: ignored (used for compatibility with older releases) + /// + /// + /// -u: also include uninstalled packages + /// + /// + /// + public string[] Arguments { get; set; } /// /// Gets the list of packages currently installed on the device. They key is the name of the package; @@ -107,30 +124,33 @@ public virtual void RefreshPackages() { ValidateDevice(); - PackageManagerReceiver pmr = new(this); + StringBuilder requestBuilder = new StringBuilder().Append(ListFull); - if (ThirdPartyOnly) + if (Arguments != null) { - AdbClient.ExecuteShellCommand(Device, ListThirdPartyOnly, pmr); - } - else - { - AdbClient.ExecuteShellCommand(Device, ListFull, pmr); + foreach (string argument in Arguments) + { + _ = requestBuilder.AppendFormat(" {0}", argument); + } } + + string cmd = requestBuilder.ToString(); + PackageManagerReceiver pmr = new(this); + AdbClient.ExecuteShellCommand(Device, cmd, pmr); } /// /// Installs an Android application on device. /// /// The absolute file system path to file on local host to install. - /// if re-install of app should be performed; otherwise, . - public virtual void InstallPackage(string packageFilePath, bool reinstall) + /// The arguments to pass to adb install. + public virtual void InstallPackage(string packageFilePath, params string[] arguments) { ValidateDevice(); string remoteFilePath = SyncPackageToDevice(packageFilePath, OnSyncProgressChanged); - InstallRemotePackage(remoteFilePath, reinstall); + InstallRemotePackage(remoteFilePath, arguments); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, 1, PackageInstallProgressState.PostInstall)); RemoveRemotePackage(remoteFilePath); @@ -146,8 +166,8 @@ void OnSyncProgressChanged(object sender, SyncProgressChangedEventArgs args) => /// Installs the application package that was pushed to a temporary location on the device. /// /// absolute file path to package file on device. - /// Set to if re-install of app should be performed. - public virtual void InstallRemotePackage(string remoteFilePath, bool reinstall) + /// The arguments to pass to adb install. + public virtual void InstallRemotePackage(string remoteFilePath, params string[] arguments) { InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.Installing)); @@ -155,9 +175,12 @@ public virtual void InstallRemotePackage(string remoteFilePath, bool reinstall) StringBuilder requestBuilder = new StringBuilder().Append("pm install"); - if (reinstall) + if (arguments != null) { - _ = requestBuilder.Append(" -r"); + foreach (string argument in arguments) + { + _ = requestBuilder.AppendFormat(" {0}", argument); + } } _ = requestBuilder.AppendFormat(" \"{0}\"", remoteFilePath); @@ -177,8 +200,8 @@ public virtual void InstallRemotePackage(string remoteFilePath, bool reinstall) /// /// The absolute base app file system path to file on local host to install. /// The absolute split app file system paths to file on local host to install. - /// Set to if re-install of app should be performed. - public virtual void InstallMultiplePackage(string basePackageFilePath, IList splitPackageFilePaths, bool reinstall) + /// The arguments to pass to pm install-create. + public virtual void InstallMultiplePackage(string basePackageFilePath, IList splitPackageFilePaths, params string[] arguments) { ValidateDevice(); @@ -212,7 +235,7 @@ void OnSplitSyncProgressChanged(object sender, SyncProgressChangedEventArgs args splitRemoteFilePaths[i] = SyncPackageToDevice(splitPackageFilePaths[i], OnSplitSyncProgressChanged); } - InstallMultipleRemotePackage(baseRemoteFilePath, splitRemoteFilePaths, reinstall); + InstallMultipleRemotePackage(baseRemoteFilePath, splitRemoteFilePaths, arguments); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFilePaths.Length + 1, PackageInstallProgressState.PostInstall)); int count = 0; @@ -233,8 +256,8 @@ void OnSplitSyncProgressChanged(object sender, SyncProgressChangedEventArgs args /// /// The absolute split app file system paths to file on local host to install. /// The absolute package name of the base app. - /// Set to if re-install of app should be performed. - public virtual void InstallMultiplePackage(IList splitPackageFilePaths, string packageName, bool reinstall) + /// The arguments to pass to pm install-create. + public virtual void InstallMultiplePackage(IList splitPackageFilePaths, string packageName, params string[] arguments) { ValidateDevice(); @@ -263,7 +286,7 @@ void OnSyncProgressChanged(object sender, SyncProgressChangedEventArgs args) splitRemoteFilePaths[i] = SyncPackageToDevice(splitPackageFilePaths[i], OnSyncProgressChanged); } - InstallMultipleRemotePackage(splitRemoteFilePaths, packageName, reinstall); + InstallMultipleRemotePackage(splitRemoteFilePaths, packageName, arguments); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFilePaths.Length, PackageInstallProgressState.PostInstall)); int count = 0; @@ -281,14 +304,14 @@ void OnSyncProgressChanged(object sender, SyncProgressChangedEventArgs args) /// /// The absolute base app file path to package file on device. /// The absolute split app file paths to package file on device. - /// Set to if re-install of app should be performed. - public virtual void InstallMultipleRemotePackage(string baseRemoteFilePath, ICollection splitRemoteFilePaths, bool reinstall) + /// The arguments to pass to pm install-create. + public virtual void InstallMultipleRemotePackage(string baseRemoteFilePath, ICollection splitRemoteFilePaths, params string[] arguments) { InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.CreateSession)); ValidateDevice(); - string session = CreateInstallSession(reinstall); + string session = CreateInstallSession(arguments: arguments); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFilePaths.Count + 1, PackageInstallProgressState.WriteSession)); @@ -319,14 +342,14 @@ public virtual void InstallMultipleRemotePackage(string baseRemoteFilePath, ICol /// /// The absolute split app file paths to package file on device. /// The absolute package name of the base app. - /// Set to if re-install of app should be performed. - public virtual void InstallMultipleRemotePackage(ICollection splitRemoteFilePaths, string packageName, bool reinstall) + /// The arguments to pass to pm install-create. + public virtual void InstallMultipleRemotePackage(ICollection splitRemoteFilePaths, string packageName, params string[] arguments) { InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.CreateSession)); ValidateDevice(); - string session = CreateInstallSession(reinstall, packageName); + string session = CreateInstallSession(packageName, arguments); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFilePaths.Count, PackageInstallProgressState.WriteSession)); @@ -352,12 +375,26 @@ public virtual void InstallMultipleRemotePackage(ICollection splitRemote /// Uninstalls a package from the device. /// /// The name of the package to uninstall. - public virtual void UninstallPackage(string packageName) + /// The arguments to pass to pm uninstall. + public virtual void UninstallPackage(string packageName, params string[] arguments) { ValidateDevice(); + StringBuilder requestBuilder = new StringBuilder().Append("pm uninstall"); + + if (arguments != null) + { + foreach (string argument in arguments) + { + _ = requestBuilder.AppendFormat(" {0}", argument); + } + } + + _ = requestBuilder.AppendFormat(" {0}", packageName); + + string cmd = requestBuilder.ToString(); InstallOutputReceiver receiver = new(); - AdbClient.ExecuteShellCommand(Device, $"pm uninstall {packageName}", receiver); + AdbClient.ExecuteShellCommand(Device, cmd, receiver); if (!string.IsNullOrEmpty(receiver.ErrorMessage)) { throw new PackageInstallationException(receiver.ErrorMessage); @@ -463,23 +500,26 @@ protected virtual void RemoveRemotePackage(string remoteFilePath) /// /// Like "install", but starts an install session. /// - /// Set to if re-install of app should be performed. /// The absolute package name of the base app. + /// The arguments to pass to pm install-create. /// Session ID. - protected virtual string CreateInstallSession(bool reinstall, string? packageName = null) + protected virtual string CreateInstallSession(string? packageName = null, params string[] arguments) { ValidateDevice(); StringBuilder requestBuilder = new StringBuilder().Append("pm install-create"); - if (reinstall) + if (!StringExtensions.IsNullOrWhiteSpace(packageName)) { - _ = requestBuilder.Append(" -r"); + _ = requestBuilder.AppendFormat(" -p {0}", packageName); } - if (!StringExtensions.IsNullOrWhiteSpace(packageName)) + if (arguments != null) { - requestBuilder.Append($" -p {packageName}"); + foreach (string argument in arguments) + { + _ = requestBuilder.AppendFormat(" {0}", argument); + } } string cmd = requestBuilder.ToString(); diff --git a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs index 1c81e7fc..7585b6ab 100644 --- a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs +++ b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs @@ -314,6 +314,17 @@ public static Task InstallMultipleAsync(this IAdbClient client, DeviceData devic public static Task InstallCreateAsync(this IAdbClient client, DeviceData device, string? packageName = null, params string[] arguments) => client.InstallCreateAsync(device, packageName, default, arguments); + /// + /// Uninstalls an Android application on an device. + /// + /// An instance of a class that implements the interface. + /// The device on which to install the application. + /// The name of the package to uninstall. + /// The arguments to pass to adb uninstall. + /// A which represents the asynchronous operation. + public static Task UninstallAsync(this IAdbClient client, DeviceData device, string packageName, params string[] arguments) => + client.UninstallAsync(device, packageName, default, arguments); + /// /// Clear the input text. The input should be in focus. Use if the element isn't focused. /// diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs b/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs index 5e6e5440..dc7cb43f 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs @@ -365,6 +365,16 @@ public partial interface IAdbClient /// A which represents the asynchronous operation. Task InstallCommitAsync(DeviceData device, string session, CancellationToken cancellationToken); + /// + /// Uninstalls an Android application on an device. + /// + /// The device on which to install the application. + /// The name of the package to uninstall. + /// A which can be used to cancel the asynchronous operation. + /// The arguments to pass to adb uninstall. + /// A which represents the asynchronous operation. + Task UninstallAsync(DeviceData device, string packageName, CancellationToken cancellationToken, params string[] arguments); + /// /// Lists all features supported by the current device. /// diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs b/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs index 52ba3b3c..b297fd4d 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs @@ -368,6 +368,14 @@ public partial interface IAdbClient /// The session ID of the install session. void InstallCommit(DeviceData device, string session); + /// + /// Uninstalls an Android application on an device. + /// + /// The device on which to install the application. + /// The name of the package to uninstall. + /// The arguments to pass to adb uninstall. + void Uninstall(DeviceData device, string packageName, params string[] arguments); + /// /// Lists all features supported by the current device. /// diff --git a/README.md b/README.md index ef43737c..f933f633 100644 --- a/README.md +++ b/README.md @@ -313,7 +313,7 @@ static void Main(string[] args) { ... PackageManager manager = new PackageManager(client, device); - manager.InstallPackage(@"C:\Users\me\Documents\mypackage.apk", reinstall: false); + manager.InstallPackage(@"C:\Users\me\Documents\mypackage.apk"); manager.UninstallPackage("com.android.app"); ... } @@ -328,6 +328,7 @@ static void Main(string[] args) using (FileStream stream = File.OpenRead("Application.apk")) { client.Install(device, stream); + client.Uninstall(device, "com.android.app"); } ... } @@ -339,8 +340,8 @@ static void Main(string[] args) { ... PackageManager manager = new PackageManager(client, device); - manager.InstallMultiplePackage(@"C:\Users\me\Documents\base.apk", new[] { @"C:\Users\me\Documents\split_1.apk", @"C:\Users\me\Documents\split_2.apk" }, reinstall: false); // Install split app whith base app - manager.InstallMultiplePackage(new[] { @"C:\Users\me\Documents\split_3.apk", @"C:\Users\me\Documents\split_4.apk" }, "com.android.app", reinstall: false); // Add split app to base app which packagename is 'com.android.app' + manager.InstallMultiplePackage(@"C:\Users\me\Documents\base.apk", new[] { @"C:\Users\me\Documents\split_1.apk", @"C:\Users\me\Documents\split_2.apk" }); // Install split app whith base app + manager.InstallMultiplePackage(new[] { @"C:\Users\me\Documents\split_3.apk", @"C:\Users\me\Documents\split_4.apk" }, "com.android.app"); // Add split app to base app which packagename is 'com.android.app' ... } ``` From b9d23780cb40592b061e7d7c10ed01d7e52e8d12 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Wed, 1 Nov 2023 01:44:25 +0800 Subject: [PATCH 57/66] Try to fix test --- .../AdbClientTests.Async.cs | 35 +++++++++-------- .../AdbClientTests.cs | 35 +++++++++-------- .../DeviceExtensionsTests.Async.cs | 11 +++--- .../DeviceCommands/DeviceExtensionsTests.cs | 11 +++--- .../PackageManagerTests.Async.cs | 14 +------ .../DeviceCommands/PackageManagerTests.cs | 16 +------- .../Models/FramebufferTests.cs | 22 +++-------- .../SocketBasedTests.cs | 15 +++----- .../SyncServiceTests.Async.cs | 4 +- AdvancedSharpAdbClient/AdbClient.cs | 11 +++++- AdvancedSharpAdbClient/Models/Framebuffer.cs | 38 ++++++++++++++++--- 11 files changed, 107 insertions(+), 105 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs index 91d82c01..ef87d743 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs @@ -328,25 +328,24 @@ public async void ExecuteRemoteCommandAsyncUnresponsiveTest() [Fact] public async void GetFrameBufferAsyncTest() { - DummyAdbSocket socket = new(); - - socket.Responses.Enqueue(AdbResponse.OK); - socket.Responses.Enqueue(AdbResponse.OK); - - socket.Requests.Add("host:transport:169.254.109.177:5555"); - socket.Requests.Add("framebuffer:"); - - socket.SyncDataReceived.Enqueue(File.ReadAllBytes("Assets/framebufferheader.bin")); - socket.SyncDataReceived.Enqueue(File.ReadAllBytes("Assets/framebuffer.bin")); - - Framebuffer framebuffer = null; + string[] requests = + [ + "host:transport:169.254.109.177:5555", + "framebuffer:" + ]; - using (FactoriesLocker locker = await FactoriesLocker.WaitAsync()) - { - Factories.AdbSocketFactory = (endPoint) => socket; - framebuffer = await TestClient.GetFrameBufferAsync(Device); - Factories.Reset(); - } + Framebuffer framebuffer = await RunTestAsync( + OkResponses(2), + NoResponseMessages, + requests, + NoSyncRequests, + NoSyncResponses, + [ + await File.ReadAllBytesAsync("Assets/framebufferheader.bin"), + await File.ReadAllBytesAsync("Assets/framebuffer.bin") + ], + null, + () => TestClient.GetFrameBufferAsync(Device)); Assert.NotNull(framebuffer); Assert.Equal(Device, framebuffer.Device); diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs index 26f76987..bff1ee48 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs @@ -439,25 +439,24 @@ public void CreateRefreshableFramebufferTest() [Fact] public void GetFrameBufferTest() { - DummyAdbSocket socket = new(); - - socket.Responses.Enqueue(AdbResponse.OK); - socket.Responses.Enqueue(AdbResponse.OK); - - socket.Requests.Add("host:transport:169.254.109.177:5555"); - socket.Requests.Add("framebuffer:"); - - socket.SyncDataReceived.Enqueue(File.ReadAllBytes("Assets/framebufferheader.bin")); - socket.SyncDataReceived.Enqueue(File.ReadAllBytes("Assets/framebuffer.bin")); - - Framebuffer framebuffer = null; + string[] requests = + [ + "host:transport:169.254.109.177:5555", + "framebuffer:" + ]; - using (FactoriesLocker locker = FactoriesLocker.Wait()) - { - Factories.AdbSocketFactory = (endPoint) => socket; - framebuffer = TestClient.GetFrameBuffer(Device); - Factories.Reset(); - } + Framebuffer framebuffer = RunTest( + OkResponses(2), + NoResponseMessages, + requests, + NoSyncRequests, + NoSyncResponses, + [ + File.ReadAllBytes("Assets/framebufferheader.bin"), + File.ReadAllBytes("Assets/framebuffer.bin") + ], + null, + () => TestClient.GetFrameBuffer(Device)); Assert.NotNull(framebuffer); Assert.Equal(Device, framebuffer.Device); diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs index 55c2c2bf..e07e8b09 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs @@ -19,14 +19,15 @@ public async void StatAsyncTest() ISyncService mock = Substitute.For(); mock.StatAsync("/test", Arg.Any()).Returns(tcs.Task); - using FactoriesLocker locker = await FactoriesLocker.WaitAsync(); + DeviceData device = new(); - Factories.SyncServiceFactory = (c, d) => mock; + Factories.SyncServiceFactory = (c, d) => + { + Factories.Reset(); + return mock; + }; - DeviceData device = new(); Assert.Equal(await tcs.Task, await client.StatAsync(device, "/test")); - - Factories.Reset(); } [Fact] diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.cs index 489c249c..d96a0bcf 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.cs @@ -19,14 +19,15 @@ public void StatTest() ISyncService mock = Substitute.For(); mock.Stat("/test").Returns(stats); - using FactoriesLocker locker = FactoriesLocker.Wait(); + DeviceData device = new(); - Factories.SyncServiceFactory = (c, d) => mock; + Factories.SyncServiceFactory = (c, d) => + { + Factories.Reset(); + return mock; + }; - DeviceData device = new(); Assert.Equal(stats, client.Stat(device, "/test")); - - Factories.Reset(); } [Fact] diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs index 7f527469..fa0986b7 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs @@ -37,10 +37,6 @@ public async void InstallPackageAsyncTest() { DummySyncService syncService = new(); - using FactoriesLocker locker = await FactoriesLocker.WaitAsync(); - - Factories.SyncServiceFactory = (c, d) => syncService; - DummyAdbClient adbClient = new(); adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; @@ -52,7 +48,7 @@ public async void InstallPackageAsyncTest() State = DeviceState.Online }; - PackageManager manager = new(adbClient, device); + PackageManager manager = new(adbClient, device, (c, d) => syncService); await manager.InstallPackageAsync("Assets/test.txt"); Assert.Equal(3, adbClient.ReceivedCommands.Count); @@ -129,10 +125,6 @@ public async void InstallMultiplePackageAsyncTest() { DummySyncService syncService = new(); - using FactoriesLocker locker = await FactoriesLocker.WaitAsync(); - - Factories.SyncServiceFactory = (c, d) => syncService; - DummyAdbClient adbClient = new(); adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; @@ -151,7 +143,7 @@ public async void InstallMultiplePackageAsyncTest() State = DeviceState.Online }; - PackageManager manager = new(adbClient, device); + PackageManager manager = new(adbClient, device, (c, d) => syncService); await manager.InstallMultiplePackageAsync("Assets/test.txt", ["Assets/gapps.txt", "Assets/logcat.bin"]); Assert.Equal(9, adbClient.ReceivedCommands.Count); @@ -184,8 +176,6 @@ public async void InstallMultiplePackageAsyncTest() Assert.Equal(2, syncService.UploadedFiles.Count); Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/gapps.txt")); Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/logcat.bin")); - - Factories.Reset(); } [Fact] diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs index 11041da1..44bbc537 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs @@ -65,10 +65,6 @@ public void InstallPackageTest() { DummySyncService syncService = new(); - using FactoriesLocker locker = FactoriesLocker.Wait(); - - Factories.SyncServiceFactory = (c, d) => syncService; - DummyAdbClient adbClient = new(); adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; @@ -80,7 +76,7 @@ public void InstallPackageTest() State = DeviceState.Online }; - PackageManager manager = new(adbClient, device); + PackageManager manager = new(adbClient, device, (c, d) => syncService); manager.InstallPackage("Assets/test.txt"); Assert.Equal(3, adbClient.ReceivedCommands.Count); @@ -89,8 +85,6 @@ public void InstallPackageTest() Assert.Single(syncService.UploadedFiles); Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/test.txt")); - - Factories.Reset(); } [Fact] @@ -157,10 +151,6 @@ public void InstallMultiplePackageTest() { DummySyncService syncService = new(); - using FactoriesLocker locker = FactoriesLocker.Wait(); - - Factories.SyncServiceFactory = (c, d) => syncService; - DummyAdbClient adbClient = new(); adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; @@ -179,7 +169,7 @@ public void InstallMultiplePackageTest() State = DeviceState.Online }; - PackageManager manager = new(adbClient, device); + PackageManager manager = new(adbClient, device, (c, d) => syncService); manager.InstallMultiplePackage("Assets/test.txt", ["Assets/gapps.txt", "Assets/logcat.bin"]); Assert.Equal(9, adbClient.ReceivedCommands.Count); @@ -212,8 +202,6 @@ public void InstallMultiplePackageTest() Assert.Equal(2, syncService.UploadedFiles.Count); Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/gapps.txt")); Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/logcat.bin")); - - Factories.Reset(); } [Fact] diff --git a/AdvancedSharpAdbClient.Tests/Models/FramebufferTests.cs b/AdvancedSharpAdbClient.Tests/Models/FramebufferTests.cs index 1c90c8f8..be8e0a92 100644 --- a/AdvancedSharpAdbClient.Tests/Models/FramebufferTests.cs +++ b/AdvancedSharpAdbClient.Tests/Models/FramebufferTests.cs @@ -43,14 +43,9 @@ public void RefreshTest() socket.SyncDataReceived.Enqueue(File.ReadAllBytes("Assets/framebufferheader.bin")); socket.SyncDataReceived.Enqueue(File.ReadAllBytes("Assets/framebuffer.bin")); - using Framebuffer framebuffer = new(device); + using Framebuffer framebuffer = new(device, (endPoint) => socket); - using (FactoriesLocker locker = FactoriesLocker.Wait()) - { - Factories.AdbSocketFactory = (endPoint) => socket; - framebuffer.Refresh(); - Factories.Reset(); - } + framebuffer.Refresh(); Assert.NotNull(framebuffer); Assert.Equal(device, framebuffer.Device); @@ -106,17 +101,12 @@ public async void RefreshAsyncTest() socket.Requests.Add("host:transport:169.254.109.177:5555"); socket.Requests.Add("framebuffer:"); - socket.SyncDataReceived.Enqueue(File.ReadAllBytes("Assets/framebufferheader.bin")); - socket.SyncDataReceived.Enqueue(File.ReadAllBytes("Assets/framebuffer.bin")); + socket.SyncDataReceived.Enqueue(await File.ReadAllBytesAsync("Assets/framebufferheader.bin")); + socket.SyncDataReceived.Enqueue(await File.ReadAllBytesAsync("Assets/framebuffer.bin")); - using Framebuffer framebuffer = new(device); + using Framebuffer framebuffer = new(device, (endPoint) => socket); - using (FactoriesLocker locker = await FactoriesLocker.WaitAsync()) - { - Factories.AdbSocketFactory = (endPoint) => socket; - await framebuffer.RefreshAsync(); - Factories.Reset(); - } + await framebuffer.RefreshAsync(); Assert.NotNull(framebuffer); Assert.Equal(device, framebuffer.Device); diff --git a/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs b/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs index c57f01fb..9afb513b 100644 --- a/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs +++ b/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs @@ -12,7 +12,7 @@ public class SocketBasedTests { protected SocketBasedTests(bool integrationTest, bool doDispose) { - using FactoriesLocker locker = FactoriesLocker.Wait(); + Func AdbSocketFactory; // this.EndPoint = AdbClient.Instance.EndPoint; #if DEBUG @@ -21,13 +21,12 @@ protected SocketBasedTests(bool integrationTest, bool doDispose) if (integrationTest) { TracingAdbSocket tracingSocket = new(EndPoint) { DoDispose = doDispose }; - - Factories.AdbSocketFactory = (endPoint) => tracingSocket; + AdbSocketFactory = (endPoint) => tracingSocket; } else { DummyAdbSocket socket = new(); - Factories.AdbSocketFactory = (endPoint) => socket; + AdbSocketFactory = (endPoint) => socket; } IntegrationTest = integrationTest; @@ -35,14 +34,12 @@ protected SocketBasedTests(bool integrationTest, bool doDispose) // In release mode (e.g. on the build server), // never run integration tests. DummyAdbSocket socket = new(); - Factories.AdbSocketFactory = (endPoint) => socket; + AdbSocketFactory = (endPoint) => socket; IntegrationTest = false; #endif - Socket = (IDummyAdbSocket)Factories.AdbSocketFactory(EndPoint); - - TestClient = new AdbClient(); + Socket = (IDummyAdbSocket)AdbSocketFactory(EndPoint); - Factories.Reset(); + TestClient = new AdbClient(AdbSocketFactory); } protected static AdbResponse[] NoResponses { get; } = []; diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs index c358501d..08078e9b 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs @@ -154,7 +154,7 @@ public async void GetAsyncListingTest() public async void PullAsyncTest() { await using MemoryStream stream = new(); - byte[] content = File.ReadAllBytes("Assets/fstab.bin"); + byte[] content = await File.ReadAllBytesAsync("Assets/fstab.bin"); byte[] contentLength = BitConverter.GetBytes(content.Length); await RunTestAsync( @@ -189,7 +189,7 @@ await RunTestAsync( public async void PushAsyncTest() { FileStream stream = File.OpenRead("Assets/fstab.bin"); - byte[] content = File.ReadAllBytes("Assets/fstab.bin"); + byte[] content = await File.ReadAllBytesAsync("Assets/fstab.bin"); byte[] contentMessage = [ .. SyncCommandConverter.GetBytes(SyncCommand.DATA), diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index 804206b6..3c46c917 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -112,6 +112,15 @@ public AdbClient(string host, int port, Func adbSocketFact { } + /// + /// Initializes a new instance of the class. + /// + /// The to create . + public AdbClient(Func adbSocketFactory) + : this(new IPEndPoint(IPAddress.Loopback, AdbServerPort), adbSocketFactory) + { + } + /// /// Get or set default encoding. /// @@ -399,7 +408,7 @@ public Framebuffer CreateRefreshableFramebuffer(DeviceData device) { EnsureDevice(device); - return new Framebuffer(device, this); + return new Framebuffer(device, this, adbSocketFactory); } /// diff --git a/AdvancedSharpAdbClient/Models/Framebuffer.cs b/AdvancedSharpAdbClient/Models/Framebuffer.cs index d0e32809..63b5c0da 100644 --- a/AdvancedSharpAdbClient/Models/Framebuffer.cs +++ b/AdvancedSharpAdbClient/Models/Framebuffer.cs @@ -14,24 +14,52 @@ namespace AdvancedSharpAdbClient.Models /// /// The device for which to fetch the frame buffer. /// The at which the adb server is listening. - public class Framebuffer(DeviceData device, EndPoint endPoint) : IDisposable + /// The to create . + public class Framebuffer(DeviceData device, EndPoint endPoint, Func adbSocketFactory) : IDisposable { private byte[] headerData = new byte[56]; private bool headerInitialized; private bool disposed = false; + /// + /// The to create . + /// + protected readonly Func adbSocketFactory = adbSocketFactory; + + /// + /// Initializes a new instance of the class. + /// + /// The device for which to fetch the frame buffer. + /// The at which the adb server is listening. + public Framebuffer(DeviceData device, EndPoint endPoint) : this(device, endPoint, Factories.AdbSocketFactory) { } + /// /// Initializes a new instance of the class. /// /// The device for which to fetch the frame buffer. /// A which manages the connection with adb. - public Framebuffer(DeviceData device, IAdbClient client) : this(device, client?.EndPoint!) { } + /// The to create . + public Framebuffer(DeviceData device, IAdbClient client, Func adbSocketFactory) : this(device, client?.EndPoint!, adbSocketFactory) { } + + /// + /// Initializes a new instance of the class. + /// + /// The device for which to fetch the frame buffer. + /// A which manages the connection with adb. + public Framebuffer(DeviceData device, IAdbClient client) : this(device, client?.EndPoint!, Factories.AdbSocketFactory) { } + + /// + /// Initializes a new instance of the class. + /// + /// The device for which to fetch the frame buffer. + /// The to create . + public Framebuffer(DeviceData device, Func adbSocketFactory) : this(device, AdbClient.DefaultEndPoint, adbSocketFactory) { } /// /// Initializes a new instance of the class. /// /// The device for which to fetch the frame buffer. - public Framebuffer(DeviceData device) : this(device, AdbClient.DefaultEndPoint) { } + public Framebuffer(DeviceData device) : this(device, AdbClient.DefaultEndPoint, Factories.AdbSocketFactory) { } /// /// Gets the device for which to fetch the frame buffer. @@ -65,7 +93,7 @@ public virtual void Refresh(bool reset = false) { EnsureNotDisposed(); - using IAdbSocket socket = Factories.AdbSocketFactory(EndPoint); + using IAdbSocket socket = adbSocketFactory(EndPoint); // Select the target device socket.SetDevice(Device); @@ -117,7 +145,7 @@ public virtual async Task RefreshAsync(bool reset = false, CancellationToken can { EnsureNotDisposed(); - using IAdbSocket socket = Factories.AdbSocketFactory(EndPoint); + using IAdbSocket socket = adbSocketFactory(EndPoint); // Select the target device await socket.SetDeviceAsync(Device, cancellationToken).ConfigureAwait(false); From 9983fd8e73ce0f6a876356b417f91341e53c699f Mon Sep 17 00:00:00 2001 From: wherewhere Date: Thu, 2 Nov 2023 21:53:01 +0800 Subject: [PATCH 58/66] Update Readme --- .github/ISSUE_TEMPLATE/config.yml | 8 ++++++++ .github/workflows/build-and-test.yml | 3 +++ README.md | 6 +++--- 3 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..91a2e8ea --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: true +contact_links: + - name: Question + url: https://github.com/SharpAdb/AdvancedSharpAdbClient/discussions/new?category=q-a + about: Ask a question + - name: Discussion + url: https://github.com/SharpAdb/AdvancedSharpAdbClient/discussions/new?category=general + about: Start a discussion diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 3e3dc3f0..68fcfc13 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -8,6 +8,9 @@ on: pull_request: branches: - main + paths: + - '.github/workflows/build-and-test.yml' + - 'AdvancedSharpAdbClient**' workflow_dispatch: env: diff --git a/README.md b/README.md index f933f633..bcf64b1a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ | Issues | License | NuGet | |--------|---------|-------| -[![Github Issues](https://img.shields.io/github/issues/yungd1plomat/AdvancedSharpAdbClient)](https://github.com/yungd1plomat/AdvancedSharpAdbClient/issues)|[![License](https://img.shields.io/github/license/yungd1plomat/AdvancedSharpAdbClient)](https://github.com/yungd1plomat/AdvancedSharpAdbClient/blob/main/LICENSE)|[![NuGet Status](https://img.shields.io/nuget/dt/AdvancedSharpAdbClient.svg?style=flat)](https://www.nuget.org/packages/AdvancedSharpAdbClient/) +[![Github Issues](https://img.shields.io/github/issues/SharpAdb/AdvancedSharpAdbClient)](https://github.com/SharpAdb/AdvancedSharpAdbClient/issues)|[![License](https://img.shields.io/github/license/SharpAdb/AdvancedSharpAdbClient)](https://github.com/SharpAdb/AdvancedSharpAdbClient/blob/main/LICENSE)|[![NuGet Status](https://img.shields.io/nuget/dt/AdvancedSharpAdbClient.svg?style=flat)](https://www.nuget.org/packages/AdvancedSharpAdbClient/) # A .NET client for adb, the Android Debug Bridge (AdvancedSharpAdbClient) @@ -442,10 +442,10 @@ AdbClient.SetEncoding(Encoding.ASCII); ``` ## Contributors -[![Contributors](https://contrib.rocks/image?repo=yungd1plomat/AdvancedSharpAdbClient)](https://github.com/yungd1plomat/AdvancedSharpAdbClient/graphs/contributors) +[![Contributors](https://contrib.rocks/image?repo=SharpAdb/AdvancedSharpAdbClient)](https://github.com/SharpAdb/AdvancedSharpAdbClient/graphs/contributors) ## Consulting and Support -Please open an [**issue**](https://github.com/yungd1plomat/AdvancedSharpAdbClient/issues) on if you have suggestions or problems. +Please open an [**issue**](https://github.com/SharpAdb/AdvancedSharpAdbClient/issues) on if you have suggestions or problems. ## History AdvancedSharpAdbClient is a fork of [SharpAdbClient](https://github.com/quamotion/madb) and [madb](https://github.com/camalot/madb) which in itself is a .NET port of the [ddmlib Java Library](https://android.googlesource.com/platform/tools/base/+/master/ddmlib/). From 2bab86e038d314946ac160abc3dec9bd3cc67442 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Fri, 3 Nov 2023 17:51:26 +0800 Subject: [PATCH 59/66] Add IReadOnlyList to FramebufferHeader --- .../Models/FramebufferHeaderTests.cs | 18 +- AdvancedSharpAdbClient/AdbSocket.Async.cs | 7 +- AdvancedSharpAdbClient/AdbSocket.cs | 4 + .../Extensions/EnumerableBuilder.cs | 34 ++++ .../Extensions/Extensions.cs | 3 + AdvancedSharpAdbClient/Logs/LogReader.cs | 21 ++- AdvancedSharpAdbClient/Models/ColorData.cs | 74 +++++++- AdvancedSharpAdbClient/Models/Framebuffer.cs | 2 +- .../Models/FramebufferHeader.cs | 178 +++++++++++++++--- .../Attributes/CollectionBuilderAttribute.cs | 38 ++++ .../Interfaces/IReadOnlyCollection.cs | 22 +++ .../Polyfills/Interfaces/IReadOnlyList.cs | 21 +++ AdvancedSharpAdbClient/SyncService.Async.cs | 13 +- AdvancedSharpAdbClient/SyncService.cs | 13 +- Directory.Build.props | 8 +- 15 files changed, 413 insertions(+), 43 deletions(-) create mode 100644 AdvancedSharpAdbClient/Extensions/EnumerableBuilder.cs create mode 100644 AdvancedSharpAdbClient/Polyfills/Attributes/CollectionBuilderAttribute.cs create mode 100644 AdvancedSharpAdbClient/Polyfills/Interfaces/IReadOnlyCollection.cs create mode 100644 AdvancedSharpAdbClient/Polyfills/Interfaces/IReadOnlyList.cs diff --git a/AdvancedSharpAdbClient.Tests/Models/FramebufferHeaderTests.cs b/AdvancedSharpAdbClient.Tests/Models/FramebufferHeaderTests.cs index 39c5f027..3dead9d8 100644 --- a/AdvancedSharpAdbClient.Tests/Models/FramebufferHeaderTests.cs +++ b/AdvancedSharpAdbClient.Tests/Models/FramebufferHeaderTests.cs @@ -16,7 +16,7 @@ public void ReadFramebufferTest() { byte[] data = File.ReadAllBytes("Assets/framebufferheader-v1.bin"); - FramebufferHeader header = FramebufferHeader.Read(data); + FramebufferHeader header = [.. data]; Assert.Equal(8u, header.Alpha.Length); Assert.Equal(24u, header.Alpha.Offset); @@ -32,6 +32,13 @@ public void ReadFramebufferTest() Assert.Equal(1440u, header.Width); Assert.Equal(1u, header.Version); Assert.Equal(0u, header.ColorSpace); + + for (int i = 0; i < header.Count; i++) + { + Assert.Equal(data[i], header[i]); + } + + Assert.Equal(data, [.. header]); } [Fact] @@ -39,7 +46,7 @@ public void ReadFramebufferV2Test() { byte[] data = File.ReadAllBytes("Assets/framebufferheader-v2.bin"); - FramebufferHeader header = FramebufferHeader.Read(data); + FramebufferHeader header = [.. data]; Assert.Equal(8u, header.Alpha.Length); Assert.Equal(24u, header.Alpha.Offset); @@ -55,6 +62,13 @@ public void ReadFramebufferV2Test() Assert.Equal(1080u, header.Width); Assert.Equal(2u, header.Version); Assert.Equal(0u, header.ColorSpace); + + for (int i = 0; i < header.Count; i++) + { + Assert.Equal(data[i], header[i]); + } + + Assert.Equal(data, [.. header]); } #if WINDOWS diff --git a/AdvancedSharpAdbClient/AdbSocket.Async.cs b/AdvancedSharpAdbClient/AdbSocket.Async.cs index 4e52dd9a..28566c4e 100644 --- a/AdvancedSharpAdbClient/AdbSocket.Async.cs +++ b/AdvancedSharpAdbClient/AdbSocket.Async.cs @@ -193,7 +193,12 @@ public virtual async Task ReadSyncStringAsync(CancellationToken cancella Array.Reverse(reply); } - int len = BitConverter.ToInt32(reply, 0); + int len = +#if HAS_BUFFERS + BitConverter.ToInt32(reply); +#else + BitConverter.ToInt32(reply, 0); +#endif // And get the string reply = new byte[len]; diff --git a/AdvancedSharpAdbClient/AdbSocket.cs b/AdvancedSharpAdbClient/AdbSocket.cs index 9b2136ba..0e18fadd 100644 --- a/AdvancedSharpAdbClient/AdbSocket.cs +++ b/AdvancedSharpAdbClient/AdbSocket.cs @@ -263,7 +263,11 @@ public virtual string ReadSyncString() Array.Reverse(reply); } +#if HAS_BUFFERS + int len = BitConverter.ToInt32(reply); +#else int len = BitConverter.ToInt32(reply, 0); +#endif // And get the string reply = new byte[len]; diff --git a/AdvancedSharpAdbClient/Extensions/EnumerableBuilder.cs b/AdvancedSharpAdbClient/Extensions/EnumerableBuilder.cs new file mode 100644 index 00000000..e4ceecf9 --- /dev/null +++ b/AdvancedSharpAdbClient/Extensions/EnumerableBuilder.cs @@ -0,0 +1,34 @@ +#if HAS_BUFFERS +// +// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. +// + +using System; +using System.ComponentModel; + +namespace AdvancedSharpAdbClient +{ + /// + /// A collection builder provide for collection expressions. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public static class EnumerableBuilder + { + /// + /// Build a struct. + /// + /// The data that feeds the struct. + /// A new instance of struct. + public static ColorData ColorDataCreate(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))); + + /// + /// Build a struct. + /// + /// The data that feeds the struct. + /// A new instance of struct. + public static FramebufferHeader FramebufferHeaderCreate(ReadOnlySpan values) => new(values); + } +} +#endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Extensions/Extensions.cs b/AdvancedSharpAdbClient/Extensions/Extensions.cs index 00bbcd25..487f5ac0 100644 --- a/AdvancedSharpAdbClient/Extensions/Extensions.cs +++ b/AdvancedSharpAdbClient/Extensions/Extensions.cs @@ -17,6 +17,9 @@ namespace AdvancedSharpAdbClient { + /// + /// Extension methods for the namespace. + /// internal static class Extensions { public static char[] NewLineSeparator { get; } = ['\r', '\n']; diff --git a/AdvancedSharpAdbClient/Logs/LogReader.cs b/AdvancedSharpAdbClient/Logs/LogReader.cs index b75fa581..35dbf9d5 100644 --- a/AdvancedSharpAdbClient/Logs/LogReader.cs +++ b/AdvancedSharpAdbClient/Logs/LogReader.cs @@ -232,7 +232,12 @@ protected void ReadLogEntry(BinaryReader reader, ICollection parent) { byte[]? data = ReadBytesSafe(2); - return data == null ? null : BitConverter.ToUInt16(data, 0); + return data == null ? null +#if HAS_BUFFERS + : BitConverter.ToUInt16(data); +#else + : BitConverter.ToUInt16(data, 0); +#endif } /// @@ -242,7 +247,12 @@ protected void ReadLogEntry(BinaryReader reader, ICollection parent) { byte[]? data = ReadBytesSafe(4); - return data == null ? null : BitConverter.ToUInt32(data, 0); + return data == null ? null +#if HAS_BUFFERS + : BitConverter.ToUInt32(data); +#else + : BitConverter.ToUInt32(data, 0); +#endif } /// @@ -252,7 +262,12 @@ protected void ReadLogEntry(BinaryReader reader, ICollection parent) { byte[]? data = ReadBytesSafe(4); - return data == null ? null : BitConverter.ToInt32(data, 0); + return data == null ? null +#if HAS_BUFFERS + : BitConverter.ToInt32(data); +#else + : BitConverter.ToInt32(data, 0); +#endif } /// diff --git a/AdvancedSharpAdbClient/Models/ColorData.cs b/AdvancedSharpAdbClient/Models/ColorData.cs index 8d8f4245..90c4fe4b 100644 --- a/AdvancedSharpAdbClient/Models/ColorData.cs +++ b/AdvancedSharpAdbClient/Models/ColorData.cs @@ -2,6 +2,11 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + namespace AdvancedSharpAdbClient.Models { /// @@ -11,29 +16,84 @@ namespace AdvancedSharpAdbClient.Models /// For example, in a 24-bit RGB structure, the first byte contains the red color, /// the next byte the green color and the last byte the blue color. /// - public readonly record struct ColorData(uint Length, uint Offset) + /// The offset, in bits, within the byte array for a pixel, at which the + /// 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))] +#endif + public readonly record struct ColorData(uint Offset, uint Length) : IReadOnlyList { + /// + /// Gets or sets the offset, in bits, within the byte array for a pixel, at which the + /// bytes that contain information for this color are stored. + /// + public uint Offset { get; init; } = Offset; + /// /// Gets or sets the number of bits that contain information for this color. /// public uint Length { get; init; } = Length; /// - /// Gets or sets the offset, in bits, within the byte array for a pixel, at which the - /// bytes that contain information for this color are stored. + /// The length of in bytes. /// - public uint Offset { get; init; } = Offset; + public readonly int Count => 8; + + /// + public readonly byte this[int index] + { + get + { + if (index < 0 || index >= Count) + { + throw new IndexOutOfRangeException("Index was out of range. Must be non-negative and less than the size of the collection."); + } + + return index switch + { + 0 => (byte)Offset, + 1 => (byte)(Offset >> 8), + 2 => (byte)(Offset >> 16), + 3 => (byte)(Offset >> 24), + + 4 => (byte)Length, + 5 => (byte)(Length >> 8), + 6 => (byte)(Length >> 16), + 7 => (byte)(Length >> 24), + + _ => throw new IndexOutOfRangeException("Index was out of range. Must be non-negative and less than the size of the collection.") + }; + } + } /// /// Deconstruct the struct. /// - /// The number of bits that contain information for this color. /// The offset, in bits, within the byte array for a pixel, at which the /// bytes that contain information for this color are stored. - public readonly void Deconstruct(out uint length, out uint offset) + /// The number of bits that contain information for this color. + public readonly void Deconstruct(out uint offset, out uint length) { - length = Length; offset = Offset; + length = Length; + } + + /// + public readonly IEnumerator GetEnumerator() + { + yield return (byte)Offset; + yield return (byte)(Offset >> 8); + yield return (byte)(Offset >> 16); + yield return (byte)(Offset >> 24); + + yield return (byte)Length; + yield return (byte)(Length >> 8); + yield return (byte)(Length >> 16); + yield return (byte)(Length >> 24); } + + /// + readonly IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } diff --git a/AdvancedSharpAdbClient/Models/Framebuffer.cs b/AdvancedSharpAdbClient/Models/Framebuffer.cs index 63b5c0da..139fd1b6 100644 --- a/AdvancedSharpAdbClient/Models/Framebuffer.cs +++ b/AdvancedSharpAdbClient/Models/Framebuffer.cs @@ -17,7 +17,7 @@ namespace AdvancedSharpAdbClient.Models /// The to create . public class Framebuffer(DeviceData device, EndPoint endPoint, Func adbSocketFactory) : IDisposable { - private byte[] headerData = new byte[56]; + private byte[] headerData = new byte[FramebufferHeader.MaxLength]; private bool headerInitialized; private bool disposed = false; diff --git a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs index a2df3dfc..2885b914 100644 --- a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs +++ b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs @@ -3,11 +3,13 @@ // using System; -using System.IO; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Text; #if WINDOWS_UWP +using System.IO; using System.Threading; #endif @@ -18,19 +20,40 @@ namespace AdvancedSharpAdbClient.Models /// of the framebuffer, prefixed with a object that contains more /// information about the framebuffer. /// - public readonly struct FramebufferHeader +#if HAS_BUFFERS + [CollectionBuilder(typeof(EnumerableBuilder), nameof(EnumerableBuilder.FramebufferHeaderCreate))] +#endif + public readonly struct FramebufferHeader : IReadOnlyList { + /// + /// The length of the head when is . + /// + public const int MaxLength = 56; + + /// + /// The length of the head when is . + /// + public const int MiniLength = 52; + /// /// Initializes a new instance of the struct based on a byte array which contains the data. /// /// The data that feeds the struct. /// As defined in +#if HAS_BUFFERS + public FramebufferHeader(ReadOnlySpan data) +#else public FramebufferHeader(byte[] data) +#endif { - // Read the data from a MemoryStream so we can use the BinaryReader to process the data. - using MemoryStream stream = new(data); - using BinaryReader reader = new(stream, Encoding.ASCII); - Version = reader.ReadUInt32(); + if (data.Length is < MiniLength or > MaxLength) + { + throw new ArgumentOutOfRangeException(nameof(data), $"The length of {nameof(data)} must between {MiniLength} and {MaxLength}."); + } + + int index = 0; + + Version = ReadUInt32(ref data); if (Version > 2) { @@ -39,40 +62,49 @@ public FramebufferHeader(byte[] data) throw new InvalidOperationException($"Framebuffer version {Version} is not supported"); } - Bpp = reader.ReadUInt32(); + Bpp = ReadUInt32(ref data); - if (Version == 2) + if (Version >= 2) { - ColorSpace = reader.ReadUInt32(); + ColorSpace = ReadUInt32(ref data); } - Size = reader.ReadUInt32(); - Width = reader.ReadUInt32(); - Height = reader.ReadUInt32(); + Size = ReadUInt32(ref data); + Width = ReadUInt32(ref data); + Height = ReadUInt32(ref data); Red = new ColorData { - Offset = reader.ReadUInt32(), - Length = reader.ReadUInt32() + Offset = ReadUInt32(ref data), + Length = ReadUInt32(ref data) }; - + Blue = new ColorData { - Offset = reader.ReadUInt32(), - Length = reader.ReadUInt32() + Offset = ReadUInt32(ref data), + Length = ReadUInt32(ref data) }; - + Green = new ColorData { - Offset = reader.ReadUInt32(), - Length = reader.ReadUInt32() + Offset = ReadUInt32(ref data), + Length = ReadUInt32(ref data) }; Alpha = new ColorData { - Offset = reader.ReadUInt32(), - Length = reader.ReadUInt32() + Offset = ReadUInt32(ref data), + Length = ReadUInt32(ref data) }; + +#if HAS_BUFFERS + uint ReadUInt32(ref ReadOnlySpan data) +#else + uint ReadUInt32(ref byte[] data) +#endif + { + return (uint)(data[index++] | (data[index++] << 8) | (data[index++] << 16) | (data[index++] << 24)); + } } /// @@ -125,12 +157,69 @@ public FramebufferHeader(byte[] data) /// public ColorData Alpha { get; init; } + /// + /// The length of the head in bytes. + /// + public int Count => Version < 2 ? MiniLength : MaxLength; + + /// + public byte this[int index] + { + get + { + if (index < 0 || index >= Count) + { + throw new IndexOutOfRangeException("Index was out of range. Must be non-negative and less than the size of the collection."); + } + + if (index > 7 && Version < 2) + { + index += 4; + } + + return index switch + { + < 4 => GetByte(Version), + < 8 => GetByte(Bpp), + < 12 => GetByte(ColorSpace), + < 16 => GetByte(Size), + < 20 => GetByte(Width), + < 24 => GetByte(Height), + < 28 => GetByte(Red.Offset), + < 32 => GetByte(Red.Length), + < 36 => GetByte(Blue.Offset), + < 40 => GetByte(Blue.Length), + < 44 => GetByte(Green.Offset), + < 48 => GetByte(Green.Length), + < 52 => GetByte(Alpha.Offset), + < 56 => GetByte(Alpha.Length), + _ => throw new IndexOutOfRangeException("Index was out of range. Must be non-negative and less than the size of the collection.") + }; + + byte GetByte(uint value) + { + return (index % 4) switch + { + 0 => (byte)value, + 1 => (byte)(value >> 8), + 2 => (byte)(value >> 16), + 3 => (byte)(value >> 24), + _ => throw new InvalidOperationException() + }; + } + } + } + /// /// Creates a new object based on a byte array which contains the data. /// /// The data that feeds the struct. /// A new object. +#if HAS_BUFFERS + public static FramebufferHeader Read(ReadOnlySpan data) => new(data); +#else public static FramebufferHeader Read(byte[] data) => new(data); +#endif #if HAS_IMAGING /// @@ -404,5 +493,48 @@ private readonly PixelFormat StandardizePixelFormat(byte[] buffer) } } #endif + + /// + public IEnumerator GetEnumerator() + { + foreach (uint value in GetEnumerable()) + { + yield return (byte)value; + yield return (byte)(value >> 8); + yield return (byte)(value >> 16); + yield return (byte)(value >> 24); + } + } + + /// + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + private IEnumerable GetEnumerable() + { + yield return Version; + + yield return Bpp; + + if (Version >= 2) + { + yield return ColorSpace; + } + + yield return Size; + yield return Width; + yield return Height; + + yield return Red.Offset; + yield return Red.Length; + + yield return Blue.Offset; + yield return Blue.Length; + + yield return Green.Offset; + yield return Green.Length; + + yield return Alpha.Offset; + yield return Alpha.Length; + } } } diff --git a/AdvancedSharpAdbClient/Polyfills/Attributes/CollectionBuilderAttribute.cs b/AdvancedSharpAdbClient/Polyfills/Attributes/CollectionBuilderAttribute.cs new file mode 100644 index 00000000..d61470c1 --- /dev/null +++ b/AdvancedSharpAdbClient/Polyfills/Attributes/CollectionBuilderAttribute.cs @@ -0,0 +1,38 @@ +#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/Interfaces/IReadOnlyCollection.cs b/AdvancedSharpAdbClient/Polyfills/Interfaces/IReadOnlyCollection.cs new file mode 100644 index 00000000..cb3ca93c --- /dev/null +++ b/AdvancedSharpAdbClient/Polyfills/Interfaces/IReadOnlyCollection.cs @@ -0,0 +1,22 @@ +#if NETFRAMEWORK && !NET45_OR_GREATER +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +namespace AdvancedSharpAdbClient.Polyfills +{ + /// + /// Represents a strongly-typed, read-only collection of elements. + /// + /// The type of the elements. + public interface IReadOnlyCollection : IEnumerable + { + /// + /// Gets the number of elements in the collection. + /// + /// The number of elements in the collection. + int Count { get; } + } +} +#endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Polyfills/Interfaces/IReadOnlyList.cs b/AdvancedSharpAdbClient/Polyfills/Interfaces/IReadOnlyList.cs new file mode 100644 index 00000000..07822794 --- /dev/null +++ b/AdvancedSharpAdbClient/Polyfills/Interfaces/IReadOnlyList.cs @@ -0,0 +1,21 @@ +#if NETFRAMEWORK && !NET45_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 AdvancedSharpAdbClient.Polyfills +{ + /// + /// Represents a read-only collection of elements that can be accessed by index. + /// + /// The type of elements in the read-only list. + public interface IReadOnlyList : IReadOnlyCollection + { + /// + /// Gets the element at the specified index in the read-only list. + /// + /// The zero-based index of the element to get. + /// The element at the specified index in the read-only list. + T this[int index] { get; } + } +} +#endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/SyncService.Async.cs b/AdvancedSharpAdbClient/SyncService.Async.cs index 6e4acbfc..dd245c40 100644 --- a/AdvancedSharpAdbClient/SyncService.Async.cs +++ b/AdvancedSharpAdbClient/SyncService.Async.cs @@ -193,7 +193,12 @@ public virtual async Task PullAsync(string remoteFilePath, Stream stream, IProgr Array.Reverse(reply); } - int size = BitConverter.ToInt32(reply, 0); + int size = +#if HAS_BUFFERS + BitConverter.ToInt32(reply); +#else + BitConverter.ToInt32(reply, 0); +#endif if (size > MaxBufferSize) { @@ -315,9 +320,15 @@ private async Task ReadStatisticsAsync(FileStatistics value, CancellationToken c Array.Reverse(statResult, 8, 4); } +#if HAS_BUFFERS + value.FileType = (UnixFileType)BitConverter.ToInt32(statResult); + value.Size = BitConverter.ToInt32(statResult.AsSpan(4)); + value.Time = DateTimeExtensions.FromUnixTimeSeconds(BitConverter.ToInt32(statResult.AsSpan(8))); +#else value.FileType = (UnixFileType)BitConverter.ToInt32(statResult, 0); value.Size = BitConverter.ToInt32(statResult, 4); value.Time = DateTimeExtensions.FromUnixTimeSeconds(BitConverter.ToInt32(statResult, 8)); +#endif } } } diff --git a/AdvancedSharpAdbClient/SyncService.cs b/AdvancedSharpAdbClient/SyncService.cs index 10e8e2f3..d7577720 100644 --- a/AdvancedSharpAdbClient/SyncService.cs +++ b/AdvancedSharpAdbClient/SyncService.cs @@ -271,7 +271,12 @@ public virtual void Pull(string remoteFilePath, Stream stream, IProgress? p Array.Reverse(reply); } - int size = BitConverter.ToInt32(reply, 0); + int size = +#if HAS_BUFFERS + BitConverter.ToInt32(reply); +#else + BitConverter.ToInt32(reply, 0); +#endif if (size > MaxBufferSize) { @@ -382,9 +387,15 @@ private void ReadStatistics(FileStatistics value) Array.Reverse(statResult, 8, 4); } +#if HAS_BUFFERS + value.FileType = (UnixFileType)BitConverter.ToInt32(statResult); + value.Size = BitConverter.ToInt32(statResult.AsSpan(4)); + value.Time = DateTimeExtensions.FromUnixTimeSeconds(BitConverter.ToInt32(statResult.AsSpan(8))); +#else value.FileType = (UnixFileType)BitConverter.ToInt32(statResult, 0); value.Size = BitConverter.ToInt32(statResult, 4); value.Time = DateTimeExtensions.FromUnixTimeSeconds(BitConverter.ToInt32(statResult, 8)); +#endif } } } diff --git a/Directory.Build.props b/Directory.Build.props index 2a2d56ad..cf4ee39e 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -12,16 +12,16 @@ True latest Icon.png - https://raw.githubusercontent.com/yungd1plomat/AdvancedSharpAdbClient/main/logo.png + https://raw.githubusercontent.com/SharpAdb/AdvancedSharpAdbClient/main/logo.png Apache-2.0 - https://github.com/yungd1plomat/AdvancedSharpAdbClient + https://github.com/SharpAdb/AdvancedSharpAdbClient True - https://github.com/yungd1plomat/AdvancedSharpAdbClient/releases + https://github.com/SharpAdb/AdvancedSharpAdbClient/releases Android;ADB;Communicate;UWP;Xamarin;MAUI;WinUI;Mono;Unity;SharpAdbClient;AdvancedSharpAdbClient git AdvancedSharpAdbClient: A .NET client for the Android Debug Bridge (adb) True - https://github.com/yungd1plomat/AdvancedSharpAdbClient + https://github.com/SharpAdb/AdvancedSharpAdbClient snupkg .NET client for adb, Android Debug Bridge (AdvancedSharpAdbClient) 3.0.9 From f45effcc9e55569688759cb1b29078c2f0b7bbad Mon Sep 17 00:00:00 2001 From: wherewhere Date: Fri, 3 Nov 2023 23:21:19 +0800 Subject: [PATCH 60/66] Fix progress notify of InstallProgressChanged Add test of InstallProgressChanged --- .../PackageManagerTests.Async.cs | 89 ++++++++-- .../DeviceCommands/PackageManagerTests.cs | 153 ++++++++++++++++-- .../DeviceCommands/DeviceExtensions.Async.cs | 4 + .../DeviceCommands/DeviceExtensions.cs | 4 + .../Models/InstallProgressEventArgs.cs | 10 +- .../DeviceCommands/PackageManager.Async.cs | 121 ++++++++------ .../DeviceCommands/PackageManager.cs | 101 +++++++----- AdvancedSharpAdbClient/Models/ColorData.cs | 16 +- .../Models/FramebufferHeader.cs | 4 +- .../Models/SyncProgressChangedEventArgs.cs | 6 +- 10 files changed, 378 insertions(+), 130 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs index fa0986b7..80672c78 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs @@ -20,13 +20,21 @@ public async void InstallRemotePackageAsyncTest() }; PackageManager manager = new(adbClient, device); - await manager.InstallRemotePackageAsync("/data/test.apk"); + + using (EventTestHost eventTestHost = new(manager, PackageInstallProgressState.Installing)) + { + await manager.InstallRemotePackageAsync("/data/test.apk"); + } Assert.Equal(2, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install \"/data/test.apk\"", adbClient.ReceivedCommands[1]); adbClient.ReceivedCommands.Clear(); - await manager.InstallRemotePackageAsync("/data/test.apk", "-r", "-t"); + + using (EventTestHost eventTestHost = new(manager, PackageInstallProgressState.Installing)) + { + await manager.InstallRemotePackageAsync("/data/test.apk", "-r", "-t"); + } Assert.Single(adbClient.ReceivedCommands); Assert.Equal("shell:pm install -r -t \"/data/test.apk\"", adbClient.ReceivedCommands[0]); @@ -49,7 +57,16 @@ public async void InstallPackageAsyncTest() }; PackageManager manager = new(adbClient, device, (c, d) => syncService); - await manager.InstallPackageAsync("Assets/test.txt"); + + using (EventTestHost eventTestHost = new( + manager, + PackageInstallProgressState.Uploading, + PackageInstallProgressState.Installing, + PackageInstallProgressState.PostInstall, + PackageInstallProgressState.Finished)) + { + await manager.InstallPackageAsync("Assets/test.txt"); + } Assert.Equal(3, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[1]); @@ -82,7 +99,15 @@ public async void InstallMultipleRemotePackageAsyncTest() }; PackageManager manager = new(adbClient, device); - await manager.InstallMultipleRemotePackageAsync("/data/base.apk", ["/data/split-dpi.apk", "/data/split-abi.apk"]); + + using (EventTestHost eventTestHost = new( + manager, + PackageInstallProgressState.CreateSession, + PackageInstallProgressState.WriteSession, + PackageInstallProgressState.Installing)) + { + await manager.InstallMultipleRemotePackageAsync("/data/base.apk", ["/data/split-dpi.apk", "/data/split-abi.apk"]); + } Assert.Equal(6, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create", adbClient.ReceivedCommands[1]); @@ -92,7 +117,15 @@ public async void InstallMultipleRemotePackageAsyncTest() Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[5]); adbClient.ReceivedCommands.Clear(); - await manager.InstallMultipleRemotePackageAsync("/data/base.apk", ["/data/split-dpi.apk", "/data/split-abi.apk"], "-r", "-t"); + + using (EventTestHost eventTestHost = new( + manager, + PackageInstallProgressState.CreateSession, + PackageInstallProgressState.WriteSession, + PackageInstallProgressState.Installing)) + { + await manager.InstallMultipleRemotePackageAsync("/data/base.apk", ["/data/split-dpi.apk", "/data/split-abi.apk"], "-r", "-t"); + } Assert.Equal(5, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create -r -t", adbClient.ReceivedCommands[0]); @@ -102,7 +135,15 @@ public async void InstallMultipleRemotePackageAsyncTest() Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[4]); adbClient.ReceivedCommands.Clear(); - await manager.InstallMultipleRemotePackageAsync(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms"); + + using (EventTestHost eventTestHost = new( + manager, + PackageInstallProgressState.CreateSession, + PackageInstallProgressState.WriteSession, + PackageInstallProgressState.Installing)) + { + await manager.InstallMultipleRemotePackageAsync(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms"); + } Assert.Equal(4, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[0]); @@ -111,7 +152,15 @@ public async void InstallMultipleRemotePackageAsyncTest() Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[3]); adbClient.ReceivedCommands.Clear(); - await manager.InstallMultipleRemotePackageAsync(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms", "-r", "-t"); + + using (EventTestHost eventTestHost = new( + manager, + PackageInstallProgressState.CreateSession, + PackageInstallProgressState.WriteSession, + PackageInstallProgressState.Installing)) + { + await manager.InstallMultipleRemotePackageAsync(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms", "-r", "-t"); + } Assert.Equal(4, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create -p com.google.android.gms -r -t", adbClient.ReceivedCommands[0]); @@ -144,7 +193,18 @@ public async void InstallMultiplePackageAsyncTest() }; PackageManager manager = new(adbClient, device, (c, d) => syncService); - await manager.InstallMultiplePackageAsync("Assets/test.txt", ["Assets/gapps.txt", "Assets/logcat.bin"]); + + using (EventTestHost eventTestHost = new( + manager, + PackageInstallProgressState.Uploading, + PackageInstallProgressState.CreateSession, + PackageInstallProgressState.WriteSession, + PackageInstallProgressState.Installing, + PackageInstallProgressState.PostInstall, + PackageInstallProgressState.Finished)) + { + await manager.InstallMultiplePackageAsync("Assets/test.txt", ["Assets/gapps.txt", "Assets/logcat.bin"]); + } Assert.Equal(9, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create", adbClient.ReceivedCommands[1]); @@ -163,7 +223,18 @@ public async void InstallMultiplePackageAsyncTest() syncService.UploadedFiles.Clear(); adbClient.ReceivedCommands.Clear(); - await manager.InstallMultiplePackageAsync(["Assets/gapps.txt", "Assets/logcat.bin"], "com.google.android.gms"); + + using (EventTestHost eventTestHost = new( + manager, + PackageInstallProgressState.Uploading, + PackageInstallProgressState.CreateSession, + PackageInstallProgressState.WriteSession, + PackageInstallProgressState.Installing, + PackageInstallProgressState.PostInstall, + PackageInstallProgressState.Finished)) + { + await manager.InstallMultiplePackageAsync(["Assets/gapps.txt", "Assets/logcat.bin"], "com.google.android.gms"); + } Assert.Equal(6, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[0]); diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs index 44bbc537..ab772950 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs @@ -48,13 +48,21 @@ public void InstallRemotePackageTest() }; PackageManager manager = new(adbClient, device); - manager.InstallRemotePackage("/data/test.apk"); + + using (EventTestHost eventTestHost = new(manager, PackageInstallProgressState.Installing)) + { + manager.InstallRemotePackage("/data/test.apk"); + } Assert.Equal(2, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install \"/data/test.apk\"", adbClient.ReceivedCommands[1]); adbClient.ReceivedCommands.Clear(); - manager.InstallRemotePackage("/data/test.apk", "-r", "-t"); + + using (EventTestHost eventTestHost = new(manager, PackageInstallProgressState.Installing)) + { + manager.InstallRemotePackage("/data/test.apk", "-r", "-t"); + } Assert.Single(adbClient.ReceivedCommands); Assert.Equal("shell:pm install -r -t \"/data/test.apk\"", adbClient.ReceivedCommands[0]); @@ -77,7 +85,16 @@ public void InstallPackageTest() }; PackageManager manager = new(adbClient, device, (c, d) => syncService); - manager.InstallPackage("Assets/test.txt"); + + using (EventTestHost eventTestHost = new( + manager, + PackageInstallProgressState.Uploading, + PackageInstallProgressState.Installing, + PackageInstallProgressState.PostInstall, + PackageInstallProgressState.Finished)) + { + manager.InstallPackage("Assets/test.txt"); + } Assert.Equal(3, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[1]); @@ -108,7 +125,15 @@ public void InstallMultipleRemotePackageTest() }; PackageManager manager = new(adbClient, device); - manager.InstallMultipleRemotePackage("/data/base.apk", ["/data/split-dpi.apk", "/data/split-abi.apk"]); + + using (EventTestHost eventTestHost = new( + manager, + PackageInstallProgressState.CreateSession, + PackageInstallProgressState.WriteSession, + PackageInstallProgressState.Installing)) + { + manager.InstallMultipleRemotePackage("/data/base.apk", ["/data/split-dpi.apk", "/data/split-abi.apk"]); + } Assert.Equal(6, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create", adbClient.ReceivedCommands[1]); @@ -118,7 +143,16 @@ public void InstallMultipleRemotePackageTest() Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[5]); adbClient.ReceivedCommands.Clear(); - manager.InstallMultipleRemotePackage("/data/base.apk", ["/data/split-dpi.apk", "/data/split-abi.apk"], "-r", "-t"); + + + using (EventTestHost eventTestHost = new( + manager, + PackageInstallProgressState.CreateSession, + PackageInstallProgressState.WriteSession, + PackageInstallProgressState.Installing)) + { + manager.InstallMultipleRemotePackage("/data/base.apk", ["/data/split-dpi.apk", "/data/split-abi.apk"], "-r", "-t"); + } Assert.Equal(5, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create -r -t", adbClient.ReceivedCommands[0]); @@ -128,7 +162,15 @@ public void InstallMultipleRemotePackageTest() Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[4]); adbClient.ReceivedCommands.Clear(); - manager.InstallMultipleRemotePackage(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms"); + + using (EventTestHost eventTestHost = new( + manager, + PackageInstallProgressState.CreateSession, + PackageInstallProgressState.WriteSession, + PackageInstallProgressState.Installing)) + { + manager.InstallMultipleRemotePackage(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms"); + } Assert.Equal(4, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[0]); @@ -137,7 +179,15 @@ public void InstallMultipleRemotePackageTest() Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[3]); adbClient.ReceivedCommands.Clear(); - manager.InstallMultipleRemotePackage(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms", "-r", "-t"); + + using (EventTestHost eventTestHost = new( + manager, + PackageInstallProgressState.CreateSession, + PackageInstallProgressState.WriteSession, + PackageInstallProgressState.Installing)) + { + manager.InstallMultipleRemotePackage(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms", "-r", "-t"); + } Assert.Equal(4, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create -p com.google.android.gms -r -t", adbClient.ReceivedCommands[0]); @@ -170,7 +220,18 @@ public void InstallMultiplePackageTest() }; PackageManager manager = new(adbClient, device, (c, d) => syncService); - manager.InstallMultiplePackage("Assets/test.txt", ["Assets/gapps.txt", "Assets/logcat.bin"]); + + using (EventTestHost eventTestHost = new( + manager, + PackageInstallProgressState.Uploading, + PackageInstallProgressState.CreateSession, + PackageInstallProgressState.WriteSession, + PackageInstallProgressState.Installing, + PackageInstallProgressState.PostInstall, + PackageInstallProgressState.Finished)) + { + manager.InstallMultiplePackage("Assets/test.txt", ["Assets/gapps.txt", "Assets/logcat.bin"]); + } Assert.Equal(9, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create", adbClient.ReceivedCommands[1]); @@ -189,7 +250,18 @@ public void InstallMultiplePackageTest() syncService.UploadedFiles.Clear(); adbClient.ReceivedCommands.Clear(); - manager.InstallMultiplePackage(["Assets/gapps.txt", "Assets/logcat.bin"], "com.google.android.gms"); + + using (EventTestHost eventTestHost = new( + manager, + PackageInstallProgressState.Uploading, + PackageInstallProgressState.CreateSession, + PackageInstallProgressState.WriteSession, + PackageInstallProgressState.Installing, + PackageInstallProgressState.PostInstall, + PackageInstallProgressState.Finished)) + { + manager.InstallMultiplePackage(["Assets/gapps.txt", "Assets/logcat.bin"], "com.google.android.gms"); + } Assert.Equal(6, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[0]); @@ -238,5 +310,68 @@ public void GetPackageVersionInfoTest() Assert.Equal(11062448, versionInfo.VersionCode); Assert.Equal("11.0.62 (448-160311229)", versionInfo.VersionName); } + + private class EventTestHost : IDisposable + { + private readonly PackageManager manager; + private readonly PackageInstallProgressState[] states; + + private PackageInstallProgressState? state; + private int packageFinished; + private int packageRequired; + private double uploadProgress; + + private int step = 0; + + public EventTestHost(PackageManager manager, params PackageInstallProgressState[] states) + { + this.states = states; + this.manager = manager; + manager.InstallProgressChanged += OnInstallProgressChanged; + } + + public void OnInstallProgressChanged(object sender, InstallProgressEventArgs args) + { + if (args.State == state) + { + Assert.True(uploadProgress <= args.UploadProgress, $"{nameof(args.UploadProgress)}: {args.UploadProgress} is less than {uploadProgress}."); + Assert.True(packageFinished <= args.PackageFinished, $"{nameof(args.PackageFinished)}: {args.PackageFinished} is less than {packageFinished}."); + } + else + { + Assert.Equal(states[step++], args.State); + } + + if (args.State is + PackageInstallProgressState.CreateSession + or PackageInstallProgressState.Installing + or PackageInstallProgressState.Finished) + { + Assert.Equal(0, args.UploadProgress); + Assert.Equal(0, args.PackageRequired); + Assert.Equal(0, args.PackageFinished); + } + else + { + if (packageRequired == 0) + { + packageRequired = args.PackageRequired; + } + else + { + Assert.Equal(packageRequired, args.PackageRequired); + } + } + + state = args.State; + packageFinished = args.PackageFinished; + uploadProgress = args.UploadProgress; + } + + public void Dispose() + { + manager.InstallProgressChanged -= OnInstallProgressChanged; + } + } } } diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs index 0ba679ae..5b78f26f 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs @@ -84,9 +84,11 @@ public static async Task PullAsync(this IAdbClient client, DeviceData device, using ISyncService service = Factories.SyncServiceFactory(client, device); if (syncProgressEventHandler != null) { + service.SyncProgressChanged -= syncProgressEventHandler; service.SyncProgressChanged += syncProgressEventHandler; } await service.PullAsync(remotePath, stream, progress, cancellationToken).ConfigureAwait(false); + service.SyncProgressChanged -= syncProgressEventHandler; } /// @@ -110,9 +112,11 @@ public static async Task PushAsync(this IAdbClient client, DeviceData device, using ISyncService service = Factories.SyncServiceFactory(client, device); if (syncProgressEventHandler != null) { + service.SyncProgressChanged -= syncProgressEventHandler; service.SyncProgressChanged += syncProgressEventHandler; } await service.PushAsync(stream, remotePath, permissions, timestamp, progress, cancellationToken).ConfigureAwait(false); + service.SyncProgressChanged -= syncProgressEventHandler; } /// diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs index 1a7d2b72..450e5b86 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs @@ -80,9 +80,11 @@ public static void Pull(this IAdbClient client, DeviceData device, using ISyncService service = Factories.SyncServiceFactory(client, device); if (syncProgressEventHandler != null) { + service.SyncProgressChanged -= syncProgressEventHandler; service.SyncProgressChanged += syncProgressEventHandler; } service.Pull(remotePath, stream, progress, in isCancelled); + service.SyncProgressChanged -= syncProgressEventHandler; } /// @@ -106,9 +108,11 @@ public static void Push(this IAdbClient client, DeviceData device, using ISyncService service = Factories.SyncServiceFactory(client, device); if (syncProgressEventHandler != null) { + service.SyncProgressChanged -= syncProgressEventHandler; service.SyncProgressChanged += syncProgressEventHandler; } service.Push(stream, remotePath, permissions, timestamp, progress, in isCancelled); + service.SyncProgressChanged -= syncProgressEventHandler; } /// diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/InstallProgressEventArgs.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/InstallProgressEventArgs.cs index c4c533e3..e572ea14 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/InstallProgressEventArgs.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/InstallProgressEventArgs.cs @@ -33,7 +33,7 @@ public class InstallProgressEventArgs(PackageInstallProgressState state) : Event public int PackageRequired { get; init; } /// - /// Upload percentage completed. + /// Upload percentage (from to ) completed. /// Used only in state. /// public double UploadProgress { get; init; } @@ -51,11 +51,13 @@ public InstallProgressEventArgs(int packageUploaded, int packageRequired, double /// /// Initializes a new instance of the class. - /// Which is used for and state. + /// Which is used for + /// and + /// state. /// - public InstallProgressEventArgs(int packageCleaned, int packageRequired, PackageInstallProgressState state) : this(state) + public InstallProgressEventArgs(int packageFinished, int packageRequired, PackageInstallProgressState state) : this(state) { - PackageFinished = packageCleaned; + PackageFinished = packageFinished; PackageRequired = packageRequired; } } diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs index 18469a07..feb5aa9f 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs @@ -58,6 +58,9 @@ public virtual async Task InstallPackageAsync(string packageFilePath, Cancellati { ValidateDevice(); + void OnSyncProgressChanged(string? sender, SyncProgressChangedEventArgs args) => + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(sender is null ? 1 : 0, 1, args.ProgressPercentage)); + string remoteFilePath = await SyncPackageToDeviceAsync(packageFilePath, OnSyncProgressChanged, cancellationToken).ConfigureAwait(false); await InstallRemotePackageAsync(remoteFilePath, cancellationToken, arguments).ConfigureAwait(false); @@ -67,9 +70,6 @@ public virtual async Task InstallPackageAsync(string packageFilePath, Cancellati InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(1, 1, PackageInstallProgressState.PostInstall)); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.Finished)); - - void OnSyncProgressChanged(object sender, SyncProgressChangedEventArgs args) => - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(sender is true ? 1 : 0, 1, args.ProgressPercentage)); } /// @@ -123,7 +123,7 @@ public virtual async Task InstallRemotePackageAsync(string remoteFilePath, Cance /// The absolute split app file system paths to file on local host to install. /// The arguments to pass to pm install-create. /// A which represents the asynchronous operation. - public Task InstallMultiplePackageAsync(string basePackageFilePath, ICollection splitPackageFilePaths, params string[] arguments) => + public Task InstallMultiplePackageAsync(string basePackageFilePath, IEnumerable splitPackageFilePaths, params string[] arguments) => InstallMultiplePackageAsync(basePackageFilePath, splitPackageFilePaths, default, arguments); /// @@ -134,39 +134,47 @@ public Task InstallMultiplePackageAsync(string basePackageFilePath, ICollection< /// A which can be used to cancel the asynchronous operation. /// The arguments to pass to pm install-create. /// A which represents the asynchronous operation. - public virtual async Task InstallMultiplePackageAsync(string basePackageFilePath, ICollection splitPackageFilePaths, CancellationToken cancellationToken, params string[] arguments) + public virtual async Task InstallMultiplePackageAsync(string basePackageFilePath, IEnumerable splitPackageFilePaths, CancellationToken cancellationToken, params string[] arguments) { ValidateDevice(); - void OnMainSyncProgressChanged(object sender, SyncProgressChangedEventArgs args) => - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(sender is true ? 1 : 0, splitPackageFilePaths.Count + 1, args.ProgressPercentage / 2)); + int splitPackageFileCount = splitPackageFilePaths.Count(); + + void OnMainSyncProgressChanged(string? sender, SyncProgressChangedEventArgs args) => + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(sender is null ? 1 : 0, splitPackageFileCount + 1, args.ProgressPercentage / 2)); string baseRemoteFilePath = await SyncPackageToDeviceAsync(basePackageFilePath, OnMainSyncProgressChanged, cancellationToken).ConfigureAwait(false); - Dictionary progress = new(splitPackageFilePaths.Count); - void OnSplitSyncProgressChanged(object sender, SyncProgressChangedEventArgs args) + int progressCount = 1; + Dictionary progress = new(splitPackageFileCount); + void OnSplitSyncProgressChanged(string? sender, SyncProgressChangedEventArgs args) { lock (progress) { - int count = 1; - if (sender is string path) + if (sender is null) { - progress[path] = args.ProgressPercentage; + progressCount++; } - else if (sender is true) + else if (sender is string path) { - count++; + // Note: The progress may be less than the previous progress when async. + if (progress.TryGetValue(path, out double oldValue) + && oldValue > args.ProgressPercentage) + { + return; + } + progress[path] = args.ProgressPercentage; } - double present = progress.Values.Select(x => x / splitPackageFilePaths.Count / 2).Sum(); - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitPackageFilePaths.Count + 1, present)); + double present = (progress.Values.Select(x => x / splitPackageFileCount).Sum() + 100) / 2; + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(progressCount, splitPackageFileCount + 1, present)); } } string[] splitRemoteFilePaths = await splitPackageFilePaths.Select(x => SyncPackageToDeviceAsync(x, OnSplitSyncProgressChanged, cancellationToken)).ToArrayAsync().ConfigureAwait(false); - if (splitRemoteFilePaths.Length < splitPackageFilePaths.Count) + if (splitRemoteFilePaths.Length < splitPackageFileCount) { - throw new PackageInstallationException($"{nameof(SyncPackageToDeviceAsync)} failed. {splitPackageFilePaths.Count} should process but only {splitRemoteFilePaths.Length} processed."); + throw new PackageInstallationException($"{nameof(SyncPackageToDeviceAsync)} failed. {splitPackageFileCount} should process but only {splitRemoteFilePaths.Length} processed."); } await InstallMultipleRemotePackageAsync(baseRemoteFilePath, splitRemoteFilePaths, cancellationToken, arguments); @@ -198,7 +206,7 @@ await Extensions.WhenAll(splitRemoteFilePaths.Select(async x => /// The absolute package name of the base app. /// The arguments to pass to pm install-create. /// A which represents the asynchronous operation. - public Task InstallMultiplePackageAsync(ICollection splitPackageFilePaths, string packageName, params string[] arguments) => + public Task InstallMultiplePackageAsync(IEnumerable splitPackageFilePaths, string packageName, params string[] arguments) => InstallMultiplePackageAsync(splitPackageFilePaths, packageName, default, arguments); /// @@ -209,34 +217,42 @@ public Task InstallMultiplePackageAsync(ICollection splitPackageFilePath /// A which can be used to cancel the asynchronous operation. /// The arguments to pass to pm install-create. /// A which represents the asynchronous operation. - public virtual async Task InstallMultiplePackageAsync(ICollection splitPackageFilePaths, string packageName, CancellationToken cancellationToken, params string[] arguments) + public virtual async Task InstallMultiplePackageAsync(IEnumerable splitPackageFilePaths, string packageName, CancellationToken cancellationToken, params string[] arguments) { ValidateDevice(); - Dictionary progress = new(splitPackageFilePaths.Count); - void OnSyncProgressChanged(object sender, SyncProgressChangedEventArgs args) + int splitPackageFileCount = splitPackageFilePaths.Count(); + + int progressCount = 0; + Dictionary progress = new(splitPackageFileCount); + void OnSyncProgressChanged(string? sender, SyncProgressChangedEventArgs args) { lock (progress) { - int count = 1; - if (sender is string path) + if (sender is null) { - progress[path] = args.ProgressPercentage; + progressCount++; } - else if (sender is true) + else if (sender is string path) { - count++; + // Note: The progress may be less than the previous progress when async. + if (progress.TryGetValue(path, out double oldValue) + && oldValue > args.ProgressPercentage) + { + return; + } + progress[path] = args.ProgressPercentage; } - double present = progress.Values.Select(x => x / splitPackageFilePaths.Count).Sum(); - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitPackageFilePaths.Count, present)); + double present = progress.Values.Select(x => x / splitPackageFileCount).Sum(); + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(progressCount, splitPackageFileCount, present)); } } string[] splitRemoteFilePaths = await splitPackageFilePaths.Select(x => SyncPackageToDeviceAsync(x, OnSyncProgressChanged, cancellationToken)).ToArrayAsync().ConfigureAwait(false); - if (splitRemoteFilePaths.Length < splitPackageFilePaths.Count) + if (splitRemoteFilePaths.Length < splitPackageFileCount) { - throw new PackageInstallationException($"{nameof(SyncPackageToDeviceAsync)} failed. {splitPackageFilePaths.Count} should process but only {splitRemoteFilePaths.Length} processed."); + throw new PackageInstallationException($"{nameof(SyncPackageToDeviceAsync)} failed. {splitPackageFileCount} should process but only {splitRemoteFilePaths.Length} processed."); } await InstallMultipleRemotePackageAsync(splitRemoteFilePaths, packageName, cancellationToken, arguments); @@ -265,7 +281,7 @@ await Extensions.WhenAll(splitRemoteFilePaths.Select(async x => /// The absolute split app file paths to package file on device. /// The arguments to pass to pm install-create. /// A which represents the asynchronous operation. - public Task InstallMultipleRemotePackageAsync(string baseRemoteFilePath, ICollection splitRemoteFilePaths, params string[] arguments) => + public Task InstallMultipleRemotePackageAsync(string baseRemoteFilePath, IEnumerable splitRemoteFilePaths, params string[] arguments) => InstallMultipleRemotePackageAsync(baseRemoteFilePath, splitRemoteFilePaths, default, arguments); /// @@ -276,7 +292,7 @@ public Task InstallMultipleRemotePackageAsync(string baseRemoteFilePath, ICollec /// A which can be used to cancel the asynchronous operation. /// The arguments to pass to pm install-create. /// A which represents the asynchronous operation. - public virtual async Task InstallMultipleRemotePackageAsync(string baseRemoteFilePath, ICollection splitRemoteFilePaths, CancellationToken cancellationToken, params string[] arguments) + public virtual async Task InstallMultipleRemotePackageAsync(string baseRemoteFilePath, IEnumerable splitRemoteFilePaths, CancellationToken cancellationToken, params string[] arguments) { InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.CreateSession)); @@ -284,22 +300,24 @@ public virtual async Task InstallMultipleRemotePackageAsync(string baseRemoteFil string session = await CreateInstallSessionAsync(cancellationToken: cancellationToken, arguments: arguments).ConfigureAwait(false); - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFilePaths.Count + 1, PackageInstallProgressState.WriteSession)); + int splitRemoteFileCount = splitRemoteFilePaths.Count(); + + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFileCount + 1, PackageInstallProgressState.WriteSession)); await WriteInstallSessionAsync(session, "base", baseRemoteFilePath, cancellationToken).ConfigureAwait(false); - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(1, splitRemoteFilePaths.Count + 1, PackageInstallProgressState.WriteSession)); + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(1, splitRemoteFileCount + 1, PackageInstallProgressState.WriteSession)); int count = 0; await Extensions.WhenAll(splitRemoteFilePaths.Select(async (splitRemoteFilePath) => { await WriteInstallSessionAsync(session, $"split{count++}", splitRemoteFilePath, cancellationToken).ConfigureAwait(false); - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitRemoteFilePaths.Count + 1, PackageInstallProgressState.WriteSession)); + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitRemoteFileCount + 1, PackageInstallProgressState.WriteSession)); })).ConfigureAwait(false); - if (count < splitRemoteFilePaths.Count) + if (count < splitRemoteFileCount) { - throw new PackageInstallationException($"{nameof(WriteInstallSessionAsync)} failed. {splitRemoteFilePaths.Count} should process but only {count} processed."); + throw new PackageInstallationException($"{nameof(WriteInstallSessionAsync)} failed. {splitRemoteFileCount} should process but only {count} processed."); } InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.Installing)); @@ -320,7 +338,7 @@ await Extensions.WhenAll(splitRemoteFilePaths.Select(async (splitRemoteFilePath) /// The absolute package name of the base app. /// The arguments to pass to pm install-create. /// A which represents the asynchronous operation. - public Task InstallMultipleRemotePackageAsync(ICollection splitRemoteFilePaths, string packageName, params string[] arguments) => + public Task InstallMultipleRemotePackageAsync(IEnumerable splitRemoteFilePaths, string packageName, params string[] arguments) => InstallMultipleRemotePackageAsync(splitRemoteFilePaths, packageName, default, arguments); /// @@ -331,7 +349,7 @@ public Task InstallMultipleRemotePackageAsync(ICollection splitRemoteFil /// A which can be used to cancel the asynchronous operation. /// The arguments to pass to pm install-create. /// A which represents the asynchronous operation. - public virtual async Task InstallMultipleRemotePackageAsync(ICollection splitRemoteFilePaths, string packageName, CancellationToken cancellationToken, params string[] arguments) + public virtual async Task InstallMultipleRemotePackageAsync(IEnumerable splitRemoteFilePaths, string packageName, CancellationToken cancellationToken, params string[] arguments) { InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.CreateSession)); @@ -339,18 +357,20 @@ public virtual async Task InstallMultipleRemotePackageAsync(ICollection string session = await CreateInstallSessionAsync(packageName, cancellationToken, arguments).ConfigureAwait(false); - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFilePaths.Count, PackageInstallProgressState.WriteSession)); + int splitRemoteFileCount = splitRemoteFilePaths.Count(); + + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFileCount, PackageInstallProgressState.WriteSession)); int count = 0; await Extensions.WhenAll(splitRemoteFilePaths.Select(async (splitRemoteFilePath) => { await WriteInstallSessionAsync(session, $"split{count++}", splitRemoteFilePath, cancellationToken).ConfigureAwait(false); - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitRemoteFilePaths.Count, PackageInstallProgressState.WriteSession)); + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitRemoteFileCount, PackageInstallProgressState.WriteSession)); })).ConfigureAwait(false); - if (count < splitRemoteFilePaths.Count) + if (count < splitRemoteFileCount) { - throw new PackageInstallationException($"{nameof(WriteInstallSessionAsync)} failed. {splitRemoteFilePaths.Count} should process but only {count} processed."); + throw new PackageInstallationException($"{nameof(WriteInstallSessionAsync)} failed. {splitRemoteFileCount} should process but only {count} processed."); } InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.Installing)); @@ -428,9 +448,9 @@ public virtual async Task GetVersionInfoAsync(string packageName, C /// A which can be used to cancel the asynchronous operation. /// A which return the destination path on device for file. /// If fatal error occurred when pushing file. - protected virtual async Task SyncPackageToDeviceAsync(string localFilePath, Action? progress, CancellationToken cancellationToken = default) + protected virtual async Task SyncPackageToDeviceAsync(string localFilePath, Action? progress, CancellationToken cancellationToken = default) { - progress?.Invoke(localFilePath, new SyncProgressChangedEventArgs(0, 0)); + progress?.Invoke(localFilePath, new SyncProgressChangedEventArgs(0, 100)); ValidateDevice(); @@ -447,9 +467,12 @@ protected virtual async Task SyncPackageToDeviceAsync(string localFilePa using (ISyncService sync = syncServiceFactory(AdbClient, Device)) { + void OnSyncProgressChanged(object? sender, SyncProgressChangedEventArgs args) => progress!(localFilePath, args); + if (progress != null) { - sync.SyncProgressChanged += (sender, e) => progress(localFilePath, e); + sync.SyncProgressChanged -= OnSyncProgressChanged; + sync.SyncProgressChanged += OnSyncProgressChanged; } #if NETCOREAPP3_0_OR_GREATER @@ -461,6 +484,8 @@ protected virtual async Task SyncPackageToDeviceAsync(string localFilePa // As C# can't use octal, the octal literal 666 (rw-Permission) is here converted to decimal (438) await sync.PushAsync(stream, remoteFilePath, 438, File.GetLastWriteTime(localFilePath), null, cancellationToken).ConfigureAwait(false); + + sync.SyncProgressChanged -= OnSyncProgressChanged; } return remoteFilePath; @@ -472,7 +497,7 @@ protected virtual async Task SyncPackageToDeviceAsync(string localFilePa } finally { - progress?.Invoke(true, new SyncProgressChangedEventArgs(0, 0)); + progress?.Invoke(null, new SyncProgressChangedEventArgs(100, 100)); } } diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs index 3acbc9c5..72331e96 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs @@ -148,6 +148,9 @@ public virtual void InstallPackage(string packageFilePath, params string[] argum { ValidateDevice(); + void OnSyncProgressChanged(string? sender, SyncProgressChangedEventArgs args) => + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(sender is null ? 1 : 0, 1, args.ProgressPercentage)); + string remoteFilePath = SyncPackageToDevice(packageFilePath, OnSyncProgressChanged); InstallRemotePackage(remoteFilePath, arguments); @@ -157,9 +160,6 @@ public virtual void InstallPackage(string packageFilePath, params string[] argum InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(1, 1, PackageInstallProgressState.PostInstall)); InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.Finished)); - - void OnSyncProgressChanged(object sender, SyncProgressChangedEventArgs args) => - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(sender is true ? 1 : 0, 1, args.ProgressPercentage)); } /// @@ -201,44 +201,47 @@ public virtual void InstallRemotePackage(string remoteFilePath, params string[] /// The absolute base app file system path to file on local host to install. /// The absolute split app file system paths to file on local host to install. /// The arguments to pass to pm install-create. - public virtual void InstallMultiplePackage(string basePackageFilePath, IList splitPackageFilePaths, params string[] arguments) + public virtual void InstallMultiplePackage(string basePackageFilePath, IEnumerable splitPackageFilePaths, params string[] arguments) { ValidateDevice(); - void OnMainSyncProgressChanged(object sender, SyncProgressChangedEventArgs args) => - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(sender is true ? 1 : 0, splitPackageFilePaths.Count + 1, args.ProgressPercentage / 2)); + int splitPackageFileCount = splitPackageFilePaths.Count(); + + void OnMainSyncProgressChanged(string? sender, SyncProgressChangedEventArgs args) => + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(sender is null ? 1 : 0, splitPackageFileCount + 1, args.ProgressPercentage / 2)); string baseRemoteFilePath = SyncPackageToDevice(basePackageFilePath, OnMainSyncProgressChanged); - Dictionary progress = new(splitPackageFilePaths.Count); - void OnSplitSyncProgressChanged(object sender, SyncProgressChangedEventArgs args) + int progressCount = 1; + Dictionary progress = new(splitPackageFileCount); + void OnSplitSyncProgressChanged(string? sender, SyncProgressChangedEventArgs args) { lock (progress) { - int count = 1; - if (sender is string path) + if (sender is null) { - progress[path] = args.ProgressPercentage; + progressCount++; } - else if (sender is true) + else if (sender is string path) { - count++; + progress[path] = args.ProgressPercentage; } - double present = progress.Values.Select(x => x / splitPackageFilePaths.Count / 2).Sum(); - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitPackageFilePaths.Count + 1, present)); + double present = (progress.Values.Select(x => x / splitPackageFileCount).Sum() + 100) / 2; + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(progressCount, splitPackageFileCount + 1, present)); } } - string[] splitRemoteFilePaths = new string[splitPackageFilePaths.Count]; - for (int i = 0; i < splitPackageFilePaths.Count; i++) + int i = 0; + string[] splitRemoteFilePaths = new string[splitPackageFileCount]; + foreach (string splitPackageFilePath in splitPackageFilePaths) { - splitRemoteFilePaths[i] = SyncPackageToDevice(splitPackageFilePaths[i], OnSplitSyncProgressChanged); + splitRemoteFilePaths[i++] = SyncPackageToDevice(splitPackageFilePath, OnSplitSyncProgressChanged); } InstallMultipleRemotePackage(baseRemoteFilePath, splitRemoteFilePaths, arguments); - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFilePaths.Length + 1, PackageInstallProgressState.PostInstall)); int count = 0; + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFilePaths.Length + 1, PackageInstallProgressState.PostInstall)); foreach (string splitRemoteFilePath in splitRemoteFilePaths) { RemoveRemotePackage(splitRemoteFilePath); @@ -257,33 +260,36 @@ void OnSplitSyncProgressChanged(object sender, SyncProgressChangedEventArgs args /// The absolute split app file system paths to file on local host to install. /// The absolute package name of the base app. /// The arguments to pass to pm install-create. - public virtual void InstallMultiplePackage(IList splitPackageFilePaths, string packageName, params string[] arguments) + public virtual void InstallMultiplePackage(IEnumerable splitPackageFilePaths, string packageName, params string[] arguments) { ValidateDevice(); - Dictionary progress = new(splitPackageFilePaths.Count); - void OnSyncProgressChanged(object sender, SyncProgressChangedEventArgs args) + int splitPackageFileCount = splitPackageFilePaths.Count(); + + int progressCount = 0; + Dictionary progress = new(splitPackageFileCount); + void OnSyncProgressChanged(string? sender, SyncProgressChangedEventArgs args) { lock (progress) { - int count = 1; - if (sender is string path) + if (sender is null) { - progress[path] = args.ProgressPercentage; + progressCount++; } - else if (sender is true) + else if (sender is string path) { - count++; + progress[path] = args.ProgressPercentage; } - double present = progress.Values.Select(x => x / splitPackageFilePaths.Count).Sum(); - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitPackageFilePaths.Count, present)); + double present = progress.Values.Select(x => x / splitPackageFileCount).Sum(); + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(progressCount, splitPackageFileCount, present)); } } - string[] splitRemoteFilePaths = new string[splitPackageFilePaths.Count]; - for (int i = 0; i < splitPackageFilePaths.Count; i++) + int i = 0; + string[] splitRemoteFilePaths = new string[splitPackageFileCount]; + foreach (string splitPackageFilePath in splitPackageFilePaths) { - splitRemoteFilePaths[i] = SyncPackageToDevice(splitPackageFilePaths[i], OnSyncProgressChanged); + splitRemoteFilePaths[i++] = SyncPackageToDevice(splitPackageFilePath, OnSyncProgressChanged); } InstallMultipleRemotePackage(splitRemoteFilePaths, packageName, arguments); @@ -305,7 +311,7 @@ void OnSyncProgressChanged(object sender, SyncProgressChangedEventArgs args) /// The absolute base app file path to package file on device. /// The absolute split app file paths to package file on device. /// The arguments to pass to pm install-create. - public virtual void InstallMultipleRemotePackage(string baseRemoteFilePath, ICollection splitRemoteFilePaths, params string[] arguments) + public virtual void InstallMultipleRemotePackage(string baseRemoteFilePath, IEnumerable splitRemoteFilePaths, params string[] arguments) { InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.CreateSession)); @@ -313,17 +319,19 @@ public virtual void InstallMultipleRemotePackage(string baseRemoteFilePath, ICol string session = CreateInstallSession(arguments: arguments); - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFilePaths.Count + 1, PackageInstallProgressState.WriteSession)); + int splitRemoteFileCount = splitRemoteFilePaths.Count(); + + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFileCount + 1, PackageInstallProgressState.WriteSession)); WriteInstallSession(session, "base", baseRemoteFilePath); - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(1, splitRemoteFilePaths.Count + 1, PackageInstallProgressState.WriteSession)); + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(1, splitRemoteFileCount + 1, PackageInstallProgressState.WriteSession)); int count = 0; foreach (string splitRemoteFilePath in splitRemoteFilePaths) { WriteInstallSession(session, $"split{count++}", splitRemoteFilePath); - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitRemoteFilePaths.Count + 1, PackageInstallProgressState.WriteSession)); + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitRemoteFileCount + 1, PackageInstallProgressState.WriteSession)); } InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.Installing)); @@ -343,7 +351,7 @@ public virtual void InstallMultipleRemotePackage(string baseRemoteFilePath, ICol /// The absolute split app file paths to package file on device. /// The absolute package name of the base app. /// The arguments to pass to pm install-create. - public virtual void InstallMultipleRemotePackage(ICollection splitRemoteFilePaths, string packageName, params string[] arguments) + public virtual void InstallMultipleRemotePackage(IEnumerable splitRemoteFilePaths, string packageName, params string[] arguments) { InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.CreateSession)); @@ -351,13 +359,15 @@ public virtual void InstallMultipleRemotePackage(ICollection splitRemote string session = CreateInstallSession(packageName, arguments); - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFilePaths.Count, PackageInstallProgressState.WriteSession)); + int splitRemoteFileCount = splitRemoteFilePaths.Count(); + + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(0, splitRemoteFileCount, PackageInstallProgressState.WriteSession)); int count = 0; foreach (string splitRemoteFilePath in splitRemoteFilePaths) { WriteInstallSession(session, $"split{count++}", splitRemoteFilePath); - InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitRemoteFilePaths.Count, PackageInstallProgressState.WriteSession)); + InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(count, splitRemoteFileCount, PackageInstallProgressState.WriteSession)); } InstallProgressChanged?.Invoke(this, new InstallProgressEventArgs(PackageInstallProgressState.Installing)); @@ -433,9 +443,9 @@ protected void ValidateDevice() /// An optional parameter which, when specified, returns progress notifications. /// Destination path on device for file. /// If fatal error occurred when pushing file. - protected virtual string SyncPackageToDevice(string localFilePath, Action? progress) + protected virtual string SyncPackageToDevice(string localFilePath, Action? progress) { - progress?.Invoke(localFilePath, new SyncProgressChangedEventArgs(0, 0)); + progress?.Invoke(localFilePath, new SyncProgressChangedEventArgs(0, 100)); ValidateDevice(); @@ -452,9 +462,12 @@ protected virtual string SyncPackageToDevice(string localFilePath, Action progress!(localFilePath, args); + if (progress != null) { - sync.SyncProgressChanged += (sender, e) => progress(localFilePath, e); + sync.SyncProgressChanged -= OnSyncProgressChanged; + sync.SyncProgressChanged += OnSyncProgressChanged; } using FileStream stream = File.OpenRead(localFilePath); @@ -463,6 +476,8 @@ protected virtual string SyncPackageToDevice(string localFilePath, Action 8; /// - public readonly byte this[int index] - { - get - { - if (index < 0 || index >= Count) - { - throw new IndexOutOfRangeException("Index was out of range. Must be non-negative and less than the size of the collection."); - } - - return index switch + public readonly byte this[int index] => + index < 0 || index >= Count + ? throw new IndexOutOfRangeException("Index was out of range. Must be non-negative and less than the size of the collection.") + : index switch { 0 => (byte)Offset, 1 => (byte)(Offset >> 8), @@ -64,8 +58,6 @@ public readonly byte this[int index] _ => throw new IndexOutOfRangeException("Index was out of range. Must be non-negative and less than the size of the collection.") }; - } - } /// /// Deconstruct the struct. diff --git a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs index 2885b914..7a967b29 100644 --- a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs +++ b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs @@ -78,13 +78,13 @@ public FramebufferHeader(byte[] data) Offset = ReadUInt32(ref data), Length = ReadUInt32(ref data) }; - + Blue = new ColorData { Offset = ReadUInt32(ref data), Length = ReadUInt32(ref data) }; - + Green = new ColorData { Offset = ReadUInt32(ref data), diff --git a/AdvancedSharpAdbClient/Models/SyncProgressChangedEventArgs.cs b/AdvancedSharpAdbClient/Models/SyncProgressChangedEventArgs.cs index d869a58b..68f2dbed 100644 --- a/AdvancedSharpAdbClient/Models/SyncProgressChangedEventArgs.cs +++ b/AdvancedSharpAdbClient/Models/SyncProgressChangedEventArgs.cs @@ -9,13 +9,13 @@ namespace AdvancedSharpAdbClient.Models /// /// Provides data for the event. /// - public class SyncProgressChangedEventArgs(long received, long total) : EventArgs + public class SyncProgressChangedEventArgs(long current, long total) : EventArgs { /// /// Gets the number of bytes sync to the local computer. /// /// An representing the number of sync bytes. - public long ReceivedBytesSize { get; } = received; + public long ReceivedBytesSize { get; } = current; /// /// Gets the total number of bytes for the sync operation. @@ -24,7 +24,7 @@ public class SyncProgressChangedEventArgs(long received, long total) : EventArgs public long TotalBytesToReceive { get; } = total; /// - /// Gets the number of progress percentage for the sync operation. + /// Gets the number of progress percentage (from to ) for the sync operation. /// public double ProgressPercentage => TotalBytesToReceive != 0L ? ReceivedBytesSize * 100.0 / TotalBytesToReceive : 0.0; } From 8d33bb312875c9e87f68125be8e51f842606c8c9 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Wed, 8 Nov 2023 14:23:14 +0800 Subject: [PATCH 61/66] Update xunit to 2.6.1 --- .../AdvancedSharpAdbClient.Tests.csproj | 2 +- .../Models/FramebufferHeaderTests.cs | 7 ++++--- AdvancedSharpAdbClient/AdbSocket.Async.cs | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj b/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj index 3ac4d63b..5b447117 100644 --- a/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj +++ b/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj @@ -13,7 +13,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/AdvancedSharpAdbClient.Tests/Models/FramebufferHeaderTests.cs b/AdvancedSharpAdbClient.Tests/Models/FramebufferHeaderTests.cs index 3dead9d8..e1440868 100644 --- a/AdvancedSharpAdbClient.Tests/Models/FramebufferHeaderTests.cs +++ b/AdvancedSharpAdbClient.Tests/Models/FramebufferHeaderTests.cs @@ -1,4 +1,5 @@ -using System.Drawing; +using System; +using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Runtime.Versioning; @@ -38,7 +39,7 @@ public void ReadFramebufferTest() Assert.Equal(data[i], header[i]); } - Assert.Equal(data, [.. header]); + Assert.Equal(data.AsSpan(), [.. header]); } [Fact] @@ -68,7 +69,7 @@ public void ReadFramebufferV2Test() Assert.Equal(data[i], header[i]); } - Assert.Equal(data, [.. header]); + Assert.Equal(data.AsSpan(), [.. header]); } #if WINDOWS diff --git a/AdvancedSharpAdbClient/AdbSocket.Async.cs b/AdvancedSharpAdbClient/AdbSocket.Async.cs index 28566c4e..20dcf3c0 100644 --- a/AdvancedSharpAdbClient/AdbSocket.Async.cs +++ b/AdvancedSharpAdbClient/AdbSocket.Async.cs @@ -352,7 +352,7 @@ public virtual Task ReadAsync(byte[] data, CancellationToken cancellationTo /// Returns if all data was written; otherwise, . /// This uses the default time out value. #if HAS_BUFFERS - protected virtual async Task WriteAsync(Memory data, CancellationToken cancellationToken = default) + protected virtual async ValueTask WriteAsync(Memory data, CancellationToken cancellationToken = default) #else protected virtual async Task WriteAsync(byte[] data, CancellationToken cancellationToken = default) #endif From 36424aaf4a1ba631b3309ebe02ddfdb28cc694bd Mon Sep 17 00:00:00 2001 From: wherewhere Date: Fri, 10 Nov 2023 18:57:46 +0800 Subject: [PATCH 62/66] Reopen SyncService when GetDirectoryListing received 0000 #19 --- .../AdvancedSharpAdbClient.Tests.csproj | 2 +- .../Dummys/DummyAdbServer.cs | 2 +- .../Dummys/DummyAdbSocket.cs | 4 +- .../Dummys/DummySyncService.cs | 10 +- .../Dummys/DummyTcpSocket.cs | 12 +- .../Dummys/TracingAdbSocket.cs | 5 +- AdvancedSharpAdbClient/AdbClient.Async.cs | 148 ++++++++++------- AdvancedSharpAdbClient/AdbClient.cs | 153 ++++++++++-------- .../AdbCommandLineClient.Async.cs | 22 +-- .../AdbCommandLineClient.cs | 23 +-- AdvancedSharpAdbClient/AdbSocket.Async.cs | 12 +- AdvancedSharpAdbClient/AdbSocket.cs | 3 +- AdvancedSharpAdbClient/DeviceMonitor.Async.cs | 6 +- AdvancedSharpAdbClient/DeviceMonitor.cs | 2 +- .../Extensions/SyncCommandConverter.cs | 8 +- .../Interfaces/IAdbSocket.Async.cs | 6 +- .../Interfaces/IAdbSocket.cs | 3 +- .../Interfaces/ISyncService.Async.cs | 7 + .../Interfaces/ISyncService.cs | 5 + .../Interfaces/ITcpSocket.Async.cs | 6 +- .../Interfaces/ITcpSocket.cs | 3 +- AdvancedSharpAdbClient/Models/AdbResponse.cs | 11 ++ .../Models/Enums/SyncCommand.cs | 2 +- .../Models/FramebufferHeader.cs | 32 ++-- .../Extensions/ExceptionExtensions.cs | 2 +- AdvancedSharpAdbClient/SyncService.Async.cs | 74 +++++---- AdvancedSharpAdbClient/SyncService.cs | 57 ++++--- AdvancedSharpAdbClient/TcpSocket.Async.cs | 37 +++-- AdvancedSharpAdbClient/TcpSocket.cs | 14 +- Directory.Build.props | 5 - 30 files changed, 388 insertions(+), 288 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj b/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj index 5b447117..c11c4348 100644 --- a/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj +++ b/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj @@ -11,7 +11,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbServer.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbServer.cs index 3ea33ef6..95b5f5eb 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbServer.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbServer.cs @@ -79,7 +79,7 @@ public async Task StartServerAsync(string adbPath, bool resta public void StopServer() => Status = Status with { IsRunning = false }; /// - public async Task StopServerAsync(CancellationToken cancellationToken) + public async Task StopServerAsync(CancellationToken cancellationToken = default) { await Task.Yield(); StopServer(); diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs index 9f9dd95f..1d8593c8 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs @@ -276,9 +276,9 @@ public async Task SetDeviceAsync(DeviceData device, CancellationToken cancellati public void Close() => IsConnected = false; - public void Reconnect() => DidReconnect = true; + public void Reconnect(bool isForce = false) => DidReconnect = true; - public async ValueTask ReconnectAsync(CancellationToken cancellationToken = default) + public async ValueTask ReconnectAsync(bool isForce, CancellationToken cancellationToken = default) { await Task.Yield(); DidReconnect = true; diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs index 7690991b..fd1108ae 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs @@ -21,7 +21,7 @@ internal class DummySyncService : ISyncService public void Open() => IsOpen = true; - public async Task OpenAsync(CancellationToken cancellationToken) + public async Task OpenAsync(CancellationToken cancellationToken = default) { await Task.Yield(); IsOpen = true; @@ -69,6 +69,14 @@ public async Task PushAsync(Stream stream, string remotePath, int permissions, D } } + public void Reopen() => IsOpen = true; + + public async Task ReopenAsync(CancellationToken cancellationToken = default) + { + await Task.Yield(); + IsOpen = true; + } + #region Not Implemented IAsyncEnumerable ISyncService.GetDirectoryAsyncListing(string remotePath, CancellationToken cancellationToken) => throw new NotImplementedException(); diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyTcpSocket.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyTcpSocket.cs index ee28c764..28b64577 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyTcpSocket.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyTcpSocket.cs @@ -30,15 +30,15 @@ internal class DummyTcpSocket : ITcpSocket public void Connect(EndPoint endPoint) => Connected = true; - public async ValueTask ConnectAsync(EndPoint endPoint, CancellationToken cancellationToken) + public async ValueTask ConnectAsync(EndPoint endPoint, CancellationToken cancellationToken = default) { await Task.Yield(); Connected = true; } - public void Reconnect() => Connected = true; + public void Reconnect(bool isForce = false) => Connected = true; - public async ValueTask ReconnectAsync(CancellationToken cancellationToken) + public async ValueTask ReconnectAsync(bool isForce, CancellationToken cancellationToken = default) { await Task.Yield(); Connected = true; @@ -54,9 +54,9 @@ public async ValueTask ReconnectAsync(CancellationToken cancellationToken) public int Receive(Span buffer, SocketFlags socketFlags) => InputStream.Read(buffer); - public Task ReceiveAsync(byte[] buffer, int size, SocketFlags socketFlags, CancellationToken cancellationToken) => InputStream.ReadAsync(buffer, 0, size, cancellationToken); + public Task ReceiveAsync(byte[] buffer, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) => InputStream.ReadAsync(buffer, 0, size, cancellationToken); - public Task ReceiveAsync(byte[] buffer, int offset, int size, SocketFlags socketFlags, CancellationToken cancellationToken) => InputStream.ReadAsync(buffer, offset, size, cancellationToken); + public Task ReceiveAsync(byte[] buffer, int offset, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) => InputStream.ReadAsync(buffer, offset, size, cancellationToken); public ValueTask ReceiveAsync(Memory buffer, SocketFlags socketFlags, CancellationToken cancellationToken = default) => InputStream.ReadAsync(buffer, cancellationToken); @@ -78,7 +78,7 @@ public int Send(ReadOnlySpan buffer, SocketFlags socketFlags) return buffer.Length; } - public async Task SendAsync(byte[] buffer, int size, SocketFlags socketFlags, CancellationToken cancellationToken) + public async Task SendAsync(byte[] buffer, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) { await OutputStream.WriteAsync(buffer.AsMemory(0, size), cancellationToken).ConfigureAwait(false); return size; diff --git a/AdvancedSharpAdbClient.Tests/Dummys/TracingAdbSocket.cs b/AdvancedSharpAdbClient.Tests/Dummys/TracingAdbSocket.cs index 595ac507..918da2b2 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/TracingAdbSocket.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/TracingAdbSocket.cs @@ -130,7 +130,7 @@ public override string ReadSyncString() return value; } - public override async Task ReadStringAsync(CancellationToken cancellationToken) + public override async Task ReadStringAsync(CancellationToken cancellationToken = default) { string value = await base.ReadStringAsync(cancellationToken); ResponseMessages.Enqueue(value); @@ -168,10 +168,9 @@ public override SyncCommand ReadSyncResponse() return response; } - public override void Reconnect() + public override void Reconnect(bool isForce = false) { base.Reconnect(); - DidReconnect = true; } } diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index 17d7f2d2..37ce6e95 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -24,8 +24,9 @@ public partial class AdbClient public async Task GetAdbVersionAsync(CancellationToken cancellationToken = default) { using IAdbSocket socket = adbSocketFactory(EndPoint); + await socket.SendAdbRequestAsync("host:version", cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); string version = await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); return int.Parse(version, NumberStyles.HexNumber); @@ -45,12 +46,12 @@ public async Task KillAdbAsync(CancellationToken cancellationToken = default) public async Task> GetDevicesAsync(CancellationToken cancellationToken = default) { using IAdbSocket socket = adbSocketFactory(EndPoint); + await socket.SendAdbRequestAsync("host:devices-l", cancellationToken).ConfigureAwait(false); _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); string reply = await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); string[] data = reply.Split(separator, StringSplitOptions.RemoveEmptyEntries); - return data.Select(x => new DeviceData(x)); } @@ -65,8 +66,8 @@ public async Task CreateForwardAsync(DeviceData device, string local, strin await socket.SendAdbRequestAsync($"host-serial:{device.Serial}:forward:{rebind}{local};{remote}", cancellationToken).ConfigureAwait(false); _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); - string portString = await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); + string portString = await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); return portString != null && int.TryParse(portString, out int port) ? port : 0; } @@ -76,15 +77,15 @@ public async Task CreateReverseForwardAsync(DeviceData device, string remot EnsureDevice(device); using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); + await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); string rebind = allowRebind ? string.Empty : "norebind:"; await socket.SendAdbRequestAsync($"reverse:forward:{rebind}{remote};{local}", cancellationToken).ConfigureAwait(false); _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); - string portString = await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); + string portString = await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); return portString != null && int.TryParse(portString, out int port) ? port : 0; } @@ -97,7 +98,7 @@ public async Task RemoveReverseForwardAsync(DeviceData device, string remote, Ca await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); await socket.SendAdbRequestAsync($"reverse:killforward:{remote}", cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); } /// @@ -109,7 +110,7 @@ public async Task RemoveAllReverseForwardsAsync(DeviceData device, CancellationT await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); await socket.SendAdbRequestAsync($"reverse:killforward-all", cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); } /// @@ -119,7 +120,7 @@ public async Task RemoveForwardAsync(DeviceData device, int localPort, Cancellat using IAdbSocket socket = adbSocketFactory(EndPoint); await socket.SendAdbRequestAsync($"host-serial:{device.Serial}:killforward:tcp:{localPort}", cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); } /// @@ -129,7 +130,7 @@ public async Task RemoveAllForwardsAsync(DeviceData device, CancellationToken ca using IAdbSocket socket = adbSocketFactory(EndPoint); await socket.SendAdbRequestAsync($"host-serial:{device.Serial}:killforward-all", cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); } /// @@ -139,12 +140,10 @@ public async Task> ListForwardAsync(DeviceData device, using IAdbSocket socket = adbSocketFactory(EndPoint); await socket.SendAdbRequestAsync($"host-serial:{device.Serial}:list-forward", cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); - + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); string data = await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); string[] parts = data.Split(separator, StringSplitOptions.RemoveEmptyEntries); - return parts.Select(x => new ForwardData(x)); } @@ -157,18 +156,17 @@ public async Task> ListReverseForwardAsync(DeviceData d await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); await socket.SendAdbRequestAsync($"reverse:list-forward", cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); - + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); string data = await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); string[] parts = data.Split(separator, StringSplitOptions.RemoveEmptyEntries); - return parts.Select(x => new ForwardData(x)); } /// public async Task ExecuteServerCommandAsync(string target, string command, Encoding encoding, CancellationToken cancellationToken = default) { + ExceptionExtensions.ThrowIfNull(encoding); using IAdbSocket socket = adbSocketFactory(EndPoint); await ExecuteServerCommandAsync(target, command, socket, encoding, cancellationToken); } @@ -176,6 +174,8 @@ public async Task ExecuteServerCommandAsync(string target, string command, Encod /// public async Task ExecuteServerCommandAsync(string target, string command, IAdbSocket socket, Encoding encoding, CancellationToken cancellationToken = default) { + ExceptionExtensions.ThrowIfNull(encoding); + StringBuilder request = new(); if (!StringExtensions.IsNullOrWhiteSpace(target)) { @@ -191,6 +191,7 @@ public async Task ExecuteServerCommandAsync(string target, string command, IAdbS public async Task ExecuteRemoteCommandAsync(string command, DeviceData device, Encoding encoding, CancellationToken cancellationToken = default) { EnsureDevice(device); + ExceptionExtensions.ThrowIfNull(encoding); using IAdbSocket socket = adbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken); @@ -201,6 +202,7 @@ public async Task ExecuteRemoteCommandAsync(string command, DeviceData device, E /// public async Task ExecuteServerCommandAsync(string target, string command, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken = default) { + ExceptionExtensions.ThrowIfNull(encoding); using IAdbSocket socket = adbSocketFactory(EndPoint); await ExecuteServerCommandAsync(target, command, socket, receiver, encoding, cancellationToken); } @@ -208,6 +210,8 @@ public async Task ExecuteServerCommandAsync(string target, string command, IShel /// public async Task ExecuteServerCommandAsync(string target, string command, IAdbSocket socket, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken = default) { + ExceptionExtensions.ThrowIfNull(encoding); + StringBuilder request = new(); if (!StringExtensions.IsNullOrWhiteSpace(target)) { @@ -255,6 +259,7 @@ public async Task ExecuteServerCommandAsync(string target, string command, IAdbS public async Task ExecuteRemoteCommandAsync(string command, DeviceData device, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken = default) { EnsureDevice(device); + ExceptionExtensions.ThrowIfNull(encoding); using IAdbSocket socket = adbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken); @@ -277,9 +282,8 @@ public async Task GetFrameBufferAsync(DeviceData device, Cancellati /// public async Task RunLogServiceAsync(DeviceData device, Action messageSink, CancellationToken cancellationToken, params LogId[] logNames) { - ExceptionExtensions.ThrowIfNull(messageSink); - EnsureDevice(device); + ExceptionExtensions.ThrowIfNull(messageSink); // The 'log' service has been deprecated, see // https://android.googlesource.com/platform/system/core/+/7aa39a7b199bb9803d3fd47246ee9530b4a96177 @@ -294,7 +298,7 @@ public async Task RunLogServiceAsync(DeviceData device, Action message } await socket.SendAdbRequestAsync(request.ToString(), cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); #if NETCOREAPP3_0_OR_GREATER await @@ -331,12 +335,11 @@ public async Task RebootAsync(string into, DeviceData device, CancellationToken { EnsureDevice(device); - string request = $"reboot:{into}"; - using IAdbSocket socket = adbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); - await socket.SendAdbRequestAsync(request, cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + + await socket.SendAdbRequestAsync($"reboot:{into}", cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); } /// @@ -346,9 +349,9 @@ public async Task PairAsync(DnsEndPoint endpoint, string code, Cancellat using IAdbSocket socket = adbSocketFactory(EndPoint); await socket.SendAdbRequestAsync($"host:pair:{code}:{endpoint.Host}:{endpoint.Port}", cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); - string results = await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); - return results; + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + + return await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); } /// @@ -358,9 +361,9 @@ public async Task ConnectAsync(DnsEndPoint endpoint, CancellationToken c using IAdbSocket socket = adbSocketFactory(EndPoint); await socket.SendAdbRequestAsync($"host:connect:{endpoint.Host}:{endpoint.Port}", cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); - string results = await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); - return results; + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + + return await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); } /// @@ -370,9 +373,9 @@ public async Task DisconnectAsync(DnsEndPoint endpoint, CancellationToke using IAdbSocket socket = adbSocketFactory(EndPoint); await socket.SendAdbRequestAsync($"host:disconnect:{endpoint.Host}:{endpoint.Port}", cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); - string results = await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); - return results; + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + + return await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); } /// @@ -394,8 +397,9 @@ protected async Task RootAsync(string request, DeviceData device, CancellationTo using IAdbSocket socket = adbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); + await socket.SendAdbRequestAsync(request, cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); // ADB will send some additional data byte[] buffer = new byte[1024]; @@ -426,7 +430,6 @@ protected async Task RootAsync(string request, DeviceData device, CancellationTo public async Task InstallAsync(DeviceData device, Stream apk, CancellationToken cancellationToken, params string[] arguments) { EnsureDevice(device); - ExceptionExtensions.ThrowIfNull(apk); if (!apk.CanRead || !apk.CanSeek) @@ -452,7 +455,7 @@ public async Task InstallAsync(DeviceData device, Stream apk, CancellationToken await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); await socket.SendAdbRequestAsync(requestBuilder.ToString(), cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); byte[] buffer = new byte[32 * 1024]; int read = 0; @@ -484,7 +487,7 @@ public async Task InstallAsync(DeviceData device, Stream apk, CancellationToken public async Task InstallMultipleAsync(DeviceData device, IEnumerable splitAPKs, string packageName, CancellationToken cancellationToken, params string[] arguments) { EnsureDevice(device); - + ExceptionExtensions.ThrowIfNull(splitAPKs); ExceptionExtensions.ThrowIfNull(packageName); string session = await InstallCreateAsync(device, packageName, cancellationToken, arguments).ConfigureAwait(false); @@ -515,8 +518,8 @@ await Extensions.WhenAll(splitAPKs.Select(async splitAPK => public async Task InstallMultipleAsync(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, CancellationToken cancellationToken, params string[] arguments) { EnsureDevice(device); - ExceptionExtensions.ThrowIfNull(baseAPK); + ExceptionExtensions.ThrowIfNull(splitAPKs); if (!baseAPK.CanRead || !baseAPK.CanSeek) { @@ -573,7 +576,7 @@ public async Task InstallCreateAsync(DeviceData device, string? packageN await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); await socket.SendAdbRequestAsync(requestBuilder.ToString(), cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); using StreamReader reader = new(socket.GetShellStream(), Encoding); string result = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false) @@ -593,18 +596,15 @@ public async Task InstallCreateAsync(DeviceData device, string? packageN public async Task InstallWriteAsync(DeviceData device, Stream apk, string apkName, string session, CancellationToken cancellationToken = default) { EnsureDevice(device); - ExceptionExtensions.ThrowIfNull(apk); + ExceptionExtensions.ThrowIfNull(apkName); + ExceptionExtensions.ThrowIfNull(session); if (!apk.CanRead || !apk.CanSeek) { throw new ArgumentOutOfRangeException(nameof(apk), "The apk stream must be a readable and seekable stream"); } - ExceptionExtensions.ThrowIfNull(session); - - ExceptionExtensions.ThrowIfNull(apkName); - StringBuilder requestBuilder = new StringBuilder().Append($"exec:cmd package 'install-write'") // add size parameter [required for streaming installs] @@ -616,7 +616,7 @@ public async Task InstallWriteAsync(DeviceData device, Stream apk, string apkNam await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); await socket.SendAdbRequestAsync(requestBuilder.ToString(), cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); byte[] buffer = new byte[32 * 1024]; int read = 0; @@ -651,7 +651,7 @@ public async Task InstallCommitAsync(DeviceData device, string session, Cancella await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); await socket.SendAdbRequestAsync($"exec:cmd package 'install-commit' {session}", cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); using StreamReader reader = new(socket.GetShellStream(), Encoding); string? result = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false); @@ -682,7 +682,7 @@ public async Task UninstallAsync(DeviceData device, string packageName, Cancella await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); await socket.SendAdbRequestAsync(requestBuilder.ToString(), cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); using StreamReader reader = new(socket.GetShellStream(), Encoding); string? result = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false); @@ -695,10 +695,11 @@ public async Task UninstallAsync(DeviceData device, string packageName, Cancella /// public async Task> GetFeatureSetAsync(DeviceData device, CancellationToken cancellationToken = default) { + EnsureDevice(device); + using IAdbSocket socket = adbSocketFactory(EndPoint); await socket.SendAdbRequestAsync($"host-serial:{device.Serial}:features", cancellationToken).ConfigureAwait(false); - - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); string features = await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); IEnumerable featureList = features.Trim().Split('\n', ','); @@ -709,20 +710,25 @@ public async Task> GetFeatureSetAsync(DeviceData device, Can public async Task DumpScreenStringAsync(DeviceData device, CancellationToken cancellationToken = default) { EnsureDevice(device); + using IAdbSocket socket = adbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); + await socket.SendAdbRequestAsync("shell:uiautomator dump /dev/tty", cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + using StreamReader reader = new(socket.GetShellStream(), Encoding); string xmlString = reader.ReadToEnd() .Replace("Events injected: 1\r\n", string.Empty) .Replace("UI hierchary dumped to: /dev/tty", string.Empty) .Trim(); + if (string.IsNullOrEmpty(xmlString) || xmlString.StartsWith(" DumpScreenStringAsync(DeviceData device, CancellationT /// public async Task DumpScreenAsync(DeviceData device, CancellationToken cancellationToken = default) { + EnsureDevice(device); string xmlString = await DumpScreenStringAsync(device, cancellationToken).ConfigureAwait(false); XmlDocument doc = new(); if (!string.IsNullOrEmpty(xmlString)) @@ -744,6 +751,7 @@ public async Task DumpScreenStringAsync(DeviceData device, CancellationT /// public async Task DumpScreenWinRTAsync(DeviceData device, CancellationToken cancellationToken = default) { + EnsureDevice(device); string xmlString = await DumpScreenStringAsync(device, cancellationToken).ConfigureAwait(false); Windows.Data.Xml.Dom.XmlDocument doc = new(); if (!string.IsNullOrEmpty(xmlString)) @@ -762,10 +770,13 @@ public async Task ClickAsync(DeviceData device, Point cords, CancellationToken c using IAdbSocket socket = adbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); + await socket.SendAdbRequestAsync($"shell:input tap {cords.X} {cords.Y}", cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + using StreamReader reader = new(socket.GetShellStream(), Encoding); string result = await reader.ReadToEndAsync(cancellationToken).ContinueWith(x => x.Result.TrimStart()).ConfigureAwait(false); + if (result.StartsWith("java.lang.")) { throw JavaException.Parse(result); @@ -783,10 +794,13 @@ public async Task ClickAsync(DeviceData device, int x, int y, CancellationToken using IAdbSocket socket = adbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); + await socket.SendAdbRequestAsync($"shell:input tap {x} {y}", cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + using StreamReader reader = new(socket.GetShellStream(), Encoding); string result = await reader.ReadToEndAsync(cancellationToken).ContinueWith(x => x.Result.TrimStart()).ConfigureAwait(false); + if (result.StartsWith("java.lang.")) { throw JavaException.Parse(result); @@ -804,10 +818,13 @@ public async Task SwipeAsync(DeviceData device, Element first, Element second, l using IAdbSocket socket = adbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); + await socket.SendAdbRequestAsync($"shell:input swipe {first.Center.X} {first.Center.Y} {second.Center.X} {second.Center.Y} {speed}", cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + using StreamReader reader = new(socket.GetShellStream(), Encoding); string result = await reader.ReadToEndAsync(cancellationToken).ContinueWith(x => x.Result.TrimStart()).ConfigureAwait(false); + if (result.StartsWith("java.lang.")) { throw JavaException.Parse(result); @@ -825,10 +842,13 @@ public async Task SwipeAsync(DeviceData device, int x1, int y1, int x2, int y2, using IAdbSocket socket = adbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); + await socket.SendAdbRequestAsync($"shell:input swipe {x1} {y1} {x2} {y2} {speed}", cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + using StreamReader reader = new(socket.GetShellStream(), Encoding); string result = await reader.ReadToEndAsync(cancellationToken).ContinueWith(x => x.Result.TrimStart()).ConfigureAwait(false); + if (result.StartsWith("java.lang.")) { throw JavaException.Parse(result); @@ -846,8 +866,10 @@ public async Task IsAppRunningAsync(DeviceData device, string packageName, using IAdbSocket socket = adbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); + await socket.SendAdbRequestAsync($"shell:pidof {packageName}", cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + using StreamReader reader = new(socket.GetShellStream(), Encoding); string? result = await reader.ReadToEndAsync(cancellationToken).ContinueWith(x => x.Result.TrimStart().Split(' ', StringSplitOptions.RemoveEmptyEntries).FirstOrDefault()).ConfigureAwait(false); bool intParsed = int.TryParse(result, out int pid); @@ -861,8 +883,10 @@ public async Task IsAppInForegroundAsync(DeviceData device, string package using IAdbSocket socket = adbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); + await socket.SendAdbRequestAsync($"shell:dumpsys activity activities | grep mResumedActivity", cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + using StreamReader reader = new(socket.GetShellStream(), Encoding); string result = await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false); return result.Contains(packageName); @@ -1019,10 +1043,13 @@ public async Task SendKeyEventAsync(DeviceData device, string key, CancellationT using IAdbSocket socket = adbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); + await socket.SendAdbRequestAsync($"shell:input keyevent {key}", cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + using StreamReader reader = new(socket.GetShellStream(), Encoding); string result = await reader.ReadToEndAsync(cancellationToken).ContinueWith(x => x.Result.TrimStart()).ConfigureAwait(false); + if (result.StartsWith("java.lang.")) { throw JavaException.Parse(result); @@ -1040,10 +1067,13 @@ public async Task SendTextAsync(DeviceData device, string text, CancellationToke using IAdbSocket socket = adbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); + await socket.SendAdbRequestAsync($"shell:input text {text}", cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + using StreamReader reader = new(socket.GetShellStream(), Encoding); string result = await reader.ReadToEndAsync(cancellationToken).ContinueWith(x => x.Result.TrimStart()).ConfigureAwait(false); + if (result.StartsWith("java.lang.")) { throw JavaException.Parse(result); @@ -1061,8 +1091,9 @@ public async Task StartAppAsync(DeviceData device, string packageName, Cancellat using IAdbSocket socket = adbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); + await socket.SendAdbRequestAsync($"shell:monkey -p {packageName} 1", cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); } /// @@ -1072,8 +1103,9 @@ public async Task StopAppAsync(DeviceData device, string packageName, Cancellati using IAdbSocket socket = adbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); + await socket.SendAdbRequestAsync($"shell:am force-stop {packageName}", cancellationToken).ConfigureAwait(false); - AdbResponse response = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); } } } diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index 3c46c917..f22cc0ca 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; @@ -172,8 +173,9 @@ public static byte[] CreateAdbForwardRequest(string address, int port) public int GetAdbVersion() { using IAdbSocket socket = adbSocketFactory(EndPoint); + socket.SendAdbRequest("host:version"); - AdbResponse response = socket.ReadAdbResponse(); + _ = socket.ReadAdbResponse(); string version = socket.ReadString(); return int.Parse(version, NumberStyles.HexNumber); @@ -193,12 +195,12 @@ public void KillAdb() public IEnumerable GetDevices() { using IAdbSocket socket = adbSocketFactory(EndPoint); + socket.SendAdbRequest("host:devices-l"); _ = socket.ReadAdbResponse(); string reply = socket.ReadString(); string[] data = reply.Split(separator, StringSplitOptions.RemoveEmptyEntries); - return data.Select(x => new DeviceData(x)); } @@ -213,8 +215,8 @@ public int CreateForward(DeviceData device, string local, string remote, bool al socket.SendAdbRequest($"host-serial:{device.Serial}:forward:{rebind}{local};{remote}"); _ = socket.ReadAdbResponse(); _ = socket.ReadAdbResponse(); - string portString = socket.ReadString(); + string portString = socket.ReadString(); return portString != null && int.TryParse(portString, out int port) ? port : 0; } @@ -224,15 +226,15 @@ public int CreateReverseForward(DeviceData device, string remote, string local, EnsureDevice(device); using IAdbSocket socket = adbSocketFactory(EndPoint); - socket.SetDevice(device); + socket.SetDevice(device); string rebind = allowRebind ? string.Empty : "norebind:"; socket.SendAdbRequest($"reverse:forward:{rebind}{remote};{local}"); _ = socket.ReadAdbResponse(); _ = socket.ReadAdbResponse(); - string portString = socket.ReadString(); + string portString = socket.ReadString(); return portString != null && int.TryParse(portString, out int port) ? port : 0; } @@ -245,7 +247,7 @@ public void RemoveReverseForward(DeviceData device, string remote) socket.SetDevice(device); socket.SendAdbRequest($"reverse:killforward:{remote}"); - AdbResponse response = socket.ReadAdbResponse(); + _ = socket.ReadAdbResponse(); } /// @@ -257,7 +259,7 @@ public void RemoveAllReverseForwards(DeviceData device) socket.SetDevice(device); socket.SendAdbRequest($"reverse:killforward-all"); - AdbResponse response = socket.ReadAdbResponse(); + _ = socket.ReadAdbResponse(); } /// @@ -267,7 +269,7 @@ public void RemoveForward(DeviceData device, int localPort) using IAdbSocket socket = adbSocketFactory(EndPoint); socket.SendAdbRequest($"host-serial:{device.Serial}:killforward:tcp:{localPort}"); - AdbResponse response = socket.ReadAdbResponse(); + _ = socket.ReadAdbResponse(); } /// @@ -277,7 +279,7 @@ public void RemoveAllForwards(DeviceData device) using IAdbSocket socket = adbSocketFactory(EndPoint); socket.SendAdbRequest($"host-serial:{device.Serial}:killforward-all"); - AdbResponse response = socket.ReadAdbResponse(); + _ = socket.ReadAdbResponse(); } /// @@ -287,12 +289,10 @@ public IEnumerable ListForward(DeviceData device) using IAdbSocket socket = adbSocketFactory(EndPoint); socket.SendAdbRequest($"host-serial:{device.Serial}:list-forward"); - AdbResponse response = socket.ReadAdbResponse(); - + _ = socket.ReadAdbResponse(); string data = socket.ReadString(); string[] parts = data.Split(separator, StringSplitOptions.RemoveEmptyEntries); - return parts.Select(x => new ForwardData(x)); } @@ -305,18 +305,17 @@ public IEnumerable ListReverseForward(DeviceData device) socket.SetDevice(device); socket.SendAdbRequest($"reverse:list-forward"); - AdbResponse response = socket.ReadAdbResponse(); - + _ = socket.ReadAdbResponse(); string data = socket.ReadString(); string[] parts = data.Split(separator, StringSplitOptions.RemoveEmptyEntries); - return parts.Select(x => new ForwardData(x)); } /// public void ExecuteServerCommand(string target, string command, Encoding encoding) { + ExceptionExtensions.ThrowIfNull(encoding); using IAdbSocket socket = adbSocketFactory(EndPoint); ExecuteServerCommand(target, command, socket, encoding); } @@ -324,6 +323,8 @@ public void ExecuteServerCommand(string target, string command, Encoding encodin /// public void ExecuteServerCommand(string target, string command, IAdbSocket socket, Encoding encoding) { + ExceptionExtensions.ThrowIfNull(encoding); + StringBuilder request = new(); if (!StringExtensions.IsNullOrWhiteSpace(target)) { @@ -339,6 +340,7 @@ public void ExecuteServerCommand(string target, string command, IAdbSocket socke public void ExecuteRemoteCommand(string command, DeviceData device, Encoding encoding) { EnsureDevice(device); + ExceptionExtensions.ThrowIfNull(encoding); using IAdbSocket socket = adbSocketFactory(EndPoint); socket.SetDevice(device); @@ -349,6 +351,7 @@ public void ExecuteRemoteCommand(string command, DeviceData device, Encoding enc /// public void ExecuteServerCommand(string target, string command, IShellOutputReceiver receiver, Encoding encoding) { + ExceptionExtensions.ThrowIfNull(encoding); using IAdbSocket socket = adbSocketFactory(EndPoint); ExecuteServerCommand(target, command, socket, receiver, encoding); } @@ -356,6 +359,8 @@ public void ExecuteServerCommand(string target, string command, IShellOutputRece /// public void ExecuteServerCommand(string target, string command, IAdbSocket socket, IShellOutputReceiver receiver, Encoding encoding) { + ExceptionExtensions.ThrowIfNull(encoding); + StringBuilder request = new(); if (!StringExtensions.IsNullOrWhiteSpace(target)) { @@ -376,9 +381,7 @@ public void ExecuteServerCommand(string target, string command, IAdbSocket socke while (true) { string? line = reader.ReadLine(); - if (line == null) { break; } - receiver?.AddOutput(line); } } @@ -407,7 +410,6 @@ public void ExecuteRemoteCommand(string command, DeviceData device, IShellOutput public Framebuffer CreateRefreshableFramebuffer(DeviceData device) { EnsureDevice(device); - return new Framebuffer(device, this, adbSocketFactory); } @@ -426,9 +428,8 @@ public Framebuffer GetFrameBuffer(DeviceData device) /// public void RunLogService(DeviceData device, Action messageSink, params LogId[] logNames) { - ExceptionExtensions.ThrowIfNull(messageSink); - EnsureDevice(device); + ExceptionExtensions.ThrowIfNull(messageSink); // The 'log' service has been deprecated, see // https://android.googlesource.com/platform/system/core/+/7aa39a7b199bb9803d3fd47246ee9530b4a96177 @@ -443,7 +444,7 @@ public void RunLogService(DeviceData device, Action messageSink, param } socket.SendAdbRequest(request.ToString()); - AdbResponse response = socket.ReadAdbResponse(); + _ = socket.ReadAdbResponse(); using Stream stream = socket.GetShellStream(); LogReader reader = new(stream); @@ -477,12 +478,11 @@ public void Reboot(string into, DeviceData device) { EnsureDevice(device); - string request = $"reboot:{into}"; - using IAdbSocket socket = adbSocketFactory(EndPoint); socket.SetDevice(device); - socket.SendAdbRequest(request); - AdbResponse response = socket.ReadAdbResponse(); + + socket.SendAdbRequest($"reboot:{into}"); + _ = socket.ReadAdbResponse(); } /// @@ -492,9 +492,9 @@ public string Pair(DnsEndPoint endpoint, string code) using IAdbSocket socket = adbSocketFactory(EndPoint); socket.SendAdbRequest($"host:pair:{code}:{endpoint.Host}:{endpoint.Port}"); - AdbResponse response = socket.ReadAdbResponse(); - string results = socket.ReadString(); - return results; + _ = socket.ReadAdbResponse(); + + return socket.ReadString(); } /// @@ -504,9 +504,9 @@ public string Connect(DnsEndPoint endpoint) using IAdbSocket socket = adbSocketFactory(EndPoint); socket.SendAdbRequest($"host:connect:{endpoint.Host}:{endpoint.Port}"); - AdbResponse response = socket.ReadAdbResponse(); - string results = socket.ReadString(); - return results; + _ = socket.ReadAdbResponse(); + + return socket.ReadString(); } /// @@ -516,9 +516,9 @@ public string Disconnect(DnsEndPoint endpoint) using IAdbSocket socket = adbSocketFactory(EndPoint); socket.SendAdbRequest($"host:disconnect:{endpoint.Host}:{endpoint.Port}"); - AdbResponse response = socket.ReadAdbResponse(); - string results = socket.ReadString(); - return results; + _ = socket.ReadAdbResponse(); + + return socket.ReadString(); } /// @@ -539,7 +539,7 @@ protected void Root(string request, DeviceData device) using IAdbSocket socket = adbSocketFactory(EndPoint); socket.SetDevice(device); socket.SendAdbRequest(request); - AdbResponse response = socket.ReadAdbResponse(); + _ = socket.ReadAdbResponse(); // ADB will send some additional data byte[] buffer = new byte[1024]; @@ -565,7 +565,7 @@ protected void Root(string request, DeviceData device) #if HAS_PROCESS && !WINDOWS_UWP Thread.Sleep(3000); #else - Extensions.Delay(3000).GetAwaiter().GetResult(); + Extensions.Delay(3000).Wait(); #endif } } @@ -574,7 +574,6 @@ protected void Root(string request, DeviceData device) public void Install(DeviceData device, Stream apk, params string[] arguments) { EnsureDevice(device); - ExceptionExtensions.ThrowIfNull(apk); if (!apk.CanRead || !apk.CanSeek) @@ -600,7 +599,7 @@ public void Install(DeviceData device, Stream apk, params string[] arguments) socket.SetDevice(device); socket.SendAdbRequest(requestBuilder.ToString()); - AdbResponse response = socket.ReadAdbResponse(); + _ = socket.ReadAdbResponse(); byte[] buffer = new byte[32 * 1024]; int read = 0; @@ -632,7 +631,7 @@ public void Install(DeviceData device, Stream apk, params string[] arguments) public void InstallMultiple(DeviceData device, IEnumerable splitAPKs, string packageName, params string[] arguments) { EnsureDevice(device); - + ExceptionExtensions.ThrowIfNull(splitAPKs); ExceptionExtensions.ThrowIfNull(packageName); string session = InstallCreate(device, packageName, arguments); @@ -663,8 +662,8 @@ public void InstallMultiple(DeviceData device, IEnumerable splitAPKs, st public void InstallMultiple(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, params string[] arguments) { EnsureDevice(device); - ExceptionExtensions.ThrowIfNull(baseAPK); + ExceptionExtensions.ThrowIfNull(splitAPKs); if (!baseAPK.CanRead || !baseAPK.CanSeek) { @@ -721,7 +720,7 @@ public string InstallCreate(DeviceData device, string? packageName = null, param socket.SetDevice(device); socket.SendAdbRequest(requestBuilder.ToString()); - AdbResponse response = socket.ReadAdbResponse(); + _ = socket.ReadAdbResponse(); using StreamReader reader = new(socket.GetShellStream(), Encoding); string result = reader.ReadLine() ?? throw new AdbException($"The {nameof(result)} of {nameof(InstallCreate)} is null."); @@ -740,18 +739,15 @@ public string InstallCreate(DeviceData device, string? packageName = null, param public void InstallWrite(DeviceData device, Stream apk, string apkName, string session) { EnsureDevice(device); - ExceptionExtensions.ThrowIfNull(apk); + ExceptionExtensions.ThrowIfNull(apkName); + ExceptionExtensions.ThrowIfNull(session); if (!apk.CanRead || !apk.CanSeek) { throw new ArgumentOutOfRangeException(nameof(apk), "The apk stream must be a readable and seekable stream"); } - ExceptionExtensions.ThrowIfNull(session); - - ExceptionExtensions.ThrowIfNull(apkName); - StringBuilder requestBuilder = new StringBuilder().Append($"exec:cmd package 'install-write'") // add size parameter [required for streaming installs] @@ -763,7 +759,7 @@ public void InstallWrite(DeviceData device, Stream apk, string apkName, string s socket.SetDevice(device); socket.SendAdbRequest(requestBuilder.ToString()); - AdbResponse response = socket.ReadAdbResponse(); + _ = socket.ReadAdbResponse(); byte[] buffer = new byte[32 * 1024]; int read = 0; @@ -800,7 +796,7 @@ public void InstallCommit(DeviceData device, string session) socket.SetDevice(device); socket.SendAdbRequest($"exec:cmd package 'install-commit' {session}"); - AdbResponse response = socket.ReadAdbResponse(); + _ = socket.ReadAdbResponse(); using StreamReader reader = new(socket.GetShellStream(), Encoding); string? result = reader.ReadLine(); @@ -831,7 +827,7 @@ public void Uninstall(DeviceData device, string packageName, params string[] arg socket.SetDevice(device); socket.SendAdbRequest(requestBuilder.ToString()); - AdbResponse response = socket.ReadAdbResponse(); + _ = socket.ReadAdbResponse(); using StreamReader reader = new(socket.GetShellStream(), Encoding); string? result = reader.ReadLine(); @@ -848,8 +844,7 @@ public IEnumerable GetFeatureSet(DeviceData device) using IAdbSocket socket = adbSocketFactory(EndPoint); socket.SendAdbRequest($"host-serial:{device.Serial}:features"); - - AdbResponse response = socket.ReadAdbResponse(); + _ = socket.ReadAdbResponse(); string features = socket.ReadString(); IEnumerable featureList = features.Trim().Split('\n', ','); @@ -860,19 +855,24 @@ public IEnumerable GetFeatureSet(DeviceData device) public string DumpScreenString(DeviceData device) { EnsureDevice(device); + using IAdbSocket socket = adbSocketFactory(EndPoint); socket.SetDevice(device); + socket.SendAdbRequest("shell:uiautomator dump /dev/tty"); - AdbResponse response = socket.ReadAdbResponse(); + _ = socket.ReadAdbResponse(); + using StreamReader reader = new(socket.GetShellStream(), Encoding); string xmlString = reader.ReadToEnd() .Replace("Events injected: 1\r\n", string.Empty) .Replace("UI hierchary dumped to: /dev/tty", string.Empty) .Trim(); + if (string.IsNullOrEmpty(xmlString) || xmlString.StartsWith(" public XmlDocument? DumpScreen(DeviceData device) { + EnsureDevice(device); XmlDocument doc = new(); string xmlString = DumpScreenString(device); if (!string.IsNullOrEmpty(xmlString)) @@ -894,6 +895,7 @@ public string DumpScreenString(DeviceData device) /// public Windows.Data.Xml.Dom.XmlDocument? DumpScreenWinRT(DeviceData device) { + EnsureDevice(device); Windows.Data.Xml.Dom.XmlDocument doc = new(); string xmlString = DumpScreenString(device); if (!string.IsNullOrEmpty(xmlString)) @@ -912,10 +914,13 @@ public void Click(DeviceData device, Point cords) using IAdbSocket socket = adbSocketFactory(EndPoint); socket.SetDevice(device); + socket.SendAdbRequest($"shell:input tap {cords.X} {cords.Y}"); - AdbResponse response = socket.ReadAdbResponse(); + _ = socket.ReadAdbResponse(); + using StreamReader reader = new(socket.GetShellStream(), Encoding); string result = reader.ReadToEnd().TrimStart(); + if (result.StartsWith("java.lang.")) { throw JavaException.Parse(result); @@ -933,10 +938,13 @@ public void Click(DeviceData device, int x, int y) using IAdbSocket socket = adbSocketFactory(EndPoint); socket.SetDevice(device); + socket.SendAdbRequest($"shell:input tap {x} {y}"); - AdbResponse response = socket.ReadAdbResponse(); + _ = socket.ReadAdbResponse(); + using StreamReader reader = new(socket.GetShellStream(), Encoding); string result = reader.ReadToEnd().TrimStart(); + if (result.StartsWith("java.lang.")) { throw JavaException.Parse(result); @@ -954,10 +962,13 @@ public void Swipe(DeviceData device, Element first, Element second, long speed) using IAdbSocket socket = adbSocketFactory(EndPoint); socket.SetDevice(device); + socket.SendAdbRequest($"shell:input swipe {first.Center.X} {first.Center.Y} {second.Center.X} {second.Center.Y} {speed}"); - AdbResponse response = socket.ReadAdbResponse(); + _ = socket.ReadAdbResponse(); + using StreamReader reader = new(socket.GetShellStream(), Encoding); string result = reader.ReadToEnd().TrimStart(); + if (result.StartsWith("java.lang.")) { throw JavaException.Parse(result); @@ -975,10 +986,13 @@ public void Swipe(DeviceData device, int x1, int y1, int x2, int y2, long speed) using IAdbSocket socket = adbSocketFactory(EndPoint); socket.SetDevice(device); + socket.SendAdbRequest($"shell:input swipe {x1} {y1} {x2} {y2} {speed}"); - AdbResponse response = socket.ReadAdbResponse(); + _ = socket.ReadAdbResponse(); + using StreamReader reader = new(socket.GetShellStream(), Encoding); string result = reader.ReadToEnd().TrimStart(); + if (result.StartsWith("java.lang.")) { throw JavaException.Parse(result); @@ -996,8 +1010,10 @@ public bool IsAppRunning(DeviceData device, string packageName) using IAdbSocket socket = adbSocketFactory(EndPoint); socket.SetDevice(device); + socket.SendAdbRequest($"shell:pidof {packageName}"); - AdbResponse response = socket.ReadAdbResponse(); + _ = socket.ReadAdbResponse(); + using StreamReader reader = new(socket.GetShellStream(), Encoding); string? result = reader.ReadToEnd().TrimStart().Split(' ', StringSplitOptions.RemoveEmptyEntries).FirstOrDefault(); bool intParsed = int.TryParse(result, out int pid); @@ -1011,8 +1027,10 @@ public bool IsAppInForeground(DeviceData device, string packageName) using IAdbSocket socket = adbSocketFactory(EndPoint); socket.SetDevice(device); + socket.SendAdbRequest($"shell:dumpsys activity activities | grep mResumedActivity"); - AdbResponse response = socket.ReadAdbResponse(); + _ = socket.ReadAdbResponse(); + using StreamReader reader = new(socket.GetShellStream(), Encoding); string result = reader.ReadToEnd(); return result.Contains(packageName); @@ -1100,10 +1118,13 @@ public void SendKeyEvent(DeviceData device, string key) using IAdbSocket socket = adbSocketFactory(EndPoint); socket.SetDevice(device); + socket.SendAdbRequest($"shell:input keyevent {key}"); - AdbResponse response = socket.ReadAdbResponse(); + _ = socket.ReadAdbResponse(); + using StreamReader reader = new(socket.GetShellStream(), Encoding); string result = reader.ReadToEnd().TrimStart(); + if (result.StartsWith("java.lang.")) { throw JavaException.Parse(result); @@ -1121,10 +1142,13 @@ public void SendText(DeviceData device, string text) using IAdbSocket socket = adbSocketFactory(EndPoint); socket.SetDevice(device); + socket.SendAdbRequest($"shell:input text {text}"); - AdbResponse response = socket.ReadAdbResponse(); + _ = socket.ReadAdbResponse(); + using StreamReader reader = new(socket.GetShellStream(), Encoding); string result = reader.ReadToEnd().TrimStart(); + if (result.StartsWith("java.lang.")) { throw JavaException.Parse(result); @@ -1141,8 +1165,9 @@ public void StartApp(DeviceData device, string packageName) using IAdbSocket socket = adbSocketFactory(EndPoint); socket.SetDevice(device); + socket.SendAdbRequest($"shell:monkey -p {packageName} 1"); - AdbResponse response = socket.ReadAdbResponse(); + _ = socket.ReadAdbResponse(); } /// @@ -1152,8 +1177,9 @@ public void StopApp(DeviceData device, string packageName) using IAdbSocket socket = adbSocketFactory(EndPoint); socket.SetDevice(device); + socket.SendAdbRequest($"shell:am force-stop {packageName}"); - AdbResponse response = socket.ReadAdbResponse(); + _ = socket.ReadAdbResponse(); } /// @@ -1168,10 +1194,9 @@ public void StopApp(DeviceData device, string packageName) /// if does not have a valid serial number. /// /// A object to validate. - protected static void EnsureDevice(DeviceData device) + protected static void EnsureDevice([NotNull] DeviceData? device) { ExceptionExtensions.ThrowIfNull(device); - if (string.IsNullOrEmpty(device.Serial)) { throw new ArgumentOutOfRangeException(nameof(device), "You must specific a serial number for the device"); diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs index 1b117ef9..05906f5f 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs @@ -23,12 +23,10 @@ public virtual async Task GetVersionAsync(CancellationToken cancellatio { // Run the adb.exe version command and capture the output. List standardOutput = []; - await RunAdbProcessAsync("version", null, standardOutput, cancellationToken).ConfigureAwait(false); // Parse the output to get the version. Version version = GetVersionFromOutput(standardOutput) ?? throw new AdbException($"The version of the adb executable at {AdbPath} could not be determined."); - if (version < AdbServer.RequiredAdbVersion) { AdbException ex = new($"Required minimum version of adb: {AdbServer.RequiredAdbVersion}. Current version is {version}"); @@ -47,12 +45,7 @@ public virtual async Task GetVersionAsync(CancellationToken cancellatio public virtual async Task StartServerAsync(CancellationToken cancellationToken = default) { int status = await RunAdbProcessInnerAsync("start-server", null, null, cancellationToken).ConfigureAwait(false); - - if (status == 0) - { - return; - } - + if (status == 0) { return; } #if HAS_PROCESS && !WINDOWS_UWP try { @@ -83,7 +76,6 @@ public virtual async Task StartServerAsync(CancellationToken cancellationToken = // This platform does not support getting a list of processes. } #endif - // Try again. This time, we don't call "Inner", and an exception will be thrown if the start operation fails // again. We'll let that exception bubble up the stack. await RunAdbProcessAsync("start-server", null, null, cancellationToken).ConfigureAwait(false); @@ -106,11 +98,7 @@ public virtual async Task StartServerAsync(CancellationToken cancellationToken = protected virtual async Task RunAdbProcessAsync(string command, ICollection? errorOutput, ICollection? standardOutput, CancellationToken cancellationToken = default) { int status = await RunAdbProcessInnerAsync(command, errorOutput, standardOutput, cancellationToken).ConfigureAwait(false); - - if (status != 0) - { - throw new AdbException($"The adb process returned error code {status} when running command {command}"); - } + if (status != 0) { throw new AdbException($"The adb process returned error code {status} when running command {command}"); } } /// @@ -129,10 +117,7 @@ protected virtual async Task RunAdbProcessAsync(string command, ICollection RunAdbProcessInnerAsync(string command, ICollection? errorOutput, ICollection? standardOutput, CancellationToken cancellationToken = default) { ExceptionExtensions.ThrowIfNull(command); - - int status = await RunProcessAsync(AdbPath, command, errorOutput, standardOutput, cancellationToken).ConfigureAwait(false); - - return status; + return await RunProcessAsync(AdbPath, command, errorOutput, standardOutput, cancellationToken).ConfigureAwait(false); } /// @@ -160,7 +145,6 @@ protected virtual async Task RunProcessAsync(string filename, string comman string standardOutputString = await process.StandardOutput.ReadToEndAsync(cancellationToken).ConfigureAwait(false); errorOutput?.AddRange(standardErrorString.Split(separator, StringSplitOptions.RemoveEmptyEntries)); - standardOutput?.AddRange(standardOutputString.Split(separator, StringSplitOptions.RemoveEmptyEntries)); #if NET5_0_OR_GREATER diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.cs index 504100e1..9c9c5b66 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.cs @@ -69,12 +69,10 @@ public virtual Version GetVersion() { // Run the adb.exe version command and capture the output. List standardOutput = []; - RunAdbProcess("version", null, standardOutput); // Parse the output to get the version. Version version = GetVersionFromOutput(standardOutput) ?? throw new AdbException($"The version of the adb executable at {AdbPath} could not be determined."); - if (version < AdbServer.RequiredAdbVersion) { AdbException ex = new($"Required minimum version of adb: {AdbServer.RequiredAdbVersion}. Current version is {version}"); @@ -91,12 +89,7 @@ public virtual Version GetVersion() public virtual void StartServer() { int status = RunAdbProcessInner("start-server", null, null); - - if (status == 0) - { - return; - } - + if (status == 0) { return; } #if HAS_PROCESS && !WINDOWS_UWP try { @@ -127,7 +120,6 @@ public virtual void StartServer() // This platform does not support getting a list of processes. } #endif - // Try again. This time, we don't call "Inner", and an exception will be thrown if the start operation fails // again. We'll let that exception bubble up the stack. RunAdbProcess("start-server", null, null); @@ -191,7 +183,6 @@ protected virtual void EnsureIsValidAdbFile(string adbPath) return new Version(majorVersion, minorVersion, microVersion); } } - return null; } @@ -210,11 +201,7 @@ protected virtual void EnsureIsValidAdbFile(string adbPath) protected virtual void RunAdbProcess(string command, ICollection? errorOutput, ICollection? standardOutput) { int status = RunAdbProcessInner(command, errorOutput, standardOutput); - - if (status != 0) - { - throw new AdbException($"The adb process returned error code {status} when running command {command}"); - } + if (status != 0) { throw new AdbException($"The adb process returned error code {status} when running command {command}"); } } /// @@ -232,10 +219,7 @@ protected virtual void RunAdbProcess(string command, ICollection? errorO protected virtual int RunAdbProcessInner(string command, ICollection? errorOutput, ICollection? standardOutput) { ExceptionExtensions.ThrowIfNull(command); - - int status = RunProcess(AdbPath, command, errorOutput, standardOutput); - - return status; + return RunProcess(AdbPath, command, errorOutput, standardOutput); } /// @@ -263,7 +247,6 @@ protected virtual int RunProcess(string filename, string command, ICollection + public +#if NET6_0_OR_GREATER + ValueTask +#else + Task +#endif + ReconnectAsync(CancellationToken cancellationToken = default) => ReconnectAsync(false, cancellationToken); + /// public virtual #if NET6_0_OR_GREATER @@ -20,7 +29,7 @@ public virtual #else Task #endif - ReconnectAsync(CancellationToken cancellationToken = default) => Socket.ReconnectAsync(cancellationToken); + ReconnectAsync(bool isForce, CancellationToken cancellationToken = default) => Socket.ReconnectAsync(isForce, cancellationToken); /// public virtual async Task SendAsync(byte[] data, int length, CancellationToken cancellationToken = default) @@ -213,7 +222,6 @@ public virtual async Task ReadSyncResponseAsync(CancellationToken c { byte[] data = new byte[4]; _ = await ReadAsync(data, cancellationToken).ConfigureAwait(false); - return SyncCommandConverter.GetCommand(data); } diff --git a/AdvancedSharpAdbClient/AdbSocket.cs b/AdvancedSharpAdbClient/AdbSocket.cs index 0e18fadd..15799621 100644 --- a/AdvancedSharpAdbClient/AdbSocket.cs +++ b/AdvancedSharpAdbClient/AdbSocket.cs @@ -93,7 +93,7 @@ public AdbSocket(ITcpSocket socket, ILogger? logger = null) public bool Connected => Socket.Connected; /// - public virtual void Reconnect() => Socket.Reconnect(); + public virtual void Reconnect(bool isForce = false) => Socket.Reconnect(isForce); /// public virtual void Send(byte[] data, int length) @@ -282,7 +282,6 @@ public virtual SyncCommand ReadSyncResponse() { byte[] data = new byte[4]; _ = Read(data); - return SyncCommandConverter.GetCommand(data); } diff --git a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs index 7b08b37c..181a7267 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs @@ -13,8 +13,8 @@ namespace AdvancedSharpAdbClient public partial class DeviceMonitor { /// - /// When the method is called, this - /// is used to block the method until the + /// When the method is called, this + /// is used to block the method until the /// has processed the first list of devices. /// protected readonly ManualResetEvent firstDeviceListParsed = new(false); @@ -184,7 +184,7 @@ protected virtual async Task DeviceMonitorLoopAsync(CancellationToken cancellati { // The adb server was killed, for whatever reason. Try to restart it and recover from this. await AdbServer.Instance.RestartServerAsync(cancellationToken).ConfigureAwait(false); - Socket.Reconnect(); + Socket.Reconnect(false); await InitializeSocketAsync(cancellationToken).ConfigureAwait(false); } else diff --git a/AdvancedSharpAdbClient/DeviceMonitor.cs b/AdvancedSharpAdbClient/DeviceMonitor.cs index b9318815..dd2bb661 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.cs @@ -278,7 +278,7 @@ protected virtual void DeviceMonitorLoop() { // The adb server was killed, for whatever reason. Try to restart it and recover from this. AdbServer.Instance.RestartServer(); - Socket.Reconnect(); + Socket.Reconnect(false); InitializeSocket(); } else diff --git a/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs b/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs index 7163a090..e265e090 100644 --- a/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs +++ b/AdvancedSharpAdbClient/Extensions/SyncCommandConverter.cs @@ -18,6 +18,11 @@ public static class SyncCommandConverter /// A byte array that represents the . public static byte[] GetBytes(SyncCommand command) { + if (command == 0) + { + return [0, 0, 0, 0]; + } + if (command is not (SyncCommand.LIST or SyncCommand.RECV or SyncCommand.SEND @@ -58,8 +63,7 @@ public static SyncCommand GetCommand(byte[] value) } string commandText = AdbClient.Encoding.GetString(value); - - return EnumExtensions.TryParse(commandText, true, out SyncCommand command) ? command : throw new ArgumentOutOfRangeException(nameof(value), $"{commandText} is not a valid sync command"); + return commandText == "\0\0\0\0" ? 0 : EnumExtensions.TryParse(commandText, true, out SyncCommand command) ? command : throw new ArgumentOutOfRangeException(nameof(value), $"{commandText} is not a valid sync command"); } } } diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbSocket.Async.cs b/AdvancedSharpAdbClient/Interfaces/IAdbSocket.Async.cs index 6ab0421f..d2af3b88 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbSocket.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbSocket.Async.cs @@ -17,17 +17,19 @@ public partial interface IAdbSocket /// Reconnects the to the same endpoint it was initially connected to. /// Use this when the socket was disconnected by adb and you have restarted adb. /// + /// Force reconnect whatever the socket is connected or not. /// A which can be used to cancel the asynchronous task. /// A that represents the asynchronous operation. - ValueTask ReconnectAsync(CancellationToken cancellationToken); + ValueTask ReconnectAsync(bool isForce, CancellationToken cancellationToken); #else /// /// Reconnects the to the same endpoint it was initially connected to. /// Use this when the socket was disconnected by adb and you have restarted adb. /// + /// Force reconnect whatever the socket is connected or not. /// A which can be used to cancel the asynchronous task. /// A that represents the asynchronous operation. - Task ReconnectAsync(CancellationToken cancellationToken); + Task ReconnectAsync(bool isForce, CancellationToken cancellationToken); #endif /// diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbSocket.cs b/AdvancedSharpAdbClient/Interfaces/IAdbSocket.cs index 988ec151..cb631fc6 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbSocket.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbSocket.cs @@ -24,7 +24,8 @@ public partial interface IAdbSocket : IDisposable /// Reconnects the to the same endpoint it was initially connected to. /// Use this when the socket was disconnected by adb and you have restarted adb. /// - void Reconnect(); + /// Force reconnect whatever the socket is connected or not. + void Reconnect(bool isForce); /// /// Sends the specified number of bytes of data to a , diff --git a/AdvancedSharpAdbClient/Interfaces/ISyncService.Async.cs b/AdvancedSharpAdbClient/Interfaces/ISyncService.Async.cs index 0e52b36d..65d193a8 100644 --- a/AdvancedSharpAdbClient/Interfaces/ISyncService.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/ISyncService.Async.cs @@ -66,6 +66,13 @@ public partial interface ISyncService /// A that can be used to cancel the task. /// A which represents the asynchronous operation. Task OpenAsync(CancellationToken cancellationToken); + + /// + /// Reopen this connection. Use this when the socket was disconnected by adb and you have restarted adb. + /// + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + Task ReopenAsync(CancellationToken cancellationToken = default); } } #endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Interfaces/ISyncService.cs b/AdvancedSharpAdbClient/Interfaces/ISyncService.cs index 88a47bc5..c5763c9f 100644 --- a/AdvancedSharpAdbClient/Interfaces/ISyncService.cs +++ b/AdvancedSharpAdbClient/Interfaces/ISyncService.cs @@ -60,6 +60,11 @@ public partial interface ISyncService : IDisposable /// void Open(); + /// + /// Reopen this connection. Use this when the socket was disconnected by adb and you have restarted adb. + /// + void Reopen(); + /// /// Occurs when there is a change in the status of the sync. /// diff --git a/AdvancedSharpAdbClient/Interfaces/ITcpSocket.Async.cs b/AdvancedSharpAdbClient/Interfaces/ITcpSocket.Async.cs index da6bc6fd..70e6441f 100644 --- a/AdvancedSharpAdbClient/Interfaces/ITcpSocket.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/ITcpSocket.Async.cs @@ -25,9 +25,10 @@ public partial interface ITcpSocket /// Re-establishes the connection to a remote host. Assumes you have resolved the reason that caused the /// socket to disconnect. /// + /// Force reconnect whatever the socket is connected or not. /// A which can be used to cancel the asynchronous task. /// A that represents the asynchronous operation. - ValueTask ReconnectAsync(CancellationToken cancellationToken); + ValueTask ReconnectAsync(bool isForce, CancellationToken cancellationToken); #else /// /// Begins an asynchronous request for a connection to a remote host. @@ -41,9 +42,10 @@ public partial interface ITcpSocket /// Re-establishes the connection to a remote host. Assumes you have resolved the reason that caused the /// socket to disconnect. /// + /// Force reconnect whatever the socket is connected or not. /// A which can be used to cancel the asynchronous task. /// A that represents the asynchronous operation. - Task ReconnectAsync(CancellationToken cancellationToken); + Task ReconnectAsync(bool isForce, CancellationToken cancellationToken); #endif /// diff --git a/AdvancedSharpAdbClient/Interfaces/ITcpSocket.cs b/AdvancedSharpAdbClient/Interfaces/ITcpSocket.cs index 6ed4ae35..783513e0 100644 --- a/AdvancedSharpAdbClient/Interfaces/ITcpSocket.cs +++ b/AdvancedSharpAdbClient/Interfaces/ITcpSocket.cs @@ -37,7 +37,8 @@ public partial interface ITcpSocket : IDisposable /// Re-establishes the connection to a remote host. Assumes you have resolved the reason that caused the /// socket to disconnect. /// - void Reconnect(); + /// Force reconnect whatever the socket is connected or not. + void Reconnect(bool isForce = false); /// /// Sends the specified number of bytes of data to a connected diff --git a/AdvancedSharpAdbClient/Models/AdbResponse.cs b/AdvancedSharpAdbClient/Models/AdbResponse.cs index 43975418..0bd6e50d 100644 --- a/AdvancedSharpAdbClient/Models/AdbResponse.cs +++ b/AdvancedSharpAdbClient/Models/AdbResponse.cs @@ -57,6 +57,17 @@ namespace AdvancedSharpAdbClient.Models /// The message. public string Message { get; init; } + /// + /// Throw if or is . + /// + public void Throw() + { + if (!IOSuccess || !Okay) + { + new AdbException($"An error occurred while reading a response from ADB: {Message}", this); + } + } + /// /// Creates a new instance of the class, based on an /// error message returned by adb. diff --git a/AdvancedSharpAdbClient/Models/Enums/SyncCommand.cs b/AdvancedSharpAdbClient/Models/Enums/SyncCommand.cs index 60188e2c..5d179772 100644 --- a/AdvancedSharpAdbClient/Models/Enums/SyncCommand.cs +++ b/AdvancedSharpAdbClient/Models/Enums/SyncCommand.cs @@ -12,7 +12,7 @@ public enum SyncCommand : byte /// /// List the files in a folder. /// - LIST, + LIST = 1, /// /// Retrieve a file from device. diff --git a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs index 7a967b29..b79e3251 100644 --- a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs +++ b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs @@ -53,7 +53,7 @@ public FramebufferHeader(byte[] data) int index = 0; - Version = ReadUInt32(ref data); + Version = ReadUInt32(in data); if (Version > 2) { @@ -62,45 +62,45 @@ public FramebufferHeader(byte[] data) throw new InvalidOperationException($"Framebuffer version {Version} is not supported"); } - Bpp = ReadUInt32(ref data); + Bpp = ReadUInt32(in data); if (Version >= 2) { - ColorSpace = ReadUInt32(ref data); + ColorSpace = ReadUInt32(in data); } - Size = ReadUInt32(ref data); - Width = ReadUInt32(ref data); - Height = ReadUInt32(ref data); + Size = ReadUInt32(in data); + Width = ReadUInt32(in data); + Height = ReadUInt32(in data); Red = new ColorData { - Offset = ReadUInt32(ref data), - Length = ReadUInt32(ref data) + Offset = ReadUInt32(in data), + Length = ReadUInt32(in data) }; Blue = new ColorData { - Offset = ReadUInt32(ref data), - Length = ReadUInt32(ref data) + Offset = ReadUInt32(in data), + Length = ReadUInt32(in data) }; Green = new ColorData { - Offset = ReadUInt32(ref data), - Length = ReadUInt32(ref data) + Offset = ReadUInt32(in data), + Length = ReadUInt32(in data) }; Alpha = new ColorData { - Offset = ReadUInt32(ref data), - Length = ReadUInt32(ref data) + Offset = ReadUInt32(in data), + Length = ReadUInt32(in data) }; #if HAS_BUFFERS - uint ReadUInt32(ref ReadOnlySpan data) + uint ReadUInt32(in ReadOnlySpan data) #else - uint ReadUInt32(ref byte[] data) + uint ReadUInt32(in byte[] data) #endif { return (uint)(data[index++] | (data[index++] << 8) | (data[index++] << 16) | (data[index++] << 24)); diff --git a/AdvancedSharpAdbClient/Polyfills/Extensions/ExceptionExtensions.cs b/AdvancedSharpAdbClient/Polyfills/Extensions/ExceptionExtensions.cs index 7085e5c4..41a2f0fa 100644 --- a/AdvancedSharpAdbClient/Polyfills/Extensions/ExceptionExtensions.cs +++ b/AdvancedSharpAdbClient/Polyfills/Extensions/ExceptionExtensions.cs @@ -15,7 +15,7 @@ public static class ExceptionExtensions /// /// The reference type argument to validate as non-null. /// The name of the parameter with which corresponds. - public static void ThrowIfNull([NotNull] object argument, [CallerArgumentExpression(nameof(argument))] string? paramName = null) + public static void ThrowIfNull([NotNull] object? argument, [CallerArgumentExpression(nameof(argument))] string? paramName = null) { #if NET6_0_OR_GREATER ArgumentNullException.ThrowIfNull(argument, paramName); diff --git a/AdvancedSharpAdbClient/SyncService.Async.cs b/AdvancedSharpAdbClient/SyncService.Async.cs index dd245c40..a895ce6c 100644 --- a/AdvancedSharpAdbClient/SyncService.Async.cs +++ b/AdvancedSharpAdbClient/SyncService.Async.cs @@ -23,36 +23,17 @@ public virtual async Task OpenAsync(CancellationToken cancellationToken = defaul _ = await Socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); } - /// - /// Reopen this connection. - /// - /// A that enables to connection with the adb server. - /// A which can be used to cancel the asynchronous operation. - /// A which represents the asynchronous operation. - public virtual Task ReopenAsync(IAdbSocket socket, CancellationToken cancellationToken = default) + /// + public virtual async Task ReopenAsync(CancellationToken cancellationToken = default) { - if (Socket != null) - { - Socket.Dispose(); - Socket = null!; - } - Socket = socket; - return OpenAsync(cancellationToken); + await Socket.ReconnectAsync(true, cancellationToken).ConfigureAwait(false); + await OpenAsync(cancellationToken).ConfigureAwait(false); } - /// - /// Reopen this connection. - /// - /// A connection to an adb server. - /// A which can be used to cancel the asynchronous operation. - /// A which represents the asynchronous operation. - public Task ReopenAsync(IAdbClient client, CancellationToken cancellationToken = default) => ReopenAsync(Factories.AdbSocketFactory(client.EndPoint), cancellationToken); - /// public virtual async Task PushAsync(Stream stream, string remotePath, int permissions, DateTimeOffset timestamp, IProgress? progress = null, CancellationToken cancellationToken = default) { ExceptionExtensions.ThrowIfNull(stream); - ExceptionExtensions.ThrowIfNull(remotePath); if (remotePath.Length > MaxPathLength) @@ -153,7 +134,6 @@ public virtual async Task PushAsync(Stream stream, string remotePath, int permis public virtual async Task PullAsync(string remoteFilePath, Stream stream, IProgress? progress = null, CancellationToken cancellationToken = default) { ExceptionExtensions.ThrowIfNull(remoteFilePath); - ExceptionExtensions.ThrowIfNull(stream); // Get file information, including the file size, used to calculate the total amount of bytes to receive. @@ -231,9 +211,10 @@ public virtual async Task StatAsync(string remotePath, Cancellat // create the stat request message. await Socket.SendSyncRequestAsync(SyncCommand.STAT, remotePath, cancellationToken).ConfigureAwait(false); - if (await Socket.ReadSyncResponseAsync(cancellationToken).ContinueWith(x => x.Result != SyncCommand.STAT).ConfigureAwait(false)) + SyncCommand response = await Socket.ReadSyncResponseAsync(cancellationToken).ConfigureAwait(false); + if (response != SyncCommand.STAT) { - throw new AdbException($"The server returned an invalid sync response."); + throw new AdbException($"The server returned an invalid sync response {response}."); } // read the result, in a byte array containing 3 int @@ -251,6 +232,9 @@ public virtual async Task StatAsync(string remotePath, Cancellat /// public virtual async Task> GetDirectoryListingAsync(string remotePath, CancellationToken cancellationToken = default) { + bool isLocked = false; + + start: List value = []; // create the stat request message. @@ -260,13 +244,26 @@ public virtual async Task> GetDirectoryListingAsync(string { SyncCommand response = await Socket.ReadSyncResponseAsync(cancellationToken).ConfigureAwait(false); - if (response == SyncCommand.DONE) + if (response == 0) + { + if (isLocked) + { + throw new AdbException("The server returned an empty sync response."); + } + else + { + 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."); + throw new AdbException($"The server returned an invalid sync response {response}."); } FileStatistics entry = new(); @@ -283,6 +280,9 @@ public virtual async Task> GetDirectoryListingAsync(string /// public virtual async IAsyncEnumerable GetDirectoryAsyncListing(string remotePath, [EnumeratorCancellation] CancellationToken cancellationToken = default) { + bool isLocked = false; + + start: // create the stat request message. await Socket.SendSyncRequestAsync(SyncCommand.LIST, remotePath, cancellationToken).ConfigureAwait(false); @@ -290,13 +290,26 @@ public virtual async IAsyncEnumerable GetDirectoryAsyncListing(s { SyncCommand response = await Socket.ReadSyncResponseAsync(cancellationToken).ConfigureAwait(false); - if (response == SyncCommand.DONE) + if (response == 0) + { + if (isLocked) + { + throw new AdbException("The server returned an empty sync response."); + } + else + { + 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."); + throw new AdbException($"The server returned an invalid sync response {response}."); } FileStatistics entry = new(); @@ -304,6 +317,7 @@ public virtual async IAsyncEnumerable GetDirectoryAsyncListing(s entry.Path = await Socket.ReadSyncStringAsync(cancellationToken).ConfigureAwait(false); yield return entry; + isLocked = true; } } #endif diff --git a/AdvancedSharpAdbClient/SyncService.cs b/AdvancedSharpAdbClient/SyncService.cs index d7577720..0378f683 100644 --- a/AdvancedSharpAdbClient/SyncService.cs +++ b/AdvancedSharpAdbClient/SyncService.cs @@ -49,6 +49,15 @@ public partial class SyncService : ISyncService /// public event EventHandler? SyncProgressChanged; + /// + /// Initializes a new instance of the class. + /// + /// The device on which to interact with the files. + public SyncService(DeviceData device) + : this(Factories.AdbSocketFactory(new IPEndPoint(IPAddress.Loopback, AdbClient.AdbServerPort)), device) + { + } + /// /// Initializes a new instance of the class. /// @@ -110,32 +119,17 @@ public virtual void Open() _ = Socket.ReadAdbResponse(); } - /// - /// Reopen this connection. - /// - /// A that enables to connection with the adb server. - public virtual void Reopen(IAdbSocket socket) + /// + public virtual void Reopen() { - if (Socket != null) - { - Socket.Dispose(); - Socket = null!; - } - Socket = socket; + Socket.Reconnect(true); Open(); } - /// - /// Reopen this connection. - /// - /// A connection to an adb server. - public void Reopen(IAdbClient client) => Reopen(Factories.AdbSocketFactory(client.EndPoint)); - /// public virtual void Push(Stream stream, string remotePath, int permissions, DateTimeOffset timestamp, IProgress? progress = null, in bool isCancelled = false) { ExceptionExtensions.ThrowIfNull(stream); - ExceptionExtensions.ThrowIfNull(remotePath); if (remotePath.Length > MaxPathLength) @@ -232,7 +226,6 @@ public virtual void Push(Stream stream, string remotePath, int permissions, Date public virtual void Pull(string remoteFilePath, Stream stream, IProgress? progress = null, in bool isCancelled = false) { ExceptionExtensions.ThrowIfNull(remoteFilePath); - ExceptionExtensions.ThrowIfNull(stream); // Get file information, including the file size, used to calculate the total amount of bytes to receive. @@ -309,9 +302,10 @@ public virtual FileStatistics Stat(string remotePath) // create the stat request message. Socket.SendSyncRequest(SyncCommand.STAT, remotePath); - if (Socket.ReadSyncResponse() != SyncCommand.STAT) + SyncCommand response = Socket.ReadSyncResponse(); + if (response != SyncCommand.STAT) { - throw new AdbException($"The server returned an invalid sync response."); + throw new AdbException($"The server returned an invalid sync response {response}."); } // read the result, in a byte array containing 3 int @@ -329,6 +323,9 @@ public virtual FileStatistics Stat(string remotePath) /// public virtual IEnumerable GetDirectoryListing(string remotePath) { + bool isLocked = false; + + start: // create the stat request message. Socket.SendSyncRequest(SyncCommand.LIST, remotePath); @@ -336,13 +333,26 @@ public virtual IEnumerable GetDirectoryListing(string remotePath { SyncCommand response = Socket.ReadSyncResponse(); - if (response == SyncCommand.DONE) + if (response == 0) + { + if (isLocked) + { + throw new AdbException("The server returned an empty sync response."); + } + else + { + 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."); + throw new AdbException($"The server returned an invalid sync response {response}."); } FileStatistics entry = new(); @@ -350,6 +360,7 @@ public virtual IEnumerable GetDirectoryListing(string remotePath entry.Path = Socket.ReadSyncString(); yield return entry; + isLocked = true; } } diff --git a/AdvancedSharpAdbClient/TcpSocket.Async.cs b/AdvancedSharpAdbClient/TcpSocket.Async.cs index 415494a0..13452d07 100644 --- a/AdvancedSharpAdbClient/TcpSocket.Async.cs +++ b/AdvancedSharpAdbClient/TcpSocket.Async.cs @@ -29,19 +29,19 @@ public virtual async ValueTask ConnectAsync(EndPoint endPoint, CancellationToken } /// - public virtual ValueTask ReconnectAsync(CancellationToken cancellationToken = default) + public virtual ValueTask ReconnectAsync(bool isForce, CancellationToken cancellationToken = default) { - if (Socket.Connected) - { - // Already connected - nothing to do. - return ValueTask.CompletedTask; - } - else + if (isForce || !Socket.Connected) { Socket.Dispose(); Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); return ConnectAsync(EndPoint!, cancellationToken); } + else + { + // Already connected - nothing to do. + return ValueTask.CompletedTask; + } } #else /// @@ -63,22 +63,31 @@ public virtual async Task ConnectAsync(EndPoint endPoint, CancellationToken canc } /// - public virtual Task ReconnectAsync(CancellationToken cancellationToken = default) + public virtual Task ReconnectAsync(bool isForce, CancellationToken cancellationToken = default) { - if (Socket.Connected) - { - // Already connected - nothing to do. - return Extensions.CompletedTask; - } - else + if (isForce || !Socket.Connected) { Socket.Dispose(); Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); return ConnectAsync(EndPoint!, cancellationToken); } + else + { + // Already connected - nothing to do. + return Extensions.CompletedTask; + } } #endif + /// + public +#if NET6_0_OR_GREATER + ValueTask +#else + Task +#endif + ReconnectAsync(CancellationToken cancellationToken = default) => ReconnectAsync(false, cancellationToken); + /// public virtual Task SendAsync(byte[] buffer, int size, SocketFlags socketFlags, CancellationToken cancellationToken = default) => Socket.SendAsync(buffer, size, socketFlags, cancellationToken); diff --git a/AdvancedSharpAdbClient/TcpSocket.cs b/AdvancedSharpAdbClient/TcpSocket.cs index f6573d2e..fdcf020d 100644 --- a/AdvancedSharpAdbClient/TcpSocket.cs +++ b/AdvancedSharpAdbClient/TcpSocket.cs @@ -55,19 +55,19 @@ public virtual void Connect(EndPoint endPoint) } /// - public virtual void Reconnect() + public virtual void Reconnect(bool isForce = false) { - if (Socket.Connected) - { - // Already connected - nothing to do. - return; - } - else + if (isForce || !Socket.Connected) { Socket.Dispose(); Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); Connect(EndPoint!); } + else + { + // Already connected - nothing to do. + return; + } } /// diff --git a/Directory.Build.props b/Directory.Build.props index cf4ee39e..31af2be7 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -39,11 +39,6 @@ .NETCore,Version=v5.0 - - True - - 10.0 From 8f3556aa35564cb625b41cbd309194ebf0916366 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Sat, 11 Nov 2023 00:31:39 +0800 Subject: [PATCH 63/66] Catch XmlException when FindElement #84 --- .github/ISSUE_TEMPLATE/question.yaml | 12 ----- AdvancedSharpAdbClient/AdbClient.Async.cs | 59 ++++++++++++++--------- AdvancedSharpAdbClient/AdbClient.cs | 43 ++++++++++++----- 3 files changed, 66 insertions(+), 48 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/question.yaml diff --git a/.github/ISSUE_TEMPLATE/question.yaml b/.github/ISSUE_TEMPLATE/question.yaml deleted file mode 100644 index cf371cf2..00000000 --- a/.github/ISSUE_TEMPLATE/question.yaml +++ /dev/null @@ -1,12 +0,0 @@ -name: Question -description: Ask a question -title: "Question title" -labels: [question] -body: - - type: textarea - validations: - required: true - attributes: - label: Describe your question - description: A clear and concise description of your question. - placeholder: Can I ask that [...] \ No newline at end of file diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index 37ce6e95..220797b9 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -914,23 +914,27 @@ public async Task GetAppStatusAsync(DeviceData device, string package { while (!cancellationToken.IsCancellationRequested) { - XmlDocument? doc = await DumpScreenAsync(device, cancellationToken).ConfigureAwait(false); - if (doc != null) + try { - XmlNode? xmlNode = doc.SelectSingleNode(xpath); - if (xmlNode != null) + XmlDocument? doc = await DumpScreenAsync(device, cancellationToken).ConfigureAwait(false); + if (doc != null) { - Element? element = Element.FromXmlNode(this, device, xmlNode); - if (element != null) + XmlNode? xmlNode = doc.SelectSingleNode(xpath); + if (xmlNode != null) { - return element; + Element? element = Element.FromXmlNode(this, device, xmlNode); + if (element != null) + { + return element; + } } } } - if (cancellationToken == default) + catch (XmlException) { - break; + // Ignore XmlException and try again } + if (cancellationToken == default) { break; } } } catch (Exception e) @@ -953,26 +957,33 @@ public async Task> FindElementsAsync(DeviceData device, str { while (!cancellationToken.IsCancellationRequested) { - XmlDocument? doc = await DumpScreenAsync(device, cancellationToken).ConfigureAwait(false); - if (doc != null) + try { - XmlNodeList? xmlNodes = doc.SelectNodes(xpath); - if (xmlNodes != null) + XmlDocument? doc = await DumpScreenAsync(device, cancellationToken).ConfigureAwait(false); + if (doc != null) { - IEnumerable FindElements() + XmlNodeList? xmlNodes = doc.SelectNodes(xpath); + if (xmlNodes != null) { - for (int i = 0; i < xmlNodes.Count; i++) + IEnumerable FindElements() { - Element? element = Element.FromXmlNode(this, device, xmlNodes[i]); - if (element != null) + for (int i = 0; i < xmlNodes.Count; i++) { - yield return element; + Element? element = Element.FromXmlNode(this, device, xmlNodes[i]); + if (element != null) + { + yield return element; + } } } + return FindElements(); } - return FindElements(); } } + catch (XmlException) + { + // Ignore XmlException and try again + } if (cancellationToken == default) { break; } } } @@ -1001,6 +1012,10 @@ public async IAsyncEnumerable FindAsyncElements(DeviceData device, stri { doc = await DumpScreenAsync(device, cancellationToken).ConfigureAwait(false); } + catch (XmlException) + { + // Ignore XmlException and try again + } catch (Exception e) { // If a cancellation was requested, this main loop is interrupted with an exception @@ -1028,10 +1043,8 @@ public async IAsyncEnumerable FindAsyncElements(DeviceData device, stri break; } } - if (cancellationToken == default) - { - break; - } + + if (cancellationToken == default) { break; } } } #endif diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index f22cc0ca..c350f623 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -1059,26 +1059,31 @@ public AppStatus GetAppStatus(DeviceData device, string packageName) EnsureDevice(device); Stopwatch stopwatch = new(); stopwatch.Start(); - while (timeout == TimeSpan.Zero || stopwatch.Elapsed < timeout) + do { - XmlDocument? doc = DumpScreen(device); - if (doc != null) + try { - XmlNode? xmlNode = doc.SelectSingleNode(xpath); - if (xmlNode != null) + XmlDocument? doc = DumpScreen(device); + if (doc != null) { - Element? element = Element.FromXmlNode(this, device, xmlNode); - if (element != null) + XmlNode? xmlNode = doc.SelectSingleNode(xpath); + if (xmlNode != null) { - return element; + Element? element = Element.FromXmlNode(this, device, xmlNode); + if (element != null) + { + return element; + } } } } - if (timeout == TimeSpan.Zero) + catch (XmlException) { - break; + // Ignore XmlException and try again } + if (timeout == default) { break; } } + while (stopwatch.Elapsed < timeout); return null; } @@ -1088,9 +1093,19 @@ public IEnumerable FindElements(DeviceData device, string xpath = "hier EnsureDevice(device); Stopwatch stopwatch = new(); stopwatch.Start(); - while (timeout == TimeSpan.Zero || stopwatch.Elapsed < timeout) + do { - XmlDocument? doc = DumpScreen(device); + XmlDocument? doc = null; + + try + { + doc = DumpScreen(device); + } + catch (XmlException) + { + // Ignore XmlException and try again + } + if (doc != null) { XmlNodeList? xmlNodes = doc.SelectNodes(xpath); @@ -1107,8 +1122,10 @@ public IEnumerable FindElements(DeviceData device, string xpath = "hier break; } } - if (timeout == TimeSpan.Zero) { break; } + + if (timeout == default) { break; } } + while (stopwatch.Elapsed < timeout); } /// From 75675a05cedc64e755ae0ba45d91e2c960e54782 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Sat, 11 Nov 2023 16:05:26 +0800 Subject: [PATCH 64/66] Use foreach on List of DeviceExtensions --- AdvancedSharpAdbClient/AdbClient.Async.cs | 6 +- .../DeviceCommands/DeviceExtensions.Async.cs | 2 +- .../DeviceCommands/DeviceExtensions.cs | 5 +- AdvancedSharpAdbClient/Models/AdbResponse.cs | 2 +- ...taEventArgs.cs => DeviceData.EventArgs.cs} | 2 +- AdvancedSharpAdbClient/Models/Element.cs | 8 +-- .../Models/Enums/UnixFileType.cs | 2 +- .../Models/FileStatistics.cs | 7 +-- .../Models/FramebufferHeader.cs | 21 +++---- ...dEventArgs.cs => SyncService.EventArgs.cs} | 4 +- .../Polyfills/Extensions/StringExtensions.cs | 60 ++++++++++++++++--- AdvancedSharpAdbClient/SyncService.cs | 27 ++++----- 12 files changed, 91 insertions(+), 55 deletions(-) rename AdvancedSharpAdbClient/Models/{DeviceDataEventArgs.cs => DeviceData.EventArgs.cs} (94%) rename AdvancedSharpAdbClient/Models/{SyncProgressChangedEventArgs.cs => SyncService.EventArgs.cs} (83%) diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index 220797b9..6d278134 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -965,18 +965,18 @@ public async Task> FindElementsAsync(DeviceData device, str XmlNodeList? xmlNodes = doc.SelectNodes(xpath); if (xmlNodes != null) { - IEnumerable FindElements() + static IEnumerable FindElements(IAdbClient client, DeviceData device, XmlNodeList xmlNodes) { for (int i = 0; i < xmlNodes.Count; i++) { - Element? element = Element.FromXmlNode(this, device, xmlNodes[i]); + Element? element = Element.FromXmlNode(client, device, xmlNodes[i]); if (element != null) { yield return element; } } } - return FindElements(); + return FindElements(this, device, xmlNodes); } } } diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs index 5b78f26f..b61a470b 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs @@ -59,7 +59,7 @@ public static async Task StatAsync(this IAdbClient client, Devic /// The path to the directory on the device. /// A that can be used to cancel the task. /// A which return for each child item of the directory, a object with information of the item. - public static async Task> List(this IAdbClient client, DeviceData device, string remotePath, CancellationToken cancellationToken = default) + public static async Task> ListAsync(this IAdbClient client, DeviceData device, string remotePath, CancellationToken cancellationToken = default) { using ISyncService service = Factories.SyncServiceFactory(client, device); return await service.GetDirectoryListingAsync(remotePath, cancellationToken).ConfigureAwait(false); diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs index 450e5b86..5799897e 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs @@ -58,7 +58,10 @@ public static FileStatistics Stat(this IAdbClient client, DeviceData device, str public static IEnumerable List(this IAdbClient client, DeviceData device, string remotePath) { using ISyncService service = Factories.SyncServiceFactory(client, device); - return service.GetDirectoryListing(remotePath); + foreach (FileStatistics fileStatistics in service.GetDirectoryListing(remotePath)) + { + yield return fileStatistics; + } } /// diff --git a/AdvancedSharpAdbClient/Models/AdbResponse.cs b/AdvancedSharpAdbClient/Models/AdbResponse.cs index 0bd6e50d..3a9ccf9e 100644 --- a/AdvancedSharpAdbClient/Models/AdbResponse.cs +++ b/AdvancedSharpAdbClient/Models/AdbResponse.cs @@ -64,7 +64,7 @@ public void Throw() { if (!IOSuccess || !Okay) { - new AdbException($"An error occurred while reading a response from ADB: {Message}", this); + throw new AdbException($"An error occurred while reading a response from ADB: {Message}", this); } } diff --git a/AdvancedSharpAdbClient/Models/DeviceDataEventArgs.cs b/AdvancedSharpAdbClient/Models/DeviceData.EventArgs.cs similarity index 94% rename from AdvancedSharpAdbClient/Models/DeviceDataEventArgs.cs rename to AdvancedSharpAdbClient/Models/DeviceData.EventArgs.cs index 9ae1c49e..56581276 100644 --- a/AdvancedSharpAdbClient/Models/DeviceDataEventArgs.cs +++ b/AdvancedSharpAdbClient/Models/DeviceData.EventArgs.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // diff --git a/AdvancedSharpAdbClient/Models/Element.cs b/AdvancedSharpAdbClient/Models/Element.cs index fe1280f7..454a89a5 100644 --- a/AdvancedSharpAdbClient/Models/Element.cs +++ b/AdvancedSharpAdbClient/Models/Element.cs @@ -60,7 +60,7 @@ public Element(IAdbClient client, DeviceData device, XmlNode? xmlNode) } } - IEnumerable FindElements() + static IEnumerable FindElements(IAdbClient client, DeviceData device, XmlNode? xmlNode) { XmlNodeList? childNodes = xmlNode?.ChildNodes; if (childNodes != null) @@ -75,7 +75,7 @@ IEnumerable FindElements() } } } - Children = FindElements(); + Children = FindElements(client, device, xmlNode); } #if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER @@ -110,7 +110,7 @@ public Element(IAdbClient client, DeviceData device, Windows.Data.Xml.Dom.IXmlNo } } - IEnumerable FindElements() + static IEnumerable FindElements(IAdbClient client, DeviceData device, Windows.Data.Xml.Dom.IXmlNode xmlNode) { Windows.Data.Xml.Dom.XmlNodeList childNodes = xmlNode.ChildNodes; if (childNodes != null) @@ -125,7 +125,7 @@ IEnumerable FindElements() } } } - Children = FindElements(); + Children = FindElements(client, device, xmlNode); } #endif diff --git a/AdvancedSharpAdbClient/Models/Enums/UnixFileType.cs b/AdvancedSharpAdbClient/Models/Enums/UnixFileType.cs index 05fecae9..6190f701 100644 --- a/AdvancedSharpAdbClient/Models/Enums/UnixFileType.cs +++ b/AdvancedSharpAdbClient/Models/Enums/UnixFileType.cs @@ -10,7 +10,7 @@ namespace AdvancedSharpAdbClient.Models /// Describes the properties of a file on an Android device. /// [Flags] - public enum UnixFileType : ushort + public enum UnixFileType { /// /// The mask that can be used to retrieve the file type from a . diff --git a/AdvancedSharpAdbClient/Models/FileStatistics.cs b/AdvancedSharpAdbClient/Models/FileStatistics.cs index a567f963..dbd766c7 100644 --- a/AdvancedSharpAdbClient/Models/FileStatistics.cs +++ b/AdvancedSharpAdbClient/Models/FileStatistics.cs @@ -37,11 +37,8 @@ public FileStatistics() { } /// public DateTimeOffset Time { get; set; } - /// - /// Gets a that represents the current object. - /// - /// The of the current object. - public override string ToString() => Path; + /// + public override string ToString() => StringExtensions.Join("\t", FileType, Time, FileType, Path); /// public override bool Equals([NotNullWhen(true)] object? obj) => Equals(obj as FileStatistics); diff --git a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs index b79e3251..5c329740 100644 --- a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs +++ b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs @@ -102,9 +102,7 @@ uint ReadUInt32(in ReadOnlySpan data) #else uint ReadUInt32(in byte[] data) #endif - { - return (uint)(data[index++] | (data[index++] << 8) | (data[index++] << 16) | (data[index++] << 24)); - } + => (uint)(data[index++] | (data[index++] << 8) | (data[index++] << 16) | (data[index++] << 24)); } /// @@ -196,17 +194,14 @@ public byte this[int index] _ => throw new IndexOutOfRangeException("Index was out of range. Must be non-negative and less than the size of the collection.") }; - byte GetByte(uint value) + byte GetByte(uint value) => (index % 4) switch { - return (index % 4) switch - { - 0 => (byte)value, - 1 => (byte)(value >> 8), - 2 => (byte)(value >> 16), - 3 => (byte)(value >> 24), - _ => throw new InvalidOperationException() - }; - } + 0 => (byte)value, + 1 => (byte)(value >> 8), + 2 => (byte)(value >> 16), + 3 => (byte)(value >> 24), + _ => throw new InvalidOperationException() + }; } } diff --git a/AdvancedSharpAdbClient/Models/SyncProgressChangedEventArgs.cs b/AdvancedSharpAdbClient/Models/SyncService.EventArgs.cs similarity index 83% rename from AdvancedSharpAdbClient/Models/SyncProgressChangedEventArgs.cs rename to AdvancedSharpAdbClient/Models/SyncService.EventArgs.cs index 68f2dbed..9503363d 100644 --- a/AdvancedSharpAdbClient/Models/SyncProgressChangedEventArgs.cs +++ b/AdvancedSharpAdbClient/Models/SyncService.EventArgs.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // @@ -7,7 +7,7 @@ namespace AdvancedSharpAdbClient.Models { /// - /// Provides data for the event. + /// Provides data for the event. /// public class SyncProgressChangedEventArgs(long current, long total) : EventArgs { diff --git a/AdvancedSharpAdbClient/Polyfills/Extensions/StringExtensions.cs b/AdvancedSharpAdbClient/Polyfills/Extensions/StringExtensions.cs index 443982ae..1b1db8ba 100644 --- a/AdvancedSharpAdbClient/Polyfills/Extensions/StringExtensions.cs +++ b/AdvancedSharpAdbClient/Polyfills/Extensions/StringExtensions.cs @@ -94,6 +94,52 @@ public static string[] Split(this string text, char separator, int count, String public static bool EndsWith(this string text, char value) => text.EndsWith(new string([value])); #endif + /// + /// Concatenates the elements of an object array, using the specified separator between each element. + /// + /// The string to use as a separator. is included + /// in the returned string only if has more than one element. + /// An array that contains the elements to concatenate. + /// A string that consists of the elements of delimited by the + /// string.-or- if values has zero elements. + public static string Join(string? separator, params object?[] values) + { +#if NETFRAMEWORK && !NET40_OR_GREATER + ExceptionExtensions.ThrowIfNull(values); + + if (values.Length == 0 || values[0] == null) + { + return string.Empty; + } + + separator ??= string.Empty; + + StringBuilder stringBuilder = new(); + string? text = values[0]?.ToString(); + if (text != null) + { + _ = stringBuilder.Append(text); + } + + for (int i = 1; i < values.Length; i++) + { + _ = stringBuilder.Append(separator); + if (values[i] != null) + { + text = values[i]?.ToString(); + if (text != null) + { + _ = stringBuilder.Append(text); + } + } + } + + return stringBuilder.ToString(); +#else + return string.Join(separator, values); +#endif + } + /// /// Concatenates the members of a constructed collection of type , /// using the specified separator between each member. @@ -110,24 +156,24 @@ public static string Join(string? separator, IEnumerable values) separator ??= string.Empty; - using IEnumerator en = values.GetEnumerator(); - if (!en.MoveNext()) + using IEnumerator enumerator = values.GetEnumerator(); + if (!enumerator.MoveNext()) { return string.Empty; } StringBuilder result = new(); - if (en.Current != null) + if (enumerator.Current != null) { - _ = result.Append(en.Current); + _ = result.Append(enumerator.Current); } - while (en.MoveNext()) + while (enumerator.MoveNext()) { _ = result.Append(separator); - if (en.Current != null) + if (enumerator.Current != null) { - _ = result.Append(en.Current); + _ = result.Append(enumerator.Current); } } return result.ToString(); diff --git a/AdvancedSharpAdbClient/SyncService.cs b/AdvancedSharpAdbClient/SyncService.cs index 0378f683..d0d6ec18 100644 --- a/AdvancedSharpAdbClient/SyncService.cs +++ b/AdvancedSharpAdbClient/SyncService.cs @@ -386,27 +386,22 @@ public void Dispose() GC.SuppressFinalize(this); } - private void ReadStatistics(FileStatistics value) + /// + /// Reads the statistics of a file from the socket. + /// + /// The to store the statistics. + protected void ReadStatistics(FileStatistics value) { byte[] statResult = new byte[12]; _ = Socket.Read(statResult); - if (!BitConverter.IsLittleEndian) - { - Array.Reverse(statResult, 0, 4); - Array.Reverse(statResult, 4, 4); - Array.Reverse(statResult, 8, 4); - } + int index = 0; -#if HAS_BUFFERS - value.FileType = (UnixFileType)BitConverter.ToInt32(statResult); - value.Size = BitConverter.ToInt32(statResult.AsSpan(4)); - value.Time = DateTimeExtensions.FromUnixTimeSeconds(BitConverter.ToInt32(statResult.AsSpan(8))); -#else - value.FileType = (UnixFileType)BitConverter.ToInt32(statResult, 0); - value.Size = BitConverter.ToInt32(statResult, 4); - value.Time = DateTimeExtensions.FromUnixTimeSeconds(BitConverter.ToInt32(statResult, 8)); -#endif + value.FileType = (UnixFileType)ReadInt32(in statResult); + value.Size = ReadInt32(in statResult); + value.Time = DateTimeExtensions.FromUnixTimeSeconds(ReadInt32(in statResult)); + + int ReadInt32(in byte[] data) => data[index++] | (data[index++] << 8) | (data[index++] << 16) | (data[index++] << 24); } } } From 44a8a1926693a316ec06ca08790908b8b7de730a Mon Sep 17 00:00:00 2001 From: wherewhere Date: Mon, 13 Nov 2023 20:10:01 +0800 Subject: [PATCH 65/66] Change some class to struct --- .../Models/ForwardSpecTests.cs | 3 -- .../SyncServiceTests.Async.cs | 1 - .../SyncServiceTests.cs | 1 - AdvancedSharpAdbClient/AdbClient.Async.cs | 4 +- AdvancedSharpAdbClient/AdbClient.cs | 4 +- .../DeviceCommands/Models/AndroidProcess.cs | 6 +-- .../Extensions/AdbClientExtensions.Async.cs | 6 +-- .../Extensions/AdbClientExtensions.cs | 6 +-- AdvancedSharpAdbClient/Models/AdbResponse.cs | 25 +++-------- AdvancedSharpAdbClient/Models/DeviceData.cs | 4 +- .../Models/FileStatistics.cs | 43 ++++++++++++------ AdvancedSharpAdbClient/Models/ForwardSpec.cs | 41 ++++++++--------- .../Models/FramebufferHeader.cs | 20 ++++----- AdvancedSharpAdbClient/SyncService.Async.cs | 44 ++++++++----------- AdvancedSharpAdbClient/SyncService.cs | 24 +++++----- 15 files changed, 110 insertions(+), 122 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/Models/ForwardSpecTests.cs b/AdvancedSharpAdbClient.Tests/Models/ForwardSpecTests.cs index 276729b2..bc766576 100644 --- a/AdvancedSharpAdbClient.Tests/Models/ForwardSpecTests.cs +++ b/AdvancedSharpAdbClient.Tests/Models/ForwardSpecTests.cs @@ -13,7 +13,6 @@ public void TcpTest() { ForwardSpec value = ForwardSpec.Parse("tcp:1234"); - Assert.NotNull(value); Assert.Equal(ForwardProtocol.Tcp, value.Protocol); Assert.Equal(1234, value.Port); Assert.Equal(0, value.ProcessId); @@ -27,7 +26,6 @@ public void SocketText() { ForwardSpec value = ForwardSpec.Parse("localabstract:/tmp/1234"); - Assert.NotNull(value); Assert.Equal(ForwardProtocol.LocalAbstract, value.Protocol); Assert.Equal(0, value.Port); Assert.Equal(0, value.ProcessId); @@ -41,7 +39,6 @@ public void JdwpTest() { ForwardSpec value = ForwardSpec.Parse("jdwp:1234"); - Assert.NotNull(value); Assert.Equal(ForwardProtocol.JavaDebugWireProtocol, value.Protocol); Assert.Equal(0, value.Port); Assert.Equal(1234, value.ProcessId); diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs index 08078e9b..ad0bafe2 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs @@ -28,7 +28,6 @@ public async void StatAsyncTest() return await service.StatAsync("/fstab.donatello"); }); - Assert.NotNull(value); Assert.Equal(UnixFileType.Regular, value.FileType & UnixFileType.TypeMask); Assert.Equal(597, value.Size); Assert.Equal(DateTimeExtensions.Epoch.ToLocalTime(), value.Time); diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs index 8c6b98f5..cff3b5fa 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs @@ -38,7 +38,6 @@ public void StatTest() return service.Stat("/fstab.donatello"); }); - Assert.NotNull(value); Assert.Equal(UnixFileType.Regular, value.FileType & UnixFileType.TypeMask); Assert.Equal(597, value.Size); Assert.Equal(DateTimeExtensions.Epoch.ToLocalTime(), value.Time); diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index 6d278134..c15d9a52 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -144,7 +144,7 @@ public async Task> ListForwardAsync(DeviceData device, string data = await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); string[] parts = data.Split(separator, StringSplitOptions.RemoveEmptyEntries); - return parts.Select(x => new ForwardData(x)); + return parts.Select(x => ForwardData.FromString(x)); } /// @@ -160,7 +160,7 @@ public async Task> ListReverseForwardAsync(DeviceData d string data = await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false); string[] parts = data.Split(separator, StringSplitOptions.RemoveEmptyEntries); - return parts.Select(x => new ForwardData(x)); + return parts.Select(x => ForwardData.FromString(x)); } /// diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index c350f623..59ebd5d0 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -293,7 +293,7 @@ public IEnumerable ListForward(DeviceData device) string data = socket.ReadString(); string[] parts = data.Split(separator, StringSplitOptions.RemoveEmptyEntries); - return parts.Select(x => new ForwardData(x)); + return parts.Select(x => ForwardData.FromString(x)); } /// @@ -309,7 +309,7 @@ public IEnumerable ListReverseForward(DeviceData device) string data = socket.ReadString(); string[] parts = data.Split(separator, StringSplitOptions.RemoveEmptyEntries); - return parts.Select(x => new ForwardData(x)); + return parts.Select(x => ForwardData.FromString(x)); } /// diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs index 683dd4fd..7bd1dd7f 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs @@ -9,15 +9,15 @@ namespace AdvancedSharpAdbClient.Models.DeviceCommands /// /// Represents a process running on an Android device. /// - public class AndroidProcess + public readonly struct AndroidProcess { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the struct. /// public AndroidProcess() { } /// - /// Initializes a new instance of the class from it representation. + /// Initializes a new instance of the struct from it representation. /// /// A which represents a . /// A value indicating whether the output of /proc/{pid}/stat is prefixed with /proc/{pid}/cmdline or not. diff --git a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs index 7585b6ab..be2808eb 100644 --- a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs +++ b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs @@ -29,7 +29,7 @@ public static partial class AdbClientExtensions /// If your requested to start forwarding to local port TCP:0, the port number of the TCP port /// which has been opened. In all other cases, 0. public static Task CreateForwardAsync(this IAdbClient client, DeviceData device, ForwardSpec local, ForwardSpec remote, bool allowRebind, CancellationToken cancellationToken = default) => - client.CreateForwardAsync(device, local?.ToString()!, remote?.ToString()!, allowRebind, cancellationToken); + client.CreateForwardAsync(device, local.ToString(), remote.ToString(), allowRebind, cancellationToken); /// /// Creates a port forwarding between a local and a remote port. @@ -77,7 +77,7 @@ public static Task CreateForwardAsync(this IAdbClient client, DeviceData de /// If your requested to start reverse to remote port TCP:0, the port number of the TCP port /// which has been opened. In all other cases, 0. public static Task CreateReverseForwardAsync(this IAdbClient client, DeviceData device, ForwardSpec remote, ForwardSpec local, bool allowRebind, CancellationToken cancellationToken = default) => - client.CreateReverseForwardAsync(device, remote?.ToString()!, local?.ToString()!, allowRebind, cancellationToken); + client.CreateReverseForwardAsync(device, remote.ToString(), local.ToString(), allowRebind, cancellationToken); /// /// Remove a reverse port forwarding between a remote and a local port. @@ -88,7 +88,7 @@ public static Task CreateReverseForwardAsync(this IAdbClient client, Device /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. public static Task RemoveReverseForwardAsync(this IAdbClient client, DeviceData device, ForwardSpec remote, CancellationToken cancellationToken = default) => - client.RemoveReverseForwardAsync(device, remote?.ToString()!, cancellationToken); + client.RemoveReverseForwardAsync(device, remote.ToString(), cancellationToken); /// /// Executes a command on the adb server. diff --git a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.cs b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.cs index 41467308..7cb1158b 100644 --- a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.cs +++ b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.cs @@ -26,7 +26,7 @@ public static partial class AdbClientExtensions /// If your requested to start forwarding to local port TCP:0, the port number of the TCP port /// which has been opened. In all other cases, 0. public static int CreateForward(this IAdbClient client, DeviceData device, ForwardSpec local, ForwardSpec remote, bool allowRebind) => - client.CreateForward(device, local?.ToString()!, remote?.ToString()!, allowRebind); + client.CreateForward(device, local.ToString(), remote.ToString(), allowRebind); /// /// Asks the ADB server to reverse forward local connections from @@ -41,7 +41,7 @@ public static int CreateForward(this IAdbClient client, DeviceData device, Forwa /// If your requested to start reverse to remote port TCP:0, the port number of the TCP port /// which has been opened. In all other cases, 0. public static int CreateReverseForward(this IAdbClient client, DeviceData device, ForwardSpec remote, ForwardSpec local, bool allowRebind) => - client.CreateReverseForward(device, remote?.ToString()!, local?.ToString()!, allowRebind); + client.CreateReverseForward(device, remote.ToString(), local.ToString(), allowRebind); /// /// Remove a reverse port forwarding between a remote and a local port. @@ -50,7 +50,7 @@ public static int CreateReverseForward(this IAdbClient client, DeviceData device /// The device on which to remove the reverse port forwarding /// Specification of the remote that was forwarded public static void RemoveReverseForward(this IAdbClient client, DeviceData device, ForwardSpec remote) => - client.RemoveReverseForward(device, remote?.ToString()!); + client.RemoveReverseForward(device, remote.ToString()); /// /// Executes a command on the adb server. diff --git a/AdvancedSharpAdbClient/Models/AdbResponse.cs b/AdvancedSharpAdbClient/Models/AdbResponse.cs index 3a9ccf9e..6b6013e5 100644 --- a/AdvancedSharpAdbClient/Models/AdbResponse.cs +++ b/AdvancedSharpAdbClient/Models/AdbResponse.cs @@ -81,35 +81,24 @@ public void Throw() Timeout = false }; - /// - /// Determines whether the specified is equal to the current object. - /// - /// The to compare with the current object. - /// if the specified object is equal to the current object; otherwise, . - public override readonly bool Equals([NotNullWhen(true)] object? obj) => obj is AdbResponse other && Equals(other); + /// + public override bool Equals([NotNullWhen(true)] object? obj) => obj is AdbResponse other && Equals(other); - /// - /// Determines whether the specified is equal to the current object. - /// - /// The to compare with the current object. - /// if the specified object is equal to the current object; otherwise, . - public readonly bool Equals(AdbResponse other) => + /// + public bool Equals(AdbResponse other) => other.IOSuccess == IOSuccess && string.Equals(other.Message, Message, StringComparison.OrdinalIgnoreCase) && other.Okay == Okay && other.Timeout == Timeout; - /// - /// Gets the hash code for the current . - /// - /// A hash code for the current . - public override readonly int GetHashCode() => HashCode.Combine(IOSuccess, Message, Okay, Timeout); + /// + public override int GetHashCode() => HashCode.Combine(IOSuccess, Message, Okay, Timeout); /// /// Returns a that represents the current . /// /// OK if the response is an OK response, or Error: {Message} if the response indicates an error. - public override readonly string ToString() => Equals(OK) ? "OK" : $"Error: {Message}"; + public override string ToString() => Equals(OK) ? "OK" : $"Error: {Message}"; /// /// Tests whether two objects are equally. diff --git a/AdvancedSharpAdbClient/Models/DeviceData.cs b/AdvancedSharpAdbClient/Models/DeviceData.cs index ffd40e37..6bced8e0 100644 --- a/AdvancedSharpAdbClient/Models/DeviceData.cs +++ b/AdvancedSharpAdbClient/Models/DeviceData.cs @@ -62,7 +62,7 @@ public DeviceData(string data) /// /// Gets or sets the device state. /// - public DeviceState State { get; init; } + public DeviceState State { get; set; } /// /// Gets or sets the device model name. @@ -147,7 +147,7 @@ public override int GetHashCode() /// /// The device state string. /// The device state. - internal static DeviceState GetStateFromString(string state) + public static DeviceState GetStateFromString(string state) { // Default to the unknown state DeviceState value; diff --git a/AdvancedSharpAdbClient/Models/FileStatistics.cs b/AdvancedSharpAdbClient/Models/FileStatistics.cs index dbd766c7..f478c94a 100644 --- a/AdvancedSharpAdbClient/Models/FileStatistics.cs +++ b/AdvancedSharpAdbClient/Models/FileStatistics.cs @@ -10,10 +10,10 @@ namespace AdvancedSharpAdbClient.Models /// /// Contains information about a file on the remote device. /// - public class FileStatistics : IEquatable + public struct FileStatistics : IEquatable { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the struct. /// public FileStatistics() { } @@ -25,33 +25,48 @@ public FileStatistics() { } /// /// Gets or sets the attributes of the file. /// - public UnixFileType FileType { get; set; } + public UnixFileType FileType { get; init; } /// /// Gets or sets the total file size, in bytes. /// - public int Size { get; set; } + public int Size { get; init; } /// /// Gets or sets the time of last modification. /// - public DateTimeOffset Time { get; set; } + public DateTimeOffset Time { get; init; } /// - public override string ToString() => StringExtensions.Join("\t", FileType, Time, FileType, Path); + public override readonly bool Equals([NotNullWhen(true)] object? obj) => obj is FileStatistics other && Equals(other); /// - public override bool Equals([NotNullWhen(true)] object? obj) => Equals(obj as FileStatistics); + public readonly bool Equals(FileStatistics other) => + Path == other.Path + && FileType == other.FileType + && Size == other.Size + && Time == other.Time; /// - public bool Equals([NotNullWhen(true)] FileStatistics? other) => - other is not null - && Path == other.Path - && FileType == other.FileType - && Size == other.Size - && Time == other.Time; + public override readonly int GetHashCode() => HashCode.Combine(Path, FileType, Size, Time); /// - public override int GetHashCode() => HashCode.Combine(Path, FileType, Size, Time); + public override readonly string ToString() => StringExtensions.Join("\t", FileType, Time, FileType, Path); + + /// + /// 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 ==(FileStatistics left, FileStatistics 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 !=(FileStatistics left, FileStatistics right) => !left.Equals(right); } } diff --git a/AdvancedSharpAdbClient/Models/ForwardSpec.cs b/AdvancedSharpAdbClient/Models/ForwardSpec.cs index d44d8cbb..f61ab3bf 100644 --- a/AdvancedSharpAdbClient/Models/ForwardSpec.cs +++ b/AdvancedSharpAdbClient/Models/ForwardSpec.cs @@ -12,7 +12,7 @@ namespace AdvancedSharpAdbClient.Models /// /// Represents an adb forward specification as used by the various adb port forwarding functions. /// - public class ForwardSpec : IEquatable + public readonly struct ForwardSpec : IEquatable { /// /// Provides a mapping between a and a @@ -30,12 +30,12 @@ public class ForwardSpec : IEquatable }; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the struct. /// public ForwardSpec() { } /// - /// Initializes a new instance of the class from its representation. + /// Initializes a new instance of the struct from its representation. /// /// A which represents a . public ForwardSpec(string spec) @@ -66,7 +66,6 @@ public ForwardSpec(string spec) { throw new ArgumentOutOfRangeException(nameof(spec)); } - ProcessId = intValue; break; @@ -75,7 +74,6 @@ public ForwardSpec(string spec) { throw new ArgumentOutOfRangeException(nameof(spec)); } - Port = intValue; break; @@ -122,9 +120,9 @@ or ForwardProtocol.LocalReserved /// public override string ToString() { - string protocolString = Mappings.FirstOrDefault(v => v.Value == Protocol).Key; - - return Protocol switch + ForwardProtocol protocol = Protocol; + string protocolString = Mappings.FirstOrDefault(v => v.Value == protocol).Key; + return protocol switch { ForwardProtocol.JavaDebugWireProtocol => $"{protocolString}:{ProcessId}", ForwardProtocol.Tcp => $"{protocolString}:{Port}", @@ -140,21 +138,20 @@ or ForwardProtocol.LocalReserved public override int GetHashCode() => HashCode.Combine(Protocol, Port, ProcessId, SocketName); /// - public override bool Equals([NotNullWhen(true)] object? obj) => Equals(obj as ForwardSpec); + public override bool Equals([NotNullWhen(true)] object? obj) => obj is ForwardSpec other && Equals(other); /// - public bool Equals([NotNullWhen(true)] ForwardSpec? other) => - other != null - && other.Protocol == Protocol - && Protocol switch - { - ForwardProtocol.JavaDebugWireProtocol => ProcessId == other.ProcessId, - ForwardProtocol.Tcp => Port == other.Port, - ForwardProtocol.LocalAbstract - or ForwardProtocol.LocalFilesystem - or ForwardProtocol.LocalReserved - or ForwardProtocol.Device => string.Equals(SocketName, other.SocketName), - _ => false, - }; + public bool Equals(ForwardSpec other) => + other.Protocol == Protocol + && Protocol switch + { + ForwardProtocol.JavaDebugWireProtocol => ProcessId == other.ProcessId, + ForwardProtocol.Tcp => Port == other.Port, + ForwardProtocol.LocalAbstract + or ForwardProtocol.LocalFilesystem + or ForwardProtocol.LocalReserved + or ForwardProtocol.Device => string.Equals(SocketName, other.SocketName), + _ => false, + }; } } diff --git a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs index 5c329740..a827df56 100644 --- a/AdvancedSharpAdbClient/Models/FramebufferHeader.cs +++ b/AdvancedSharpAdbClient/Models/FramebufferHeader.cs @@ -226,7 +226,7 @@ public byte this[int index] #if NET [SupportedOSPlatform("windows")] #endif - public readonly Bitmap? ToImage(byte[] buffer) + public Bitmap? ToImage(byte[] buffer) { ExceptionExtensions.ThrowIfNull(buffer); @@ -260,9 +260,9 @@ public byte this[int index] #if NET [SupportedOSPlatform("windows")] #endif - private readonly PixelFormat StandardizePixelFormat(Span buffer) + private PixelFormat StandardizePixelFormat(Span buffer) #else - private readonly PixelFormat StandardizePixelFormat(byte[] buffer) + private PixelFormat StandardizePixelFormat(byte[] buffer) #endif { // Initial parameter validation. @@ -375,16 +375,16 @@ private readonly PixelFormat StandardizePixelFormat(byte[] buffer) /// A which can be used to cancel the asynchronous task. /// A that represents the image contained in the frame buffer, or /// if the framebuffer does not contain any data. This can happen when DRM is enabled on the device. - public readonly Task ToBitmap(byte[] buffer, CoreDispatcher dispatcher, CancellationToken cancellationToken = default) + public Task ToBitmap(byte[] buffer, CoreDispatcher dispatcher, CancellationToken cancellationToken = default) { - FramebufferHeader self = this; - if (dispatcher.HasThreadAccess) { return ToBitmap(buffer, cancellationToken); } else { + FramebufferHeader self = this; + TaskCompletionSource taskCompletionSource = new(); _ = dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () => @@ -413,16 +413,16 @@ private readonly PixelFormat StandardizePixelFormat(byte[] buffer) /// A that represents the image contained in the frame buffer, or /// if the framebuffer does not contain any data. This can happen when DRM is enabled on the device. [ContractVersion(typeof(UniversalApiContract), 327680u)] - public readonly Task ToBitmap(byte[] buffer, DispatcherQueue dispatcher, CancellationToken cancellationToken = default) + public Task ToBitmap(byte[] buffer, DispatcherQueue dispatcher, CancellationToken cancellationToken = default) { - FramebufferHeader self = this; - if (ApiInformation.IsMethodPresent("Windows.System.DispatcherQueue", "HasThreadAccess") && dispatcher.HasThreadAccess) { return ToBitmap(buffer, cancellationToken); } else { + FramebufferHeader self = this; + TaskCompletionSource taskCompletionSource = new(); if (!dispatcher.TryEnqueue(async () => @@ -452,7 +452,7 @@ private readonly PixelFormat StandardizePixelFormat(byte[] buffer) /// A which can be used to cancel the asynchronous task. /// A that represents the image contained in the frame buffer, or /// if the framebuffer does not contain any data. This can happen when DRM is enabled on the device. - public readonly async Task ToBitmap(byte[] buffer, CancellationToken cancellationToken = default) + public async Task ToBitmap(byte[] buffer, CancellationToken cancellationToken = default) { if (buffer == null) { diff --git a/AdvancedSharpAdbClient/SyncService.Async.cs b/AdvancedSharpAdbClient/SyncService.Async.cs index a895ce6c..5bae67d2 100644 --- a/AdvancedSharpAdbClient/SyncService.Async.cs +++ b/AdvancedSharpAdbClient/SyncService.Async.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Reflection; using System.Runtime.CompilerServices; using System.Threading; @@ -219,12 +220,8 @@ public virtual async Task StatAsync(string remotePath, Cancellat // read the result, in a byte array containing 3 int // (mode, size, time) - FileStatistics value = new() - { - Path = remotePath - }; - - await ReadStatisticsAsync(value, cancellationToken).ConfigureAwait(false); + FileStatistics value = await ReadStatisticsAsync(cancellationToken).ConfigureAwait(false); + value.Path = remotePath; return value; } @@ -266,8 +263,7 @@ public virtual async Task> GetDirectoryListingAsync(string throw new AdbException($"The server returned an invalid sync response {response}."); } - FileStatistics entry = new(); - await ReadStatisticsAsync(entry, cancellationToken).ConfigureAwait(false); + FileStatistics entry = await ReadStatisticsAsync(cancellationToken).ConfigureAwait(false); entry.Path = await Socket.ReadSyncStringAsync(cancellationToken).ConfigureAwait(false); value.Add(entry); @@ -312,8 +308,7 @@ public virtual async IAsyncEnumerable GetDirectoryAsyncListing(s throw new AdbException($"The server returned an invalid sync response {response}."); } - FileStatistics entry = new(); - await ReadStatisticsAsync(entry, cancellationToken).ConfigureAwait(false); + FileStatistics entry = await ReadStatisticsAsync(cancellationToken).ConfigureAwait(false); entry.Path = await Socket.ReadSyncStringAsync(cancellationToken).ConfigureAwait(false); yield return entry; @@ -322,27 +317,26 @@ public virtual async IAsyncEnumerable GetDirectoryAsyncListing(s } #endif - private async Task ReadStatisticsAsync(FileStatistics value, CancellationToken cancellationToken = default) + /// + /// Reads the statistics of a file from the socket. + /// + /// A that can be used to cancel the task. + /// A which return a object that contains information about the file. + protected async Task ReadStatisticsAsync(CancellationToken cancellationToken = default) { byte[] statResult = new byte[12]; _ = await Socket.ReadAsync(statResult, cancellationToken).ConfigureAwait(false); - if (!BitConverter.IsLittleEndian) + int index = 0; + + return new FileStatistics { - Array.Reverse(statResult, 0, 4); - Array.Reverse(statResult, 4, 4); - Array.Reverse(statResult, 8, 4); - } + FileType = (UnixFileType)ReadInt32(in statResult), + Size = ReadInt32(in statResult), + Time = DateTimeExtensions.FromUnixTimeSeconds(ReadInt32(in statResult)) + }; -#if HAS_BUFFERS - value.FileType = (UnixFileType)BitConverter.ToInt32(statResult); - value.Size = BitConverter.ToInt32(statResult.AsSpan(4)); - value.Time = DateTimeExtensions.FromUnixTimeSeconds(BitConverter.ToInt32(statResult.AsSpan(8))); -#else - value.FileType = (UnixFileType)BitConverter.ToInt32(statResult, 0); - value.Size = BitConverter.ToInt32(statResult, 4); - value.Time = DateTimeExtensions.FromUnixTimeSeconds(BitConverter.ToInt32(statResult, 8)); -#endif + int ReadInt32(in byte[] data) => data[index++] | (data[index++] << 8) | (data[index++] << 16) | (data[index++] << 24); } } } diff --git a/AdvancedSharpAdbClient/SyncService.cs b/AdvancedSharpAdbClient/SyncService.cs index d0d6ec18..e8e118d2 100644 --- a/AdvancedSharpAdbClient/SyncService.cs +++ b/AdvancedSharpAdbClient/SyncService.cs @@ -310,12 +310,8 @@ public virtual FileStatistics Stat(string remotePath) // read the result, in a byte array containing 3 int // (mode, size, time) - FileStatistics value = new() - { - Path = remotePath - }; - - ReadStatistics(value); + FileStatistics value = ReadStatistics(); + value.Path = remotePath; return value; } @@ -355,8 +351,7 @@ public virtual IEnumerable GetDirectoryListing(string remotePath throw new AdbException($"The server returned an invalid sync response {response}."); } - FileStatistics entry = new(); - ReadStatistics(entry); + FileStatistics entry = ReadStatistics(); entry.Path = Socket.ReadSyncString(); yield return entry; @@ -389,17 +384,20 @@ public void Dispose() /// /// Reads the statistics of a file from the socket. /// - /// The to store the statistics. - protected void ReadStatistics(FileStatistics value) + /// A object that contains information about the file. + protected FileStatistics ReadStatistics() { byte[] statResult = new byte[12]; _ = Socket.Read(statResult); int index = 0; - value.FileType = (UnixFileType)ReadInt32(in statResult); - value.Size = ReadInt32(in statResult); - value.Time = DateTimeExtensions.FromUnixTimeSeconds(ReadInt32(in statResult)); + 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); } From 9781d3dd9f6735ec249a9fd132810f825400bfe2 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Wed, 15 Nov 2023 00:20:56 +0800 Subject: [PATCH 66/66] Upgrade .NET nuget to version 8.0 --- .github/ISSUE_TEMPLATE/bug_report.yaml | 1 + .github/workflows/build-and-test.yml | 2 -- AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 5dc4b579..a8c5d798 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -65,6 +65,7 @@ body: - ".NET 6" - ".NET 7" - ".NET 8" + - ".NET 9" - "Others" - type: dropdown attributes: diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 68fcfc13..09af876b 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -34,7 +34,6 @@ jobs: uses: actions/setup-dotnet@v3 with: dotnet-version: ${{ env.DOTNET_VERSION }} - dotnet-quality: 'preview' - name: Install Dependencies run: dotnet restore -p:FullTargets=false @@ -60,7 +59,6 @@ jobs: uses: actions/setup-dotnet@v3 with: dotnet-version: ${{ env.DOTNET_VERSION }} - dotnet-quality: 'preview' - name: Install Dependencies run: dotnet restore -p:FullTargets=true diff --git a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj index eec44c2b..b7f76945 100644 --- a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj +++ b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj @@ -71,7 +71,7 @@ - +