quamotion/madb

DeviceMonitor failure

ningma97 opened this issue · 7 comments

Hi,

I have been using SharpAdbClient with great pleasure. However, recently I notice that occasionally DeviceMonitor would throw an exception and failed to detect new adb devices

07:34 E/AdbSocket: Got reply 'FAIL', diag='device offline'
Exception thrown: 'SharpAdbClient.Exceptions.AdbException' in SharpAdbClient.dll
Exception thrown: 'SharpAdbClient.Exceptions.AdbException' in SharpAdbClient.dll
Exception thrown: 'System.AggregateException' in mscorlib.dll
Exception thrown: 'SharpAdbClient.Exceptions.AdbException' in SharpAdbClient.dll
Exception thrown: 'SharpAdbClient.Exceptions.AdbException' in SharpAdbClient.dll

When this happens, I can see the device via adb command line. I guess I can try to catch this exception and restart the monitor, but thought it might be better to understand what has caused the problem.

My code is simply below.

var result = AdbServer.Instance.StartServer(@"adb.exe", true);
adbMonitor = new DeviceMonitor(new AdbSocket(AdbServer.Instance.EndPoint));
adbMonitor.DeviceConnected += this.OnDeviceConnected;
adbMonitor.DeviceDisconnected += this.OnDeviceDisconnected;
adbMonitor.Start();

Did I do anything wrong?

Many thanks.

Ning

Hi Ning,

No, I don't think you did anything wrong, and I'm assuming it's a bug in our codebase that we'd better fix :)

It would help if you can share more information about the exceptions that are being thrown.

Is the exception being caught by your application code? If so, can you share the stack trace and the error message of the exceptions?

Thanks!

Thanks for your prompt reply!

The exception was thrown when a new device was connected and I was accessing the DeviceData in the OnDeviceConnected handler with AdbClient.Instance.ExecuteRemoteCommand().

As I said this exception does not occur all the time but I almost always get it if I plug/unplug my device for a few times (I waited a few seconds before plugging the device back in). When the exception occurs, I am able to execute the same command in the command line using adb shell.

Some more information about the exception below. Let me know if you need any further information.

SharpAdbClient.Exceptions.AdbException occurred
  AdbError=device offline
  ConnectionReset=false
  HResult=-2146233088
  Message=An error occurred while reading a response from ADB: device offline
  Source=SharpAdbClient
  StackTrace:
       at SharpAdbClient.AdbSocket.ReadAdbResponse()
  InnerException: 

The Call Stack before the exception is

 	SharpAdbClient.dll!SharpAdbClient.AdbSocket.ReadAdbResponse()	Unknown
 	SharpAdbClient.dll!SharpAdbClient.AdbClient.SetDevice(SharpAdbClient.IAdbSocket socket, SharpAdbClient.DeviceData device)	Unknown
 	SharpAdbClient.dll!SharpAdbClient.AdbClient.ExecuteRemoteCommandAsync(string command, SharpAdbClient.DeviceData device, SharpAdbClient.IShellOutputReceiver receiver, System.Threading.CancellationToken cancellationToken, int maxTimeToOutputResponse)	Unknown
 	mscorlib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start<SharpAdbClient.AdbClient.<ExecuteRemoteCommandAsync>d__25>(ref SharpAdbClient.AdbClient.<ExecuteRemoteCommandAsync>d__25 stateMachine)	Unknown
 	SharpAdbClient.dll!SharpAdbClient.AdbClient.ExecuteRemoteCommandAsync(string command, SharpAdbClient.DeviceData device, SharpAdbClient.IShellOutputReceiver receiver, System.Threading.CancellationToken cancellationToken, int maxTimeToOutputResponse)	Unknown
 	SharpAdbClient.dll!SharpAdbClient.AdbClientExtensions.ExecuteRemoteCommand(SharpAdbClient.IAdbClient client, string command, SharpAdbClient.DeviceData device, SharpAdbClient.IShellOutputReceiver rcvr)	Unknown
>	TheiaCamera.exe!TheiaCamera.Form1.DeviceFileExists(SharpAdbClient.DeviceData device, string remotePath) Line 48	C#
 	TheiaCamera.exe!TheiaCamera.Form1.IsValidDevice(SharpAdbClient.DeviceData device) Line 41	C#
 	TheiaCamera.exe!TheiaCamera.Form1.OnDeviceConnected(object sender, SharpAdbClient.DeviceDataEventArgs e) Line 130	C#
 	SharpAdbClient.dll!SharpAdbClient.DeviceMonitor.OnDeviceConnected(SharpAdbClient.DeviceDataEventArgs e)	Unknown
 	SharpAdbClient.dll!SharpAdbClient.DeviceMonitor.UpdateDevices(System.Collections.Generic.List<SharpAdbClient.DeviceData> devices)	Unknown
 	SharpAdbClient.dll!SharpAdbClient.DeviceMonitor.ProcessIncomingDeviceData(string result)	Unknown
 	SharpAdbClient.dll!SharpAdbClient.DeviceMonitor.DeviceMonitorLoopAsync(System.Threading.CancellationToken cancellationToken)	Unknown
 	mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext(object stateMachine)	Unknown
 	mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)	Unknown
 	mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)	Unknown
 	mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.Run()	Unknown
 	mscorlib.dll!System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(System.Action action, bool allowInlining, ref System.Threading.Tasks.Task currentTask)	Unknown
 	mscorlib.dll!System.Threading.Tasks.Task.FinishContinuations()	Unknown
 	mscorlib.dll!System.Threading.Tasks.Task.FinishStageThree()	Unknown
 	mscorlib.dll!System.Threading.Tasks.Task<System.__Canon>.TrySetResult(System.__Canon result)	Unknown
 	mscorlib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder<string>.SetResult(string result)	Unknown
 	SharpAdbClient.dll!SharpAdbClient.AdbSocket.ReadStringAsync(System.Threading.CancellationToken cancellationToken)	Unknown
 	mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext(object stateMachine)	Unknown
 	mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)	Unknown
 	mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)	Unknown
 	mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.Run()	Unknown
 	mscorlib.dll!System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(System.Action action, bool allowInlining, ref System.Threading.Tasks.Task currentTask)	Unknown
 	mscorlib.dll!System.Threading.Tasks.Task.FinishContinuations()	Unknown
 	mscorlib.dll!System.Threading.Tasks.Task.FinishStageThree()	Unknown
 	mscorlib.dll!System.Threading.Tasks.Task<int>.TrySetResult(int result)	Unknown
 	mscorlib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.SetResult(int result)	Unknown
 	SharpAdbClient.dll!SharpAdbClient.AdbSocket.ReadAsync(byte[] data, int length, System.Threading.CancellationToken cancellationToken)	Unknown
 	mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext(object stateMachine)	Unknown
 	mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)	Unknown
 	mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)	Unknown
 	mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.Run()	Unknown
 	mscorlib.dll!System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(System.Action action, bool allowInlining, ref System.Threading.Tasks.Task currentTask)	Unknown
 	mscorlib.dll!System.Threading.Tasks.Task.FinishContinuations()	Unknown
 	mscorlib.dll!System.Threading.Tasks.Task.FinishStageThree()	Unknown
 	mscorlib.dll!System.Threading.Tasks.Task<int>.TrySetResult(int result)	Unknown
 	mscorlib.dll!System.Threading.Tasks.TaskCompletionSource<int>.TrySetResult(int result)	Unknown
 	SharpAdbClient.dll!SharpAdbClient.SocketExtensions.ReceiveAsync.AnonymousMethod__1(System.IAsyncResult iar)	Unknown
 	System.dll!System.Net.LazyAsyncResult.Complete(System.IntPtr userToken)	Unknown
 	System.dll!System.Net.ContextAwareResult.CompleteCallback(object state)	Unknown
 	mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)	Unknown
 	mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)	Unknown
 	mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state)	Unknown
 	System.dll!System.Net.ContextAwareResult.Complete(System.IntPtr userToken)	Unknown
 	System.dll!System.Net.LazyAsyncResult.ProtectedInvokeCallback(object result, System.IntPtr userToken)	Unknown
 	System.dll!System.Net.Sockets.BaseOverlappedAsyncResult.CompletionPortCallback(uint errorCode, uint numBytes, System.Threading.NativeOverlapped* nativeOverlapped)	Unknown
 	mscorlib.dll!System.Threading._IOCompletionCallback.PerformIOCompletionCallback(uint errorCode, uint numBytes, System.Threading.NativeOverlapped* pOVERLAP)	Unknown

Forgot to say that I am using the prerelease version 2.1.0-beta338...

Ok – this starts to make sense. Are you plugging & unplugging hour device really fast? It looks like your IsValidDevice method is still executing when you’ve disconnected the device, hence the exception.

It bubbles up because the DeviceMonitor raises the event, so if your code has an unhandled exception, the device monitor stops.

Could you wrap OnDeviceConnected in a try/catch block? That should fix it. On our side, we should make the device monitor more defensive – exceptions in event handlers should not cause the device monitor to stop.

I was playing with detection of device insertion using DeviceMonitor, and was not plugging/unplugging the device really fast.

My IsValidDevice method is used to detect if the newly inserted device is a specific type that I am interested in, and thus it was executed in OnDeviceConnected which was triggered when I connected the device, ie. the exception was thrown when I've just connected the device.

I could catch it, but want to understand if I am doing something wrong.

  1. Is it possible to access the device in OnDeviceConnected? I notice if I set a break point in OnDeviceConnected, therefore paused a bit before calling ExecuteRemoteCommand, I don't get the exception. It almost feels like occasionally the device was not ready before I called ExecuteRemoteCommand in OnDeviceConnected
  2. In case of an exception, is there a proper way to reset things and try again? I ask because when the exception is thrown, and my program breaks, I can execute the same command using adb shell

Thanks for the clarification! If I understand it correctly, what you're saying is that the device remains connected, and your assumption is that the event is raised before ExecuteRemoteCommand can reliable execute?

That may make sense, you should be able to test it by just adding a Thread.Sleep in your event handler and see if the issue goes away.

If it does, we have a timing issue - the event is fired too soon. The problem is, the DeviceMonitor doesn't really do much, and it receives all its events from adb. The same with ExecuteRemoteCommand -- all it does is to invoke the same adb command as adb shell.

W.r.t. the exception, SharpAdbClient doesn't keep much state to start with, so except for restarting the DeviceMonitor there's not much you need to do to try again.

Yes the device remained plugged in when the exception was thrown. It certainly seems so... however the problem doesn't occur every time I plug in the device but comes after a few times.

I will try to pause the thread a little bit and see if it goes away.

Thanks for your help.