In my application I encounter strange behavior of network layer. Sometimes my application receives time-out exceptions and requests still reaches the server after some (significant) period of time. I don't know exact conditions when and why this happens in production but I can reproduce it using voice call. Moreover, it seems that the issue is not in my application. Using some tricks I can reproduce it even on standard BlackBerry Browser at least in simulator.
Excuse my in advance for very long and boring post but this is the only way I see to state the facts I know.
I'm going to get into standard BlackBerry browser implementation as reference implementation of network usage. It is hard to do, since RIM doesnxxx8217;t give us code but it is still possible to do something. To follow my experiment you will need some JDE, BlackBerry device simulator, MDS, some network sniffer and some patience. I used JDE 4.2.1 with BB-8800, MDS from JDE 4.1 and MS Network Monitor (available for free at w(w)w.microsoft(dot)com/downloads/details.aspx?displaylang=en&FamilyID=983b941d-06cb-4658-b7f6-3088333d062f you'll have to decrypt the link because I'm newbie on this forum).
The way I use to discover RIMxxx8217;s implementation of different things is debugging. When JDE stops at breakpoint it shows several nice things (accessible via View menu): Call Stack (Alt+7) and Locals (Alt+4) for simple things and VM Byte Code, VM Locals and VM Stack for some advanced debugging (this time wexxx8217;ll need it). And it works even if debugger stops not in ours code! All we need is to find public methods that RIM uses, place breakpoint there and wait until it will be triggered by RIMxxx8217;s code.
Letxxx8217;s try to do it. To begin with I need two breakpoints: in Connector.open and inside RadioInfo.isDataServiceSuspended. I used a simple application that calls both methods to set breakpoints there (you can find it in zip attachment). Application is quite simple. There is a single screen with two menu items that just call the methods I need.
First set breakpoint at
conn = (HttpConnection) Connector.open(url, Connector.READ_WRITE, true);
line and activate corresponding menu item. When debugger stops on breakpoint delete it (we will not need it further), step into (F11), set breakpoint and disable it (not delete, but just disable with Ctrl+F9, wexxx8217;ll activate it later).
Again set breakpoint at RadioInfo.isDataServiceSuspended call and activate corresponding menu item. When debugger stops on breakpoint and step into. Here we want to set breakpoint not at the beginning of the method. So donxxx8217;t delete our breakpoint now, you might need it if you miss proper place first time. Show Call Stack debug window (Alt+7) and Use Step into and Step Out (Shift+F11) until you reach such stack trace: CallCommandHandler.isActive(). Show VM Byte Code window and activate it. Use Step Over (F10) until you reach ireturn statement (Note: youxxx8217;d better dock this particular window. Sometimes Step In/Out works strange if this window is not docked. You can see what I have in JDE at this moment on attached screen shot). Set breakpoint at ireturn, disable it and release debugger (F5). Now you can delete breakpoint in my code.
We have to disable breakpoint because these methods, especially CallCommandHandler.isActive, are used a lot by background threads and breakpoint will be triggered too often.
Now we are almost ready. Start MDS. Start your sniffer and configure it to catch HTTP traffic. (In MS Network monitor create new capture tab, set HTTP as either Display or Capture filter and Start Capture). In JDE Show Breakpoints window (Alt+F9) and enable breakpoint in Connector.open. Navigate to any URL that is not cached by the browser (to be sure I use some new fake search request in google). Debugger will stop at breakpoint at Connector.open. Find in call stack method RawDataCache.get(FetchRequest), click on it in the call stack window, and set and disable breakpoint there. Disable Connector.open breakpoint and let simulator Go. Look at the sniffer. You should see your HTTP requests.
OK, this is what I expected in this case. But interesting things happens with time-out. Wexxx8217;ll simulate it with voice call. But since Browser checks it, we have to cheat it. Start voice call (to any number) and Answer it on pop-up dialog. Switch back to the browser having active voice call. Enable breakpoint at RawDataCache.get(FetchRequest) and open some other not cached URL. Debugger will stop at our breakpoint. Enable breakpoint at CallCommandHandler.isActive and release debugger (F5). Debugger will stop at it. However there might calls from code different from the browser. This is not what we need. If you had one just use Go (F5). Call that you need will be on the same thread with RawDataCache.get(FetchRequest) in the stack trace. To me the easiest way to catch it is to check that start of call stack is RenderThread.run(). When you are at breakpoint with proper call stack, show VM Stack window (Alt+6). Youxxx8217;ll see single value 0x00000001 (Boolean true) in the stack. Change it to 0x00000000 (false), disable breakpoint and let simulator Go. Browser will show progress bar. Wait for several minutes until error message appears. Close error message. Now disconnect your voice call and look at the sniffer. Youxxx8217;ll see you HTTP requests there! (at least I had). If you are curious you also might set breakpoint on java.io.InterrupetedIOException and ensure that browserxxx8217;s request failed with timeout.
We know that request didnxxx8217;t leave device at the moment when connection was closed by timeout but still it reached the server after it. And here is my main question: Is such behavior bug or feature? To me it seems like bug. For example, in my application I send some updates to server via HTTP POST request and expect to receive update data from server. In such case server processes request, updates data but its response is ignored by mobile client because connection was closed long time ago. Moreover in case of any network error my application believes that update didnxxx8217;t reach server and sends it again and again. It means that the same update is performed several times by the server. This is definitely not the thing I want (and not the thing I expect). Is there way to avoid such behavior? Is RadioInfo.isDataServiceSuspended() check before actual request enough for it?
Thanks in advance, any advice will be appreciated.