CBL2CAPChannel not responsive while app is backgrounded - ios

I'm facing an issue where L2CAP (or NSStream) stops notifying events to the app after app went to background (after 2-10 seconds). App is probably being suspended (as expected).
My expectation is that when there's BLE activity, app should be awaken by the system.
The app supports 'background modes - uses LE Accessory. Also supports state restoration)
Here are some common use-cases (app is 'central')
app triggered 'connect'.. app gets suspended.. peripheral comes in range -> app awaken (works)
app is connected .. app gets suspended.. peripheral updates notifying characteristic
-> app awaken (works)
app is connected.. app gets suspended.. peripheral writes over L2CAP -> app awaken (does not work)
1 and 2 are very reliable (even after rebooting).
The 3 - does not work at all. Sadly I cannot find any relevant documentation so i assume it should be the same (as with characteristics)
From Apple (regarding 'bluetooth-central'):
When your app declares this, the system wakes it up from a suspended state to allow it to handle Bluetooth-related events
The main difference seems to be that L2CAP is built on top of NSStream - but I don't know what difference that makes on system level. NSStream runs on RunLoop.main (default).
Further on, if app is suspended, and peripheral performs L2CAP writes... (nothing happens), BUT then if peripheral notifies characteristic, it wakes up the app and it receives L2CAP messages + it can continue to receive L2CAP data for some time (2-10s) then it stops again. This could be some workaround (to always notify via characteristic) but I wish this was documented...
Is L2CAP not considered 'BLE' activity and will not wake the app by design?
Further from docs,
In addition, the system wakes up your app when any of the CBCentralManagerDelegate or CBPeripheralDelegate delegate methods are invoked, allowing your app to handle important central role events, such as when a connection is established or torn down, when a peripheral sends updated characteristic values, and when a central manager’s state changes.
This obviously does not include 'NSStream' callbacks...

Related

Best approach for continually scanning bluetooth devices on iOS as well as storing the peripheral data in background?

I am currently using CoreBluetooth to scan for peripherals. Every 30 seconds it sends its packet of information to the cloud by using a Timer. This is working good in foreground. I would like this exact operation to function seamlessly in the background too.
I have declared a CBUUID ahead of time for it seek out the designated peripheral in the background. Upon entering background mode, the scan stops functioning after 10~ seconds. How do I continually make the scan operate continue in the background?
I was looking into Bluetooth State Preservation, would this alleviate the issue? Should it also not be on the main thread?
After the packets are advertised I would like them stored in memory. I am aware the DiscoverPeripherals logs the peripherals... is it possible to log the RSSI and additional peripheral data in chronological order and have it an operation take affect per an interval? I was looking at BGProcessingTask to fire off a function in the background. Would a better approach be to use CoreData to store the memory and clear it after? Best/easy suggestion is appreicated.
I have changed the CBUUID to the right peripheral for centralManager.scanForPeripherals(withServices: [uuid], options: nil)
with uuid being the CBUUID. That seem to be allowing the peripheral to be detected in the background. It stopped logging the scan after a short period of time. Right when I put the app in background, it functions properly for a bit. I theorize it could just be calling it on the same thread and it may not be operating in the background all together. I did test this by changing the the withServices to nil, the result was that the operating was not being logged at all once I closed to the background.
I was expecting the operation to continually be scanning on the basis of the Timer every period, after the period is up it would send it the cloud just how it was doing it in the foreground.
There are lots of restrictions on iOS apps when they are not in the foreground. Once an app moves from the foreground it is suspended and can only execute in the background for specific reasons and for limited durations.
In general, anything based on a Timer will not fire when the app is not in the foreground.
Some Core Bluetooth events are delivered while your app is in the background:
Pending connect operations can complete with a corresponding delivery to your app.
Peripheral disconnections will be delivered to your app
GATT Notify/Indicate operations from a connected peripheral will be delivered to your app.
Discovery of new peripherals advertising a service that you are specifically scanning for will be delivered to your app.
It is this last behaviour that you are relying on.
While you have done the right thing by specifying the specific service you are interested in, your plans are being thwarted by the fact that Core Bluetooth will not deliver repeated discovery notifications for a particular peripheral.
When your app is in the foreground you can use the CBCentralManagerAllowDuplicatesKey option to request a discovery notification each time a peripheral advertisement is seen, even if an advertisement from that peripheral has been seen before. This option has no effect when your app is not in the background.
The best way to gather data on a periodic basis from a peripheral (whether in the background or foreground) is for that peripheral to send its data via Notify/Indicate, however you seem to be trying to scan for the existence of peripherals rather than gather specific data from them, so this may not work for you.
When one of the supported Core Bluetooth background events occurs and your app has been jettisoned, State restoration allows your app to respond after iOS relaunches your app. It will not help you in this case.
It probably isn't possible to do what you want, at least not without changing the behaviour of your peripheral.

didUpdateValueForCharacteristic not called when the app is in suspended state

I'm developing an App that needs, in certan scenarios, to be connected to a bluetooth Peripheral indefinitely.
Before I go on with my question, I want to confirm that:
I'm initializing my CBCoreBluetoothManager in didFinishLaunchingWithOptions:.
I have bluetooth-central background capabilities enabled.
I am using State Preservation and Restoration by having the Manager being initialized with a key, and implementing willRestoreState:
Before my problem happens, I have a successful connection with my Peripheral, I get its service (I'm only looking for one) and its characteristic (again I'm only looking for one) for which I do set setNotifyValue: to YES. Aditionally, while both in the Foreground, and the background, I get callbacks on didUpdateValueForCharacteristic: everytime the characteristic's value updates.
I am simulating the "suspended state" with kill(getpid(), SIGKILL) (I have also tested it by just putting the app in the background, and letting it sit for 1 hour+)
When I say suspended, I mean the actual Suspended state. Which I believe every app eventually gets there, after being put in the background for an amount of time.
The problem that I am having, is with getting callbacks on didUpdateValueForCharacteristic: when the app is suspended. Again I do get callbacks if the app is in the background, but not when it is suspended. Meaning, the app does not get woken up by a value update on the characteristic.
The funny thing is that while I have the app in suspended mode, and I walk out of the Peripheral range to disconnect, and then back in range to re-connect, the app will be woken up, because it will try to restore (it calls willRestoreState:) and for a brief time, will listen to any characteristic values updates.
To sum up, while my app is in the suspended state, and in range of the Peripheral, didUpdateValueForCharacteristic does not get called, meaning the app does not get woken up, by what I am 100% sure, is a characteristic value update. It would however, wake up the app, if I walk out of the Peripheral's range, and back in, because I am supporting State Preservation and Restoration.
From Apple's documentation:
The system wakes up your app when any of the CBCentralManagerDelegate or CBPeripheralDelegate delegate methods are invoked, allowing your app to handle important central role events, such as when a connection is established or torn down, when a peripheral sends updated characteristic values, and when a central manager’s state changes.
It seems that I should be getting those updates, has anybody ever had a problem like this?
Any help is appreciated!
Thanks in advance

iOS: Can a BLE device (not an iBeacon) wake up my app?

Is it possible to associate a regular Bluetooth Low Energy device (not an iBeacon!) with my iPhone app so that when the device sends data my app gets woken up by the iPhone even if the iPhone is locked and the app is terminated (not even in the background)?
As long as your app specifies Bluetooth Central background mode then it will be woken if
Your app has a current connection to the device and it sends data (i.e. the device is in range and it notifies or indicates on a characteristic)
Your app has a pending connection to the device and it comes into range(i.e. the device was out of range, but you have called connect to automatically reconnect when it comes into range)
Your app was scanning for specific service types and a device advertising one of these service types comes into range
The case where you app is terminated is slightly different. For these scenarios to work in this case your app must implement state preservation and restoration
Core Bluetooth supports state preservation and restoration for apps
that implement the central role, peripheral role, or both.
When your
app implements the central role and adds support for state
preservation and restoration, the system saves the state of your
central manager object when the system is about to terminate your app
to free up memory (if your app has multiple central managers, you can
choose which ones you want the system to keep track of). In
particular, for a given CBCentralManager object, the system keeps
track of:
The services the central manager was scanning for (and any scan
options specified when the scan started)
The peripherals the central
manager was trying to connect to or had already connected to
The
characteristics the central manager was subscribed to
The Apple guide talks about the situation where your App is terminated due to memory pressure. It doesn't specify what happens if the app is terminated by the user "swiping up" in the app switcher - In many cases iOS takes this as an indication that the user doesn't want the app to run at all and won't restore it in this case.
There are two states for a bluetooth device to interact with your app:
It has never interacted with your app before
It has already connected to the use's device once and to the app once
In either case, an iBeacon device will be able to interact with your app.
If the device hasn't connected with your app before, I'm not entirely certain if there is a way to make it work. I have tried and failed to get it to wake up the app.
However, if the bluetooth device has connected before, then you can use CBCentralManager and its delegate methods to communicate between the device and your app.
Core Bluetooth should wake up your app from the OS if registered. Once awake, it's running in the background like normal. You have up to 3 minutes to perform whatever tasks you need to.

App never be suspended after enter background?

My app uses bluetooth low energy wireless technology, and needs to run in background to perform certain bluetooth-related tasks, so declare that it supports a core bluetooth background execution mode in the Info.plist file. App would start to read RSSI per second after a connection is established from a ble device, the delegate method "peripheralDidUpdateRSSI:error:" of CBPeripheralDelegate will be invoked . According to apple docs, the bluetooth-central Background Execution Mode chapter describes:
In addition, the system
wakes up your app when any of the CBCentralManagerDelegate or
CBPeripheralDelegate delegate methods are invoked, allowing your app to handle
important central role events, such as when a connection is established or torn
down, when a peripheral sends updated characteristic values, and when a central
manager’s state changes.
Because my app always read rssi both foreground and background per second. Whether my app never be suspends? Thanks.
Because you have specified in your .plist that your application requires background processing, your app will always wake up when ever the CBCentralManagerDelegate or CBPeripheralDelegate delegate methods are invoked. Otherwise your app will be sleeping away like a baby in a cradle under a peaceful and blissful night.

How to wake up iOS app with bluetooth signal (BLE)

When using the BLE with CoreBluetooth (no iBeacon), is there a way to wake an app that is not running when the device receives a Bluetooth signal?
I'm simulating a beacon with the RedBearlab's BLE Shield (http://redbearlab.com/bleshield/).
Thanks,
DAN
* UPDATE 03/05/14 *
It looks like Apple has introduced a major update with iOS 7.1: now iOS will open your app for you if it detects a UUID that matches your app. The app only needs to be installed, it doesn't have to be running (logic in AppDelegate needed to answer the wake-up call).
If the app was running in the background and then closed (I mean here terminated - and you do not see it anymore in the list of background apps when you double click the home button) due to memory pressure from other apps, then the iOS will take care of waking it up again when a delegate is called (assuming you have background modes listed in .plist).
If the app was running in the background and then closed by the user (again I mean here terminated. So the user double clicked to get the list of apps running in the background and then clicked on your app in the background list until it wiggled and then pressed the 'x' next to the app to kill it) then this is a clear indication that the user does not want the app running in the background and the app will not be re-launched. The user has to relaunch the app for its delegates to start functioning again in the background (new in iOS7).
Update:
I am not sure if this is in the documentation but it is in CoreBluetooth WWDC 2013 Video. I recommend you watch it. They spent a good portion of the video on how CoreBluetooth behaves in the background.
From what I understand, if your app has not previously connected with the BLE Peripheral, then no.
If your app has previously connected with the BLE Peripheral, then use:
-connectPeripheral:options
Connection requests don't time out. The best place to call this method is when your app loses connectivity with the BLE peripheral. You will get notified when you lose connection to the peripheral in the CBCentralManagerDelegate Protocol:
-centralManager: didDisconnectPeripheral: error
So the next time your App comes in range of the BLE Peripheral, it will trigger this method. Also note that you will need to set up State Preservation and Restoration when you initialize a CBCentralManager.
https://developer.apple.com/library/ios/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/CoreBluetoothBackgroundProcessingForIOSApps/PerformingTasksWhileYourAppIsInTheBackground.html
the system wakes up your app when any of the CBCentralManagerDelegate or CBPeripheralDelegate delegate methods are
invoked, allowing your app to handle important central role events,
such as when a connection is established or torn down, when a
peripheral sends updated characteristic values, and when a central
manager’s state changes.
To perform certain peripheral role tasks while in the background, you
must include the UIBackgroundModes key with the bluetooth-peripheral
value in your app’s Info.plist file. When this key-value pair is
included in the app’s Info.plist file, the system wakes up your app to
process read, write, and subscription events.
You could try to declare voip in info.plist. For the time being, my application was automatically relaunched after a time, even user terminated it.

Resources