I'm developing an app that uses Bluetooth to communicate with Smart Devices.
It needs to authorise iPhone with such scheme:
Scanning devices with some filters of services UUIDs
Connecting to a device, discovering services, discovering characteristics
Calling read operation to retrieve public keys
Exchange received public keys (using ECDH)
Generate token
Write encrypted token (using AES) to device using writeValue
That's all, after that I have stable connection and able to interact with my Smart Device (writing values to protected properties).
The question is that when I restart my app and do all of the steps above, except Step_5 (because I have my token generated and validated previous time), on Step_6 delegate method didWriteValueFor: doesn't even get called, instead of this delegate method didDisconnectPeripheral being called with Error Domain=CBErrorDomain Code=0 "Unknown error." UserInfo={NSLocalizedDescription=Unknown error.} and I can't write values to protected characteristics.
I can't get what I'm doing wrong. Any help would be nice.
UPD[0]: Sometimes I can get a callback for didWriteValueFor: on Step_6 but IMMEDIATELY (like less than a 0.5 sec) iPhone is being disconnected with an error from above.
UPD[1]: Also I was trying to cancelPeripheralConnection() before closing my app because Smart Device does not handle multiple connections in hardware reasons, but no luck here.
UPD[2]: Well, after three days of searching for information, I realized that I was digging in the wrong direction at all. The problem was that I interrupted the application with the “Stop” button in Xcode, and didn’t close it manually via iPhone, therefore, willTerminate was not called, and even if it was called, it had a very limited amount of time to execute all the processes that were supposed to be executed, as a result, the connection didn’t interrupt (the cancelPeripheralConnection method was not called), and the next time I started the App, I tried to establish a connection with a device that already has a connection to my App.
At least it started connecting often, but I'm still getting this error sometimes. The main question is: how properly disconnect from the device? For now I’m unsubscribing from all the properties that have isNotify: true (setting to false) and call cancelPeripheralConnection. Do I need to wait the “unsubscribe” process before disconnecting?
UPD[3]: It doesn't matter if I wait for unsubscribe finished or not. At least it have no effect.
Well, I found the reason why disconnects are appears:
Since Smart Device has several characteristics of public keys that need to be read, encrypted and then sent back, the order keys I read and write was important. Since writeValue and readValue are asynchronous, my order went astray. As soon as I began to keep order, everything worked like a clock.
Related
I'm working with Movesense 2.0.0 on a HR+ sensor and the latest MDSlib for Android.
I have an App that calls my custom WB services to download some data stored on sensor's EEPROM.
It happens that sometimes (once every few hundred calls, not always on the same endpoint and when it happens it keeps doing it for a few subsequent requests) sensor's requests handler is not called, and I get this messages from debugger:
ERROR: SF-N invalid CRC
ERROR: SF-N frame too short
ERROR: SF-N invalid CRC
Usually if I send the request again after a few seconds it's correctly handled.
I also tried sending hundreds of thousands of requests from wbcmd through serial port, but the error never appeared.
Is there something I can look at to troubleshoot this issue?
The CRC error indicates that the datapipe experienced dropping or incorrect data, which was caught by the whiteboard protocol CRC checks. Corruption usually happens when the radio link is on the verge of dropping completely and there really is not much that can be done.
You can read the radio link RSSI value (signal strength) in both iOS and Android to see bad the connection is. Most of the time I don't bother. Easier just to retry failed operations later.
Full Disclaimer: I work for the Movesense team
I implemented an UWP Server Socket following the sample here and it correctly works.
Now I want to make the app able to continuously accept requests, but I expect that when the app is suspendeded and a client sends a request, the server is not able to respond. If I am correct, what is the best way to avoid this status change? If possible, I would prefer a solution with Extended Execution instead of implementing a Background Task, but I don't know if the following code in the OnSuspending method is enough to keep the app in the Running status:
var newSession = new ExtendedExecutionSession();
newSession.Reason = ExtendedExecutionReason.Unspecified;
newSession.Revoked += SessionRevoked;
I saw people calling a "LongRunningWork()" function in other samples, but in my case the code to execute is already defined in the code-behind of the view as shown in the link above, so I would like simply keeping the app always running. Keep in mind that it is a LOB application, so I don't have Store limits.
For a toy project I want to find out if a mobile phone is connected to gsm or not. So I thought "Okay, let's use my local sip provider and see".
But in both cases, the thing goes like this:
I send an INVITE
0 s: I get a 100 Trying
5 s: I get a 183 Session description
I get an audio stream, in the one case with the ringing, in the other case with a "The person you are calling is…"
If I wait long enough (~ 40 s), I get a more appropiate status code like 180 Ringing.
Audio analysis is not an option, really.
Any hints on where to go now?
(I used twinkle for testing and a local german sip-provider.)
This issue is endemic in the way telephone networks work, and is not specific to SIP or IP. It's why, when you place a call to another country and the number is busy, you might sometimes hear your local country's busy tone, or you might hear a different busy tone that comes from the other country. In the latter case you cannot detect except by audio analysis, what the problem is. In SS7 and ISDN we speak of Q.931 cause codes instead of SIP error codes, but the principle is the same.
There's an argument to be made for configuring telephone systems to emit status codes instead of audio error messages. For callers using normal phones, the originating switch (the one closest to the caller) can then map that code to the appropriate spoken error message or audio tone. That way, when the call is being placed by software rather than by a person, the software can have access to the actual error code right away.
On the other hand you can also argue for having the remote switch (the one nearest the destination or the one that encounters the problem) speak its own error message. That switch knows best what the actual problem is. For example, a mobile operator can emit a spoken error message saying that the mobile phone you are trying to call is currently out of range. There is no Q.931 code (or SIP error code for that matter) with that meaning. It could return 27=Destination out of order?? Or 35=Destination unattainable?? Both of those codes are so esoteric, who knows what error message the local switch would translate them to (in practice: probably just a reorder tone, which is really user-unfriendly to a human caller). And when you try to map Q.931 cause codes to SIP error codes back and forth, even more information is lost because the codes really don't match up well at all. It's likely to be a much better user experience for the caller if the remote switch just plays back an informative, appropriate, recording which describes the problem.
Since there is this dilemma (arguments on both sides), we can conclude that this will not likely be resolved by completely standardizing on one way or ther other way anytime soon.
Anyway, sometimes this is configurable: your SIP provider may be able to configure your trunk for coded errors instead of recorded messages. If they offer this (some do), it's worth a try to set this option. But results will vary: this option only affects its local behaviour. In general if you want immediately call clearing with cause code and are instead getting a recorded error message from the other end, you will not be able to do anything about it, because the switch that makes the decision on which way it's going to respond is the remote one.
When using the audio message method, a proper Q.931 cause code or SIP error code usually comes eventually (after the recording is finished), but as you point out, it's probably too late by then.
My iPhone application supports a proprietary network protocol using the CocoaAsyncSocket library. I need to be able to send a network message out when my iPhone application is closed. The code that sends the message is getting called from the app delegate, but the application shuts down before the message actually goes out. Is there a way to keep the application alive long enough for the message to go out?
Bruce
The docs from Apple don't specifically state this, but the sense I get from looking around the Web and from personal experience is that you have about 4 to 5 seconds after the user hits the Home button to shut your app before your application actually terminates. The iPhone OS is controlling this so you can't block the termination to allow your program to finish first. Basically when your time is up, your program is killed.
There may be another solution, though. First I'd confirm that your code is really taking more than 5 seconds to run. Perhaps you can have it run in response to a button tap, and time how long it runs. If it is more than 5 seconds, you probably are running into this time out issue.
You might then find a way to trigger a message to be sent from a server that is always running. You should have enough time to trigger a remote action, which in turn could then take as long as it needs to run.
Or perhaps you could save the vital information to the iPhone file system on exit, and send that message the next time someone starts the application, which should theoretically give you enough time.
Hope this helps!
I assume you're already calling it from your AppDelegate's:
- (void)applicationWillTerminate:(UIApplication *)application
But as you've discovered there's no guarantee it'll be called or will be allowed to finish. There are a few options that may or may not work depending on what you're trying to do:
If you need the server to perform some sort of cleaning operation triggered by when the client app is gone then you could try watching for TCP socket closure on the server and treating that as the triggering event. But if you explicitly need to send data back with the closure this may not work.
If the data you're sending back is not time-sensitive then you can do like most of the analytics libraries do and cache the data (along with a uuid) on the client then try to send it on app closure. If it goes through, you can clear the cache (or do it the next time the app is run). If it doesn't, it's saved and you can send out when the app is run next. On the server, you would use the uuid to avoid duplicate requests.
If the material is time-sensitive then your best bet is to implement heartbeat and send periodic updated values to the server. Then when the client app dies the server times out the heartbeat and can use the last received value as the final closing point of data.
In either case, if an explicit closure event is required by your custom protocol then you may want to reconsider using it in a real-life mobile environment where things have to be much more fluid and tolerant of failure.
As others have noted, there's no way to be absolutely certain that you'll be able to send this, but there are approaches to help.
As Ken notes, you do in practice get a few seconds between "willTerminate" and forced termination, so there generally is time to do what you need.
A problem you're almost certainly running into is with CocoaAsyncSocket. When you get the "willTerminate" message, you're on the last run loop of the main thread. So if you block the main thread, and CocoaAsyncSocket is running on the main thread, it'll never get processed. As I recall, CocoaAsyncSocket won't actually send all the data until the next event loop.
One approach, therefore, is to keep pumping the event loop yourself:
- (void)applicationWillTerminate:(UIApplication *)application
{
// ...Send your message with CocoaAsyncSocket...
while (! ...test to see if it sent...)
{
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
}
I've also looked at putting this work onto a background thread and letting the main thread terminate, in theory letting us go back to Springboard while continuing to run for a few seconds. It's not immediately clear to me whether this will work properly using NSThread (which are detached). Using POSIX threads (which are joinable by default) may work, but probably circumvents any advantages of the background thread. Anyway, it's something to look at if useful. In my apps, we've used the "post next time we launch" approach, since that always works (even if you crash).
Say I have an application that wants a persistent connection to a server. How do I implement connection/re-connection logic so that I'm not wasting resources (power/bandwidth) and I have fast reconnect time when connectivity appears/improves? If I only use connectivity notifications, I can get stuck on problems not related to the local network.
Bonus if you could show me the C# version.
This is a very "huge" question. I can say that we use an O/R Mapper and each "query" to the database needs an object called PersistenceBroker. This class is in charge of all the DB Stuff related to connecting, authenticating etc.
We've written a PersistenceBrokerFactory.GetCurrentBroker() which returns the "working" broker. If the DB suddenly fails (for whatever reason), the CONN object will "timeout()" after 30secs (or whatever you define). If that happens, we show the user that he/she is offline and display a reconnect button.
On the other hand, to provide a visual indication that the user has connectivity, we have a thread running in the background, that checks for Internet connectivity every 15 seconds. We do 1 ping to google.com. ;) If that fails, we assume Internet is somehow broken, and we update a status bar.
I could show you all that code for the network health monitor if you wanted. I took some bits from google and other I made myself :)