Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initialize FirebaseApp without google-services.json #66

Open
paularius opened this issue Oct 9, 2018 · 51 comments
Open

Initialize FirebaseApp without google-services.json #66

paularius opened this issue Oct 9, 2018 · 51 comments
Labels
tracking-internally type: bug Something isn't working

Comments

@paularius
Copy link

paularius commented Oct 9, 2018

  • Android Studio version: 3.2
  • Firebase Component: Core/Analytics(Database, Firestore, Storage, Functions, etc)
  • Component version: 16.0.4

[REQUIRED] Step 3: Describe the problem

I am trying to initialize the FirebaseApp, only by using the FirebaseOptions builder, like this, first thing in my application (I use only applicationId and ApiKey, since I only want the analytics for start) :

FirebaseOptions options = new FirebaseOptions.Builder()
                    .setApplicationId(applicationID) // Required for Analytics.
                    .setApiKey(apiKey) // Required for Auth.
                    .build();
FirebaseApp.initializeApp(context, options);

I do not have a google-services.json file in my project, I have removed the FirebaseInitProvider, like this, in my AndroidManifest.xml:

<provider
    android:name="com.google.firebase.provider.FirebaseInitProvider"
    android:authorities="${applicationId}.firebaseinitprovider"
    tools:node="remove"/>.

When starting the application, while trying to View events in the Android Studio debug log, like this:
adb shell setprop log.tag.FA VERBOSE
adb shell setprop log.tag.FA-SVC VERBOSE
adb logcat -v time -s FA FA-SVC

I get Missing google_app_id. Firebase Analytics disabled. See https://goo.gl/NAOOOI .

@samtstern
Copy link
Contributor

@paularius could you try adding a string entry to your project (in strings.xml)

<!-- Use the same value you used for applicationID above -->
<string name="google_app_id">YOUR_APPLICATION_ID</string>

For a more comprehensive guide on not using the json file, see this blog post:
https://medium.com/@samstern_58566/how-to-use-firebase-on-android-without-the-google-services-plugin-93ecc7dc6c4

@paularius
Copy link
Author

@samtstern Thanks for your reply.
I have already tried this, and seems to work. The thing is that I would like to initialize the FirebaseApp more dynamically, since I would like to have different projects within the same apk (so using different flavors for the xml, would not be useful).
I am trying a part of your approach and try to change the resource dynamically, using reflection, before initializeApp, but that doesn't seem to work either.
Moreover, what is the use of the FirebaseOptions, if I have to use a file in my project?

@samtstern
Copy link
Contributor

@paularius you're right that you shouldn't need the XML file at all. Where are you calling InitializeApp() and is it possible you're trying to use FirebaseAnalytics before that happens?

@paularius
Copy link
Author

@samtstern I call it first thing in onCreate, in my application class.
Also, before I try to use any firebaseAnalytics call, I check that the FirebaseApp (the "[DEFAULT]" one) exists.
But even if I tried to initialize it in some other place (for example when I get a response from my server), even by losing some events, should it be a problem?

@adityashri7
Copy link

I have had the same issue since months now. I have a running thread with the support team, and it doesn't look like this issue is going anywhere. In my scenario, I have the manual Firebase initialization in a library, and I have multiple apps that use that library. So based on the application thats using it, I initialize the Firebase app. Everything initializes well, but Firebase analytics, which complains about "Missing google_app_id".

@kjsingh815
Copy link

We're having the same issue, really hope Firebase team moves faster on this as it's impacting many developers and we're considering moving off of Firebase due to it.

@samtstern
Copy link
Contributor

Thanks everyone for chiming in! I filed an internal bug (b/117609738) with the analytics team since they're likely not watching this issue as that SDK is still closed source. Hopefully they can help me get to the bottom of this.

If you'll let me take one more blind stab: what happens if you create a bogus google_app_id string resource just to satisfy the SDK, but then initialize the default FirebaseApp yourself at runtime. Does Analytics send events to the right place?

@samtstern samtstern added type: bug Something isn't working tracking-internally labels Oct 11, 2018
@adityashri7
Copy link

@samtstern if I just add a google app id, Firebase initializes the app on its own, but ofc doesn't add the other parameters like database url etc. So the manual initialization also fails, because it sees that the app has already been initialized. I even tried to add all the items as generated by firebase to my resources, as mentioned in this post: https://medium.com/@samstern_58566/how-to-use-firebase-on-android-without-the-google-services-plugin-93ecc7dc6c4. It works, but then like @paularius mentioned, I wouldn't be able to switch the app environments dynamically during run time.

@dekan1
Copy link

dekan1 commented Nov 26, 2018

Please, I have same problem with initialization app, is this problem solved yet, or exist any workaround?

@renatobenks-zz
Copy link

There's no PR yet for that? How is going on the work to solve that?

I'm with the same problem, I want to load dynamically apps on runtime.

@tonyyyrocks39
Copy link

Is this issue fixed...I mean can we initialize firebase without this json file..
Also what is the purpose of firebaseoptions.builder if we can intialize firebase without the builder by simply copying the json file in project.

@damianun
Copy link

Yes, we have similar requirement where we would like to be able to switch on startup which Firebase project along with Analytics and Crashlytics should be used. Any feedback from Analytics teams on this?

@mkarecki
Copy link

Hi, we also have the same issue, it's stopping us from full migration from GA to Firebase because we need to setup Firebase in runtime with use of data received from our backend.
Is there any ongoing work on this bug?

@mmert1988
Copy link

mmert1988 commented Mar 1, 2019

Having the same issue. Will be there any fix soon?

@igorbiscanin
Copy link

@samtstern Any news here?

@ddukesterman
Copy link

@samtstern Any update on this issue?

@vokrut
Copy link

vokrut commented Aug 7, 2019

Google Analytics for Firebase (GA4F) doesn't support dynamic initialization. Our engineers are checking the possible solutions to support this. It's just that we still haven't found a definite timeline as to when (or if) this will be available.
GA4F will not work without the google-services.json file (or Gradle on your end). Even though you can initialize the FirebaseApp dynamically through code, GA4F will not recognize this and will only result in the error message you are seeing. The scenario you are getting is only specific to Google Analytics for Firebase. However, you can still use other products like Firestore, Realtime Database, Storage even if you are not using Gradle plugin.

https://stackoverflow.com/a/55006495/5862482

@pepitoria
Copy link

Just checking up on this, status of this has not changed, right?

@HarrisonRenny
Copy link

@pepitoria AFAIK the last time we spoke with google - this issue is not going to be resolved.

@RustamSitdikov
Copy link

bug with Firebase using in Chromium

@RustamSitdikov
Copy link

RustamSitdikov commented Mar 13, 2020

in Chromium problem was due to string resources obfuscation (so google_app_id was obfuscated). adding resource_type/resource_name#no_obfuscate in aapt2.config solved problem

@ashwinraghav
Copy link
Contributor

ashwinraghav commented May 28, 2020

Copied from #187 (comment)

Thanks for the question and for waiting patiently.

Many Firebase SDKs products provide an overload that allows you to pass in a custom FirebaseApp getInstance(FirebaseApp).

This API will allow you inject these Firebase instances as dependencies into your application code so they continue remaining agnostic to prod,staging,dev versions of your app.

I suspect your question pertains to eagerly initialized SDKs like Analytics, Crashlytics and Firebase Performance that require no developer interaction and start working automagically once included in the app. Note that these SDKs do not support the desired getInstance(FirebaseApp) overload.

The challenge with designing an API like getInstance(FirebaseApp) for these products is that it would have to be wired in super early in the App's lifecycle, to (say) allow Crashlytics to capture crashes that happen early on, or to (say) allow Performance to capture app start metrics early on.

In the absence of this API, you may be able to do this by disabling the default init provider and creating your own equivalent. The key here is to perform the custom initialization super early in your app's lifecycle, like in a Content Provider.

FirebaseOptions.Builder builder = new FirebaseOptions.Builder()
    .setApplicationId("1:0123456789012:android:0123456789abcdef")
    .setApiKey("your_api_key")
    .setDatabaseUrl("https://your-app.firebaseio.com")
    .setStorageBucket("your-app.appspot.com");
FirebaseApp.initializeApp(this, builder.build());

I realize is a suboptimal experience. It does get the job done for now. Feel free to propose ideas you may have to help up build an API to accommodate the nuance I described above.

@jruvel
Copy link

jruvel commented Jun 5, 2020

Sadely @ashwinraghav this doesn't seem to solve the issue. We have struggled with this for almost two years. We have a multi-tenant application with dynamic switching of the firebase project and we got that working but analytics has to all be sent to one place by hardcoding "google_app_id" in values/strings.xml - this is the only thing that somewhat works. I just tried a Content Provider and the code runs fine but still see an error "Missing google_app_id." We continue to wait hopefully for one day a getInstance(FirebaseApp) for FirebaseAnalytics. Note this all works for the iOS SDKs.

@ashwinraghav
Copy link
Contributor

Sadely @ashwinraghav this doesn't seem to solve the issue. We have struggled with this for almost two years. We have a multi-tenant application with dynamic switching of the firebase project and we got that working but analytics has to all be sent to one place by hardcoding "google_app_id" in values/strings.xml - this is the only thing that somewhat works. I just tried a Content Provider and the code runs fine but still see an error "Missing google_app_id." We continue to wait hopefully for one day a getInstance(FirebaseApp) for FirebaseAnalytics. Note this all works for the iOS SDKs.

Duly noted. This feedback is helpful for us to prioritize.

@ashwinraghav
Copy link
Contributor

The issue is two years old and it is the highest voted firebase sdk github issue, shouldn't it have a top priority?

Certainly is top priority :) We'll be sure to share our plans to address this issue with folks on this issue, to get some early feedback.

@jruvel
Copy link

jruvel commented Aug 26, 2020

@ashwinraghav - does this mean a fix on the Android side is close? firebase/firebase-ios-sdk#6142 - Not exactly related but going the right direction.

@mtrakal
Copy link

mtrakal commented Sep 4, 2020

Google Play console start showing:

Please upgrade to the generally available Firebase Crashlytics SDK to continue receiving crash reports in the Firebase console after 15 November 2020.

We really would like, but it's not possible when we still have no option to log to the correct project :)

Do you have some estimation, when this issue will be resolved? We don't like it when we have just a few days to implement new features until Google kills the old one :). When you have one month releasing window (contracting), you don't have much time...

@prilaga
Copy link

prilaga commented Sep 15, 2020

It was a wonderful time to work with the Fabric, until google bought it.
I could init crashlytics, analytics manually when I needed it, but now I can't.
Why do I need this - because I want to keep securely all the firebase keys and run firebase, crashlytics, analytics dynamically.
But for now I can init the firebase without json file, but crashlytics doesn't work in this case:

Logs when trying to build a release project:
FAILURE: Build failed with an exception.

  • What went wrong:
    Execution failed for task ':app:uploadCrashlyticsMappingFileRelease'.

Crashlytics could not find the resource file generated by Google Services. You may need to execute the :processGoogleServices Task. Please check your Firebase project configuration (https://firebase.google.com/docs/android/setup).

Please rollback to previous build project feature, like it was with Fabric.

Update:

Here is a tested workaround, how to to init crashlytics and firebase when you need:

  1. Make sure the app is ready to release and no changes will be added to the current build. Decide where you will enable default Firebase app with your keys copied from google-services.json file.
  2. Check that you have network connection.
  3. Build a release project as usually with:
google-services.json file
  dependencies {
        classpath 'com.google.gms:google-services:4.3.3'
        classpath 'com.google.firebase:firebase-crashlytics-gradle:2.3.0'
    }
// Google Services Gradle plugin
apply plugin: 'com.google.gms.google-services'
// Apply the Crashlytics Gradle plugin
apply plugin: 'com.google.firebase.crashlytics'
  1. Wait for several minutes while plugin post the mapping.txt file to firebase. Not sure how long it should be.
  2. Find and copy the key from build folder to your string.xml with next name for crashlytics:
<string tools:ignore="UnusedResources,TypographyDashes"
        name="com.crashlytics.android.build_id"
        translatable="false">*********************</string>

For analytics:

<string name="google_app_id" translatable="false">************</string>
  1. Remove all from 2 point from the project:
google-services.json file
 dependencies {
        classpath 'com.google.gms:google-services:4.3.3'
        classpath 'com.google.firebase:firebase-crashlytics-gradle:2.3.0'
    }
// Google Services Gradle plugin
apply plugin: 'com.google.gms.google-services'
// Apply the Crashlytics Gradle plugin
  1. Disable FirebaseInitProvider in AndroidManifest:
 <provider
            android:name="com.google.firebase.provider.FirebaseInitProvider"
            android:authorities="${applicationId}.firebaseinitprovider"
            tools:node="remove"/>
  1. Clean the project - build folder should be cleared and created without hardcoded keys.
  2. Build a release again and publish the app.

I created a test crash with this solution and found the report in the dashboard.

@Tapchicoma
Copy link

Workaround when you have generated xml file, but not google-services plugin itself:

val copyGoogleIdValuesTask = tasks.register("copyGoogleIdValues", Copy::class.java) {
    from("src/main/res/values/google-services.xml")
    into("${project.buildDir}/generated/res/google-services/values.xml")
}

tasks.withType<com.google.firebase.crashlytics.buildtools.gradle.tasks.UploadMappingFileTask>() {
    dependsOn(copyGoogleIdValuesTask)
}

May not work in multivariant apps.

@yash-numino
Copy link

Is there any update on this problem wrt. Firebase Analytics, as of September 29th, 2020. I'm having to solve the exact same problem, and I'm out of solutions.

Does anyone else have any other way by which this problem can be solved?

@1951FDG
Copy link

1951FDG commented Jan 6, 2021

Workaround (groovy syntax) when you have generated xml file located in src/release/res/values/values.xml

task copyGoogleIdValuesTask(type: Copy) {
    from 'src/release/res/values/values.xml'
    into "$project.buildDir/generated/res/google-services/release/values/"
}

import com.google.firebase.crashlytics.buildtools.gradle.tasks.UploadMappingFileTask

tasks.withType(UploadMappingFileTask).configureEach {
    dependsOn(copyGoogleIdValuesTask)
}

Works for release build variant.

@jae-12
Copy link

jae-12 commented Mar 23, 2021

Any update on this? Still facing this issue in March of 2021 😩

@shadowsheep1
Copy link

The @1951FDG workaround works, but if we update to crashlytics gradle plugin 2.7.+ we have this error now: #2721

@shafayathossain
Copy link

Still facing this issue in December of 2021. They resolved 926 issues. But this 66th issue is open for over 3 years.
Flutter developers have no idea that this is a native android SDK issue. Because it's working fine for iOS.

@waochi
Copy link

waochi commented Jan 18, 2022

Any update on this?

@gemunet
Copy link

gemunet commented Mar 21, 2022

Still no solution for this? March 2022 :(

@electricbolt
Copy link

Hi, we've implemented a workaround for use of Firebase Analytics. We've not tested any other usage. Your milage may vary.

Overview

It appears the actual analytics code is implemented in play-services-measurement-*.jar files (which I assume are downloaded automatically via Google Play Services).

To initialise Firebase I call the following programmatically:

FirebaseOptions.Builder builder = new FirebaseOptions.Builder()
    .setApplicationId(FirebaseConstants.APP_ID)
    .setApiKey(FirebaseConstants.API_KEY)
    .setGcmSenderId(FirebaseConstants.MESSAGING_SENDER_ID)
    .setProjectId(FirebaseConstants.PROJECT_ID);
FirebaseApp.initializeApp(getContext(), builder.build());
    
FirebaseAnalytics.getInstance(getContext());

The context provided by getContext() is internally converted by Firebase to an application context, which is then cast to an android.app.Application. The play-services-measurement-* code then calls the application's getResources() method, followed by a call to Resource.getIdentifier("google_app_id", "string", "<your-package>") to return the R.string.google_app_id constant. Then Resource.getString(R.string.google_app_id) is called to return the actual value you should have declared in strings.xml (which won't exist as we're trying to do this programmatically).

Implementation

Add the following to your android.app.Application subclass:

    private FirebaseResourcesWrapper firebaseResources;

    @Override
    public Resources getResources() {
        if (firebaseResources == null)
            firebaseResources = new FirebaseResourcesWrapper(super.getResources());
        return firebaseResources;
    }

The FirebaseResourcesWrapper subclasses android.content.res.Resources and implements the class wrapper pattern. Here we forward all visible public methods to the original wrapped class, except for getIdentifer() and getString() which we override as follows:

public class FirebaseResourcesWrapper extends Resources {

    private static final String GOOGLE_APP_ID = "google_app_id";
    private static final int R_STRING_GOOGLE_APP_ID = 1_999_999_999;

    private final Resources wrapped;

    public FirebaseResourcesWrapper(Resources wrapped) {
        super(wrapped.getAssets(), wrapped.getDisplayMetrics(), wrapped.getConfiguration());
        this.wrapped = wrapped;
    }

    public int getIdentifier(String name, String defType, String defPackage) {
        if (GOOGLE_APP_ID.equals(name) && "string".equals(defType))
            return R_STRING_GOOGLE_APP_ID;
        return wrapped.getIdentifier(name, defType, defPackage);
    }

    public String getString(int id) throws NotFoundException {
        if (id == R_STRING_GOOGLE_APP_ID)
            return FirebaseConstants.APP_ID;
        return wrapped.getString(id);
    }

    public String getString(int id, Object... formatArgs) throws NotFoundException {
        return wrapped.getString(id, formatArgs);
    }

Full implementation of FirebaseResourcesWrapper.java in this GitHub gist:

https://gist.github.com/electricbolt/423c03f09bc0303d0d5696b8beb392bd

@FarzaneGhb
Copy link

Any update on this?

daymxn pushed a commit that referenced this issue Jan 11, 2023
@DarshanMagneto
Copy link

Any update on this issue?

@jmcculloch
Copy link

👋

@leontodd
Copy link

leontodd commented Aug 22, 2023

Hi @ashwinraghav, have there been any updates on a fix for this issue?

@gongshoudao
Copy link

same issue.
image

@sergey-avagyan
Copy link

Any news on this issue?

@Hunter54
Copy link

Hunter54 commented Feb 13, 2024

Same issue here. 5-6 years later, still no plan to fix it?

Using this workaround seems to work.
#66 (comment)

@prilaga
Copy link

prilaga commented Feb 13, 2024

@sergey-avagyan @Hunter54
Stop waiting, just use FirebaseResourcesWrapper or similar solution. Gооglе never fixes issues, until they become a problem, until it threatens their business.

@as-stefit
Copy link

Hi, we've implemented a workaround for use of Firebase Analytics. We've not tested any other usage. Your milage may vary.

Overview

It appears the actual analytics code is implemented in play-services-measurement-*.jar files (which I assume are downloaded automatically via Google Play Services).

To initialise Firebase I call the following programmatically:

FirebaseOptions.Builder builder = new FirebaseOptions.Builder()
    .setApplicationId(FirebaseConstants.APP_ID)
    .setApiKey(FirebaseConstants.API_KEY)
    .setGcmSenderId(FirebaseConstants.MESSAGING_SENDER_ID)
    .setProjectId(FirebaseConstants.PROJECT_ID);
FirebaseApp.initializeApp(getContext(), builder.build());
    
FirebaseAnalytics.getInstance(getContext());

The context provided by getContext() is internally converted by Firebase to an application context, which is then cast to an android.app.Application. The play-services-measurement-* code then calls the application's getResources() method, followed by a call to Resource.getIdentifier("google_app_id", "string", "<your-package>") to return the R.string.google_app_id constant. Then Resource.getString(R.string.google_app_id) is called to return the actual value you should have declared in strings.xml (which won't exist as we're trying to do this programmatically).

Implementation

Add the following to your android.app.Application subclass:

    private FirebaseResourcesWrapper firebaseResources;

    @Override
    public Resources getResources() {
        if (firebaseResources == null)
            firebaseResources = new FirebaseResourcesWrapper(super.getResources());
        return firebaseResources;
    }

The FirebaseResourcesWrapper subclasses android.content.res.Resources and implements the class wrapper pattern. Here we forward all visible public methods to the original wrapped class, except for getIdentifer() and getString() which we override as follows:

public class FirebaseResourcesWrapper extends Resources {

    private static final String GOOGLE_APP_ID = "google_app_id";
    private static final int R_STRING_GOOGLE_APP_ID = 1_999_999_999;

    private final Resources wrapped;

    public FirebaseResourcesWrapper(Resources wrapped) {
        super(wrapped.getAssets(), wrapped.getDisplayMetrics(), wrapped.getConfiguration());
        this.wrapped = wrapped;
    }

    public int getIdentifier(String name, String defType, String defPackage) {
        if (GOOGLE_APP_ID.equals(name) && "string".equals(defType))
            return R_STRING_GOOGLE_APP_ID;
        return wrapped.getIdentifier(name, defType, defPackage);
    }

    public String getString(int id) throws NotFoundException {
        if (id == R_STRING_GOOGLE_APP_ID)
            return FirebaseConstants.APP_ID;
        return wrapped.getString(id);
    }

    public String getString(int id, Object... formatArgs) throws NotFoundException {
        return wrapped.getString(id, formatArgs);
    }

Full implementation of FirebaseResourcesWrapper.java in this GitHub gist:

https://gist.github.com/electricbolt/423c03f09bc0303d0d5696b8beb392bd

Thanks for your solution. I've got one question, how do you define FirebaseConstants? In my app, while initializing flutter app I pull secrets from secrets manager, save it in FlutterSecureStorage and initialize FirebaseApplication with appropriate secrets pulled in previous step. Firestore and crashlytics work fine, but analytics don't. The solution proposed by you looks promising, but I'm missing one part, how to share these secrets in FirebaseResourcesWrapper class. I would be grateful if you could share your ideas!

@electricbolt
Copy link

We have something similar to the following, just standard constant definitions. Since your app is distributed publicly, it's available for anyone to inspect internally by downloading the APK file and disassembling. Your constants are never truly private.

You could obfuscate your strings using an external tool, and embedding the obfuscated text, then have a deobfuscate() method to return the plain text. Commercial obfuscation tools include DexGuard, dProtect, DashO etc.

public final class FirebaseConstants {

public static String API_KEY() { return " ... "; }
public static String APP_ID() { return " ... "; }
public static String MESSAGING_SENDER_ID() { return " ... "; }
public static String PROJECT_ID() { return " ... "; }

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
tracking-internally type: bug Something isn't working
Projects
None yet
Development

No branches or pull requests