firebase / firebase-unity-sdk

The Firebase SDK for Unity
http://firebase.google.com
Apache License 2.0
216 stars 35 forks source link

Dynamic Links crashes with il2cpp on Android due to Proguard stripping #311

Closed rahuloftheramaneffect closed 11 months ago

rahuloftheramaneffect commented 5 years ago

Platforms and SDKs

Unity editor version: 2019.1.11 f1 Firebase Unity SDK version: 6.2.0 Firebase plugins in use (Auth, Database, etc.): Auth, DynamicLinks Additional SDKs you are using (Facebook, AdMob, etc.): GoogleSignIn (Github link) Facebook, AdMob Platform you are using the Unity editor on (Mac, Windows, or Linux): Windows Platform you are targeting (iOS, Android, and/or desktop): Android


Context: I'm using Firebase Dynamic Links Unity SDK to create dynamic referral links in my game. The script I wrote generates a long dynamic link from a DynamicLinkComponents instance.


Code:


DynamicLinkComponents dynamicLink = new DynamicLinkComponents(
                new Uri("https://flareupdigital.com/?invitedby=" + referralID),
                "https://flareupdigital.page.link"
);

Debug.Log(dynamicLink.LongDynamicLink); // Works fine with "Mono", Throws exception with "il2cpp"

Issue: When compiled with "il2cpp" scripting backend, dynamic link generation fails in device with the following error:

ApplicationException: internal::IsInitialized() at Firebase.DynamicLinks.FirebaseDynamicLinks.GetLongLinkInternal(Firebase.DynamicLinks.DynamicLinkComponentsInternal components)

(Full stack here - log.txt)


Important Notes: The script WORKS FINE in our devices (Google Pixel 2 XL, OnePlus 7, etc.) if compiled with the "Mono" scripting backend in Unity.


Additional References: This issue has been discussed before, but there was no solution posted or the solution does not work:

1) Unable to generate Firebase dynamic link

2) Firebase Dynamic Links Fail To Generate On Real Device

chkuang-g commented 5 years ago

Hi @rahuloftheramaneffect

I attempted to repro with

Could you try to reproduce it with our quickstart? If it crashes with the quickstart, could you send us the project and the log? It would be great to see more log other than the call stack!

Thank you! Shawn

rahuloftheramaneffect commented 5 years ago

Hi @chkuang-g Thank you for replying. I tried, but have not been able to reproduce this issue with the quickstart project.

On my own project, however, the error happens consistently. I tried deleting all external dependencies and reimporting them one by one, to make sure that conflicting jars might be handled properly, but it had no effect.

I also updated all my dependencies to their latest versions:

And also updated my Unity editor version to 2019.1.12

But this did not work either. I'm not entirely sure where the issue lies.

I've attached a more complete log from my app session below. Hopefully that might be of some help in diagnosing the issue. log.txt

rahuloftheramaneffect commented 5 years ago

Any ideas, guys? I need dynamic links for a couple of features I'm implementing in my current project, and this is being an obvious roadblock.

I've also tried to submit a bug report on the firebase website, but it keeps saying "Error submitting bug report. Please try again later". It's been this way for the last 3 weeks, so here's the only place I can actually reach out.

patm1987 commented 5 years ago

Hi @rahuloftheramaneffect,

Sorry for the delay, we've been doing a bit of digging on this one.

Right now we think you're hitting this line, which shouldn't happen unless FirebaseApp gets garbage collected. Unless you're explicitly calling Dispose on FirebaseApp, this is a bug we should try to track down.

What would be super helpful is if you could try to help us find the case in which FirebaseApp gets preemptively garbage collected. The first obvious question is, "is there a scene transition involved between FirebaseApp.CheckAndFixDepenedenciesAsync and when you use Firebase Dynamic Links?" If yes, does the crash go away for you if you do everything in one scene? I totally understand that this is a difficult task for you to undertake, so I totally understand if you can't fulfill the second part of this request.

Now for my second test and potential workaround. In whatever script you call FirebaseApp.CheckAndFixDependenciesAsync can you either create a static reference to FirebaseApp.DefaultInstance (especially if this script lives on) or spawn a MonoBehaviour marked with DontDestroyOnLoad that will hold this reference?

This is an example script I wrote for Firebase Authentication for testing a similar issue:

using System;
using Firebase;
using Firebase.Auth;
using UnityEngine;
using UnityEngine.Events;

/// <summary>
/// This should be added to a loading scene. It will initialize Firebase then indicate with an event when
/// initialization is complete, storing itself into a persisting singleton instance. I've added convenience methods to
/// support Editor specific use cases (lazily initializing instances in editor as needed), but these are designed to
/// fail on device.
/// </summary>
public class FirebaseManager : MonoBehaviour
{
    private static FirebaseManager _instance;

    public static FirebaseManager Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = LazyInitFirebaseManager();
            }

            return _instance;
        }
    }

    private FirebaseAuth _auth;

    public FirebaseAuth Auth
    {
        get
        {
            if (_auth == null)
            {
                _auth = FirebaseAuth.GetAuth(App);
            }

            return _auth;
        }
    }

    private FirebaseApp _app;

    public FirebaseApp App
    {
        get
        {
            if (_app == null)
            {
                _app = GetAppSynchronous();
            }

            return _app;
        }
    }

    public UnityEvent OnFirebaseInitialized = new UnityEvent();

    private async void Awake()
    {
        if (_instance == null)
        {
            DontDestroyOnLoad(gameObject);
            _instance = this;
            var dependencyResult = await FirebaseApp.CheckAndFixDependenciesAsync();
            if (dependencyResult == DependencyStatus.Available)
            {
                _app = FirebaseApp.DefaultInstance;
                OnFirebaseInitialized.Invoke();
            }
            else
            {
                Debug.LogError($"Failed to initialize Firebase with {dependencyResult}");
            }
        }
        else
        {
            Debug.LogError($"An instance of {nameof(FirebaseManager)} already exists!");
        }
    }

    private void OnDestroy()
    {
        _auth = null;
        _app = null;
        if (_instance == this)
        {
            _instance = null;
        }
    }

    private static FirebaseManager LazyInitFirebaseManager()
    {
        Debug.LogWarning("Firebase Manager is being initialized Lazily. This is highly discouraged.");

        if (Application.isPlaying)
        {
            var go = new GameObject($"{nameof(FirebaseManager)}");
            go.hideFlags = HideFlags.HideAndDontSave;
            return go.AddComponent<FirebaseManager>();
        }

        Debug.LogError($"{nameof(FirebaseManager)} requested before initialization");
        return null;
    }

    private FirebaseApp GetAppSynchronous()
    {
        Debug.LogWarning("You are getting the FirebaseApp synchronously. You cannot resolve dependencies this way");
        if (FirebaseApp.CheckDependencies() != DependencyStatus.Available)
        {
            throw new Exception($"Firebase not available with {FirebaseApp.CheckDependencies()}");
        }

        return FirebaseApp.DefaultInstance;
    }
}

Thanks, --Patrick

rahuloftheramaneffect commented 5 years ago

Hey Patrick!

Thanks for writing back. Pretty good timing, too, since I was just working on the same issue an hour or so ago.

I started by upgrading the SDK to 6.3.0, hoping that may have solved it, but there was no change, there. However it DID force me into doing something similar to what you suggested, but let me first cover the rest of the text:

Right now we think you're hitting this line, which shouldn't happen unless FirebaseApp gets garbage collected. Unless you're explicitly calling Dispose on FirebaseApp, this is a bug we should try to track down.

I see. Well, I've combed through the code to make sure I'm never calling Dispose on any of the Firebase classes myself. So it looks like this one might be a bug, after all.

What would be super helpful is if you could try to help us find the case in which FirebaseApp gets preemptively garbage collected. The first obvious question is, "is there a scene transition involved between FirebaseApp.CheckAndFixDepenedenciesAsync and when you use Firebase Dynamic Links?" If yes, does the crash go away for you if you do everything in one scene?

No, my game is built in a single-scene, so there are no transitions that could potentially have destroyed my firebase reference.

Now for my second test and potential workaround. In whatever script you call FirebaseApp.CheckAndFixDependenciesAsync can you either create a static reference to FirebaseApp.DefaultInstance (especially if this script lives on) or spawn a MonoBehaviour marked with DontDestroyOnLoad that will hold this reference?

Well, at the time that I initially posted this issue, I had not executed CheckAndFixDependenciesAsync, and had instead simply referenced the "DefaultInstance" directly. Since features like Auth was working fine, I hadn't given it much thought.

However, I did eventually think that this might have been the issue, and a couple of days ago I wrote a manager class that is very similar to the script that you've posted. The manager class Initializes Firebase (CheckAndFixDependenciesAsync) and then raises an event if initialization is successful. All other firebase-dependant classes (auth, messaging and dynamic link controllers) checks with the manager if Firebase is initialized and if yes, only then references things like FirebaseAuth.DefaultInstance.

Unfortunately, the result was the same. Auth works fine, but creating a dynamic link throws the same exception.

Is it possible that FirebaseAuth.DefaultInstance spins off a different FirebaseApp instance than the default one?


Edit 1: Just tried replacing FirebaseAuth.DefaultInstance with FirebaseAuth.GetAuth(cachedInstance). That didn't work either.


Edit 2: This may not be of any consequence, but if it helps, I'm also using FirebaseMessaging in my app. When the firebase app is initialized, I register listeners as follows:

FirebaseMessaging.TokenReceived += OnTokenReceived; FirebaseMessaging.MessageReceived += OnMessageReceived;

There is no chance that this interferes with anything else, right?


Edit 3: Interesting note - FirebaseAuth continues to work fine even though dynamic links does not. i.e., User signs in to app with firebase (works fine) -> User presses button to generate dyn. link (throws exception) -> User signs out of firebase (works fine) -> User signs in again (works fine).

So it seems to me that the issue is specific to dynamic links and not something to do with FirebaseApp in general.

rahuloftheramaneffect commented 5 years ago

Alright. I did a couple more tests, and I noticed that there seems to be some kind of animosity between Dynamic Links and the active firebase instance.

What I did:

At this point, nothing gets logged. It doesn't look like any error is thrown, either, so dynamic links still does not work.

BUT attempting to run an auth operation AFTER that throws an error: "FirebaseAuth is not initialized". So it looks like DynamicLinks does something to the FirebaseApp instance.

Note: Attempting to create a dynamic link after the auth fails as described above goes back to throwing the original error, so it does not look like it's a "only-one-thing-works-at-a-time" problem.

Important This error does NOT occur in the editor. Both Auth and Dynamic Links seems to be working fine in the UnityEditor.

patm1987 commented 5 years ago

That is a lot of super helpful debugging, I feel really bad that I didn't catch this yesterday!

Are you still unable to reproduce this in a quickstart? If not, can you try to get us a minimally reproducible sample? Preferably one MonoBehaviour if possible. It sounds like you might be nearly there with your tests bouncing between FDL and Auth.

Thanks for your help, --Patrick

rahuloftheramaneffect commented 5 years ago

Hey Patrick!

Thanks for writing back. I've been trying to create a reproducible project, but it's proving to be super difficult: It seems to me like the bug was born from the friction of a lot of moving parts in my current project. I haven't yet been able to identify sure which particular combination of SDKs is causing this issue. Unfortunately, I don't have a lot of time right now to dedicate to finding out, either.

In my attempt to create an MCVE, I've added the firebase auth, dynamic links and cloud messaging SDKs to the quickstart, but they seem to be working just fine over there. These are all the Firebase SDKs that I'm using in my main project, too, so I don't know why it occurs only over here.

With the limited time that I have to work on this, I am unable to create an MCVE right now.

I can only think that the bug must be caused by a non-firebase related plugin that I'm using.

It doesn't help that the issue occurs only on the device, and not in the editor, either. It may take me a long time to test the various permutations of these plugins, so I'll just list what else I'm using for your reference:

I can't imagine any of these plugins could cause the issue that I'm facing, but after days of digging through the code to find the instigator, I'm honestly at that point where I'd willingly believe almost anything that attempts to explain this.

stewartmiles commented 5 years ago

Hey @rahuloftheramaneffect since we haven't heard from you and you mentioned that the issue appears to be isolated to your project (we integration test with MechaHamster and it works with all Firebase plugins) we're closing this issue. If you manage to come up with a MCVE please share and we'll reopen to debug.

sillygalahgames commented 5 years ago

I've experienced the same error message when upgrading my Firebase Unity SDK from 6.4.0 to 6.5.0. I haven't yet had the chance to try and replicate this with the sample projects, but in the event any of this is useful:

Using Unity 2019.2.5f1, ProGuard to minify. Single scene game.

Firebase Services Used:

General flow of Firebase initialisation and usage at runtime is:

  1. Firebase.FirebaseApp.CheckAndFixDependenciesAsync()
  2. In the case of Firebase.DependencyStatus.Available, reference held for Firebase.FirebaseApp in private member of class which is never destroyed during lifetime of game. Bool value set on same class to indicate dependency check finished.
  3. When class's Update sees dependency check bool as true, then sets Firebase.Auth.FirebaseAuth private member and calls SignInAnonymouslyAsync().
  4. Once sign in finished and similar private member bool value for auth marked as true, loading finished and game starts (which might then do Cloud Function calls, will initialise Remote Config and activate that, initialises AdMob advertising, authenticates GPG services, registers callbacks on FirebaseMessaging).
  5. Dynamic Links used much later on during runtime following player actions.

3rd Parties Used:

  1. AdMob Unity SDK v3.18.3
  2. AdMob Facebook mediation adapter v2.5.0
  3. Google Play Games Services Unity SDK v0.10.03
  4. Native Share Unity Asset v1.1
  5. Mobile Notifications package v1.0.3

Other Info: For privacy reasons, my manifest contains these flags:

<meta-data android:name="firebase_analytics_collection_enabled" android:value="false" />
<meta-data android:name="google_analytics_default_allow_ad_personalization_signals" android:value="false" />
<meta-data android:name="firebase_messaging_auto_init_enabled" android:value="false" />

Only once Firebase is initialised will I then enable collection and ad personalisation (based on user's choices).

3rd Party References:

  1. https://github.com/googleads/googleads-mobile-unity/releases
  2. https://bintray.com/google/mobile-ads-adapters-unity/GoogleMobileAdsFacebookMediation#files/GoogleMobileAdsFacebookMediation%2F2.5.0
  3. https://github.com/playgameservices/play-games-plugin-for-unity/tree/android-java-client/current-build
  4. https://github.com/yasirkula/UnityNativeShare
  5. https://docs.unity3d.com/Packages/com.unity.mobile.notifications@1.0/manual/index.html
rahuloftheramaneffect commented 4 years ago

I struggled a lot with this one, ended up giving up altogether and moving everything to a new Unity Project, and suddenly everything seems to be working fine. I have no idea what caused the problems with Dyn Links, but it seems to be some kind of Unity related issue.

yakirbu commented 4 years ago

Why is this closed? it is still relevant.

chkuang-g commented 4 years ago

@sillygalahgames Thanks for the info. I was trying to reproduce the issue using the quickstart project with your configuration but still unable to see the message.

A couple of question about your situation:

Truly sorry for your experience. But I am curious why the problem is resolved for @rahuloftheramaneffect by switching to a new project. Given the symptom what you and @rahuloftheramaneffect described, it sounds like some bug in Unity. But we need a bit more information, a reproduction case for best, to determine this.

@yakirbu Are you experiencing the same issue? Could you offer more detail about your situation if you do?

Shawn

yakirbu commented 4 years ago

@chkuang-g I wanted to upgrade my project to the latest Firebase Unity SDK 6.6.0, and the following error started showing when creating a short dynamic link: "ApplicationException: internal::IsInitialized() at Firebase.DynamicLinks.FirebaseDynamicLinks..."

The error is showing both in Mono & IL2CPP

It fails 100% of the times, it doesn't crash though, only shows an error in the device logs, and doesn't create the link of-course. The rest of the firebase services, such as Auth, Database and Remote config, still work.

When downgrading Firebase Unity SDK to 6.4.0, dynamic links works as intended. Also tried 6.5.0, same issue appears.

chkuang-g commented 4 years ago

@yakirbu Yours and @sillygalahgames 's issue sounds like a regression, if @sillygalahgames can also confirm that the issue happened for both Mono and IL2CPP.

If would be helpful if you can share the entire stack trace and how you called the Firebase SDK when this happened. A full log would be great! I'm also curious if you were using custom domain.

I'll ping the team and see what we can find.

Thanks for reporting. Shawn

bilck commented 4 years ago

We are facing this exact same issue in our project and almost giving up about using Firebase Dynamic Links.

Our implementation is VERY similar to Firebase Unity SDK Dynamic Links tutorial, and I have personally reviewed every part of our Dynamic Links implementation (which used to work).

As soon as we ty to generate a long dynamic link we get the error ApplicationException: Internal::IsInitialized() at Firebase.DynamicLinks.FirebaseDynamicLinks.GetLongLinkInternal(Firebase.DynamicLinks.DynamicLinkComponentsInternal components)

We also use other plugins, such as: OneSignal, AppsFlyer, Admob and Voxelbusters' Native Share plugin.

We also use il2cpp, Proguard, Gradle builds, and C++ Release builds.

Besides that, we have some Crashlytics reports, an "Internal App Sharing" developer build and we can also share our project with one Firebase support staff, if needed.

rlehmannJC commented 4 years ago

We have the exact same problems like the others.

We are using Unity 2018.4.13f1 with Firebase SDK 6.9.0. Analytics, Crashlytics, Messaging and Remote Settings are all working perfectly. But Dynamic Links is throwing the same error like for: ApplicationException: Internal::IsInitialized()

This exception is thrown for both the GetLongLink or also GetShortenLinkAsync. We do it directly after the successful dependency check, where we also call all the other Firebase stuff. All other firebase services are also logging their successful initialisation (done inside the Firebase SDK), but Dynamic Links is not working.

Dynamic Links are working perfectly inside the editor, but on Android with il2cpp they are not working.

Thanks for help!

Branchie commented 4 years ago

We also had this same problem and have been following this thread for solutions. This is on Android only, Editor and iOS seem to be working fine.

> Failed to create dynamic link System.ApplicationException: internal::IsInitialized()
 at Firebase.DynamicLinks.FirebaseDynamicLinks.GetShortLinkInternalAsync (Firebase.DynamicLinks.DynamicLinkComponentsInternal components, Firebase.DynamicLinks.DynamicLinkOptionsInternal options) [0x00022] in Z:\tmp\tmp.rziJtR75qu\firebase\dynamic_links\client\unity\proxy\FirebaseDynamicLinks.cs:29
> at Firebase.DynamicLinks.DynamicLinks.GetShortLinkAsync (Firebase.DynamicLinks.DynamicLinkComponents components, Firebase.DynamicLinks.DynamicLinkOptions options) [0x00000] in Z:\tmp\tmp.rziJtR75qu\firebase\dynamic_links\client\unity\proxy\DynamicLinks.cs:80

This exception would be thrown when getting either long links or short links, it did not matter which.

We have tried keeping a static reference to the FirebaseApp.DefaultInstance, to prevent garbage collection, to no avail.

Our Firebase plugins are all up-to-date.

We are unable to create a minimally reproducible project, creating a fresh project, importing and setting up the plugin and calling the generate link functions works with no issue, both long and short.

Adding all of the plugins listed below to that minimal project does not reproduce the error.

We spent a lot of time trying to make this work, ripping out all of our plugins in our main project leaving only Firebase in the project. Same issue occurs. We also tried deleting Firebase only to reimport but no progress was made.

We aren’t doing anything special around this function call. When a user clicks a button we generate the link if it's not already and open it when finished.

We have tried doing this in a blank scene, no other scenes added to the build, no change in result.

We are using Unity 2018.4.6, targeting .NET 4.x and building with IL2CPP using a custom Gradle Template and Proguard File.

Plugins in the project:

We have since put this problem on hold.

rlehmannJC commented 4 years ago

Hey guys,

we found a way to resolve the issue! The problem was that proguard stripped out some dynamic link libraries (and also some google internal stuff).

ALL of the following proguard lines are REQUIRED to get it working: -keep,includedescriptorclasses public class com.google.firebase.dynamiclinks { \; }

-keep class com.google.firebase { *; } -keep class com.google.firebase.appinvite { *; } -keep class com.google.firebase.dynamiclinks { *; } -keep class com.google.firebase.dynamiclinks.FirebaseDynamicLinks { *; } -keep class com.google.android.gms.internal { *; } -keep class com.google.android.gms { *; }

npicouet commented 4 years ago

This is still happening on Unity 2019.3.13f1 with Firebase SDK 6.15

kimo-do commented 3 years ago

Hey guys,

we found a way to resolve the issue! The problem was that proguard stripped out some dynamic link libraries (and also some google internal stuff).

ALL of the following proguard lines are REQUIRED to get it working: -keep,includedescriptorclasses public class com.google.firebase.dynamiclinks { ; }

-keep class com.google.firebase { *; } -keep class com.google.firebase.appinvite* { ; } -keep class com.google.firebase.dynamiclinks { *; } -keep class com.google.firebase.dynamiclinks.FirebaseDynamicLinks { *; } -keep class com.google.android.gms.internal* { ; } -keep class com.google.android.gms { *; }

Thank you! Any insight on how you found out what you had to add to the proguard lines?

chkuang-g commented 2 years ago

Is this still happening to you guys with the latest SDK, ex. 9.2.0? Please comment and upvote if this is still relevant to you.

I think we might be able to do something for this in the editor tool. That is, adding proguard lines automatically.

I will change this to a FR since we never support this functionality before.

paulinon commented 11 months ago

Hey folks,

Please note that the Firebase Dynamic Links service will be shutdown on August 25, 2025. For now, only critical or security issues will be fixed in the SDK.

You may refer to https://firebase.google.com/support/dynamic-links-faq for more information.