Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Xamarin.AndroidTools/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@
"00240000048000009400000006020000002400005253413100040000010001008B63AA15D5222FA8E77F9DBFBE47AD82E74130B737743BC5" +
"2A7074AD85D2EB34F28C83EFD0C1812C6957BAED24836300DF569EA0E6A71EEDA71C5E88A657AB8F243F268DA584B2D4EF4FCD3245B6A370" +
"001A77DE21DE670E4678E29942F5628B153E88CD19504664FC663F9729DCA1CA2977875710EAF2CBD382DD6FD52BE9D3")]
[assembly: InternalsVisibleTo ("Xamarin.Android.Tools.AndroidSdk-Tests, PublicKey=0024000004800000940000000602000000240000525341310004000011000000438ac2a5acfbf16cbd2b2b47a62762f273df9cb2795ceccdf77d10bf508e69e7a362ea7a45455bbf3ac955e1f2e2814f144e5d817efc4c6502cc012df310783348304e3ae38573c6d658c234025821fda87a0be8a0d504df564e2c93b2b878925f42503e9d54dfef9f9586d9e6f38a305769587b1de01f6c0410328b2c9733db")]
44 changes: 19 additions & 25 deletions src/Xamarin.AndroidTools/Debugging/DebuggingExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ namespace Xamarin.AndroidTools.Debugging
/// </summary>
public static class DebuggingExtensions
{
// Twenty retries at 250 ms intervals allow up to five seconds for the process to appear.
const int GET_PID_RETRY_COUNT = 20;
const int WAIT_BEFORE_RETRY_GET_PID = 250;
const int WAIT_FOR_DEBUGGER_TO_ATTACH_MS = 1400;

Expand Down Expand Up @@ -58,14 +60,8 @@ public async static Task StartWithDebuggingAsync(this IAndroidDevice device, Exe
}

await androidDevice.ExecuteIntentCommandAsync(configuration.RunCommand, configuration.LogWiter, token).ConfigureAwait(false);
if (javaDebugging) {
try {
await androidDevice.ConnectJdwpAsync (configuration, token).ConfigureAwait(false);
} catch (Exception ex) {
if (configuration.LogWiter != null)
configuration.LogWiter ($"warning: Could not connect Jdwp. {ex}");
}
}
if (javaDebugging)
await androidDevice.ConnectJdwpAsync (configuration, token).ConfigureAwait(false);
}

/// <summary>
Expand Down Expand Up @@ -173,29 +169,27 @@ public static bool IsWSA(this AndroidDevice androidDevice)

public static async Task ConnectJdwpAsync(this AndroidDevice androidDevice, ExecutionConfiguration config, CancellationToken token)
{
if (config.RunCommand != null && config.RunCommand is AmStartCommand amStartCommand && amStartCommand.EnableDebugging)
if (config.RunCommand is AmStartCommand amStartCommand && amStartCommand.EnableDebugging)
{
var packageName = (config.RunCommand as AmStartCommand).PackageName;
var pid = await androidDevice.GetProcessIDAsync(packageName, 5, WAIT_BEFORE_RETRY_GET_PID, token);
var packageName = amStartCommand.PackageName;
var pid = await androidDevice.GetProcessIDAsync (packageName, GET_PID_RETRY_COUNT, WAIT_BEFORE_RETRY_GET_PID, token).ConfigureAwait (false);

if (pid <= 0)
{
throw new Exception("Process Not Found.");
}

var jdwpClient = new JdwpClient(config.Debugger.JdwpHostName, config.Debugger.JdwpPort);
throw new InvalidOperationException ($"Could not find process for package '{packageName}'.");

await AdbServer.Default.ForwardPort(androidDevice, "tcp", jdwpClient.Port, "jdwp", pid, token);
try {
await jdwpClient.ConnectAsync (token);
using (var jdwpClient = new JdwpClient (config.Debugger.JdwpHostName, config.Debugger.JdwpPort)) {
await AdbServer.Default.ForwardPort (androidDevice, "tcp", jdwpClient.Port, "jdwp", pid, token);
try {
await jdwpClient.ConnectAsync (token);
Comment on lines +180 to +183

// Keep the Connection for 1300 milliseconds, otherwise the Android OS ignores the connection!
// https://github.com/aosp-mirror/platform_frameworks_base/blob/6b28a227400749f4f8ad1f56799370e7c2cab149/core/java/android/os/Debug.java#L101C50-L101C54
await Task.Delay (WAIT_FOR_DEBUGGER_TO_ATTACH_MS);
// Keep the Connection for 1300 milliseconds, otherwise the Android OS ignores the connection!
// https://github.com/aosp-mirror/platform_frameworks_base/blob/6b28a227400749f4f8ad1f56799370e7c2cab149/core/java/android/os/Debug.java#L101C50-L101C54
await Task.Delay (WAIT_FOR_DEBUGGER_TO_ATTACH_MS, token);

await jdwpClient.DisconnectAsync ();
} finally {
await AdbServer.Default.KillForward (androidDevice, "tcp", jdwpClient.Port, token);
await jdwpClient.DisconnectAsync ();
} finally {
await AdbServer.Default.KillForward (androidDevice, "tcp", jdwpClient.Port, CancellationToken.None);
}
}
}
}
Expand Down
11 changes: 8 additions & 3 deletions src/Xamarin.AndroidTools/Devices/AndroidDeviceExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,15 +144,20 @@ static Task KillProcessAndWaitForExitPreIcs (AndroidDevice device, string packag
}

public static async Task<int> GetProcessIDAsync(this AndroidDevice device, string packageName, int maxAttempts, int timeBetweenAttempts, CancellationToken token)
{
return await GetProcessIDAsync (cancellationToken => device.GetProcessId (packageName, cancellationToken), maxAttempts, timeBetweenAttempts, token);
}
Comment on lines 146 to +149

internal static async Task<int> GetProcessIDAsync (Func<CancellationToken, Task<int>> getProcessId, int maxAttempts, int timeBetweenAttempts, CancellationToken token)
{
var retryCount = 1;
var pidOfResult = await device.GetProcessId(packageName, token);
var pidOfResult = await getProcessId (token);
AndroidLogger.LogDebug("GetProcessIDAsync", "PID of the application :: " + pidOfResult);

while (pidOfResult <= 0 && retryCount <= maxAttempts)
{
await Task.Delay(timeBetweenAttempts);
pidOfResult = await device.GetProcessId(packageName, token);
await Task.Delay(timeBetweenAttempts, token);
pidOfResult = await getProcessId (token);
AndroidLogger.LogDebug("GetProcessIDAsync", "Retrying " + retryCount + " time(s) to get PID of the application");
retryCount++;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;

namespace Xamarin.Android.Tools.Tests;

[TestFixture]
public class AndroidDeviceExtensionsTests
{
[Test]
public async Task GetProcessIDAsyncRetriesUntilProcessAppears ()
{
var processIds = new Queue<int> (new [] { 0, 0, 1234 });

var processId = await AndroidDeviceExtensions.GetProcessIDAsync (
_ => Task.FromResult (processIds.Dequeue ()),
maxAttempts: 20,
timeBetweenAttempts: 0,
token: CancellationToken.None
);

Assert.AreEqual (1234, processId);
Assert.AreEqual (0, processIds.Count);
}

[Test]
public async Task GetProcessIDAsyncStopsAfterMaximumAttempts ()
{
var attempts = 0;

var processId = await AndroidDeviceExtensions.GetProcessIDAsync (
_ => {
attempts++;
return Task.FromResult (0);
},
maxAttempts: 2,
timeBetweenAttempts: 0,
token: CancellationToken.None
);

Assert.AreEqual (0, processId);
Assert.AreEqual (3, attempts);
}
Comment on lines +27 to +44

[Test]
public void GetProcessIDAsyncHonorsCancellation ()
{
using (var cancellationTokenSource = new CancellationTokenSource ()) {
cancellationTokenSource.Cancel ();

Assert.ThrowsAsync<TaskCanceledException> (() => AndroidDeviceExtensions.GetProcessIDAsync (
_ => Task.FromResult (0),
maxAttempts: 20,
timeBetweenAttempts: 1,
token: cancellationTokenSource.Token
));
Comment on lines +52 to +57
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

<ItemGroup>
<ProjectReference Include="..\..\src\Xamarin.Android.Tools.AndroidSdk\Xamarin.Android.Tools.AndroidSdk.csproj" />
<ProjectReference Include="..\..\src\Xamarin.AndroidTools\Xamarin.AndroidTools.csproj" />
</ItemGroup>

</Project>