Bug 23154 - Unable to Close and Open a 2nd EASession
Summary: Unable to Close and Open a 2nd EASession
Status: RESOLVED ANSWERED
Alias: None
Product: iOS
Classification: Xamarin
Component: General ()
Version: XI 8.0.0
Hardware: PC Mac OS
: Normal normal
Target Milestone: Untriaged
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2014-09-18 12:26 UTC by Cody Beyer (MSFT)
Modified: 2016-05-25 20:53 UTC (History)
7 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:


Attachments
XCode project that demonstrates multiple connect/disconnect cycles with EASession (37.83 KB, application/zip)
2015-03-04 17:36 UTC, scott
Details


Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED ANSWERED

Description Cody Beyer (MSFT) 2014-09-18 12:26:54 UTC
When attempting to create a 2nd session of an EASession (using a bluetooth device), after disposing of the first, the following error is thrown:

2014-09-15 11:47:18.605 TestCB[270:60b] ERROR - opening session failed
2014-09-15 11:47:18.608 TestCB[270:60b] ERROR - /SourceCache/ExternalAccessory/ExternalAccessory-243.10.9/EASession.m:-[EASession dealloc] - 142 unable to close session for _accessory=0x15ec0ca0 and sessionID=65536


It would appear that disposing of the session does not complete fully.


Here is the code to recreate:

using System;
using System.Drawing;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using MonoTouch.ExternalAccessory;
using System.Linq;

namespace TestCB
{
public partial class TestCBViewController : UIViewController
{
public TestCBViewController(IntPtr handle) : base(handle)
{
}

private EASession _session = null;
private EAAccessory _accessory = null;
EAAccessory[] accessories = null;

private void UdateScreenState()
{
btnOpen.Enabled = _session == null;
btnClose.Enabled = _session != null;
}

partial void btnOpen_TouchUpInside(UIButton sender)
{
EAAccessoryManager mgr = EAAccessoryManager.SharedAccessoryManager;
accessories = mgr.ConnectedAccessories;

foreach(EAAccessory accessory in accessories)
{
if(_accessory.ProtocolStrings.Contains("com.zebra.rawport"))
{
_accessory = accessory;
_session = new EASession(_accessory, "com.zebra.rawport");
break;
}
}

UdateScreenState();
}

partial void btnClose_TouchUpInside(UIButton sender)
{

_accessory.Dispose();
_session.Dispose();
_session = null;

UdateScreenState();
}
}
}

Thanks
Comment 2 Rolf Bjarne Kvinge [MSFT] 2014-09-18 13:09:03 UTC
This can become difficult to solve without a test case.

Googling came up with this: http://prod.lists.apple.com/archives/bluetooth-dev/2014/Apr/msg00015.html - but not much more, which indicates that it's possible to run into this condition with Xcode as well.

One possible way forward would be to create an Xcode sample equivalent to the sample code in the description, and see if that works (instead of using Apple's EADemo, which is has a lot of extra code).
Comment 3 Sebastien Pouliot 2014-09-19 12:01:16 UTC
Like Rolf said it's hard to diagnose without a test case. It's also hard, with just a bit of code, to know what was tried and what's the end goal.

That being said only one session should be possible and this limit should be dealt by ExternalAccessory.framework itself. From [1]

> For a given accessory object, only one session at a time is allowed for a specific protocol. 
> If you attempt to create a session using a protocol that is already in use, the External 
> Accessory framework closes the existing session before opening the new one.

There's some logic there as there are no public way to close a session, i.e. calling Dispose only tells iOS that you do not want it anymore (just like calling `release` in ObjC would do) but it could still be alive for some time (until the connection is really closed). 

OTOH the EA.framework should still give you a new session [1]. My _guess_ is that some condition aborts the closing process.

Some things you might want to try:

(a) do not call _session.Dispose (just set _session to null). EA should give you a new one [1] so let it and the GC deal with the previous one;

(b) Are the input/output steams used (in the full app) like it's shown in [1] ? If so it _might_ be possible that EASession:

(b.1) can only be closed if those (open streams) are closed, see [2]; or

(b.2) needs to be used (stream opened) before being closed (that would be weird, but since it's very common to do so it might be a bug in Apple's EA);


[1] https://developer.apple.com/library/ios/featuredarticles/ExternalAccessoryPT/Articles/Connecting.html
[2] http://stackoverflow.com/a/23498360/220643
Comment 4 Serge Roy 2014-09-26 08:45:36 UTC
I was the user who initially submitted this issue, I'm not sure if it's appropriate for me to comment here or not, let me know if I shouldn't.  The use case it to send data to a mobile thermal printer.  The way we implemented this is to open the accessory send data and close the accessory, we thought it was best practice as it's how it's done in the EADemo:

1. Pick an accessory from the 1st screen
2. A 2nd screen opened while creating a new EASession for the selected accessory
3. Send test data from that new screen
4. Backing out of that 2nd screen release the EASession

From the EADemo you can repeat steps 1-4 as many times as you want (and I did test this out with the printer we are using and it does work).  In Xamarin I couldn't reproduce this behavior and it might be something I'm doing wrong, which is why I made the simplest sample I could, but still couldn't get an open/close/open/close flow, which is when I decided to ask for your help, to see if there was something I was doing wrong.

I did try your recommendation; to not dispose the EASession and just reopen another one and, but I still get the same error.
Comment 5 Serge Roy 2014-11-21 07:46:58 UTC
Was my last post informative enough do I need to provide more information to help with this issue?
Comment 6 Rolf Bjarne Kvinge [MSFT] 2014-11-24 04:50:05 UTC
@Serge, since we can't reproduce this ourselves (external hardware requirement), could you create a simple Xcode sample that works as expected (you could probably start with EADemo and strip away all the unused code)? That way we could compare exactly what Objective-C does and what Xamarin.iOS does, and see if there's something different somewhere.
Comment 7 scott 2015-03-04 17:36:35 UTC
Created attachment 10161 [details]
XCode project that demonstrates multiple connect/disconnect cycles with EASession

XCode project that demonstrates multiple connect/disconnect cycles with EASession
Comment 8 scott 2015-03-04 17:38:55 UTC
We're experiencing the identical issue trying to use Xamarin.iOS with a bluetooth classic serial device. 

I've attached an xcode project that shows the correct behavior - being able to go through multiple connect/disconnect cycles.

With Xamarin.iOS, we're finding that the second connection attempt (that is, creating an instance of EASession after previously disconnecting) gives us an exception:

 BluetoothLink.iOS[5491:2846997] ERROR - /SourceCache/ExternalAccessory/ExternalAccessory-287/EASession.m:-[EASession dealloc] - 141 unable to close session for _accessory=0x17440cd30 and sessionID=65536
Comment 9 Sebastien Pouliot 2015-03-04 21:08:45 UTC
Thanks for the test case, we'll have a look shortly.
Comment 10 Rolf Bjarne Kvinge [MSFT] 2015-03-05 05:00:17 UTC
Scott, here's an equivalent C# sample: https://github.com/rolfbjarne/SimpleExternalAccessorySample - can you try this and see if it works for you?
Comment 11 scott 2015-03-05 10:58:58 UTC
Thanks so much Rolf - this was great.  It pointed out that we were not calling Dispose on the InputStream/Outputstream as you are in your code sample.  This was the key for us.

You may wish to consider adding your sample to the list here: https://github.com/xamarin/monotouch-samples simply because there has not been much said in the past about consuming ExternalAccessory from Xamarin.  

I know its an old portion of iOS, but there are a few hardware applications where bluetooth classic is still required.

thanks again!



			session.InputStream.Close ();
			session.InputStream.Unschedule (NSRunLoop.Current, NSRunLoop.NSDefaultRunLoopMode);
			session.InputStream.Delegate = null;
			session.InputStream.Dispose ();

			session.OutputStream.Close ();
			session.OutputStream.Unschedule (NSRunLoop.Current, NSRunLoop.NSDefaultRunLoopMode);
			session.OutputStream.Delegate = null;
			session.OutputStream.Dispose ();
Comment 12 scott 2015-03-05 11:15:36 UTC
As a side note, we *were* disposing the session itself.  It is interesting that disposing of the session doesn't cause a dispose of the contained streams, which would seem like natural encapsulation.
Comment 13 Sebastien Pouliot 2016-05-25 20:53:13 UTC
Closing.

Request for the sample was filled in
https://trello.com/c/DmOKqG6w/230-externalaccessory-add-sample