diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs
index c95bcf51..fb08a73b 100644
--- a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs
+++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs
@@ -640,7 +640,7 @@ public async void UnrootAsyncTest()
}
///
- /// Tests the method.
+ /// Tests the method.
///
[Fact]
public async void InstallAsyncTest()
@@ -688,7 +688,7 @@ await RunTestAsync(
}
///
- /// Tests the method.
+ /// Tests the method.
///
[Fact]
public async void InstallMultipleAsyncTest()
@@ -747,7 +747,7 @@ await RunTestAsync(
}
///
- /// Tests the method.
+ /// Tests the method.
///
[Fact]
public async void InstallMultipleWithBaseAsyncTest()
@@ -847,7 +847,7 @@ public async void InstallCreateAsyncTest()
}
///
- /// Tests the method.
+ /// Tests the method.
///
[Fact]
public async void InstallWriteAsyncTest()
@@ -920,6 +920,246 @@ await RunTestAsync(
() => TestClient.InstallCommitAsync(Device, "936013062"));
}
+#if WINDOWS10_0_17763_0_OR_GREATER
+ ///
+ /// Tests the method.
+ ///
+ [Fact]
+ public async void InstallWinRTAsyncTest()
+ {
+ // The app data is sent in chunks of 32 kb
+ List applicationDataChunks = [];
+
+ await using (FileStream stream = File.OpenRead("Assets/TestApp/base.apk"))
+ {
+ byte[] buffer = new byte[32 * 1024];
+ int read = 0;
+
+ while ((read = await stream.ReadAsync(buffer.AsMemory(0, buffer.Length))) > 0)
+ {
+ byte[] array = buffer.AsSpan(0, read).ToArray();
+ applicationDataChunks.Add(array);
+ }
+ }
+
+ byte[] response = "Success\n"u8.ToArray();
+
+ StorageFile storageFile = await StorageFile.GetFileFromPathAsync(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Assets\TestApp\base.apk"));
+ using (IRandomAccessStreamWithContentType stream = await storageFile.OpenReadAsync())
+ {
+ string[] requests =
+ [
+ "host:transport:169.254.109.177:5555",
+ $"exec:cmd package 'install' -S {stream.Size}"
+ ];
+
+ await RunTestAsync(
+ OkResponses(2),
+ NoResponseMessages,
+ requests,
+ NoSyncRequests,
+ NoSyncResponses,
+ [response],
+ applicationDataChunks,
+ () => TestClient.InstallAsync(Device, stream,
+ new InstallProgress(
+ PackageInstallProgressState.Preparing,
+ PackageInstallProgressState.Uploading,
+ PackageInstallProgressState.Installing,
+ PackageInstallProgressState.Finished)));
+ }
+ }
+
+ ///
+ /// Tests the method.
+ ///
+ [Fact]
+ public async void InstallMultipleWinRTAsyncTest()
+ {
+ // The app data is sent in chunks of 32 kb
+ List applicationDataChunks = [];
+
+ await using (FileStream stream = File.OpenRead("Assets/TestApp/split_config.arm64_v8a.apk"))
+ {
+ byte[] buffer = new byte[32 * 1024];
+ int read = 0;
+
+ while ((read = await stream.ReadAsync(buffer.AsMemory(0, buffer.Length))) > 0)
+ {
+ byte[] array = buffer.AsSpan(0, read).ToArray();
+ applicationDataChunks.Add(array);
+ }
+ }
+
+ StorageFile storageFile = await StorageFile.GetFileFromPathAsync(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Assets\TestApp\split_config.arm64_v8a.apk"));
+ using IRandomAccessStreamWithContentType abiStream = await storageFile.OpenReadAsync();
+
+ string[] requests =
+ [
+ "host:transport:169.254.109.177:5555",
+ "exec:cmd package 'install-create' -p com.google.android.gms",
+ "host:transport:169.254.109.177:5555",
+ $"exec:cmd package 'install-write' -S {abiStream.Size} 936013062 splitAPK0.apk",
+ "host:transport:169.254.109.177:5555",
+ "exec:cmd package 'install-commit' 936013062"
+ ];
+
+ byte[][] responses =
+ [
+ Encoding.ASCII.GetBytes($"Success: streamed {abiStream.Size} bytes\n")
+ ];
+
+ await using MemoryStream sessionStream = new(Encoding.ASCII.GetBytes("Success: created install session [936013062]\r\n"));
+ await using MemoryStream commitStream = new("Success\n"u8.ToArray());
+
+ await RunTestAsync(
+ OkResponses(6),
+ NoResponseMessages,
+ requests,
+ NoSyncRequests,
+ NoSyncResponses,
+ responses,
+ applicationDataChunks,
+ [sessionStream, commitStream],
+ () => TestClient.InstallMultipleAsync(Device, [abiStream], "com.google.android.gms",
+ new InstallProgress(
+ PackageInstallProgressState.Preparing,
+ PackageInstallProgressState.CreateSession,
+ PackageInstallProgressState.Uploading,
+ PackageInstallProgressState.Installing,
+ PackageInstallProgressState.Finished)));
+ }
+
+ ///
+ /// Tests the method.
+ ///
+ [Fact]
+ public async void InstallMultipleWinRTWithBaseAsyncTest()
+ {
+ // The app data is sent in chunks of 32 kb
+ List applicationDataChunks = [];
+
+ await using (FileStream stream = File.OpenRead("Assets/TestApp/base.apk"))
+ {
+ byte[] buffer = new byte[32 * 1024];
+ int read = 0;
+
+ while ((read = await stream.ReadAsync(buffer.AsMemory(0, buffer.Length))) > 0)
+ {
+ byte[] array = buffer.AsSpan(0, read).ToArray();
+ applicationDataChunks.Add(array);
+ }
+ }
+
+ await using (FileStream stream = File.OpenRead("Assets/TestApp/split_config.arm64_v8a.apk"))
+ {
+ byte[] buffer = new byte[32 * 1024];
+ int read = 0;
+
+ while ((read = await stream.ReadAsync(buffer.AsMemory(0, buffer.Length))) > 0)
+ {
+ byte[] array = buffer.AsSpan(0, read).ToArray();
+ applicationDataChunks.Add(array);
+ }
+ }
+
+ StorageFile storageFile = await StorageFile.GetFileFromPathAsync(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Assets\TestApp\base.apk"));
+ using IRandomAccessStreamWithContentType baseStream = await storageFile.OpenReadAsync();
+ storageFile = await StorageFile.GetFileFromPathAsync(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Assets\TestApp\split_config.arm64_v8a.apk"));
+ using IRandomAccessStreamWithContentType abiStream = await storageFile.OpenReadAsync();
+
+ string[] requests =
+ [
+ "host:transport:169.254.109.177:5555",
+ "exec:cmd package 'install-create'",
+ "host:transport:169.254.109.177:5555",
+ $"exec:cmd package 'install-write' -S {baseStream.Size} 936013062 baseAPK.apk",
+ "host:transport:169.254.109.177:5555",
+ $"exec:cmd package 'install-write' -S {abiStream.Size} 936013062 splitAPK0.apk",
+ "host:transport:169.254.109.177:5555",
+ "exec:cmd package 'install-commit' 936013062"
+ ];
+
+ byte[][] responses =
+ [
+ Encoding.ASCII.GetBytes($"Success: streamed {baseStream.Size} bytes\n"),
+ Encoding.ASCII.GetBytes($"Success: streamed {abiStream.Size} bytes\n")
+ ];
+
+ using MemoryStream sessionStream = new(Encoding.ASCII.GetBytes("Success: created install session [936013062]\r\n"));
+ using MemoryStream commitStream = new("Success\n"u8.ToArray());
+
+ await RunTestAsync(
+ OkResponses(8),
+ NoResponseMessages,
+ requests,
+ NoSyncRequests,
+ NoSyncResponses,
+ responses,
+ applicationDataChunks,
+ [sessionStream, commitStream],
+ () => TestClient.InstallMultipleAsync(Device, baseStream, [abiStream],
+ new InstallProgress(
+ PackageInstallProgressState.Preparing,
+ PackageInstallProgressState.CreateSession,
+ PackageInstallProgressState.Uploading,
+ PackageInstallProgressState.Installing,
+ PackageInstallProgressState.Finished)));
+ }
+
+ ///
+ /// Tests the method.
+ ///
+ [Fact]
+ public async void InstallWriteWinRTAsyncTest()
+ {
+ // The app data is sent in chunks of 32 kb
+ List applicationDataChunks = [];
+
+ await using (FileStream stream = File.OpenRead("Assets/TestApp/base.apk"))
+ {
+ byte[] buffer = new byte[32 * 1024];
+ int read = 0;
+
+ while ((read = await stream.ReadAsync(buffer.AsMemory(0, buffer.Length))) > 0)
+ {
+ byte[] array = buffer.AsSpan(0, read).ToArray();
+ applicationDataChunks.Add(array);
+ }
+ }
+
+ StorageFile storageFile = await StorageFile.GetFileFromPathAsync(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Assets\TestApp\base.apk"));
+ using (IRandomAccessStreamWithContentType stream = await storageFile.OpenReadAsync())
+ {
+ string[] requests =
+ [
+ "host:transport:169.254.109.177:5555",
+ $"exec:cmd package 'install-write' -S {stream.Size} 936013062 base.apk"
+ ];
+
+ byte[] response = Encoding.ASCII.GetBytes($"Success: streamed {stream.Size} bytes\n");
+
+ double temp = 0;
+ Progress progress = new();
+ progress.ProgressChanged += (sender, args) =>
+ {
+ Assert.True(temp <= args, $"{nameof(args)}: {args} is less than {temp}.");
+ temp = args;
+ };
+
+ await RunTestAsync(
+ OkResponses(2),
+ NoResponseMessages,
+ requests,
+ NoSyncRequests,
+ NoSyncResponses,
+ [response],
+ applicationDataChunks,
+ () => TestClient.InstallWriteAsync(Device, stream, "base", "936013062", progress));
+ }
+ }
+#endif
+
///
/// Tests the method.
///
diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs
index 754f3841..b9def008 100644
--- a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs
+++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs
@@ -748,7 +748,7 @@ public void UnrootTest()
}
///
- /// Tests the method.
+ /// Tests the method.
///
[Fact]
public void InstallTest()
@@ -796,7 +796,7 @@ public void InstallTest()
}
///
- /// Tests the method.
+ /// Tests the method.
///
[Fact]
public void InstallMultipleTest()
@@ -871,7 +871,7 @@ public void InstallMultipleTest()
}
///
- /// Tests the method.
+ /// Tests the method.
///
[Fact]
public void InstallMultipleWithBaseTest()
@@ -987,7 +987,7 @@ public void InstallCreateTest()
}
///
- /// Tests the method.
+ /// Tests the method.
///
[Fact]
public void InstallWriteTest()
diff --git a/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj b/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj
index 112bf97e..e2510bfb 100644
--- a/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj
+++ b/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj
@@ -29,8 +29,8 @@
-
-
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/Models/VersionInfoTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/Models/VersionInfoTests.cs
index 094da8d6..bcd4d383 100644
--- a/AdvancedSharpAdbClient.Tests/DeviceCommands/Models/VersionInfoTests.cs
+++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/Models/VersionInfoTests.cs
@@ -34,7 +34,7 @@ public void TryAsVersionTest(int versionCode, string versionName, bool expected)
#if WINDOWS10_0_17763_0_OR_GREATER
///
- /// Tests the method.
+ /// Tests the method.
///
[Theory]
[InlineData(1231, "1.2.3.1", true)]
@@ -45,7 +45,7 @@ public void TryAsVersionTest(int versionCode, string versionName, bool expected)
[InlineData(098765456, "Unknown", false)]
public void TryAsPackageVersionTest(int versionCode, string versionName, bool expected)
{
- bool result = new VersionInfo(versionCode, versionName).TryAsPackageVersion(out Windows.ApplicationModel.PackageVersion version);
+ bool result = new VersionInfo(versionCode, versionName).TryAsPackageVersion(out PackageVersion version);
Assert.Equal(expected, result);
if (expected)
{
diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs
index 148bc6c3..073c2d08 100644
--- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs
+++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs
@@ -171,9 +171,9 @@ public Task ExecuteServerCommandAsync(string target, string command, IAdbSocket
Task IAdbClient.GetFrameBufferAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException();
- void IAdbClient.Install(DeviceData device, Stream apk, IProgress progress, params string[] arguments) => throw new NotImplementedException();
+ void IAdbClient.Install(DeviceData device, Stream apk, Action progress, params string[] arguments) => throw new NotImplementedException();
- Task IAdbClient.InstallAsync(DeviceData device, Stream apk, IProgress progress, CancellationToken cancellationToken, params string[] arguments) => throw new NotImplementedException();
+ Task IAdbClient.InstallAsync(DeviceData device, Stream apk, Action progress, CancellationToken cancellationToken, params string[] arguments) => throw new NotImplementedException();
void IAdbClient.InstallCommit(DeviceData device, string session) => throw new NotImplementedException();
@@ -183,17 +183,17 @@ public Task ExecuteServerCommandAsync(string target, string command, IAdbSocket
Task IAdbClient.InstallCreateAsync(DeviceData device, string packageName, CancellationToken cancellationToken, params string[] arguments) => throw new NotImplementedException();
- void IAdbClient.InstallMultiple(DeviceData device, IEnumerable splitAPKs, string packageName, IProgress progress, params string[] arguments) => throw new NotImplementedException();
+ void IAdbClient.InstallMultiple(DeviceData device, IEnumerable splitAPKs, string packageName, Action progress, params string[] arguments) => throw new NotImplementedException();
- void IAdbClient.InstallMultiple(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, IProgress progress, params string[] arguments) => throw new NotImplementedException();
+ void IAdbClient.InstallMultiple(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, Action progress, params string[] arguments) => throw new NotImplementedException();
- Task IAdbClient.InstallMultipleAsync(DeviceData device, IEnumerable splitAPKs, string packageName, IProgress progress, CancellationToken cancellationToken, params string[] arguments) => throw new NotImplementedException();
+ Task IAdbClient.InstallMultipleAsync(DeviceData device, IEnumerable splitAPKs, string packageName, Action progress, CancellationToken cancellationToken, params string[] arguments) => throw new NotImplementedException();
- Task IAdbClient.InstallMultipleAsync(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, IProgress progress, CancellationToken cancellationToken, params string[] arguments) => throw new NotImplementedException();
+ Task IAdbClient.InstallMultipleAsync(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, Action progress, CancellationToken cancellationToken, params string[] arguments) => throw new NotImplementedException();
- void IAdbClient.InstallWrite(DeviceData device, Stream apk, string apkName, string session, IProgress progress) => throw new NotImplementedException();
+ void IAdbClient.InstallWrite(DeviceData device, Stream apk, string apkName, string session, Action progress) => throw new NotImplementedException();
- Task IAdbClient.InstallWriteAsync(DeviceData device, Stream apk, string apkName, string session, IProgress progress, CancellationToken cancellationToken) => throw new NotImplementedException();
+ Task IAdbClient.InstallWriteAsync(DeviceData device, Stream apk, string apkName, string session, Action progress, CancellationToken cancellationToken) => throw new NotImplementedException();
void IAdbClient.KillAdb() => throw new NotImplementedException();
@@ -235,7 +235,7 @@ public Task ExecuteServerCommandAsync(string target, string command, IAdbSocket
Task IAdbClient.RootAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException();
- void IAdbClient.RunLogService(DeviceData device, Action messageSink, params LogId[] logNames) => throw new NotImplementedException();
+ void IAdbClient.RunLogService(DeviceData device, Action messageSink, in bool isCancelled, params LogId[] logNames) => throw new NotImplementedException();
Task IAdbClient.RunLogServiceAsync(DeviceData device, Action messageSink, CancellationToken cancellationToken, params LogId[] logNames) => throw new NotImplementedException();
diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs
index e54ebc2f..ff2d77b2 100644
--- a/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs
+++ b/AdvancedSharpAdbClient.Tests/Dummys/DummySyncService.cs
@@ -25,24 +25,24 @@ public async Task OpenAsync(CancellationToken cancellationToken = default)
IsOpen = true;
}
- public void Pull(string remotePath, Stream stream, IProgress progress = null, in bool isCancelled = false)
+ public void Pull(string remotePath, Stream stream, Action progress = null, in bool isCancelled = false)
{
for (int i = 0; i <= 100; i++)
{
- progress?.Report(new SyncProgressChangedEventArgs(i, 100));
+ progress?.Invoke(new SyncProgressChangedEventArgs(i, 100));
}
}
- public async Task PullAsync(string remotePath, Stream stream, IProgress progress, CancellationToken cancellationToken = default)
+ public async Task PullAsync(string remotePath, Stream stream, Action progress, CancellationToken cancellationToken = default)
{
- await Task.Yield();
for (int i = 0; i <= 100; i++)
{
- progress?.Report(new SyncProgressChangedEventArgs(i, 100));
+ await Task.Yield();
+ progress?.Invoke(new SyncProgressChangedEventArgs(i, 100));
}
}
- public void Push(Stream stream, string remotePath, int permissions, DateTimeOffset timestamp, IProgress progress = null, in bool isCancelled = false)
+ public void Push(Stream stream, string remotePath, int permissions, DateTimeOffset timestamp, Action progress = null, in bool isCancelled = false)
{
for (int i = 0; i <= 100; i++)
{
@@ -50,20 +50,20 @@ public void Push(Stream stream, string remotePath, int permissions, DateTimeOffs
{
UploadedFiles[remotePath] = stream;
}
- progress?.Report(new SyncProgressChangedEventArgs(i, 100));
+ progress?.Invoke(new SyncProgressChangedEventArgs(i, 100));
}
}
- public async 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, Action progress, CancellationToken cancellationToken = default)
{
- await Task.Yield();
for (int i = 0; i <= 100; i++)
{
+ await Task.Yield();
if (i == 100)
{
UploadedFiles[remotePath] = stream;
}
- progress?.Report(new SyncProgressChangedEventArgs(i, 100));
+ progress?.Invoke(new SyncProgressChangedEventArgs(i, 100));
}
}
diff --git a/AdvancedSharpAdbClient.Tests/Logs/LogTests.cs b/AdvancedSharpAdbClient.Tests/Logs/LogTests.cs
index 72ab9c3c..5198d70d 100644
--- a/AdvancedSharpAdbClient.Tests/Logs/LogTests.cs
+++ b/AdvancedSharpAdbClient.Tests/Logs/LogTests.cs
@@ -21,8 +21,8 @@ public void ReadLogTest()
Assert.IsType(log);
Assert.Equal(707, log.ProcessId);
- Assert.Equal(1254, log.ThreadId);
- Assert.Equal(3u, log.Id);
+ Assert.Equal(1254u, log.ThreadId);
+ Assert.Equal((LogId)3, log.Id);
Assert.NotNull(log.Data);
Assert.Equal(179, log.Data.Length);
Assert.Equal(new DateTime(2015, 11, 14, 23, 38, 20, DateTimeKind.Utc), log.TimeStamp);
@@ -50,8 +50,8 @@ public async void ReadLogAsyncTest()
Assert.IsType(log);
Assert.Equal(707, log.ProcessId);
- Assert.Equal(1254, log.ThreadId);
- Assert.Equal(3u, log.Id);
+ Assert.Equal(1254u, log.ThreadId);
+ Assert.Equal((LogId)3, log.Id);
Assert.NotNull(log.Data);
Assert.Equal(179, log.Data.Length);
Assert.Equal(new DateTime(2015, 11, 14, 23, 38, 20, DateTimeKind.Utc), log.TimeStamp);
@@ -76,8 +76,8 @@ public void ReadEventLogTest()
Assert.IsType(entry);
Assert.Equal(707, entry.ProcessId);
- Assert.Equal(1291, entry.ThreadId);
- Assert.Equal(2u, entry.Id);
+ Assert.Equal(1291u, entry.ThreadId);
+ Assert.Equal((LogId)2, entry.Id);
Assert.NotNull(entry.Data);
Assert.Equal(39, entry.Data.Length);
Assert.Equal(new DateTime(2015, 11, 16, 1, 48, 40, DateTimeKind.Utc), entry.TimeStamp);
@@ -110,8 +110,8 @@ public async void ReadEventLogAsyncTest()
Assert.IsType(entry);
Assert.Equal(707, entry.ProcessId);
- Assert.Equal(1291, entry.ThreadId);
- Assert.Equal(2u, entry.Id);
+ Assert.Equal(1291u, entry.ThreadId);
+ Assert.Equal((LogId)2, entry.Id);
Assert.NotNull(entry.Data);
Assert.Equal(39, entry.Data.Length);
Assert.Equal(new DateTime(2015, 11, 16, 1, 48, 40, DateTimeKind.Utc), entry.TimeStamp);
diff --git a/AdvancedSharpAdbClient.Tests/Properties/GlobalUsings.cs b/AdvancedSharpAdbClient.Tests/Properties/GlobalUsings.cs
index bccc2545..07658998 100644
--- a/AdvancedSharpAdbClient.Tests/Properties/GlobalUsings.cs
+++ b/AdvancedSharpAdbClient.Tests/Properties/GlobalUsings.cs
@@ -11,4 +11,11 @@
#region AdvancedSharpAdbClient.Tests
global using AdvancedSharpAdbClient.Tests;
global using AdvancedSharpAdbClient.Logs.Tests;
-#endregion
\ No newline at end of file
+#endregion
+
+#if WINDOWS10_0_17763_0_OR_GREATER
+global using Windows.ApplicationModel;
+global using System.Runtime.InteropServices.WindowsRuntime;
+global using Windows.Storage;
+global using Windows.Storage.Streams;
+#endif
\ No newline at end of file
diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs
index c7b1e540..e6a38d1d 100644
--- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs
+++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs
@@ -142,7 +142,7 @@ public async void GetAsyncListingTest()
}
///
- /// Tests the method.
+ /// Tests the method.
///
[Fact]
public async void PullAsyncTest()
@@ -177,7 +177,7 @@ await RunTestAsync(
}
///
- /// Tests the method.
+ /// Tests the method.
///
[Fact]
public async void PushAsyncTest()
@@ -208,5 +208,77 @@ await RunTestAsync(
await service.PushAsync(stream, "/sdcard/test", 0644, new DateTime(2015, 11, 2, 23, 0, 0, DateTimeKind.Utc), null);
});
}
+
+#if WINDOWS10_0_17763_0_OR_GREATER
+ ///
+ /// Tests the method.
+ ///
+ [Fact]
+ public async void PullWinRTAsyncTest()
+ {
+ using InMemoryRandomAccessStream stream = new();
+ byte[] content = await File.ReadAllBytesAsync("Assets/Fstab.bin");
+ byte[] contentLength = BitConverter.GetBytes(content.Length);
+
+ await RunTestAsync(
+ OkResponses(2),
+ 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],
+ contentLength,
+ content
+ ],
+ null,
+ async () =>
+ {
+ using SyncService service = new(Socket, Device);
+ await service.PullAsync("/fstab.donatello", stream, null);
+ });
+
+ IBuffer buffer = await stream.GetInputStreamAt(0).ReadAsync(new byte[(int)stream.Size].AsBuffer(), (uint)stream.Size, InputStreamOptions.None);
+ // Make sure the data that has been sent to the stream is the expected data
+ Assert.Equal(content, buffer.ToArray());
+ }
+
+ ///
+ /// Tests the method.
+ ///
+ [Fact]
+ public async void PushWinRTAsyncTest()
+ {
+ StorageFile storageFile = await StorageFile.GetFileFromPathAsync(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Assets\Fstab.bin"));
+ using IRandomAccessStreamWithContentType stream = await storageFile.OpenReadAsync();
+ byte[] content = await File.ReadAllBytesAsync("Assets/Fstab.bin");
+ byte[] contentMessage =
+ [
+ .. SyncCommand.DATA.GetBytes(),
+ .. BitConverter.GetBytes(content.Length),
+ .. content,
+ ];
+
+ await RunTestAsync(
+ OkResponses(2),
+ NoResponseMessages,
+ ["host:transport:169.254.109.177:5555", "sync:"],
+ [
+ (SyncCommand.SEND, "/sdcard/test,644"),
+ (SyncCommand.DONE, "1446505200")
+ ],
+ [SyncCommand.OKAY],
+ null,
+ [contentMessage],
+ 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);
+ });
+ }
+#endif
}
}
diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs
index 6791a809..88a4a159 100644
--- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs
+++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs
@@ -98,7 +98,7 @@ public void GetListingTest()
}
///
- /// Tests the method.
+ /// Tests the method.
///
[Fact]
public void PullTest()
@@ -133,7 +133,7 @@ public void PullTest()
}
///
- /// Tests the method.
+ /// Tests the method.
///
[Fact]
public void PushTest()
diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs
index 3caa4128..75f4e483 100644
--- a/AdvancedSharpAdbClient/AdbClient.Async.cs
+++ b/AdvancedSharpAdbClient/AdbClient.Async.cs
@@ -274,7 +274,7 @@ public virtual async Task GetFrameBufferAsync(DeviceData device, Ca
}
///
- public virtual async Task RunLogServiceAsync(DeviceData device, Action messageSink, CancellationToken cancellationToken, params LogId[] logNames)
+ public virtual async Task RunLogServiceAsync(DeviceData device, Action messageSink, CancellationToken cancellationToken = default, params LogId[] logNames)
{
EnsureDevice(device);
ExceptionExtensions.ThrowIfNull(messageSink);
@@ -401,9 +401,9 @@ protected async Task RootAsync(string request, DeviceData device, CancellationTo
string responseMessage =
#if HAS_BUFFERS
- Encoding.UTF8.GetString(buffer.AsSpan(0, read));
+ Encoding.GetString(buffer.AsSpan(0, read));
#else
- Encoding.UTF8.GetString(buffer, 0, read);
+ Encoding.GetString(buffer, 0, read);
#endif
// see https://android.googlesource.com/platform/packages/modules/adb/+/refs/heads/master/daemon/restart_service.cpp
@@ -421,9 +421,9 @@ protected async Task RootAsync(string request, DeviceData device, CancellationTo
}
///
- public virtual async Task InstallAsync(DeviceData device, Stream apk, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments)
+ public virtual async Task InstallAsync(DeviceData device, Stream apk, Action? progress = null, CancellationToken cancellationToken = default, params string[] arguments)
{
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Preparing));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Preparing));
EnsureDevice(device);
ExceptionExtensions.ThrowIfNull(apk);
@@ -469,30 +469,30 @@ public virtual async Task InstallAsync(DeviceData device, Stream apk, IProgress<
await socket.SendAsync(buffer, read, cancellationToken).ConfigureAwait(false);
#endif
totalBytesRead += read;
- progress?.Report(new InstallProgressEventArgs(0, 1, totalBytesToProcess == 0 ? 0 : totalBytesRead * 100d / totalBytesToProcess));
+ progress?.Invoke(new InstallProgressEventArgs(0, 1, totalBytesToProcess == 0 ? 0 : totalBytesRead * 100d / totalBytesToProcess));
}
- progress?.Report(new InstallProgressEventArgs(1, 1, 100));
+ progress?.Invoke(new InstallProgressEventArgs(1, 1, 100));
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Installing));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Installing));
read = await socket.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
string value =
#if HAS_BUFFERS
- Encoding.UTF8.GetString(buffer.AsSpan(0, read));
+ Encoding.GetString(buffer.AsSpan(0, read));
#else
- Encoding.UTF8.GetString(buffer, 0, read);
+ Encoding.GetString(buffer, 0, read);
#endif
if (!value.Contains("Success"))
{
throw new AdbException(value);
}
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Finished));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Finished));
}
///
- public virtual async Task InstallMultipleAsync(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments)
+ public virtual async Task InstallMultipleAsync(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, Action? progress = null, CancellationToken cancellationToken = default, params string[] arguments)
{
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Preparing));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Preparing));
EnsureDevice(device);
ExceptionExtensions.ThrowIfNull(baseAPK);
@@ -508,12 +508,12 @@ public virtual async Task InstallMultipleAsync(DeviceData device, Stream baseAPK
throw new ArgumentOutOfRangeException(nameof(splitAPKs), "The apk stream must be a readable and seekable stream");
}
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.CreateSession));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.CreateSession));
string session = await InstallCreateAsync(device, null, cancellationToken, arguments).ConfigureAwait(false);
int splitAPKsCount = splitAPKs.Count();
void OnMainSyncProgressChanged(string? sender, double args) =>
- progress?.Report(new InstallProgressEventArgs(sender == null ? 1 : 0, splitAPKsCount + 1, args / 2));
+ progress?.Invoke(new InstallProgressEventArgs(sender == null ? 1 : 0, splitAPKsCount + 1, args / 2));
await InstallWriteAsync(device, baseAPK, nameof(baseAPK), session, OnMainSyncProgressChanged, cancellationToken).ConfigureAwait(false);
@@ -531,22 +531,22 @@ void OnSplitSyncProgressChanged(string? sender, double args)
{
status[path] = args;
}
- progress?.Report(new InstallProgressEventArgs(progressCount, splitAPKsCount + 1, (status.Values.Select(x => x / splitAPKsCount).Sum() + 100) / 2));
+ progress?.Invoke(new InstallProgressEventArgs(progressCount, splitAPKsCount + 1, (status.Values.Select(x => x / splitAPKsCount).Sum() + 100) / 2));
}
}
int i = 0;
await splitAPKs.Select(splitAPK => InstallWriteAsync(device, splitAPK, $"{nameof(splitAPK)}{i++}", session, OnSplitSyncProgressChanged, cancellationToken)).WhenAll().ConfigureAwait(false);
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Installing));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Installing));
await InstallCommitAsync(device, session, cancellationToken).ConfigureAwait(false);
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Finished));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Finished));
}
///
- public virtual async Task InstallMultipleAsync(DeviceData device, IEnumerable splitAPKs, string packageName, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments)
+ public virtual async Task InstallMultipleAsync(DeviceData device, IEnumerable splitAPKs, string packageName, Action? progress = null, CancellationToken cancellationToken = default, params string[] arguments)
{
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Preparing));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Preparing));
EnsureDevice(device);
ExceptionExtensions.ThrowIfNull(splitAPKs);
@@ -557,7 +557,7 @@ public virtual async Task InstallMultipleAsync(DeviceData device, IEnumerable x / splitAPKsCount).Sum()));
+ progress?.Invoke(new InstallProgressEventArgs(progressCount, splitAPKsCount, status.Values.Select(x => x / splitAPKsCount).Sum()));
}
}
int i = 0;
await splitAPKs.Select(splitAPK => InstallWriteAsync(device, splitAPK, $"{nameof(splitAPK)}{i++}", session, OnSyncProgressChanged, cancellationToken)).WhenAll().ConfigureAwait(false);
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Installing));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Installing));
await InstallCommitAsync(device, session, cancellationToken).ConfigureAwait(false);
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Finished));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Finished));
}
///
- public virtual async Task InstallCreateAsync(DeviceData device, string? packageName, CancellationToken cancellationToken, params string[] arguments)
+ public virtual async Task InstallCreateAsync(DeviceData device, string? packageName, CancellationToken cancellationToken = default, params string[] arguments)
{
EnsureDevice(device);
@@ -628,9 +628,9 @@ public virtual async Task InstallCreateAsync(DeviceData device, string?
}
///
- public virtual async Task InstallWriteAsync(DeviceData device, Stream apk, string apkName, string session, IProgress? progress = null, CancellationToken cancellationToken = default)
+ public virtual async Task InstallWriteAsync(DeviceData device, Stream apk, string apkName, string session, Action? progress = null, CancellationToken cancellationToken = default)
{
- progress?.Report(0);
+ progress?.Invoke(0);
EnsureDevice(device);
ExceptionExtensions.ThrowIfNull(apk);
@@ -671,22 +671,22 @@ public virtual async Task InstallWriteAsync(DeviceData device, Stream apk, strin
await socket.SendAsync(buffer, read, cancellationToken).ConfigureAwait(false);
#endif
totalBytesRead += read;
- progress?.Report(totalBytesToProcess == 0 ? 0 : totalBytesRead * 100d / totalBytesToProcess);
+ progress?.Invoke(totalBytesToProcess == 0 ? 0 : totalBytesRead * 100d / totalBytesToProcess);
}
read = await socket.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
string value =
#if HAS_BUFFERS
- Encoding.UTF8.GetString(buffer.AsSpan(0, read));
+ Encoding.GetString(buffer.AsSpan(0, read));
#else
- Encoding.UTF8.GetString(buffer, 0, read);
+ Encoding.GetString(buffer, 0, read);
#endif
if (!value.Contains("Success"))
{
throw new AdbException(value);
}
- progress?.Report(100);
+ progress?.Invoke(100);
}
///
@@ -750,9 +750,9 @@ protected virtual async Task InstallWriteAsync(DeviceData device, Stream apk, st
read = await socket.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
string value =
#if HAS_BUFFERS
- Encoding.UTF8.GetString(buffer.AsSpan(0, read));
+ Encoding.GetString(buffer.AsSpan(0, read));
#else
- Encoding.UTF8.GetString(buffer, 0, read);
+ Encoding.GetString(buffer, 0, read);
#endif
if (!value.Contains("Success"))
@@ -779,8 +779,297 @@ public virtual async Task InstallCommitAsync(DeviceData device, string session,
}
}
+#if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER
///
- public virtual async Task UninstallAsync(DeviceData device, string packageName, CancellationToken cancellationToken, params string[] arguments)
+ public virtual async Task InstallAsync(DeviceData device, IRandomAccessStream apk, Action? progress = null, CancellationToken cancellationToken = default, params string[] arguments)
+ {
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Preparing));
+
+ EnsureDevice(device);
+ ExceptionExtensions.ThrowIfNull(apk);
+
+ if (!apk.CanRead)
+ {
+ throw new ArgumentOutOfRangeException(nameof(apk), "The apk stream must be a readable stream");
+ }
+
+ StringBuilder requestBuilder = new StringBuilder().Append("exec:cmd package 'install'");
+
+ if (arguments != null)
+ {
+ foreach (string argument in arguments)
+ {
+ _ = requestBuilder.AppendFormat(" {0}", argument);
+ }
+ }
+
+ // add size parameter [required for streaming installs]
+ // do last to override any user specified value
+ _ = requestBuilder.Append($" -S {apk.Size}");
+
+ using IAdbSocket socket = AdbSocketFactory(EndPoint);
+ await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false);
+
+ await socket.SendAdbRequestAsync(requestBuilder.ToString(), cancellationToken).ConfigureAwait(false);
+ _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false);
+
+ byte[] buffer = new byte[32 * 1024];
+
+ ulong totalBytesToProcess = apk.Size;
+ ulong totalBytesRead = 0;
+
+ while (true)
+ {
+ IBuffer results = await apk.ReadAsync(buffer.AsBuffer(), (uint)buffer.Length, InputStreamOptions.None).AsTask(cancellationToken).ConfigureAwait(false);
+ if (results.Length == 0) { break; }
+ await socket.SendAsync(buffer.AsMemory(0, (int)results.Length), cancellationToken).ConfigureAwait(false);
+ totalBytesRead += results.Length;
+ progress?.Invoke(new InstallProgressEventArgs(0, 1, totalBytesToProcess == 0 ? 0 : totalBytesRead * 100d / totalBytesToProcess));
+ }
+ progress?.Invoke(new InstallProgressEventArgs(1, 1, 100));
+
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Installing));
+ int read = await socket.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
+ string value =
+#if HAS_BUFFERS
+ Encoding.GetString(buffer.AsSpan(0, read));
+#else
+ Encoding.GetString(buffer, 0, read);
+#endif
+
+ if (!value.Contains("Success"))
+ {
+ throw new AdbException(value);
+ }
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Finished));
+ }
+
+ ///
+ public virtual async Task InstallMultipleAsync(DeviceData device, IRandomAccessStream baseAPK, IEnumerable splitAPKs, Action? progress = null, CancellationToken cancellationToken = default, params string[] arguments)
+ {
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Preparing));
+
+ EnsureDevice(device);
+ ExceptionExtensions.ThrowIfNull(baseAPK);
+ ExceptionExtensions.ThrowIfNull(splitAPKs);
+
+ if (!baseAPK.CanRead)
+ {
+ throw new ArgumentOutOfRangeException(nameof(baseAPK), "The apk stream must be a readable stream");
+ }
+
+ if (splitAPKs.Any(apk => apk == null || !apk.CanRead))
+ {
+ throw new ArgumentOutOfRangeException(nameof(splitAPKs), "The apk stream must be a readable stream");
+ }
+
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.CreateSession));
+ string session = await InstallCreateAsync(device, null, cancellationToken, arguments).ConfigureAwait(false);
+
+ int splitAPKsCount = splitAPKs.Count();
+ void OnMainSyncProgressChanged(string? sender, double args) =>
+ progress?.Invoke(new InstallProgressEventArgs(sender == null ? 1 : 0, splitAPKsCount + 1, args / 2));
+
+ await InstallWriteAsync(device, baseAPK, nameof(baseAPK), session, OnMainSyncProgressChanged, cancellationToken).ConfigureAwait(false);
+
+ int progressCount = 1;
+ Dictionary status = new(splitAPKsCount);
+ void OnSplitSyncProgressChanged(string? sender, double args)
+ {
+ lock (status)
+ {
+ if (sender == null)
+ {
+ progressCount++;
+ }
+ else if (sender is string path)
+ {
+ status[path] = args;
+ }
+ progress?.Invoke(new InstallProgressEventArgs(progressCount, splitAPKsCount + 1, (status.Values.Select(x => x / splitAPKsCount).Sum() + 100) / 2));
+ }
+ }
+
+ int i = 0;
+ await splitAPKs.Select(splitAPK => InstallWriteAsync(device, splitAPK, $"{nameof(splitAPK)}{i++}", session, OnSplitSyncProgressChanged, cancellationToken)).WhenAll().ConfigureAwait(false);
+
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Installing));
+ await InstallCommitAsync(device, session, cancellationToken).ConfigureAwait(false);
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Finished));
+ }
+
+ ///
+ public virtual async Task InstallMultipleAsync(DeviceData device, IEnumerable splitAPKs, string packageName, Action? progress = null, CancellationToken cancellationToken = default, params string[] arguments)
+ {
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Preparing));
+
+ EnsureDevice(device);
+ ExceptionExtensions.ThrowIfNull(splitAPKs);
+ ExceptionExtensions.ThrowIfNull(packageName);
+
+ if (splitAPKs.Any(apk => apk == null || !apk.CanRead))
+ {
+ throw new ArgumentOutOfRangeException(nameof(splitAPKs), "The apk stream must be a readable stream");
+ }
+
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.CreateSession));
+ string session = await InstallCreateAsync(device, packageName, cancellationToken, arguments).ConfigureAwait(false);
+
+ int progressCount = 0;
+ int splitAPKsCount = splitAPKs.Count();
+ Dictionary status = new(splitAPKsCount);
+ void OnSyncProgressChanged(string? sender, double args)
+ {
+ lock (status)
+ {
+ if (sender == null)
+ {
+ progressCount++;
+ }
+ else if (sender is string path)
+ {
+ status[path] = args;
+ }
+ progress?.Invoke(new InstallProgressEventArgs(progressCount, splitAPKsCount, status.Values.Select(x => x / splitAPKsCount).Sum()));
+ }
+ }
+
+ int i = 0;
+ await splitAPKs.Select(splitAPK => InstallWriteAsync(device, splitAPK, $"{nameof(splitAPK)}{i++}", session, OnSyncProgressChanged, cancellationToken)).WhenAll().ConfigureAwait(false);
+
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Installing));
+ await InstallCommitAsync(device, session, cancellationToken).ConfigureAwait(false);
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Finished));
+ }
+
+ ///
+ public virtual async Task InstallWriteAsync(DeviceData device, IRandomAccessStream apk, string apkName, string session, Action? progress = null, CancellationToken cancellationToken = default)
+ {
+ progress?.Invoke(0);
+
+ EnsureDevice(device);
+ ExceptionExtensions.ThrowIfNull(apk);
+ ExceptionExtensions.ThrowIfNull(apkName);
+ ExceptionExtensions.ThrowIfNull(session);
+
+ if (!apk.CanRead)
+ {
+ throw new ArgumentOutOfRangeException(nameof(apk), "The apk stream must be a readable stream");
+ }
+
+ 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.Size)
+ .AppendFormat(" {0} {1}.apk", session, apkName);
+
+ using IAdbSocket socket = AdbSocketFactory(EndPoint);
+ await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false);
+
+ await socket.SendAdbRequestAsync(requestBuilder.ToString(), cancellationToken).ConfigureAwait(false);
+ _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false);
+
+ byte[] buffer = new byte[32 * 1024];
+
+ ulong totalBytesToProcess = apk.Size;
+ ulong totalBytesRead = 0;
+
+ while (true)
+ {
+ IBuffer results = await apk.ReadAsync(buffer.AsBuffer(), (uint)buffer.Length, InputStreamOptions.None).AsTask(cancellationToken).ConfigureAwait(false);
+ if (results.Length == 0) { break; }
+ await socket.SendAsync(buffer.AsMemory(0, (int)results.Length), cancellationToken).ConfigureAwait(false);
+ totalBytesRead += results.Length;
+ progress?.Invoke(totalBytesToProcess == 0 ? 0 : totalBytesRead * 100d / totalBytesToProcess);
+ }
+
+ int read = await socket.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
+ string value =
+#if HAS_BUFFERS
+ Encoding.GetString(buffer.AsSpan(0, read));
+#else
+ Encoding.GetString(buffer, 0, read);
+#endif
+
+ if (!value.Contains("Success"))
+ {
+ throw new AdbException(value);
+ }
+ progress?.Invoke(100);
+ }
+
+ ///
+ /// Asynchronously write an apk into the given install session.
+ ///
+ /// The device on which to install the application.
+ /// A which represents the application to install.
+ /// The name of the application.
+ /// The session ID of the install session.
+ /// 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 apk which has been transferred.
+ /// A which can be used to cancel the asynchronous operation.
+ /// A which represents the asynchronous operation.
+ protected virtual async Task InstallWriteAsync(DeviceData device, IRandomAccessStream apk, string apkName, string session, Action? progress, CancellationToken cancellationToken = default)
+ {
+ progress?.Invoke(apkName, 0);
+
+ EnsureDevice(device);
+ ExceptionExtensions.ThrowIfNull(apk);
+ ExceptionExtensions.ThrowIfNull(apkName);
+ ExceptionExtensions.ThrowIfNull(session);
+
+ if (!apk.CanRead)
+ {
+ throw new ArgumentOutOfRangeException(nameof(apk), "The apk stream must be a readable stream");
+ }
+
+ 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.Size)
+ .AppendFormat(" {0} {1}.apk", session, apkName);
+
+ using IAdbSocket socket = AdbSocketFactory(EndPoint);
+ await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false);
+
+ await socket.SendAdbRequestAsync(requestBuilder.ToString(), cancellationToken).ConfigureAwait(false);
+ _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false);
+
+ byte[] buffer = new byte[32 * 1024];
+
+ ulong totalBytesToProcess = apk.Size;
+ ulong totalBytesRead = 0;
+
+ while (true)
+ {
+ IBuffer results = await apk.ReadAsync(buffer.AsBuffer(), (uint)buffer.Length, InputStreamOptions.None).AsTask(cancellationToken).ConfigureAwait(false);
+ if (results.Length == 0) { break; }
+ await socket.SendAsync(buffer.AsMemory(0, (int)results.Length), cancellationToken).ConfigureAwait(false);
+ totalBytesRead += results.Length;
+ progress?.Invoke(apkName, totalBytesToProcess == 0 ? 0 : totalBytesRead * 100d / totalBytesToProcess);
+ }
+ progress?.Invoke(apkName, 100);
+
+ int read = await socket.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
+ string value =
+#if HAS_BUFFERS
+ Encoding.GetString(buffer.AsSpan(0, read));
+#else
+ Encoding.GetString(buffer, 0, read);
+#endif
+
+ if (!value.Contains("Success"))
+ {
+ throw new AdbException(value);
+ }
+ progress?.Invoke(null, 100);
+ }
+#endif
+
+ ///
+ public virtual async Task UninstallAsync(DeviceData device, string packageName, CancellationToken cancellationToken = default, params string[] arguments)
{
EnsureDevice(device);
diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs
index 6cf12fcd..fbe4e033 100644
--- a/AdvancedSharpAdbClient/AdbClient.cs
+++ b/AdvancedSharpAdbClient/AdbClient.cs
@@ -29,6 +29,9 @@ namespace AdvancedSharpAdbClient
/// adb.c
///
public partial class AdbClient : IAdbClient
+#if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER
+ , IAdbClient.IWinRT
+#endif
{
///
/// The of s that represent a new line.
@@ -99,7 +102,7 @@ public AdbClient(EndPoint endPoint, Func adbSocketFactory)
}
EndPoint = endPoint;
- this.AdbSocketFactory = adbSocketFactory ?? throw new ArgumentNullException(nameof(adbSocketFactory));
+ AdbSocketFactory = adbSocketFactory ?? throw new ArgumentNullException(nameof(adbSocketFactory));
}
///
@@ -426,7 +429,7 @@ public Framebuffer GetFrameBuffer(DeviceData device)
}
///
- public virtual void RunLogService(DeviceData device, Action messageSink, params LogId[] logNames)
+ public virtual void RunLogService(DeviceData device, Action messageSink, in bool isCancelled = false, params LogId[] logNames)
{
EnsureDevice(device);
ExceptionExtensions.ThrowIfNull(messageSink);
@@ -449,7 +452,7 @@ public virtual void RunLogService(DeviceData device, Action messageSin
using Stream stream = socket.GetShellStream();
LogReader reader = new(stream);
- while (true)
+ while (!isCancelled)
{
LogEntry? entry = null;
@@ -547,9 +550,9 @@ protected void Root(string request, DeviceData device)
string responseMessage =
#if HAS_BUFFERS
- Encoding.UTF8.GetString(buffer.AsSpan(0, read));
+ Encoding.GetString(buffer.AsSpan(0, read));
#else
- Encoding.UTF8.GetString(buffer, 0, read);
+ Encoding.GetString(buffer, 0, read);
#endif
// see https://android.googlesource.com/platform/packages/modules/adb/+/refs/heads/master/daemon/restart_service.cpp
@@ -571,9 +574,9 @@ protected void Root(string request, DeviceData device)
}
///
- public void Install(DeviceData device, Stream apk, IProgress? progress = null, params string[] arguments)
+ public void Install(DeviceData device, Stream apk, Action? progress = null, params string[] arguments)
{
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Preparing));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Preparing));
EnsureDevice(device);
ExceptionExtensions.ThrowIfNull(apk);
@@ -619,30 +622,30 @@ public void Install(DeviceData device, Stream apk, IProgress
- public void InstallMultiple(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, IProgress? progress = null, params string[] arguments)
+ public void InstallMultiple(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, Action? progress = null, params string[] arguments)
{
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Preparing));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Preparing));
EnsureDevice(device);
ExceptionExtensions.ThrowIfNull(baseAPK);
@@ -658,12 +661,12 @@ public void InstallMultiple(DeviceData device, Stream baseAPK, IEnumerable
- progress?.Report(new InstallProgressEventArgs(sender == null ? 1 : 0, splitAPKsCount + 1, args / 2));
+ progress?.Invoke(new InstallProgressEventArgs(sender == null ? 1 : 0, splitAPKsCount + 1, args / 2));
InstallWrite(device, baseAPK, nameof(baseAPK), session, OnMainSyncProgressChanged);
@@ -681,7 +684,7 @@ void OnSplitSyncProgressChanged(string? sender, double args)
{
status[path] = args;
}
- progress?.Report(new InstallProgressEventArgs(progressCount, splitAPKsCount + 1, (status.Values.Select(x => x / splitAPKsCount).Sum() + 100) / 2));
+ progress?.Invoke(new InstallProgressEventArgs(progressCount, splitAPKsCount + 1, (status.Values.Select(x => x / splitAPKsCount).Sum() + 100) / 2));
}
}
@@ -691,15 +694,15 @@ void OnSplitSyncProgressChanged(string? sender, double args)
InstallWrite(device, splitAPK, $"{nameof(splitAPK)}{i++}", session, OnSplitSyncProgressChanged);
}
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Installing));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Installing));
InstallCommit(device, session);
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Finished));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Finished));
}
///
- public void InstallMultiple(DeviceData device, IEnumerable splitAPKs, string packageName, IProgress? progress = null, params string[] arguments)
+ public void InstallMultiple(DeviceData device, IEnumerable splitAPKs, string packageName, Action? progress = null, params string[] arguments)
{
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Preparing));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Preparing));
EnsureDevice(device);
ExceptionExtensions.ThrowIfNull(splitAPKs);
@@ -710,7 +713,7 @@ public void InstallMultiple(DeviceData device, IEnumerable splitAPKs, st
throw new ArgumentOutOfRangeException(nameof(splitAPKs), "The apk stream must be a readable and seekable stream");
}
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.CreateSession));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.CreateSession));
string session = InstallCreate(device, packageName, arguments);
int progressCount = 0;
@@ -728,7 +731,7 @@ void OnSyncProgressChanged(string? sender, double args)
{
status[path] = args;
}
- progress?.Report(new InstallProgressEventArgs(progressCount, splitAPKsCount, status.Values.Select(x => x / splitAPKsCount).Sum()));
+ progress?.Invoke(new InstallProgressEventArgs(progressCount, splitAPKsCount, status.Values.Select(x => x / splitAPKsCount).Sum()));
}
}
@@ -738,9 +741,9 @@ void OnSyncProgressChanged(string? sender, double args)
InstallWrite(device, splitAPK, $"{nameof(splitAPK)}{i++}", session, OnSyncProgressChanged);
}
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Installing));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Installing));
InstallCommit(device, session);
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Finished));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Finished));
}
///
@@ -783,9 +786,9 @@ public string InstallCreate(DeviceData device, string? packageName = null, param
}
///
- public void InstallWrite(DeviceData device, Stream apk, string apkName, string session, IProgress? progress = null)
+ public void InstallWrite(DeviceData device, Stream apk, string apkName, string session, Action? progress = null)
{
- progress?.Report(0);
+ progress?.Invoke(0);
EnsureDevice(device);
ExceptionExtensions.ThrowIfNull(apk);
@@ -826,22 +829,22 @@ public void InstallWrite(DeviceData device, Stream apk, string apkName, string s
socket.Send(buffer, read);
#endif
totalBytesRead += read;
- progress?.Report(totalBytesToProcess == 0 ? 0 : totalBytesRead * 100d / totalBytesToProcess);
+ progress?.Invoke(totalBytesToProcess == 0 ? 0 : totalBytesRead * 100d / totalBytesToProcess);
}
read = socket.Read(buffer);
string value =
#if HAS_BUFFERS
- Encoding.UTF8.GetString(buffer.AsSpan(0, read));
+ Encoding.GetString(buffer.AsSpan(0, read));
#else
- Encoding.UTF8.GetString(buffer, 0, read);
+ Encoding.GetString(buffer, 0, read);
#endif
if (!value.Contains("Success"))
{
throw new AdbException(value);
}
- progress?.Report(100);
+ progress?.Invoke(100);
}
///
@@ -902,9 +905,9 @@ protected virtual void InstallWrite(DeviceData device, Stream apk, string apkNam
read = socket.Read(buffer);
string value =
#if HAS_BUFFERS
- Encoding.UTF8.GetString(buffer.AsSpan(0, read));
+ Encoding.GetString(buffer.AsSpan(0, read));
#else
- Encoding.UTF8.GetString(buffer, 0, read);
+ Encoding.GetString(buffer, 0, read);
#endif
if (!value.Contains("Success"))
diff --git a/AdvancedSharpAdbClient/AdbServer.cs b/AdvancedSharpAdbClient/AdbServer.cs
index 6d16e6d9..76e7db1f 100644
--- a/AdvancedSharpAdbClient/AdbServer.cs
+++ b/AdvancedSharpAdbClient/AdbServer.cs
@@ -117,8 +117,8 @@ public AdbServer(EndPoint endPoint, Func adbSocketFactory,
}
EndPoint = endPoint;
- this.AdbSocketFactory = adbSocketFactory ?? throw new ArgumentNullException(nameof(adbSocketFactory));
- this.AdbCommandLineClientFactory = adbCommandLineClientFactory ?? throw new ArgumentNullException(nameof(adbCommandLineClientFactory));
+ AdbSocketFactory = adbSocketFactory ?? throw new ArgumentNullException(nameof(adbSocketFactory));
+ AdbCommandLineClientFactory = adbCommandLineClientFactory ?? throw new ArgumentNullException(nameof(adbCommandLineClientFactory));
}
///
diff --git a/AdvancedSharpAdbClient/AdbSocket.Async.cs b/AdvancedSharpAdbClient/AdbSocket.Async.cs
index d141747f..3102b21a 100644
--- a/AdvancedSharpAdbClient/AdbSocket.Async.cs
+++ b/AdvancedSharpAdbClient/AdbSocket.Async.cs
@@ -221,17 +221,7 @@ public virtual async Task ReadSyncStringAsync(CancellationToken cancella
byte[] reply = new byte[4];
_ = await ReadAsync(reply, cancellationToken).ConfigureAwait(false);
- if (!BitConverter.IsLittleEndian)
- {
- Array.Reverse(reply);
- }
-
- int len =
-#if HAS_BUFFERS
- BitConverter.ToInt32(reply);
-#else
- BitConverter.ToInt32(reply, 0);
-#endif
+ int len = reply[0] | (reply[1] << 8) | (reply[2] << 16) | (reply[3] << 24);
// And get the string
reply = new byte[len];
diff --git a/AdvancedSharpAdbClient/AdbSocket.cs b/AdvancedSharpAdbClient/AdbSocket.cs
index 5d53fbed..05abd2a2 100644
--- a/AdvancedSharpAdbClient/AdbSocket.cs
+++ b/AdvancedSharpAdbClient/AdbSocket.cs
@@ -294,16 +294,7 @@ public virtual string ReadSyncString()
byte[] reply = new byte[4];
_ = Read(reply);
- if (!BitConverter.IsLittleEndian)
- {
- Array.Reverse(reply);
- }
-
-#if HAS_BUFFERS
- int len = BitConverter.ToInt32(reply);
-#else
- int len = BitConverter.ToInt32(reply, 0);
-#endif
+ int len = reply[0] | (reply[1] << 8) | (reply[2] << 16) | (reply[3] << 24);
// And get the string
reply = new byte[len];
@@ -503,7 +494,7 @@ protected virtual string ReplyToString(byte[] reply)
string result;
try
{
- result = Encoding.ASCII.GetString(reply);
+ result = AdbClient.Encoding.GetString(reply);
}
catch (DecoderFallbackException e)
{
@@ -524,7 +515,7 @@ protected virtual string ReplyToString(ReadOnlySpan reply)
string result;
try
{
- result = Encoding.ASCII.GetString(reply);
+ result = AdbClient.Encoding.GetString(reply);
}
catch (DecoderFallbackException e)
{
diff --git a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj
index 5958db9d..22ce9bdb 100644
--- a/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj
+++ b/AdvancedSharpAdbClient/AdvancedSharpAdbClient.csproj
@@ -58,6 +58,7 @@
+
@@ -65,7 +66,7 @@
-
+
which represents the asynchronous operation.
public static async Task PullAsync(this IAdbClient client, DeviceData device,
string remotePath, Stream stream,
- IProgress? progress = null, CancellationToken cancellationToken = default)
+ Action? progress = null,
+ CancellationToken cancellationToken = default)
{
using ISyncService service = Factories.SyncServiceFactory(client, device);
await service.PullAsync(remotePath, stream, progress, cancellationToken).ConfigureAwait(false);
@@ -246,7 +247,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,
- IProgress? progress = null, CancellationToken cancellationToken = default)
+ Action? progress = null,
+ CancellationToken cancellationToken = default)
{
using ISyncService service = Factories.SyncServiceFactory(client, device);
await service.PushAsync(stream, remotePath, permissions, timestamp, progress, cancellationToken).ConfigureAwait(false);
@@ -295,6 +297,100 @@ public static async Task> GetEnvironmentVariablesAsyn
return receiver.EnvironmentVariables;
}
+ ///
+ /// Asynchronously installs an Android application on device.
+ ///
+ /// The connection to the adb server.
+ /// The device on which to uninstall the package.
+ /// The absolute file system path to file on local host to install.
+ /// An optional parameter which, when specified, returns progress notifications.
+ /// The progress is reported as , representing the state of installation.
+ /// A which can be used to cancel the asynchronous operation.
+ /// The arguments to pass to adb install.
+ /// A which represents the asynchronous operation.
+ public static Task InstallPackageAsync(this IAdbClient client, DeviceData device, string packageFilePath, Action? progress = null, CancellationToken cancellationToken = default, params string[] arguments)
+ {
+ PackageManager manager = new(client, device, skipInit: true);
+ return manager.InstallPackageAsync(packageFilePath, progress, cancellationToken, arguments);
+ }
+
+ ///
+ /// Asynchronously installs Android multiple application on device.
+ ///
+ /// The connection to the adb server.
+ /// The device on which to uninstall the package.
+ /// 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.
+ /// An optional parameter which, when specified, returns progress notifications.
+ /// The progress is reported as , representing the state of installation.
+ /// 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 static Task InstallMultiplePackageAsync(this IAdbClient client, DeviceData device, string basePackageFilePath, IEnumerable splitPackageFilePaths, Action? progress = null, CancellationToken cancellationToken = default, params string[] arguments)
+ {
+ PackageManager manager = new(client, device, skipInit: true);
+ return manager.InstallMultiplePackageAsync(basePackageFilePath, splitPackageFilePaths, progress, cancellationToken, arguments);
+ }
+
+ ///
+ /// Asynchronously installs Android multiple application on device.
+ ///
+ /// The connection to the adb server.
+ /// The device on which to uninstall the package.
+ /// The absolute split app file system paths to file on local host to install.
+ /// The absolute package name of the base app.
+ /// An optional parameter which, when specified, returns progress notifications.
+ /// The progress is reported as , representing the state of installation.
+ /// 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 static Task InstallMultiplePackageAsync(this IAdbClient client, DeviceData device, IEnumerable splitPackageFilePaths, string packageName, Action? progress = null, CancellationToken cancellationToken = default, params string[] arguments)
+ {
+ PackageManager manager = new(client, device, skipInit: true);
+ return manager.InstallMultiplePackageAsync(splitPackageFilePaths, packageName, progress, cancellationToken, arguments);
+ }
+
+#if !NETFRAMEWORK || NET40_OR_GREATER
+ ///
+ /// Asynchronously pulls (downloads) a file from the remote device.
+ ///
+ /// The to use when executing the command.
+ /// The device on which to pull the file.
+ /// 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.
+ /// A which represents the asynchronous operation.
+ public static async Task PullAsync(this IAdbClient client, DeviceData device,
+ string remotePath, Stream stream,
+ IProgress? progress = null,
+ CancellationToken cancellationToken = default)
+ {
+ using ISyncService service = Factories.SyncServiceFactory(client, device);
+ await service.PullAsync(remotePath, stream, progress == null ? null : progress.Report, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Asynchronously pushes (uploads) a file to the remote device.
+ ///
+ /// The to use when executing the command.
+ /// The device on which to put the file.
+ /// The path, on the device, to which to push the file.
+ /// A that contains the contents of the file.
+ /// 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.
+ /// A which represents the asynchronous operation.
+ public static async Task PushAsync(this IAdbClient client, DeviceData device,
+ string remotePath, Stream stream, int permissions, DateTimeOffset timestamp,
+ IProgress? progress = null,
+ CancellationToken cancellationToken = default)
+ {
+ using ISyncService service = Factories.SyncServiceFactory(client, device);
+ await service.PushAsync(stream, remotePath, permissions, timestamp, progress == null ? null : progress.Report, cancellationToken).ConfigureAwait(false);
+ }
+
///
/// Asynchronously installs an Android application on device.
///
@@ -347,6 +443,7 @@ public static Task InstallMultiplePackageAsync(this IAdbClient client, DeviceDat
PackageManager manager = new(client, device, skipInit: true);
return manager.InstallMultiplePackageAsync(splitPackageFilePaths, packageName, progress, cancellationToken, arguments);
}
+#endif
///
/// Asynchronously uninstalls a package from the device.
diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs
index b1231189..fae6b6fa 100644
--- a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs
+++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs
@@ -204,7 +204,7 @@ 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,
- IProgress? progress = null,
+ Action? progress = null,
in bool isCancelled = false)
{
using ISyncService service = Factories.SyncServiceFactory(client, device);
@@ -224,7 +224,7 @@ 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,
- IProgress? progress = null,
+ Action? progress = null,
in bool isCancelled = false)
{
using ISyncService service = Factories.SyncServiceFactory(client, device);
@@ -271,6 +271,92 @@ public static Dictionary GetEnvironmentVariables(this IAdbClient
return receiver.EnvironmentVariables;
}
+ ///
+ /// Installs an Android application on device.
+ ///
+ /// The connection to the adb server.
+ /// The device on which to uninstall the package.
+ /// The absolute file system path to file on local host to install.
+ /// An optional parameter which, when specified, returns progress notifications.
+ /// The progress is reported as , representing the state of installation.
+ /// The arguments to pass to adb install.
+ public static void InstallPackage(this IAdbClient client, DeviceData device, string packageFilePath, Action? progress = null, params string[] arguments)
+ {
+ PackageManager manager = new(client, device, skipInit: true);
+ manager.InstallPackage(packageFilePath, progress, arguments);
+ }
+
+ ///
+ /// Installs Android multiple application on device.
+ ///
+ /// The connection to the adb server.
+ /// The device on which to uninstall the package.
+ /// 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.
+ /// An optional parameter which, when specified, returns progress notifications.
+ /// The progress is reported as , representing the state of installation.
+ /// The arguments to pass to pm install-create.
+ public static void InstallMultiplePackage(this IAdbClient client, DeviceData device, string basePackageFilePath, IEnumerable splitPackageFilePaths, Action? progress = null, params string[] arguments)
+ {
+ PackageManager manager = new(client, device, skipInit: true);
+ manager.InstallMultiplePackage(basePackageFilePath, splitPackageFilePaths, progress, arguments);
+ }
+
+ ///
+ /// Installs Android multiple application on device.
+ ///
+ /// The connection to the adb server.
+ /// The device on which to uninstall the package.
+ /// The absolute split app file system paths to file on local host to install.
+ /// The absolute package name of the base app.
+ /// An optional parameter which, when specified, returns progress notifications.
+ /// The progress is reported as , representing the state of installation.
+ /// The arguments to pass to pm install-create.
+ public static void InstallMultiplePackage(this IAdbClient client, DeviceData device, IEnumerable splitPackageFilePaths, string packageName, Action? progress = null, params string[] arguments)
+ {
+ PackageManager manager = new(client, device, skipInit: true);
+ manager.InstallMultiplePackage(splitPackageFilePaths, packageName, progress, arguments);
+ }
+
+#if !NETFRAMEWORK || NET40_OR_GREATER
+ ///
+ /// Pulls (downloads) a file from the remote device.
+ ///
+ /// The to use when executing the command.
+ /// The device on which to pull the file.
+ /// 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.
+ public static void Pull(this IAdbClient client, DeviceData device,
+ string remotePath, Stream stream,
+ IProgress? progress = null,
+ in bool isCancelled = false)
+ {
+ using ISyncService service = Factories.SyncServiceFactory(client, device);
+ service.Pull(remotePath, stream, progress == null ? null : progress.Report, in isCancelled);
+ }
+
+ ///
+ /// Pushes (uploads) a file to the remote device.
+ ///
+ /// The to use when executing the command.
+ /// The device on which to put the file.
+ /// The path, on the device, to which to push the file.
+ /// A that contains the contents of the file.
+ /// 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.
+ public static void Push(this IAdbClient client, DeviceData device,
+ string remotePath, Stream stream, int permissions, DateTimeOffset timestamp,
+ IProgress? progress = null,
+ in bool isCancelled = false)
+ {
+ using ISyncService service = Factories.SyncServiceFactory(client, device);
+ service.Push(stream, remotePath, permissions, timestamp, progress == null ? null : progress.Report, in isCancelled);
+ }
+
///
/// Installs an Android application on device.
///
@@ -317,6 +403,7 @@ public static void InstallMultiplePackage(this IAdbClient client, DeviceData dev
PackageManager manager = new(client, device, skipInit: true);
manager.InstallMultiplePackage(splitPackageFilePaths, packageName, progress, arguments);
}
+#endif
///
/// Uninstalls a package from the device.
diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs
index 3bd85ed8..2282f375 100644
--- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs
+++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs
@@ -47,24 +47,24 @@ public virtual Task RefreshPackagesAsync(CancellationToken cancellationToken = d
/// 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, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments)
+ public virtual async Task InstallPackageAsync(string packageFilePath, Action? progress = null, CancellationToken cancellationToken = default, params string[] arguments)
{
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Preparing));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Preparing));
ValidateDevice();
void OnSyncProgressChanged(string? sender, SyncProgressChangedEventArgs args) =>
- progress?.Report(new InstallProgressEventArgs(sender is null ? 1 : 0, 1, args.ProgressPercentage));
+ progress?.Invoke(new InstallProgressEventArgs(sender is null ? 1 : 0, 1, args.ProgressPercentage));
string remoteFilePath = await SyncPackageToDeviceAsync(packageFilePath, OnSyncProgressChanged, cancellationToken).ConfigureAwait(false);
await InstallRemotePackageAsync(remoteFilePath, progress, cancellationToken, arguments).ConfigureAwait(false);
- progress?.Report(new InstallProgressEventArgs(0, 1, PackageInstallProgressState.PostInstall));
+ progress?.Invoke(new InstallProgressEventArgs(0, 1, PackageInstallProgressState.PostInstall));
await RemoveRemotePackageAsync(remoteFilePath, cancellationToken).ConfigureAwait(false);
- progress?.Report(new InstallProgressEventArgs(1, 1, PackageInstallProgressState.PostInstall));
+ progress?.Invoke(new InstallProgressEventArgs(1, 1, PackageInstallProgressState.PostInstall));
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Finished));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Finished));
}
///
@@ -76,9 +76,9 @@ void OnSyncProgressChanged(string? sender, SyncProgressChangedEventArgs args) =>
/// 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, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments)
+ public virtual async Task InstallRemotePackageAsync(string remoteFilePath, Action? progress = null, CancellationToken cancellationToken = default, params string[] arguments)
{
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Installing));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Installing));
ValidateDevice();
@@ -114,16 +114,16 @@ public virtual async Task InstallRemotePackageAsync(string remoteFilePath, IProg
/// 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, IEnumerable splitPackageFilePaths, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments)
+ public virtual async Task InstallMultiplePackageAsync(string basePackageFilePath, IEnumerable splitPackageFilePaths, Action? progress = null, CancellationToken cancellationToken = default, params string[] arguments)
{
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Preparing));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Preparing));
ValidateDevice();
int splitPackageFileCount = splitPackageFilePaths.Count();
void OnMainSyncProgressChanged(string? sender, SyncProgressChangedEventArgs args) =>
- progress?.Report(new InstallProgressEventArgs(sender is null ? 1 : 0, splitPackageFileCount + 1, args.ProgressPercentage / 2));
+ progress?.Invoke(new InstallProgressEventArgs(sender is null ? 1 : 0, splitPackageFileCount + 1, args.ProgressPercentage / 2));
string baseRemoteFilePath = await SyncPackageToDeviceAsync(basePackageFilePath, OnMainSyncProgressChanged, cancellationToken).ConfigureAwait(false);
@@ -147,7 +147,7 @@ void OnSplitSyncProgressChanged(string? sender, SyncProgressChangedEventArgs arg
}
status[path] = args.ProgressPercentage;
}
- progress?.Report(new InstallProgressEventArgs(progressCount, splitPackageFileCount + 1, (status.Values.Select(x => x / splitPackageFileCount).Sum() + 100) / 2));
+ progress?.Invoke(new InstallProgressEventArgs(progressCount, splitPackageFileCount + 1, (status.Values.Select(x => x / splitPackageFileCount).Sum() + 100) / 2));
}
}
@@ -160,13 +160,13 @@ void OnSplitSyncProgressChanged(string? sender, SyncProgressChangedEventArgs arg
await InstallMultipleRemotePackageAsync(baseRemoteFilePath, splitRemoteFilePaths, progress, cancellationToken, arguments);
- progress?.Report(new InstallProgressEventArgs(0, splitRemoteFilePaths.Length + 1, PackageInstallProgressState.PostInstall));
+ progress?.Invoke(new InstallProgressEventArgs(0, splitRemoteFilePaths.Length + 1, PackageInstallProgressState.PostInstall));
int count = 0;
await splitRemoteFilePaths.Select(async x =>
{
count++;
await RemoveRemotePackageAsync(x, cancellationToken).ConfigureAwait(false);
- progress?.Report(new InstallProgressEventArgs(count, splitRemoteFilePaths.Length + 1, PackageInstallProgressState.PostInstall));
+ progress?.Invoke(new InstallProgressEventArgs(count, splitRemoteFilePaths.Length + 1, PackageInstallProgressState.PostInstall));
}).WhenAll().ConfigureAwait(false);
if (count < splitRemoteFilePaths.Length)
@@ -175,9 +175,9 @@ await splitRemoteFilePaths.Select(async x =>
}
await RemoveRemotePackageAsync(baseRemoteFilePath, cancellationToken).ConfigureAwait(false);
- progress?.Report(new InstallProgressEventArgs(++count, splitRemoteFilePaths.Length + 1, PackageInstallProgressState.PostInstall));
+ progress?.Invoke(new InstallProgressEventArgs(++count, splitRemoteFilePaths.Length + 1, PackageInstallProgressState.PostInstall));
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Finished));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Finished));
}
///
@@ -190,9 +190,9 @@ await splitRemoteFilePaths.Select(async x =>
/// 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(IEnumerable splitPackageFilePaths, string packageName, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments)
+ public virtual async Task InstallMultiplePackageAsync(IEnumerable splitPackageFilePaths, string packageName, Action? progress = null, CancellationToken cancellationToken = default, params string[] arguments)
{
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Preparing));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Preparing));
ValidateDevice();
@@ -218,7 +218,7 @@ void OnSyncProgressChanged(string? sender, SyncProgressChangedEventArgs args)
}
status[path] = args.ProgressPercentage;
}
- progress?.Report(new InstallProgressEventArgs(progressCount, splitPackageFileCount, status.Values.Select(x => x / splitPackageFileCount).Sum()));
+ progress?.Invoke(new InstallProgressEventArgs(progressCount, splitPackageFileCount, status.Values.Select(x => x / splitPackageFileCount).Sum()));
}
}
@@ -231,13 +231,13 @@ void OnSyncProgressChanged(string? sender, SyncProgressChangedEventArgs args)
await InstallMultipleRemotePackageAsync(splitRemoteFilePaths, packageName, progress, cancellationToken, arguments);
- progress?.Report(new InstallProgressEventArgs(0, splitRemoteFilePaths.Length, PackageInstallProgressState.PostInstall));
+ progress?.Invoke(new InstallProgressEventArgs(0, splitRemoteFilePaths.Length, PackageInstallProgressState.PostInstall));
int count = 0;
await splitRemoteFilePaths.Select(async x =>
{
count++;
await RemoveRemotePackageAsync(x, cancellationToken).ConfigureAwait(false);
- progress?.Report(new InstallProgressEventArgs(count, splitRemoteFilePaths.Length, PackageInstallProgressState.PostInstall));
+ progress?.Invoke(new InstallProgressEventArgs(count, splitRemoteFilePaths.Length, PackageInstallProgressState.PostInstall));
}).WhenAll().ConfigureAwait(false);
if (count < splitRemoteFilePaths.Length)
@@ -245,7 +245,7 @@ await splitRemoteFilePaths.Select(async x =>
throw new PackageInstallationException($"{nameof(RemoveRemotePackageAsync)} failed. {splitRemoteFilePaths.Length} should process but only {count} processed.");
}
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Finished));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Finished));
}
///
@@ -258,9 +258,9 @@ await splitRemoteFilePaths.Select(async x =>
/// 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, IEnumerable splitRemoteFilePaths, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments)
+ public virtual async Task InstallMultipleRemotePackageAsync(string baseRemoteFilePath, IEnumerable splitRemoteFilePaths, Action? progress = null, CancellationToken cancellationToken = default, params string[] arguments)
{
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.CreateSession));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.CreateSession));
ValidateDevice();
@@ -268,17 +268,17 @@ public virtual async Task InstallMultipleRemotePackageAsync(string baseRemoteFil
int splitRemoteFileCount = splitRemoteFilePaths.Count();
- progress?.Report(new InstallProgressEventArgs(0, splitRemoteFileCount + 1, PackageInstallProgressState.WriteSession));
+ progress?.Invoke(new InstallProgressEventArgs(0, splitRemoteFileCount + 1, PackageInstallProgressState.WriteSession));
await WriteInstallSessionAsync(session, "base", baseRemoteFilePath, cancellationToken).ConfigureAwait(false);
- progress?.Report(new InstallProgressEventArgs(1, splitRemoteFileCount + 1, PackageInstallProgressState.WriteSession));
+ progress?.Invoke(new InstallProgressEventArgs(1, splitRemoteFileCount + 1, PackageInstallProgressState.WriteSession));
int count = 0;
await splitRemoteFilePaths.Select(async (splitRemoteFilePath) =>
{
await WriteInstallSessionAsync(session, $"split{count++}", splitRemoteFilePath, cancellationToken).ConfigureAwait(false);
- progress?.Report(new InstallProgressEventArgs(count, splitRemoteFileCount + 1, PackageInstallProgressState.WriteSession));
+ progress?.Invoke(new InstallProgressEventArgs(count, splitRemoteFileCount + 1, PackageInstallProgressState.WriteSession));
}).WhenAll().ConfigureAwait(false);
if (count < splitRemoteFileCount)
@@ -286,7 +286,7 @@ await splitRemoteFilePaths.Select(async (splitRemoteFilePath) =>
throw new PackageInstallationException($"{nameof(WriteInstallSessionAsync)} failed. {splitRemoteFileCount} should process but only {count} processed.");
}
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Installing));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Installing));
InstallOutputReceiver receiver = new();
await AdbClient.ExecuteShellCommandAsync(Device, $"pm install-commit {session}", receiver, cancellationToken).ConfigureAwait(false);
@@ -307,9 +307,9 @@ await splitRemoteFilePaths.Select(async (splitRemoteFilePath) =>
/// 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(IEnumerable splitRemoteFilePaths, string packageName, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments)
+ public virtual async Task InstallMultipleRemotePackageAsync(IEnumerable splitRemoteFilePaths, string packageName, Action? progress = null, CancellationToken cancellationToken = default, params string[] arguments)
{
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.CreateSession));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.CreateSession));
ValidateDevice();
@@ -317,13 +317,13 @@ public virtual async Task InstallMultipleRemotePackageAsync(IEnumerable
int splitRemoteFileCount = splitRemoteFilePaths.Count();
- progress?.Report(new InstallProgressEventArgs(0, splitRemoteFileCount, PackageInstallProgressState.WriteSession));
+ progress?.Invoke(new InstallProgressEventArgs(0, splitRemoteFileCount, PackageInstallProgressState.WriteSession));
int count = 0;
await splitRemoteFilePaths.Select(async (splitRemoteFilePath) =>
{
await WriteInstallSessionAsync(session, $"split{count++}", splitRemoteFilePath, cancellationToken).ConfigureAwait(false);
- progress?.Report(new InstallProgressEventArgs(count, splitRemoteFileCount, PackageInstallProgressState.WriteSession));
+ progress?.Invoke(new InstallProgressEventArgs(count, splitRemoteFileCount, PackageInstallProgressState.WriteSession));
}).WhenAll().ConfigureAwait(false);
if (count < splitRemoteFileCount)
@@ -331,7 +331,7 @@ await splitRemoteFilePaths.Select(async (splitRemoteFilePath) =>
throw new PackageInstallationException($"{nameof(WriteInstallSessionAsync)} failed. {splitRemoteFileCount} should process but only {count} processed.");
}
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Installing));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Installing));
InstallOutputReceiver receiver = new();
await AdbClient.ExecuteShellCommandAsync(Device, $"pm install-commit {session}", receiver, cancellationToken).ConfigureAwait(false);
@@ -342,6 +342,84 @@ await splitRemoteFilePaths.Select(async (splitRemoteFilePath) =>
}
}
+#if !NETFRAMEWORK || NET40_OR_GREATER
+ ///
+ /// Asynchronously installs an Android application on device.
+ ///
+ /// The absolute file system path to file on local host to install.
+ /// An optional parameter which, when specified, returns progress notifications.
+ /// The progress is reported as , representing the state of installation.
+ /// 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, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments) =>
+ await InstallPackageAsync(packageFilePath, progress == null ? null : progress.Report, cancellationToken, arguments).ConfigureAwait(false);
+
+ ///
+ /// Asynchronously installs the application package that was pushed to a temporary location on the device.
+ ///
+ /// absolute file path to package file on device.
+ /// An optional parameter which, when specified, returns progress notifications.
+ /// The progress is reported as , representing the state of installation.
+ /// 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, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments) =>
+ await InstallRemotePackageAsync(remoteFilePath, progress == null ? null : progress.Report, cancellationToken, arguments).ConfigureAwait(false);
+
+ ///
+ /// Asynchronously 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.
+ /// An optional parameter which, when specified, returns progress notifications.
+ /// The progress is reported as , representing the state of installation.
+ /// 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, IEnumerable splitPackageFilePaths, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments) =>
+ await InstallMultiplePackageAsync(basePackageFilePath, splitPackageFilePaths, progress == null ? null : progress.Report, cancellationToken, arguments).ConfigureAwait(false);
+
+ ///
+ /// Asynchronously 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.
+ /// An optional parameter which, when specified, returns progress notifications.
+ /// The progress is reported as , representing the state of installation.
+ /// 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(IEnumerable splitPackageFilePaths, string packageName, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments) =>
+ await InstallMultiplePackageAsync(splitPackageFilePaths, packageName, progress == null ? null : progress.Report, cancellationToken, arguments).ConfigureAwait(false);
+
+ ///
+ /// Asynchronously 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.
+ /// An optional parameter which, when specified, returns progress notifications.
+ /// The progress is reported as , representing the state of installation.
+ /// 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, IEnumerable splitRemoteFilePaths, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments) =>
+ await InstallMultipleRemotePackageAsync(baseRemoteFilePath, splitRemoteFilePaths, progress == null ? null : progress.Report, cancellationToken, arguments).ConfigureAwait(false);
+
+ ///
+ /// Asynchronously 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.
+ /// An optional parameter which, when specified, returns progress notifications.
+ /// The progress is reported as , representing the state of installation.
+ /// 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(IEnumerable splitRemoteFilePaths, string packageName, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments) =>
+ await InstallMultipleRemotePackageAsync(splitRemoteFilePaths, packageName, progress == null ? null : progress.Report, cancellationToken, arguments).ConfigureAwait(false);
+#endif
+
///
/// Asynchronously uninstalls a package from the device.
///
@@ -445,7 +523,7 @@ protected virtual async Task SyncPackageToDeviceAsync(string localFilePa
logger.LogDebug("Uploading file onto device '{0}'", Device.Serial);
- SyncProgress? syncProgress = progress == null ? null : new SyncProgress(localFilePath, progress);
+ Action? syncProgress = progress == null ? null : args => progress.Invoke(localFilePath, args);
// 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);
diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs
index 611f44d0..5f078bc0 100644
--- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs
+++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs
@@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
-using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
@@ -57,7 +56,7 @@ public PackageManager(IAdbClient client, DeviceData device, FuncAn optional parameter which, when specified, returns progress notifications.
/// The progress is reported as , representing the state of installation.
/// The arguments to pass to adb install.
- public virtual void InstallPackage(string packageFilePath, IProgress? progress = null, params string[] arguments)
+ public virtual void InstallPackage(string packageFilePath, Action? progress = null, params string[] arguments)
{
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Preparing));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Preparing));
ValidateDevice();
void OnSyncProgressChanged(string? sender, SyncProgressChangedEventArgs args) =>
- progress?.Report(new InstallProgressEventArgs(sender is null ? 1 : 0, 1, args.ProgressPercentage));
+ progress?.Invoke(new InstallProgressEventArgs(sender is null ? 1 : 0, 1, args.ProgressPercentage));
string remoteFilePath = SyncPackageToDevice(packageFilePath, OnSyncProgressChanged);
InstallRemotePackage(remoteFilePath, progress, arguments);
- progress?.Report(new InstallProgressEventArgs(0, 1, PackageInstallProgressState.PostInstall));
+ progress?.Invoke(new InstallProgressEventArgs(0, 1, PackageInstallProgressState.PostInstall));
RemoveRemotePackage(remoteFilePath);
- progress?.Report(new InstallProgressEventArgs(1, 1, PackageInstallProgressState.PostInstall));
+ progress?.Invoke(new InstallProgressEventArgs(1, 1, PackageInstallProgressState.PostInstall));
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Finished));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Finished));
}
///
@@ -169,9 +168,9 @@ void OnSyncProgressChanged(string? sender, SyncProgressChangedEventArgs args) =>
/// An optional parameter which, when specified, returns progress notifications.
/// The progress is reported as , representing the state of installation.
/// The arguments to pass to adb install.
- public virtual void InstallRemotePackage(string remoteFilePath, IProgress? progress = null, params string[] arguments)
+ public virtual void InstallRemotePackage(string remoteFilePath, Action? progress = null, params string[] arguments)
{
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Installing));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Installing));
ValidateDevice();
@@ -205,16 +204,16 @@ public virtual void InstallRemotePackage(string remoteFilePath, IProgressAn optional parameter which, when specified, returns progress notifications.
/// The progress is reported as , representing the state of installation.
/// The arguments to pass to pm install-create.
- public virtual void InstallMultiplePackage(string basePackageFilePath, IEnumerable splitPackageFilePaths, IProgress? progress = null, params string[] arguments)
+ public virtual void InstallMultiplePackage(string basePackageFilePath, IEnumerable splitPackageFilePaths, Action? progress = null, params string[] arguments)
{
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Preparing));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Preparing));
ValidateDevice();
int splitPackageFileCount = splitPackageFilePaths.Count();
void OnMainSyncProgressChanged(string? sender, SyncProgressChangedEventArgs args) =>
- progress?.Report(new InstallProgressEventArgs(sender is null ? 1 : 0, splitPackageFileCount + 1, args.ProgressPercentage / 2));
+ progress?.Invoke(new InstallProgressEventArgs(sender is null ? 1 : 0, splitPackageFileCount + 1, args.ProgressPercentage / 2));
string baseRemoteFilePath = SyncPackageToDevice(basePackageFilePath, OnMainSyncProgressChanged);
@@ -232,7 +231,7 @@ void OnSplitSyncProgressChanged(string? sender, SyncProgressChangedEventArgs arg
{
status[path] = args.ProgressPercentage;
}
- progress?.Report(new InstallProgressEventArgs(progressCount, splitPackageFileCount + 1, (status.Values.Select(x => x / splitPackageFileCount).Sum() + 100) / 2));
+ progress?.Invoke(new InstallProgressEventArgs(progressCount, splitPackageFileCount + 1, (status.Values.Select(x => x / splitPackageFileCount).Sum() + 100) / 2));
}
}
@@ -246,17 +245,17 @@ void OnSplitSyncProgressChanged(string? sender, SyncProgressChangedEventArgs arg
InstallMultipleRemotePackage(baseRemoteFilePath, splitRemoteFilePaths, progress, arguments);
int count = 0;
- progress?.Report(new InstallProgressEventArgs(0, splitRemoteFilePaths.Length + 1, PackageInstallProgressState.PostInstall));
+ progress?.Invoke(new InstallProgressEventArgs(0, splitRemoteFilePaths.Length + 1, PackageInstallProgressState.PostInstall));
foreach (string splitRemoteFilePath in splitRemoteFilePaths)
{
RemoveRemotePackage(splitRemoteFilePath);
- progress?.Report(new InstallProgressEventArgs(++count, splitRemoteFilePaths.Length + 1, PackageInstallProgressState.PostInstall));
+ progress?.Invoke(new InstallProgressEventArgs(++count, splitRemoteFilePaths.Length + 1, PackageInstallProgressState.PostInstall));
}
RemoveRemotePackage(baseRemoteFilePath);
- progress?.Report(new InstallProgressEventArgs(++count, splitRemoteFilePaths.Length + 1, PackageInstallProgressState.PostInstall));
+ progress?.Invoke(new InstallProgressEventArgs(++count, splitRemoteFilePaths.Length + 1, PackageInstallProgressState.PostInstall));
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Finished));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Finished));
}
///
@@ -267,9 +266,9 @@ void OnSplitSyncProgressChanged(string? sender, SyncProgressChangedEventArgs arg
/// An optional parameter which, when specified, returns progress notifications.
/// The progress is reported as , representing the state of installation.
/// The arguments to pass to pm install-create.
- public virtual void InstallMultiplePackage(IEnumerable splitPackageFilePaths, string packageName, IProgress? progress = null, params string[] arguments)
+ public virtual void InstallMultiplePackage(IEnumerable splitPackageFilePaths, string packageName, Action? progress = null, params string[] arguments)
{
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Preparing));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Preparing));
ValidateDevice();
@@ -289,7 +288,7 @@ void OnSyncProgressChanged(string? sender, SyncProgressChangedEventArgs args)
{
status[path] = args.ProgressPercentage;
}
- progress?.Report(new InstallProgressEventArgs(progressCount, splitPackageFileCount, status.Values.Select(x => x / splitPackageFileCount).Sum()));
+ progress?.Invoke(new InstallProgressEventArgs(progressCount, splitPackageFileCount, status.Values.Select(x => x / splitPackageFileCount).Sum()));
}
}
@@ -302,15 +301,15 @@ void OnSyncProgressChanged(string? sender, SyncProgressChangedEventArgs args)
InstallMultipleRemotePackage(splitRemoteFilePaths, packageName, progress, arguments);
- progress?.Report(new InstallProgressEventArgs(0, splitRemoteFilePaths.Length, PackageInstallProgressState.PostInstall));
+ progress?.Invoke(new InstallProgressEventArgs(0, splitRemoteFilePaths.Length, PackageInstallProgressState.PostInstall));
int count = 0;
foreach (string splitRemoteFilePath in splitRemoteFilePaths)
{
RemoveRemotePackage(splitRemoteFilePath);
- progress?.Report(new InstallProgressEventArgs(++count, splitRemoteFilePaths.Length, PackageInstallProgressState.PostInstall));
+ progress?.Invoke(new InstallProgressEventArgs(++count, splitRemoteFilePaths.Length, PackageInstallProgressState.PostInstall));
}
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Finished));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Finished));
}
///
@@ -321,9 +320,9 @@ void OnSyncProgressChanged(string? sender, SyncProgressChangedEventArgs args)
/// An optional parameter which, when specified, returns progress notifications.
/// The progress is reported as , representing the state of installation.
/// The arguments to pass to pm install-create.
- public virtual void InstallMultipleRemotePackage(string baseRemoteFilePath, IEnumerable splitRemoteFilePaths, IProgress? progress = null, params string[] arguments)
+ public virtual void InstallMultipleRemotePackage(string baseRemoteFilePath, IEnumerable splitRemoteFilePaths, Action? progress = null, params string[] arguments)
{
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.CreateSession));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.CreateSession));
ValidateDevice();
@@ -331,20 +330,20 @@ public virtual void InstallMultipleRemotePackage(string baseRemoteFilePath, IEnu
int splitRemoteFileCount = splitRemoteFilePaths.Count();
- progress?.Report(new InstallProgressEventArgs(0, splitRemoteFileCount + 1, PackageInstallProgressState.WriteSession));
+ progress?.Invoke(new InstallProgressEventArgs(0, splitRemoteFileCount + 1, PackageInstallProgressState.WriteSession));
WriteInstallSession(session, "base", baseRemoteFilePath);
- progress?.Report(new InstallProgressEventArgs(1, splitRemoteFileCount + 1, PackageInstallProgressState.WriteSession));
+ progress?.Invoke(new InstallProgressEventArgs(1, splitRemoteFileCount + 1, PackageInstallProgressState.WriteSession));
int count = 0;
foreach (string splitRemoteFilePath in splitRemoteFilePaths)
{
WriteInstallSession(session, $"split{count++}", splitRemoteFilePath);
- progress?.Report(new InstallProgressEventArgs(count, splitRemoteFileCount + 1, PackageInstallProgressState.WriteSession));
+ progress?.Invoke(new InstallProgressEventArgs(count, splitRemoteFileCount + 1, PackageInstallProgressState.WriteSession));
}
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Installing));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Installing));
InstallOutputReceiver receiver = new();
AdbClient.ExecuteShellCommand(Device, $"pm install-commit {session}", receiver);
@@ -363,9 +362,9 @@ public virtual void InstallMultipleRemotePackage(string baseRemoteFilePath, IEnu
/// An optional parameter which, when specified, returns progress notifications.
/// The progress is reported as , representing the state of installation.
/// The arguments to pass to pm install-create.
- public virtual void InstallMultipleRemotePackage(IEnumerable splitRemoteFilePaths, string packageName, IProgress? progress = null, params string[] arguments)
+ public virtual void InstallMultipleRemotePackage(IEnumerable splitRemoteFilePaths, string packageName, Action? progress = null, params string[] arguments)
{
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.CreateSession));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.CreateSession));
ValidateDevice();
@@ -373,16 +372,16 @@ public virtual void InstallMultipleRemotePackage(IEnumerable splitRemote
int splitRemoteFileCount = splitRemoteFilePaths.Count();
- progress?.Report(new InstallProgressEventArgs(0, splitRemoteFileCount, PackageInstallProgressState.WriteSession));
+ progress?.Invoke(new InstallProgressEventArgs(0, splitRemoteFileCount, PackageInstallProgressState.WriteSession));
int count = 0;
foreach (string splitRemoteFilePath in splitRemoteFilePaths)
{
WriteInstallSession(session, $"split{count++}", splitRemoteFilePath);
- progress?.Report(new InstallProgressEventArgs(count, splitRemoteFileCount, PackageInstallProgressState.WriteSession));
+ progress?.Invoke(new InstallProgressEventArgs(count, splitRemoteFileCount, PackageInstallProgressState.WriteSession));
}
- progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Installing));
+ progress?.Invoke(new InstallProgressEventArgs(PackageInstallProgressState.Installing));
InstallOutputReceiver receiver = new();
AdbClient.ExecuteShellCommand(Device, $"pm install-commit {session}", receiver);
@@ -393,6 +392,72 @@ public virtual void InstallMultipleRemotePackage(IEnumerable splitRemote
}
}
+#if !NETFRAMEWORK || NET40_OR_GREATER
+ ///
+ /// Installs an Android application on device.
+ ///
+ /// The absolute file system path to file on local host to install.
+ /// An optional parameter which, when specified, returns progress notifications.
+ /// The progress is reported as , representing the state of installation.
+ /// The arguments to pass to adb install.
+ public virtual void InstallPackage(string packageFilePath, IProgress? progress = null, params string[] arguments) =>
+ InstallPackage(packageFilePath, progress == null ? null : progress.Report, arguments);
+
+ ///
+ /// Installs the application package that was pushed to a temporary location on the device.
+ ///
+ /// absolute file path to package file on device.
+ /// An optional parameter which, when specified, returns progress notifications.
+ /// The progress is reported as , representing the state of installation.
+ /// The arguments to pass to adb install.
+ public virtual void InstallRemotePackage(string remoteFilePath, IProgress? progress = null, params string[] arguments) =>
+ InstallRemotePackage(remoteFilePath, progress == null ? null : progress.Report, 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.
+ /// An optional parameter which, when specified, returns progress notifications.
+ /// The progress is reported as , representing the state of installation.
+ /// The arguments to pass to pm install-create.
+ public virtual void InstallMultiplePackage(string basePackageFilePath, IEnumerable splitPackageFilePaths, IProgress? progress = null, params string[] arguments) =>
+ InstallMultiplePackage(basePackageFilePath, splitPackageFilePaths, progress == null ? null : progress.Report, 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.
+ /// An optional parameter which, when specified, returns progress notifications.
+ /// The progress is reported as , representing the state of installation.
+ /// The arguments to pass to pm install-create.
+ public virtual void InstallMultiplePackage(IEnumerable splitPackageFilePaths, string packageName, IProgress? progress = null, params string[] arguments) =>
+ InstallMultiplePackage(splitPackageFilePaths, packageName, progress == null ? null : progress.Report, 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.
+ /// An optional parameter which, when specified, returns progress notifications.
+ /// The progress is reported as , representing the state of installation.
+ /// The arguments to pass to pm install-create.
+ public virtual void InstallMultipleRemotePackage(string baseRemoteFilePath, IEnumerable splitRemoteFilePaths, IProgress? progress = null, params string[] arguments) =>
+ InstallMultipleRemotePackage(baseRemoteFilePath, splitRemoteFilePaths, progress == null ? null : progress.Report, 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.
+ /// An optional parameter which, when specified, returns progress notifications.
+ /// The progress is reported as , representing the state of installation.
+ /// The arguments to pass to pm install-create.
+ public virtual void InstallMultipleRemotePackage(IEnumerable splitRemoteFilePaths, string packageName, IProgress? progress = null, params string[] arguments) =>
+ InstallMultipleRemotePackage(splitRemoteFilePaths, packageName, progress == null ? null : progress.Report, arguments);
+#endif
+
///
/// Uninstalls a package from the device.
///
@@ -490,7 +555,7 @@ protected virtual string SyncPackageToDevice(string localFilePath, Action? syncProgress = progress == null ? null : args => progress.Invoke(localFilePath, args);
// As C# can't use octal, the octal literal 666 (rw-Permission) is here converted to decimal (438)
sync.Push(stream, remoteFilePath, 438, File.GetLastWriteTime(localFilePath), syncProgress, false);
@@ -587,17 +652,5 @@ protected virtual void WriteInstallSession(string session, string apkName, strin
throw new PackageInstallationException(receiver.ErrorMessage);
}
}
-
- ///
- /// The used for .
- ///
- /// The absolute path to file on local host.
- /// An optional parameter which, when specified, returns progress notifications.
- [EditorBrowsable(EditorBrowsableState.Never)]
- protected readonly struct SyncProgress(string localFilePath, Action progress) : IProgress
- {
- ///
- public void Report(SyncProgressChangedEventArgs value) => progress?.Invoke(localFilePath, value);
- }
}
}
diff --git a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs
index f66782fb..310b2d03 100644
--- a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs
+++ b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs
@@ -4,12 +4,14 @@
//
using System;
+using System.Collections.Generic;
+using System.IO;
using System.Net;
using System.Threading;
namespace AdvancedSharpAdbClient
{
- public static partial class AdbClientExtensions
+ public partial class AdbClientExtensions
{
///
/// Asynchronously asks the ADB server to forward local connections from
@@ -266,6 +268,151 @@ public static Task ConnectAsync(this IAdbClient client, IPEndPoint endpo
public static Task ConnectAsync(this IAdbClient client, string host, int port = AdbClient.DefaultPort, CancellationToken cancellationToken = default) =>
client.ConnectAsync(Extensions.CreateDnsEndPoint(host, port), cancellationToken);
+#if !NETFRAMEWORK || NET40_OR_GREATER
+ ///
+ /// 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, IProgress messageSink, params LogId[] logNames) =>
+ client.RunLogServiceAsync(device, messageSink.Report, default, logNames);
+
+ ///
+ /// 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.
+ /// A which can be used to cancel the asynchronous operation.
+ /// Optionally, the names of the logs to receive.
+ /// A which represents the asynchronous operation.
+ public static Task RunLogServiceAsync(this IAdbClient client, DeviceData device, IProgress messageSink, CancellationToken cancellationToken, params LogId[] logNames) =>
+ client.RunLogServiceAsync(device, messageSink.Report, cancellationToken, logNames);
+
+ ///
+ /// 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.
+ /// An optional parameter which, when specified, returns progress notifications.
+ /// The progress is reported as , representing the state of installation.
+ /// A which can be used to cancel the asynchronous operation.
+ /// The arguments to pass to adb install.
+ /// A which represents the asynchronous operation.
+ public static Task InstallAsync(this IAdbClient client, DeviceData device, Stream apk, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments) =>
+ client.InstallAsync(device, apk, progress == null ? null : progress.Report, cancellationToken, 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.
+ /// An optional parameter which, when specified, returns progress notifications.
+ /// The progress is reported as , representing the state of installation.
+ /// A which can be used to cancel the asynchronous operation.
+ /// 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, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments) =>
+ client.InstallMultipleAsync(device, baseAPK, splitAPKs, progress == null ? null : progress.Report, cancellationToken, 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.
+ /// An optional parameter which, when specified, returns progress notifications.
+ /// The progress is reported as , representing the state of installation.
+ /// A which can be used to cancel the asynchronous operation.
+ /// 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, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments) =>
+ client.InstallMultipleAsync(device, splitAPKs, packageName, progress == null ? null : progress.Report, cancellationToken, arguments);
+
+ ///
+ /// Asynchronously write an apk into the given install session.
+ ///
+ /// 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 name of the application.
+ /// The session ID of the install session.
+ /// 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 apk which has been transferred.
+ /// A which can be used to cancel the asynchronous operation.
+ /// A which represents the asynchronous operation.
+ public static Task InstallWriteAsync(this IAdbClient client, DeviceData device, Stream apk, string apkName, string session, IProgress? progress = null, CancellationToken cancellationToken = default) =>
+ client.InstallWriteAsync(device, apk, apkName, session, progress == null ? null : progress.Report, cancellationToken);
+
+#if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER
+ ///
+ /// 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.
+ /// An optional parameter which, when specified, returns progress notifications.
+ /// The progress is reported as , representing the state of installation.
+ /// A which can be used to cancel the asynchronous operation.
+ /// The arguments to pass to adb install.
+ /// A which represents the asynchronous operation.
+ public static Task InstallAsync(this IAdbClient.IWinRT client, DeviceData device, IRandomAccessStream apk, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments) =>
+ client.InstallAsync(device, apk, progress == null ? null : progress.Report, cancellationToken, 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.
+ /// An optional parameter which, when specified, returns progress notifications.
+ /// The progress is reported as , representing the state of installation.
+ /// A which can be used to cancel the asynchronous operation.
+ /// The arguments to pass to adb install-create.
+ /// A which represents the asynchronous operation.
+ public static Task InstallMultipleAsync(this IAdbClient.IWinRT client, DeviceData device, IRandomAccessStream baseAPK, IEnumerable splitAPKs, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments) =>
+ client.InstallMultipleAsync(device, baseAPK, splitAPKs, progress == null ? null : progress.Report, cancellationToken, 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.
+ /// An optional parameter which, when specified, returns progress notifications.
+ /// The progress is reported as , representing the state of installation.
+ /// A which can be used to cancel the asynchronous operation.
+ /// The arguments to pass to adb install-create.
+ /// A which represents the asynchronous operation.
+ public static Task InstallMultipleAsync(this IAdbClient.IWinRT client, DeviceData device, IEnumerable splitAPKs, string packageName, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments) =>
+ client.InstallMultipleAsync(device, splitAPKs, packageName, progress == null ? null : progress.Report, cancellationToken, arguments);
+
+ ///
+ /// Asynchronously write an apk into the given install session.
+ ///
+ /// 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 name of the application.
+ /// The session ID of the install session.
+ /// 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 apk which has been transferred.
+ /// A which can be used to cancel the asynchronous operation.
+ /// A which represents the asynchronous operation.
+ public static Task InstallWriteAsync(this IAdbClient.IWinRT client, DeviceData device, IRandomAccessStream apk, string apkName, string session, IProgress? progress = null, CancellationToken cancellationToken = default) =>
+ client.InstallWriteAsync(device, apk, apkName, session, progress == null ? null : progress.Report, cancellationToken);
+#endif
+#endif
+
///
/// Like "install", but starts an install session synchronously.
///
diff --git a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.cs b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.cs
index 20f79380..75a2c35c 100644
--- a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.cs
+++ b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.cs
@@ -3,6 +3,8 @@
//
using System;
+using System.Collections.Generic;
+using System.IO;
using System.Net;
namespace AdvancedSharpAdbClient
@@ -27,6 +29,33 @@ public static partial class AdbClientExtensions
public static int CreateForward(this IAdbClient client, DeviceData device, ForwardSpec local, ForwardSpec remote, bool allowRebind) =>
client.CreateForward(device, local.ToString(), remote.ToString(), allowRebind);
+ ///
+ /// 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
+ /// 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 int CreateForward(this IAdbClient client, DeviceData device, int localPort, int remotePort) =>
+ client.CreateForward(device, $"tcp:{localPort}", $"tcp:{remotePort}", true);
+
+ ///
+ /// 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.
+ /// 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 int CreateForward(this IAdbClient client, DeviceData device, int localPort, string remoteSocket) =>
+ client.CreateForward(device, $"tcp:{localPort}", $"local:{remoteSocket}", true);
+
///
/// Asks the ADB server to reverse forward local connections from
/// to the address on the .
@@ -115,31 +144,14 @@ public static void ExecuteRemoteCommand(this IAdbClient client, string command,
client.ExecuteRemoteCommand(command, device, receiver, AdbClient.Encoding);
///
- /// 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
- /// 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 int CreateForward(this IAdbClient client, DeviceData device, int localPort, int remotePort) =>
- client.CreateForward(device, $"tcp:{localPort}", $"tcp:{remotePort}", true);
-
- ///
- /// Forwards a remote Unix socket to a local TCP socket.
+ /// Runs the event log service on a device.
///
/// 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.
- /// 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 int CreateForward(this IAdbClient client, DeviceData device, int localPort, string remoteSocket) =>
- client.CreateForward(device, $"tcp:{localPort}", $"local:{remoteSocket}", true);
+ /// 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.
+ public static void RunLogService(this IAdbClient client, DeviceData device, Action messageSink, params LogId[] logNames) =>
+ client.RunLogService(device, messageSink, false, logNames);
///
/// Reboots the specified adb socket address.
@@ -224,5 +236,79 @@ public static string Connect(this IAdbClient client, IPEndPoint endpoint) =>
/// The results from adb.
public static string Connect(this IAdbClient client, string host, int port = AdbClient.DefaultPort) =>
client.Connect(Extensions.CreateDnsEndPoint(host, port));
+
+#if !NETFRAMEWORK || NET40_OR_GREATER
+ ///
+ /// 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.
+ public static void RunLogService(this IAdbClient client, DeviceData device, IProgress messageSink, params LogId[] logNames) =>
+ client.RunLogService(device, messageSink, false, logNames);
+
+ ///
+ /// 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.
+ /// A that can be used to cancel the task.
+ /// Optionally, the names of the logs to receive.
+ public static void RunLogService(this IAdbClient client, DeviceData device, IProgress messageSink, in bool isCancelled, params LogId[] logNames) =>
+ client.RunLogService(device, messageSink.Report, isCancelled, logNames);
+
+ ///
+ /// 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.
+ /// An optional parameter which, when specified, returns progress notifications.
+ /// The progress is reported as , representing the state of installation.
+ /// The arguments to pass to adb install.
+ public static void Install(this IAdbClient client, DeviceData device, Stream apk, IProgress? progress = null, params string[] arguments) =>
+ client.Install(device, apk, progress == null ? null : progress.Report, arguments);
+
+ ///
+ /// 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.
+ /// An optional parameter which, when specified, returns progress notifications.
+ /// The progress is reported as , representing the state of installation.
+ /// The arguments to pass to adb install-create.
+ public static void InstallMultiple(this IAdbClient client, DeviceData device, Stream baseAPK, IEnumerable splitAPKs, IProgress? progress = null, params string[] arguments) =>
+ client.InstallMultiple(device, baseAPK, splitAPKs, progress == null ? null : progress.Report, arguments);
+
+ ///
+ /// 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.
+ /// An optional parameter which, when specified, returns progress notifications.
+ /// The progress is reported as , representing the state of installation.
+ /// The arguments to pass to adb install-create.
+ public static void InstallMultiple(this IAdbClient client, DeviceData device, IEnumerable splitAPKs, string packageName, IProgress? progress = null, params string[] arguments) =>
+ client.InstallMultiple(device, splitAPKs, packageName, progress == null ? null : progress.Report, arguments);
+
+ ///
+ /// Write an apk into the given install session.
+ ///
+ /// 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 name of the application.
+ /// The session ID of the install session.
+ /// 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 apk which has been transferred.
+ public static void InstallWrite(this IAdbClient client, DeviceData device, Stream apk, string apkName, string session, IProgress? progress) =>
+ client.InstallWrite(device, apk, apkName, session, progress == null ? null : progress.Report);
+#endif
}
}
diff --git a/AdvancedSharpAdbClient/Extensions/EnumerableBuilder.cs b/AdvancedSharpAdbClient/Extensions/EnumerableBuilder.cs
index 08d11360..232d56f2 100644
--- a/AdvancedSharpAdbClient/Extensions/EnumerableBuilder.cs
+++ b/AdvancedSharpAdbClient/Extensions/EnumerableBuilder.cs
@@ -4,7 +4,10 @@
//
using System;
+using System.Collections.Generic;
using System.ComponentModel;
+using System.Diagnostics;
+using System.IO;
namespace AdvancedSharpAdbClient
{
@@ -46,6 +49,241 @@ public static FileStatistics FileStatisticsCreator(ReadOnlySpan values)
};
int ReadInt32(in ReadOnlySpan data) => data[index++] | (data[index++] << 8) | (data[index++] << 16) | (data[index++] << 24);
}
+
+ ///
+ /// Build a class.
+ ///
+ /// The data that feeds the struct.
+ /// A new instance of struct.
+ public static LogEntry? LogEntryCreator(ReadOnlySpan values)
+ {
+ if (values.IsEmpty) { return null; }
+ int index = 0;
+
+ // Read the log data in binary format. This format is defined at
+ // https://android.googlesource.com/platform/system/logging/+/refs/heads/main/liblog/include/log/log_read.h#39
+ ushort? payloadLengthValue = ReadUInt16(in values);
+ ushort? headerSizeValue = payloadLengthValue == null ? null : ReadUInt16(in values);
+ int? pidValue = headerSizeValue == null ? null : ReadInt32(in values);
+ uint? tidValue = pidValue == null ? null : ReadUInt32(in values);
+ uint? secValue = tidValue == null ? null : ReadUInt32(in values);
+ uint? nsecValue = secValue == null ? null : ReadUInt32(in values);
+
+ if (nsecValue == null)
+ {
+ return null;
+ }
+
+ ushort payloadLength = payloadLengthValue!.Value;
+ ushort headerSize = headerSizeValue!.Value;
+ int pid = pidValue!.Value;
+ uint tid = tidValue!.Value;
+ uint sec = secValue!.Value;
+ uint nsec = nsecValue.Value;
+
+ // If the headerSize is not 0, we have on of the logger_entry_v* objects.
+ // In all cases, it appears that they always start with a two uint16's giving the
+ // header size and payload length.
+ // For both objects, the size should be 0x18
+ LogId id = 0;
+ uint uid = 0;
+
+ if (headerSize != 0)
+ {
+ if (headerSize >= 0x18)
+ {
+ uint? idValue = ReadUInt32(in values);
+
+ if (idValue == null)
+ {
+ return null;
+ }
+
+ uid = idValue.Value;
+ id = (LogId)uid;
+ }
+
+ if (headerSize >= 0x1c)
+ {
+ uint? uidValue = ReadUInt32(in values);
+
+ if (uidValue == null)
+ {
+ return null;
+ }
+
+ uid = uidValue.Value;
+ }
+
+ if (headerSize > 0x20)
+ {
+ if (headerSize == 0x20)
+ {
+ // Not sure what this is.
+ _ = ReadUInt32(in values);
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException(nameof(values), $"An error occurred while reading data from the ADB stream. Although the header size was expected to be 0x18, a header size of 0x{headerSize:X} was sent by the device");
+ }
+ }
+ }
+
+ ReadOnlySpan data = ReadBytesSafe(in values, payloadLength);
+
+ if (data.IsEmpty)
+ {
+ return null;
+ }
+
+ DateTimeOffset timestamp = DateTimeExtensions.FromUnixTimeSeconds(sec);
+
+ switch (id)
+ {
+ case >= LogId.Min and <= LogId.Max and not LogId.Events:
+ // format: \0\0
+ byte priority = data[0];
+
+ // Find the first \0 byte in the array. This is the separator
+ // between the tag and the actual message
+ int tagEnd = 1;
+
+ while (data[tagEnd] != '\0' && tagEnd < data.Length)
+ {
+ tagEnd++;
+ }
+
+ // Message should be null terminated, so remove the last entry, too (-2 instead of -1)
+ string tag = AdbClient.Encoding.GetString(data[1..tagEnd]);
+ string message = AdbClient.Encoding.GetString(data.Slice(tagEnd + 1, data.Length - tagEnd - 2));
+
+ return new AndroidLogEntry
+ {
+ Data = data.ToArray(),
+ PayloadLength = payloadLength,
+ HeaderSize = headerSize,
+ ProcessId = pid,
+ ThreadId = tid,
+ TimeStamp = timestamp,
+ NanoSeconds = nsec,
+ Id = id,
+ Uid = uid,
+ Priority = (Priority)priority,
+ Message = message,
+ Tag = tag
+ };
+
+ case LogId.Events:
+ byte[] dataArray = data.ToArray();
+
+ // https://android.googlesource.com/platform/system/core.git/+/master/liblog/logprint.c#547
+ EventLogEntry entry = new()
+ {
+ Data = dataArray,
+ PayloadLength = payloadLength,
+ HeaderSize = headerSize,
+ ProcessId = pid,
+ ThreadId = tid,
+ TimeStamp = timestamp,
+ NanoSeconds = nsec,
+ Id = id,
+ Uid = uid
+ };
+
+ // Use a stream on the data buffer. This will make sure that,
+ // if anything goes wrong parsing the data, we never go past
+ // the message boundary itself.
+ using (MemoryStream dataStream = new(dataArray))
+ {
+ using BinaryReader reader = new(dataStream);
+ _ = reader.ReadInt32();
+
+ while (dataStream.Position < dataStream.Length)
+ {
+ ReadLogEntry(reader, entry.Values);
+ }
+ }
+
+ return entry;
+
+ default:
+ return new LogEntry
+ {
+ Data = data.ToArray(),
+ PayloadLength = payloadLength,
+ HeaderSize = headerSize,
+ ProcessId = pid,
+ ThreadId = tid,
+ TimeStamp = timestamp,
+ NanoSeconds = nsec,
+ Id = id,
+ Uid = uid
+ };
+ }
+
+ static void ReadLogEntry(BinaryReader reader, ICollection