invertase / react-native-firebase

🔥 A well-tested feature-rich modular Firebase implementation for React Native. Supports both iOS & Android platforms for all Firebase services.
https://rnfirebase.io
Other
11.53k stars 2.19k forks source link

[🐛] 🔥 Not allowed to start service Intent ReactNativeFirebaseMessagingHeadlessService: app is in background uid #4604

Closed Olgagr closed 3 years ago

Olgagr commented 3 years ago

Issue

I implemented in my app onNotificationOpenedApp and getInitialNotification. However, both of these methods don't work as described in the documentation on Android. When the app is in the background, the notification comes, I tap on it, the app opens but the callback registered on onNotificationOpenedApp is not called. The same is true for the app that is in quit state and getInitialNotification callback.

After some debugging, I see that when the notification comes, onReceive method from ReactNativeFirebaseMessagingReceiver class is called and the error is thrown when the service should be started:

 ComponentName name = context.startService(backgroundIntent);

I see the description of this error: "Background messages only work if the message priority is set to 'high'" To send notification, we use firebase console which I believe doesn't have an ability to set priority for a notification.

My questions are:

I believe the setting priority is not required as this is not documented anywhere. Is there any other reason why this error can be thrown? Maybe I have some permissions not set up correctly. 🤔


Project Files

Javascript

Click To Expand

#### `package.json`: ```json { "name": "xxx", "version": "5.2.2", "dependencies": { "@react-native-community/async-storage": "^1.6.2", "@react-native-community/netinfo": "^4.1.1", "@react-native-community/push-notification-ios": "1.2.0", "@react-native-community/slider": "2.0.7", "@react-native-firebase/analytics": "7.6.8", "@react-native-firebase/app": "8.4.6", "@react-native-firebase/crashlytics": "8.4.11", "@react-native-firebase/messaging": "10.0.0", "axios": "0.18.1", "axios-auth-refresh": "^1.0.7", "axios-retry": "^3.1.2", "babel-plugin-idx": "^2.4.0", "date-fns": "^2.7.0", "date-fns-tz": "^1.0.10", "deep-object-diff": "^1.1.0", "filesize": "^5.0.3", "humps": "^2.0.1", "i18n-js": "^3.3.0", "idx": "^2.5.6", "iso8601-duration": "^1.2.0", "lodash": "^4.17.11", "patch-package": "^6.1.2", "postinstall-postinstall": "^2.0.0", "react": "16.8.6", "react-native": "0.60.0", "react-native-background-fetch": "^2.7.1", "react-native-config": "^0.11.7", "react-native-device-info": "^5.3.1", "react-native-exception-handler": "^2.10.8", "react-native-fs": "2.15.2", "react-native-gesture-handler": "^1.5.0", "react-native-get-random-values": "^1.4.0", "react-native-image-zoom-viewer": "^2.2.26", "react-native-inappbrowser-reborn": "^3.0.0", "react-native-inset-shadow": "^1.0.2", "react-native-keyboard-aware-scroll-view": "^0.9.1", "react-native-localize": "^1.1.4", "react-native-mail": "^4.1.0", "react-native-material-textfield": "^0.16.1", "react-native-orientation-locker": "^1.1.6", "react-native-progress": "^3.6.0", "react-native-push-notification": "^3.1.9", "react-native-rate": "^1.1.10", "react-native-safe-area-context": "^3.1.8", "react-native-safe-area-view": "^2.0.0", "react-native-svg": "^9.8.4", "react-native-swiper": "^1.5.14", "react-native-webview": "^10.10.2", "react-native-zip-archive": "^5.0.1", "react-navigation": "^4.0.10", "react-navigation-stack": "^1.7.3", "react-redux": "^7.1.0", "recyclerlistview": "^2.0.12", "redux": "^4.0.2", "redux-act": "^1.7.7", "redux-devtools-extension": "^2.13.8", "redux-persist": "^5.10.0", "redux-persist-filesystem-storage": "^2.1.0", "redux-saga": "^1.0.5", "reselect": "^4.0.0", "shallow-equal": "^1.2.0", "styled-components": "^4.3.2", "uuid": "^8.1.0", "yarn": "^1.19.2" } } ``` #### `firebase.json` for react-native-firebase v6: ```json { "crashlytics_auto_collection_enabled": true, "crashlytics_debug_enabled": true, "crashlytics_ndk_enabled": false, "react-native": { "messaging_android_headless_task_timeout": 180000 } } ```

iOS

Click To Expand

#### `ios/Podfile`: - [ ] I'm not using Pods - [x] I'm using Pods and my Podfile looks like: ```ruby # N/A ``` #### `AppDelegate.m`: ```objc // N/A ```


Android

Click To Expand

#### Have you converted to AndroidX? - [ ] my application is an AndroidX application? - [ ] I am using `android/gradle.settings` `jetifier=true` for Android compatibility? - [ x] I am using the NPM package `jetifier` for react-native compatibility? #### `android/build.gradle`: ```groovy buildscript { ext { buildToolsVersion = "28.0.3" minSdkVersion = 21 compileSdkVersion = 28 targetSdkVersion = 28 supportLibVersion = "28.0.0" } repositories { google() jcenter() maven { url 'https://maven.fabric.io/public' } } dependencies { classpath("com.android.tools.build:gradle:3.4.1") classpath 'com.google.gms:google-services:4.2.0' classpath 'io.fabric.tools:gradle:1.28.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } allprojects { repositories { mavenLocal() maven { // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm url("$rootDir/../node_modules/react-native/android") } maven { // Android JSC is installed from npm url("$rootDir/../node_modules/jsc-android/dist") } google() jcenter() } } ``` #### `android/app/build.gradle`: ```groovy buildscript { repositories { } dependencies { } } apply plugin: "com.android.application" apply plugin: 'com.google.gms.google-services' apply plugin: 'io.fabric' repositories { } project.ext.envConfigFiles = [ xxxdebug: "clients/xxx/xxx/client.env", xxxrelease: "clients/xxx/xxx/client.env", ] apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle" import com.android.build.OutputFile project.ext.react = [ entryFile: "index.js" ] apply from: "../../node_modules/react-native/react.gradle" def enableSeparateBuildPerCPUArchitecture = false def useIntlJsc = true android { compileSdkVersion rootProject.ext.compileSdkVersion flavorDimensions "default" compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } defaultConfig { applicationId "com.xxx" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion resValue "string", "build_config_package", "com.twipeappv4" multiDexEnabled true ndk { abiFilters "armeabi-v7a", "armeabi", "x86", "arm64-v8a", "x86_64" } } splits { abi { reset() enable enableSeparateBuildPerCPUArchitecture universalApk false // If true, also generate a universal APK include "armeabi-v7a", "armeabi", "x86", "arm64-v8a", "x86_64" } } signingConfigs { tmgttgRelease { storeFile file('src/tmgttg/tmgttg.jks') storePassword "xxxx" keyAlias "xxx" keyPassword "xxx" } } buildTypes { debug { debuggable true } release { minifyEnabled enableProguardInReleaseBuilds proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" } } productFlavors { xxx { minSdkVersion rootProject.ext.minSdkVersion applicationId 'uk.co.telegraph.kindlefire' targetSdkVersion rootProject.ext.targetSdkVersion resValue "string", "build_config_package", "com.twipeappv4" signingConfig signingConfigs.xxxRelease } } // applicationVariants are e.g. debug, release applicationVariants.all { variant -> variant.outputs.each { output -> // For each separate APK per architecture, set a unique version code as described here: // https://developer.android.com/studio/build/configure-apk-splits.html def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4] def abi = output.getFilter(OutputFile.ABI) if (abi != null) { // null for the universal-debug, universal-release variants output.versionCodeOverride = versionCodes.get(abi) * 1048576 + defaultConfig.versionCode } } } dexOptions { javaMaxHeapSize "4g" } } dependencies { implementation project(':react-native-push-notification') implementation project(':react-native-background-fetch') implementation project(':react-native-orientation-locker') implementation project(':react-native-gesture-handler') implementation project(':react-native-config') implementation fileTree(dir: "libs", include: ["*.jar"]) implementation 'com.android.support:multidex:1.0.3' implementation "com.facebook.react:react-native:+" // From node_modules implementation platform('com.google.firebase:firebase-bom:26.1.0') implementation 'com.google.firebase:firebase-messaging' // JSC from node_modules if (useIntlJsc) { implementation 'org.webkit:android-jsc-intl:+' } else { implementation 'org.webkit:android-jsc:+' } } // Run this once to be able to run the application with BUCK // puts all compile dependencies into folder libs for BUCK to use task copyDownloadableDepsToLibs(type: Copy) { from configurations.compile into 'libs' } apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) ``` #### `android/settings.gradle`: ```groovy rootProject.name = 'xxx' include ':rn-fetch-blob' project(':rn-fetch-blob').projectDir = new File(rootProject.projectDir, '../node_modules/rn-fetch-blob/android') include ':react-native-push-notification' project(':react-native-push-notification').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-push-notification/android') include ':react-native-background-fetch' project(':react-native-background-fetch').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-background-fetch/android') include ':react-native-orientation-locker' project(':react-native-orientation-locker').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-orientation-locker/android') include ':react-native-gesture-handler' project(':react-native-gesture-handler').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-gesture-handler/android') include ':react-native-config' project(':react-native-config').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-config/android') apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) include ':app' ``` #### `MainApplication.java`: ```java package com.twipeappv4; import android.app.Application; import android.content.Context; import androidx.multidex.MultiDex; import com.facebook.react.PackageList; import com.facebook.react.ReactApplication; import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactPackage; import com.facebook.soloader.SoLoader; import java.util.List; public class MainApplication extends Application implements ReactApplication { private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { @Override public boolean getUseDeveloperSupport() { return BuildConfig.DEBUG; } @Override protected List getPackages() { @SuppressWarnings("UnnecessaryLocalVariable") List packages = new PackageList(this).getPackages(); // Packages that cannot be autolinked yet can be added manually here, for example: // packages.add(new MyReactNativePackage()); return packages; } @Override protected String getJSMainModuleName() { return "index"; } }; @Override public ReactNativeHost getReactNativeHost() { return mReactNativeHost; } @Override public void onCreate() { setTheme(R.style.AppTheme); super.onCreate(); SoLoader.init(this, /* native exopackage */ false); } @Override protected void attachBaseContext(Context context) { super.attachBaseContext(context); MultiDex.install(this); } } ``` #### `AndroidManifest.xml`: ```xml ```


Environment

Click To Expand

**`react-native info` output:** ``` System: OS: macOS 10.15.7 CPU: (16) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz Memory: 178.00 MB / 32.00 GB Shell: 5.7.1 - /bin/zsh Binaries: Node: 12.18.4 - ~/n/bin/node Yarn: 1.19.2 - ~/Documents/projects/twipe-app-v4/node_modules/.bin/yarn npm: 6.14.6 - ~/n/bin/npm Watchman: 4.9.0 - /usr/local/bin/watchman SDKs: iOS SDK: Platforms: iOS 14.2, DriverKit 20.0, macOS 11.0, tvOS 14.2, watchOS 7.1 Android SDK: API Levels: 28, 29, 30 Build Tools: 28.0.3, 29.0.2, 30.0.2 System Images: android-29 | Intel x86 Atom, android-29 | Intel x86 Atom_64, android-29 | Google APIs Intel x86 Atom, android-30 | Google APIs Intel x86 Atom, android-30 | Google Play Intel x86 Atom IDEs: Android Studio: 4.1 AI-201.8743.12.41.6858069 Xcode: 12.2/12B45b - /usr/bin/xcodebuild npmPackages: @react-native-community/cli: ^2.9.0 => 2.9.0 react: 16.8.6 => 16.8.6 react-native: 0.60.0 => 0.60.0 ``` - **Platform that you're experiencing the issue on**: - [ ] iOS - [ ] Android - [ ] **iOS** but have not tested behavior on Android - [ x] **Android** but have not tested behavior on iOS - [ ] Both - **`react-native-firebase` version you're using that has this issue:** - `@react-native-firebase/messaging@10.0.0` - **`Firebase` module(s) you're using that has the issue:** - `onNotificationOpenedApp` - **Are you using `TypeScript`?** - `Y` & `3.5.3`


mikehardy commented 3 years ago

I think it may be that no one uses the console to send notifications normally? Once all set up initially I think most people craft JSON and post it to the REST APIs via functions and some other sort of front-end.

I'm just looking to explain the apparent documentation gap

Can you explain where you saw the message

"Background messages only work if the message priority is set to 'high'"

...was that via watching adb logcat when the message came in? Exact steps would be interesting

I would differ from your conclusion in that believe setting to high priority may be required to insure deliver, just not documented - or it may be documented in the upstream/official firebase docs

Olgagr commented 3 years ago

The message can be found in the @react-native-firebase_messaging

// class ReactNativeFirebaseMessagingReceiver

  @Override
  public void onReceive(Context context, Intent intent) {
    Log.d(TAG, "broadcast received for message");
    if (ReactNativeFirebaseApp.getApplicationContext() == null) {
      ReactNativeFirebaseApp.setApplicationContext(context.getApplicationContext());
    }
    RemoteMessage remoteMessage = new RemoteMessage(intent.getExtras());
    ReactNativeFirebaseEventEmitter emitter = ReactNativeFirebaseEventEmitter.getSharedInstance();

    // Add a RemoteMessage if the message contains a notification payload
    if (remoteMessage.getNotification() != null) {
      notifications.put(remoteMessage.getMessageId(), remoteMessage);
      ReactNativeFirebaseMessagingStoreHelper.getInstance().getMessagingStore().storeFirebaseMessage(remoteMessage);
    }

    //  |-> ---------------------
    //      App in Foreground
    //   ------------------------
    if (SharedUtils.isAppInForeground(context)) {
      emitter.sendEvent(ReactNativeFirebaseMessagingSerializer.remoteMessageToEvent(remoteMessage, false));
      return;
    }

    //  |-> ---------------------
    //    App in Background/Quit
    //   ------------------------

    try {
      Intent backgroundIntent = new Intent(context, ReactNativeFirebaseMessagingHeadlessService.class);
      backgroundIntent.putExtra("message", remoteMessage);
      ComponentName name = context.startService(backgroundIntent);
      if (name != null) {
        HeadlessJsTaskService.acquireWakeLockNow(context);
      }
    } catch (IllegalStateException ex) {
      // By default, data only messages are "default" priority and cannot trigger Headless tasks
      Log.e(
        TAG,
        "Background messages only work if the message priority is set to 'high'",
        ex
      );
    }
  }

The method onReceive is called when the push notification is delivered to the device. For some reason, this line throws an error:

ComponentName name = context.startService(backgroundIntent);

I'm not sure what background messages are. I'm sending a regular push notification, not data-only notification. I read the official docs many times: https://rnfirebase.io/messaging/notifications and there is nothing about setting high prio for notifications to make interactions on notification work.

mikehardy commented 3 years ago

Unfortunately there is no such thing as a "regular push notification", all types of notification are "regular", none are "irregular" - to be precise you may say "cloud message with notification payload", "cloud message with data payload" or "cloud message with mixed payload".

And to be most precise and avoid naming confusion, it is easiest to post the cloud messages via the REST API to the FCM system, and include the actual JSON you sent, so that we may troubleshoot with precision.

rnfirebase.io are our official docs, but we are sometimes behind the actual official docs upstream: https://firebase.google.com/docs/cloud-messaging/android/receive

I believe these restrictions are related to this: https://firebase.google.com/docs/cloud-messaging/android/receive#restricted

which leads to https://firebase.google.com/docs/cloud-messaging/android/receive#restricted

which leads to https://developer.android.com/topic/performance/power/power-details

Basically, the Android platform has progressively tightened how and when things may happen in response to cloud messages, and without it being high priority it not okay for much to happen - even if it is high priority you may only send a few of these things to do

It's very touchy getting this stuff to work, that is 100% true

Olgagr commented 3 years ago

@mikehardy Thanks for a response. You're right, I should have been more precise. I send FCM messages with the optional payload (or not, makes no difference). My app is not restricted in terms of notification. I think the links you sent are suggesting that if the user puts the restriction on the app, the notification might be not delivered. In my case, the message is always delivered but the interaction with the message is not working (eg. function registered via onNotificationOpenedApp).

mikehardy commented 3 years ago

Okay, delivery is good - note that the system restricts these things automatically - but if you still have standby bucket slots available then you get messages delivered - good

Why is the interaction not working? Unsure, I believe you logged a separate issue that resolved to effectively the same thing. I can only recommend going into node_modules and directly inspecting all of the relevant code with tracing or printouts. I personally use a full-featured local notifications package to handle these things and I recommend to everyone they do the same - local notification interaction (that is: posting notifications, reacting to them) will always, always work better with a package that is doing that as it's intended purpose

Olgagr commented 3 years ago

No worries @mikehardy . I closed the previous issue. I'm not a huge fan of adding another dependency. We already use react-native-push-notification and @react-native-firebase/messaging . However, I wonder what full-featured local notifications package do you recommend?

Anyway, I think I found the issue. This interaction callback registered via onNotificationOpenedApp and getInitialNotification are not called for react-native version 0.60.0. For project with react-native version 0.63.3 callbacks are called.

mikehardy commented 3 years ago

react-native-push-notification should be able to handle these things, and may in fact (through swizzling / taking over the notification delegate) be the reason you don't see react-native-firebase notification handlers called

Very interesting it works with the react-native upgrade 🤔 - I suppose that's at least a positive, better to be upgraded there as if I recall somewhere between 0.60 and 0.63.x there were quite a few changes required for android 10/11 and iOS14

pavm035 commented 3 years ago

I'm on 0.63.3 but still onNotificationOpenedApp not called but getInitialNotification seems to be ok

mezalejandro commented 3 years ago

Hello, this error is happening to me, I am using only rnfirebase and when I get a push it does not open the app. I am using version 0.64.0

mikehardy commented 3 years ago

@mezalejandro https://stackoverflow.com/help/minimal-reproducible-example - perhaps based on https://github.com/mikehardy/rnfbdemo/blob/master/make-demo.sh

mezalejandro commented 3 years ago

I try with @react-native-firebase/messaging version 10.3.1 and now with i test on android console i have the error: CloudMessagingReceiver: Notification pending intent canceled

mikehardy commented 3 years ago

@mezalejandro why would you attempt to troubleshoot on outdated software (possibly with fixes for whatever issue you have)? Don't do that! It has great potential to waste your time, and typically we don't spend any time looking at issues on non-current software

10.3.1

we are on 12.1.0 right now

mezalejandro commented 3 years ago

You are right, I left it in the latest version. To test delete react-native-bootsplash, thinking that there would be a problem with the manifest. I have this error: W/System.err: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2020) W/System.err: at android.os.Handler.dispatchMessage(Handler.java:112)

mikehardy commented 3 years ago

@mezalejandro - that was more of a warning to hopefully save you time, it was not really directed help. I have no idea what is going on there, sorry. There is not enough information here to reason about, and it's hijacking an old issue. I recommend producing an App.js we can drop in to the result of https://github.com/mikehardy/rnfbdemo/blob/master/make-demo.sh so we have a reproducible example https://stackoverflow.com/help/minimal-reproducible-example based on current versions, and if you can still reproduce it in a clean setting, then make a new issue and post the full error message from logcat so we may investigate

yasircodingcrafts commented 1 year ago

Hi I am getting this issue in "Pre-launch" report from Play Store:

Exception java.lang.RuntimeException: Unable to start receiver jp.co.sharp.android.iwnnime.ml.DecoEmojiListener: java.lang.IllegalStateException: Not allowed to start service Intent { cmp=jp.co.omronsoft.android.decoemojimanager_docomo/.DecoEmojiManager }: app is in background uid null
  at android.app.ActivityThread.handleReceiver (ActivityThread.java:3521)
  at android.app.ActivityThread.access$1400 (ActivityThread.java:209)
  at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1753)
  at android.os.Handler.dispatchMessage (Handler.java:106)
  at android.os.Looper.loop (Looper.java:193)
  at android.app.ActivityThread.main (ActivityThread.java:6846)
  at java.lang.reflect.Method.invoke
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:883)
Caused by java.lang.IllegalStateException: Not allowed to start service Intent { cmp=jp.co.omronsoft.android.decoemojimanager_docomo/.DecoEmojiManager }: app is in background uid null
  at android.app.ContextImpl.startServiceCommon (ContextImpl.java:1577)
  at android.app.ContextImpl.startService (ContextImpl.java:1532)
  at android.content.ContextWrapper.startService (ContextWrapper.java:664)
  at android.content.ContextWrapper.startService (ContextWrapper.java:664)
  at jp.co.sharp.android.iwnnime.ml.DecoEmojiListener.updateConfirm (DecoEmojiListener.java:211)
  at jp.co.sharp.android.iwnnime.ml.DecoEmojiListener.onReceive (DecoEmojiListener.java:184)
  at android.app.ActivityThread.handleReceiver (ActivityThread.java:3508)

My package json versions are as follows:

"react": "17.0.1",
"react-native": "0.64.2",
"@react-native-firebase/app": "^11.2.0",
"@react-native-firebase/messaging": "^11.2.0",

And after searching around I found this issue on stackoverflow and according to one answer we need to start service base on the Android version which I found in the @react-native-firebase/app package on line 54 of ReactNativeFirebaseMessagingReceiver.java file which is calling only one method startService without checking Android version:

ComponentName name = context.startService(backgroundIntent);

Do someone has any idea that if I use the patch-package to replace this line with the following will it fix this issue?

ComponentName name = null;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
  name = context.startForegroundService(intent);
}else {
  name = context.startService(backgroundIntent);
}
if (name != null) {
  HeadlessJsTaskService.acquireWakeLockNow(context);
}

I am going to do it anyways to check btw but wanted to see if someone can be a heads up as my previous app build is still in review. Thanks

mikehardy commented 1 year ago

@yasirtrimulabs your versions are unsupportably old, and failure to work on modern mobile operating systems is the inevitable result.

"@react-native-firebase/app": "^11.2.0", "@react-native-firebase/messaging": "^11.2.0",

If you keep your versions up to date here, things will continue working as new android/ios versions are released. The problems related to new operating system versions are handled in the new versions here, it is one of the primary drivers for new releases.

yasircodingcrafts commented 1 year ago

@mikehardy thanks for the quick reply

I know I have to upgrade but I am just worried that the new version will have breaking changes, can you please tell me if there will be any breaking changes for this one, because it can take some time to resolve these changes and I had to deliver the build ASAP IYKWIM :slightly_smiling_face:

mikehardy commented 1 year ago

I've written an article on the same https://invertase.io/blog/react-native-firebase-versioning There are plenty of breaking changes, one set per major version, that article will explain things

yasircodingcrafts commented 1 year ago

Thanks