boskokg / flutter_blue

Bluetooth plugin for Flutter
BSD 3-Clause "New" or "Revised" License
48 stars 23 forks source link

CentralManager Init: Background Usage and API MISUSE #13

Open trueb2 opened 2 years ago

trueb2 commented 2 years ago

Previously, https://github.com/pauldemarco/flutter_blue/pull/191 had changes for background usage on iOS and Android.

https://github.com/boskokg/flutter_blue/releases/tag/0.11.0 claims Android background usage was added in this repo, but no PR number was referenced and the iOS changes are not present currently.

There are some conflicts with master now because the centralManager initialization was moved to the first handled message. This causes a race on whatever first message is received and startup of CoreBluetooth. The workaround is to make a call like flutterBlue.instance.isOn().

The reason for this change was to delay requested permissions popups from firing as soon as the app opens the first time, so it was a necessary but undocumented regression.

rohitsangwan01 commented 2 years ago

@trueb2 , is there any way to keep app active in background with this package in both android and ios ??

trueb2 commented 2 years ago

I am not familiar with background bluetooth usage on Android in recent years yet. If you are always connected to a BLE device on iOS, then you can remain on as long as you

Even then you will still get killed every now and then to be woken up by the iOS restore interface

Word of advice: Don't plan on your app staying active in the background, just plan on gracefully handling background connections and events.

Related: I am testing and adding support for some applications that benefit from regular continuous connections w/ connection interval around 1-2s. I need flutter to not crash and need behavior to match for Android and iOS.

rohitsangwan01 commented 2 years ago

Thanks , in my case , app should remain connected to bluetooth device , and there will be continous flow of data from device to App sometimes , or sometimes nothing just stay connected , as long as user swipe from memory , i managed to achieve this in Android by keeping app Alaways Active , but no idea about ios

trueb2 commented 2 years ago

This issue is mostly just related to the race condition introduced w/ the permissions request change. With the changes in trueb2#3. You would do something like the following to make sure that restoration works correctly, relieving the race condition.

// first call sets the restoration id to use for the centralManager
final setIdBeforeInit = await flutterBlue.setUniqueId("FLUTTER BLE ID");

// second call initializes centralManager on first use w/ unique id
final isOn = await flutterBlue.isOn;

// succeeds if centralManager receives its .poweredOn callback before flutter tries to use the centralManager
flutterBlue.instance.startScan(......
trueb2 commented 2 years ago

Thanks , in my case , app should remain connected to bluetooth device , and there will be continous flow of data from device to App sometimes , or sometimes nothing just stay connected , as long as user swipe from memory , i managed to achieve this in Android by keeping app Alaways Active , but no idea about ios

With what was in this fork and the main fork, you could probably go about 30s while still connected because there wasn't anything in place to request UIBackgroundModes bluetooth-central or willRestoreState.

What you want sounds similar to what I am looking to make work. Again, there are definitely feature gaps w/ flutter_blue at the moment, but since we have access to the Java, Obj-C, and project/permission configs, flutter_blue is capable of satisfying the project requirements as well as a native implementation. Most of the work is done as well, but I am leaving boskokg#14 as a draft until I have tested on Android and iOS.

That can take a while because of Android and iOS but feel free to use that branch to do some testing of your own if you think it can solve your issue.

peekpt commented 2 years ago

I use this plugin https://pub.dev/packages/wakelock it seems to hold connection. The downfall is that your app will never enter turn off screen.

trueb2 commented 2 years ago

I use this plugin https://pub.dev/packages/wakelock it seems to hold connection. The downfall is that your app will never enter turn off screen.

That isn't relevant to background usage.

rohitsangwan01 commented 2 years ago

@trueb2 @peekpt , by using https://pub.dev/packages/flutter_background with https://pub.dev/packages/flutter_reactive_ble , am able to keep connection with bluetooth Device and perform All tasks in background as long as user does not swipe up app from memory , and it works really great , but only for andriod currently

trueb2 commented 2 years ago

flutter_background doesn't seem like a useful concept at first glance of the permissions they are trying to use. flutter_blue background BLE connections should work without another package anyways.

As for flutter_reactive_ble, they have the same problem of not following the iOS background interface. The design is also very 'swift'y, which I don't really like. I would rather make flutter_blue work.

Here is the code that initializes the central manager the same way flutter_blue does. https://github.com/PhilipsHue/flutter_reactive_ble/blob/876de0df13410a93a9034f39e65dce7833bd7126/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Central.swift#L100

If the user force kills the app, then neither package would be able to work as referenced in the table on https://developer.apple.com/library/archive/qa/qa1962/_index.html that I linked earlier.

rohitsangwan01 commented 2 years ago

@trueb2 seems we both looking for same solution , are you on telegram ?? for better communication..

trueb2 commented 2 years ago

@trueb2 seems we both looking for same solution , are you on telegram ?? for better communication..

No, I'm not on telegram. If you have more things to discuss regarding the feature gap or advice, then it would be good to keep it public anyways. A lot of iOS behaviors are found through discovery rather than documentation. I found it quite hard to find correct info.

I suggest opening a new issue for discussion or commenting on the draft PR that I have out, which I intend to address the feature gaps between Android and iOS.

rohitsangwan01 commented 2 years ago

@trueb2 , what would you sujjest , choose Flutter_Reactive_ble which is in Continours Support by Devlopers, or Flutter_blue ??

trueb2 commented 2 years ago

@trueb2 , what would you sujjest , choose Flutter_Reactive_ble which is in Continours Support by Devlopers, or Flutter_blue ??

Use whichever you want. They both have feature gaps.

As I said already, I would prefer to be hacking on flutter_blue rather than flutter_reactive_ble. I find flutter_blue easier for me to debug. I don't trust PhillipsHue to make changes any faster than I could make them to my own fork of flutter_blue.

My changes to flutter_blue that are not breaking changes will pile up in #14 until further notice.

MrCsabaToth commented 2 years ago

@trueb2 , what would you sujjest , choose Flutter_Reactive_ble which is in Continours Support by Devlopers, or Flutter_blue ??

Does flutter_reactive_ble has background processing support?

MrCsabaToth commented 2 years ago

@trueb2 @peekpt , by using https://pub.dev/packages/flutter_background with https://pub.dev/packages/flutter_reactive_ble , am able to keep connection with bluetooth Device and perform All tasks in background as long as user does not swipe up app from memory , and it works really great , but only for andriod currently

Sorry for the off-topic, but I must ask: when using flutter_background together with flutter_reactive_ble or flutter_blue + on pre Android 12 the bluetooth functionality requires location permission. When combining this with background did you have any problem releasing the app to Google Play Store? I'm asking because I have an app which was removed from the Play Store because support could not understand the situation no matter how many times I tried to explain it.

rohitsangwan01 commented 2 years ago

@MrCsabaToth , well my app is safe on PlayStore, atleast till now :-) , i used another package for background processing , i,e , https://pub.dev/packages/flutter_foreground_task with flutter_reactive_ble

MrCsabaToth commented 2 years ago

@MrCsabaToth , well my app is safe on PlayStore, atleast till now :-) , i used another package for background processing , i,e , https://pub.dev/packages/flutter_foreground_task with flutter_reactive_ble

Oh interesting. Looks like both https://pub.dev/packages/flutter_foreground_task and https://pub.dev/packages/flutter_background mainly operates with android.permission.FOREGROUND_SERVICE (and wake lock which I already have). I noticed flutter_background only because you mentioned it, it does not support iOS but the flutter_foreground_task does. So you recommend flutter_foreground_task, have you tried it with iOS? The one extra flutter_background seems to have is android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS.

rohitsangwan01 commented 2 years ago

@MrCsabaToth , Flutter Foreground too have Option to Get Permission for BatteryOptimisation and a method to check if batteryOptimisation is on or Off , plus this library gave us a method which will be calling in a repetetive interval of time (optional) , and also callbacks for buttons in Notification , working fine on Android so far , but not sure about Ios , sometimes App keep working for long time , sometimes app stops after 30sec in few iphones , am trying to figure that out , but Ios background is really not reliable , atleast nothing similar to android , which can promise exact background run solution

trueb2 commented 2 years ago

@MrCsabaToth If you look at the way the that this flutter_blue fork and the flutter_reactive_ble repo initialize their central managers, you can see that neither of them initialize the central manager with the correct arguments for background peripheral interaction. The willRestore hook and central manager instance with an identifier are required as linked in the apple documentation earlier in this thread. My fork includes those interface changes, which require init code in the AppDelegate didFinishLaunchingWithOptions. I do not have an issue with running in the background on BLE (aside from making sure that I don't use too much CPU or network).

Also, location service permissions shouldn't be an issue with bluetooth permissions. They used to always go hand in hand on Android (https://android.stackexchange.com/questions/160479/why-do-i-need-to-turn-on-location-services-to-pair-with-a-bluetooth-device). Make sure you pick the right values for what you are attempting to scan for since things changed. Android 12 can filter out beacons but the flutter_blue call will need to match (https://developer.android.com/guide/topics/connectivity/bluetooth/permissions).

I would like to also point out that running in the foreground or wake locking is intentionally not possible on iOS because it is bad for the users' battery. There is no iOS equivalent, but iOS does provide background-safe interfaces for doing anything that is actually important like uploading, notifying, or scheduling future processing.

I have also noticed that some of the trickiness in getting things working well lies in the registering of stream subscriptions considering the pattern of scan and connection events differs from when the application is foregrounded.

MrCsabaToth commented 2 years ago

@MrCsabaToth If you look at the way the that this flutter_blue fork and the flutter_reactive_ble repo initialize their central managers, you can see that neither of them initialize the central manager with the correct arguments for background peripheral interaction. The willRestore hook and central manager instance with an identifier are required as linked in the apple documentation earlier in this thread. My fork includes those interface changes, which require init code in the AppDelegate didFinishLaunchingWithOptions. I do not have an issue with running in the background on BLE (aside from making sure that I don't use too much CPU or network). ...

Thanks @trueb2 for the insights. So to be clear with your fork I would not need flutter_background or flutter_foreground_task packages. But would I need to re-architect my app to use Dart isolates, or would I just use the exact flutter blue API what I have today and it'd magically survive a locked screen or having another app in the foreground for a brief moment?

trueb2 commented 2 years ago

@MrCsabaToth If you look at the way the that this flutter_blue fork and the flutter_reactive_ble repo initialize their central managers, you can see that neither of them initialize the central manager with the correct arguments for background peripheral interaction. The willRestore hook and central manager instance with an identifier are required as linked in the apple documentation earlier in this thread. My fork includes those interface changes, which require init code in the AppDelegate didFinishLaunchingWithOptions. I do not have an issue with running in the background on BLE (aside from making sure that I don't use too much CPU or network). ...

Thanks @trueb2 for the insights. So to be clear with your fork I would not need flutter_background or flutter_foreground_task packages. But would I need to re-architect my app to use Dart isolates, or would I just use the exact flutter blue API what I have today and it'd magically survive a locked screen or having another app in the foreground for a brief moment?

For the most part, there is some usage change, and I don't have an extra isolate other than for flutter_uploader on Android. The AppDelegate needs to properly reinitialize, the read/write calls realistically have to allow failure, and the connected/scan callbacks need to deduplicate peripherals (connection state, advertisement/scan response). Also don't forget to update the plist permissions for background usage as a bluetooth central.

trueb2#6 has the important registration and initialization for the AppDelegate as well as the tryRead and tryWrite interface. I noticed some problems with hitting exceptions while debugging with hot restarts that I don't see in normal release build use.

I still have a diff that I haven't decided to merge into my branch yet that is related to trueb2#6. Since the name and the services can be out of sync after scan vs restoration, there are a couple more changes that I will probably pull into trueb2:master soon. https://github.com/trueb2/flutter_blue/pull/8

FYI, here is my Info.plist background modes and pub.yaml dependencies that keeps my iOS streaming data as much as possible (batching in the background at iOS's discretion)

<key>UIBackgroundModes</key>
<array>
    <string>bluetooth-central</string>
    <string>fetch</string>
    <string>processing</string>
    <string>remote-notification</string>
</array>
 dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.2
  connectivity_plus: ^2.1.0
  font_awesome_flutter: ^9.2.0
  webview_flutter: ^3.0.0
  path_provider: ^2.0.7
  uuid: ^3.0.5
  logging: ^1.0.2
  provider: ^6.0.1
  shared_preferences: ^2.0.9
  background_fetch: ^1.0.3
  flutter_keychain: ^2.1.0
  http: ^0.13.4
  firebase_messaging: ^11.2.4
  flutter_local_notifications: ^9.2.0
  tuple: ^2.0.0
  auto_size_text: ^3.0.0

  flutter_uploader:
    path: ../flutter_uploader

  flutter_blue:
    path: ../flutter_blue

A gotcha if you try to use mainline flutter_uploader to upload your continuous BLE data, I have a forked version of flutter_uploader because the WorkManager and CachingStreamHandler blow up memory on iOS and Android if we regularly schedule BLE packets to get uploaded. However, Android uses this to keep track of completed tasks, so this looks like a feature rather than a bug. I don't expect for my fork to merge into flutter_uploader, so I am continuing testing on my fork. (https://github.com/fluttercommunity/flutter_uploader/issues/80) (https://github.com/trueb2/flutter_uploader/pull/1)

I should say that this work is dominantly being tested for iOS.

rohitsangwan01 commented 2 years ago

@trueb2 i see you have good knowledge of native IOS , in my case i wanted my App to keep runnning in Background , and it will be updating UI too in background , few Timers are there and ofcourse bluetooth processing as Well , its an offline App so no outer fetching from network or uploading , but the issue is , after around 30sec , app Stopped working (bluetooth device is Still connected) , but App UI Updates stops (like a Audio which keep telling that Starting pocess or Timers and all) everything seems like pasued , and when i open app it resumes , already added UIBackgroundModes , any idea ??