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

Crashes when using Firebase Performance Monitoring and R8 #1476

Closed
rishun-fsi opened this issue Apr 17, 2020 · 11 comments
Closed

Crashes when using Firebase Performance Monitoring and R8 #1476

rishun-fsi opened this issue Apr 17, 2020 · 11 comments

Comments

@rishun-fsi
Copy link


[REQUIRED] Step 2: Describe your environment

Android Studio version: 3.6.1
Firebase Component: Firebase Performance Monitoring
Component version: 19.0.6
ExoPlayer version: r2.11.3

[REQUIRED] Step 3: Describe the problem

Firebase Performance Monitoring + R8 encounters problems when using obfuscated library.
This reproduction occurred when I used Google ExoPlayer.

Steps to reproduce:

  1. Obfuscate & build aar ExoPlayer in R8
    Get the latest version of release-v2 branch from GitHub of ExoPlayer.
    Enable obfuscation by R8 for library-core.
    =>set the obfuscation out of scorpe as appropriate.

    Execute the assemblyRelease task of library-core, library-dash, library-hls, library-smoothstreaming, and library-ui.
    Execute the library-all assemblyRelease with the generated jar file.
    =>ExoPlayer's aar is completed.

  2. Load the library and enable Firebase Performance Monitoring in the application.

  3. Running the app occurred an exception.
    =>Please check out the Crash logs.

Tried patterns

  1. AAR obfuscated by R8 + Enable Firebase Performance Monitoring : NG
  2. AAR obfuscated by R8 + Disable Firebase Performance Monitoring : OK
  3. Unobfuscated AAR + Enable Firebase Performance Monitoring : OK
  4. AAR obfuscated by ProGuard + Enable Firebase Performance Monitoring : OK

Relevant Code:

  1. Sample Application
import android.net.Uri;
import android.os.Bundle;
import android.view.SurfaceView;

import androidx.appcompat.app.AppCompatActivity;

import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.dash.DashMediaSource;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;

public class MainActivity extends AppCompatActivity {
    SimpleExoPlayer mExoPlayer = null;
    SurfaceView mSurfaceView = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mSurfaceView = findViewById(R.id.surface_view);
    }

    @Override
    protected void onStart() {
        super.onStart();

        mExoPlayer = new SimpleExoPlayer.Builder(this).build();
        mExoPlayer.setVideoSurfaceView(mSurfaceView);

        Uri uri = Uri.parse("http://www.bok.net/dash/tears_of_steel/cleartext/stream.mpd");
        DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(this, "User Agent");
        DashMediaSource dashMediaSource = new DashMediaSource.Factory(dataSourceFactory).createMediaSource(uri);
        mExoPlayer.prepare(dashMediaSource);
        mExoPlayer.setPlayWhenReady(true);
    }

    @Override
    protected void onStop() {
        if (mExoPlayer != null) {
            mExoPlayer.stop();
            mExoPlayer.release();
            mExoPlayer = null;
        }
        super.onStop();
    }
}

Crash Logs

    --------- beginning of crash
2020-04-09 17:22:58.634 31471-31471/com.example.testapp_exo_r8 E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.testapp_exo_r8, PID: 31471
    java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/android/exoplayer2/C;
        at com.google.android.exoplayer2.e.<init>(Unknown Source:61)
        at com.google.android.exoplayer2.e.<init>(Unknown Source:18)
        at com.google.android.exoplayer2.e.<init>(Unknown Source:8)
        at com.google.android.exoplayer2.SimpleExoPlayer$Builder.<init>(Unknown Source:7)
        at com.google.android.exoplayer2.SimpleExoPlayer$Builder.<init>(Unknown Source:5)
        at com.example.testapp_exo_r8.MainActivity.onStart(MainActivity.java:30)
        at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1333)
        at android.app.Activity.performStart(Activity.java:6992)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2780)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
        at android.os.Handler.dispatchMessage(Handler.java:105)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6541)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
     Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.android.exoplayer2.C" on path: DexPathList[[zip file "/data/app/com.example.testapp_exo_r8-9PEyGT_SiZPKi56jWRLmAQ==/base.apk"],nativeLibraryDirectories=[/data/app/com.example.testapp_exo_r8-9PEyGT_SiZPKi56jWRLmAQ==/lib/arm64, /system/lib64, /vendor/lib64]]
        at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:93)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
        at com.google.android.exoplayer2.e.<init>(Unknown Source:61) 
        at com.google.android.exoplayer2.e.<init>(Unknown Source:18) 
        at com.google.android.exoplayer2.e.<init>(Unknown Source:8) 
        at com.google.android.exoplayer2.SimpleExoPlayer$Builder.<init>(Unknown Source:7) 
        at com.google.android.exoplayer2.SimpleExoPlayer$Builder.<init>(Unknown Source:5) 
        at com.example.testapp_exo_r8.MainActivity.onStart(MainActivity.java:30) 
        at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1333) 
        at android.app.Activity.performStart(Activity.java:6992) 
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2780) 
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892) 
        at android.app.ActivityThread.-wrap11(Unknown Source:0) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593) 
        at android.os.Handler.dispatchMessage(Handler.java:105) 
        at android.os.Looper.loop(Looper.java:164) 
        at android.app.ActivityThread.main(ActivityThread.java:6541) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767) 
2020-04-09 17:23:04.017 31471-31491/com.example.testapp_exo_r8 I/zygote64: System.exit called, status: 1

For details, please check the sample project.
TestApp_Exo_R8.zip

Thanks in advance for any help!

@google-oss-bot
Copy link
Contributor

I couldn't figure out how to label this issue, so I've labeled it for a human to triage. Hang tight.

@zijianjoy
Copy link

zijianjoy commented Apr 18, 2020

Hello @rishun-fsi ,

To further investigate the issue, would you like to tell us more about the step 1 to reproduce?

  1. How do you enable obfuscation by R8 for ExoPlayer's library-core?
  2. How does the command look like when running assemblyRelease for other libraries and eventually library-all?
  3. Are you able to test with older version of ExoPlayer and see whether the problem exists?

Observation

Performance Monitoring works well when ExoPlayer is proguarded, maybe there is something to do with R8 and Performance Monitoring gradle plugin. Performance Monitoring SDK is not in the scope of concern.

@rishun-fsi
Copy link
Author

Hello @zijianjoy ,

Thank you for your answer.

How do you enable obfuscation by R8 for ExoPlayer's library-core?
How does the command look like when running assemblyRelease for other libraries and eventually library-all?
=>I have attached a project for the library.
Please confirm it.
ExoPlayer_R8.zip

Are you able to test with older version of ExoPlayer and see whether the problem exists?
=>It is occurring as confirmed in 2.8.1.

@zijianjoy
Copy link

zijianjoy commented Apr 20, 2020

Thank you @rishun-fsi ,

I have tried to build with your ExoPlayer project with the following steps. However, the library-release.aar I built didn't cause the crash on the test app you provided. Please see the steps I performed below to confirm:

Steps

Run gradle task

On Android Studio, run gradle task library -> Tasks -> assemble.

Retrieves ExoPlayer aar

library-release.aar was located in library/all/buildout/outputs/aar/library-release.aar.

Add aar to Test app

Add the aar above to Test app's app/libs/library-release.aar

Result

When I ran the test app, it doesn't crash in the emulator and video player performed well.

Would you like to confirm this is how you reproduce the issue? If not, what are the steps you have followed through to build ExoPlayer? (For example, how to set the obfuscation out of scope as appropriate?) Thank you!

@rishun-fsi
Copy link
Author

Hello @zijianjoy ,

Sorry, I'm going to detail the steps of the Run gradle task.

Step.

  1. Run dash, hls, smoothstreaming, ui, and core with assembleRelease.

  2. Copy all the generated jar files to /library/all/libs/, and rename them to avoid using the same
    name.

core:
/library/core/buildout/intermediates/packaged-classes/release/classes.jar
dash:
/library/dash/buildout/intermediates/packaged-classes/release/classes.jar
hls:
/library/hls/buildout/intermediates/packaged-classes/release/classes.jar
smoothstreaming:
/library/smoothstreaming/buildout/intermediates/packaged-classes/release/classes.jar
ui:
/library/ui/buildout/intermediates/packaged-classes/release/classes.jar

  1. Run all with assembleRelease.

The following steps are the same.
Please confirm it.

Note:

This issue is happening when you create a player based on ExoPlayer.

@zijianjoy
Copy link

Thank you @rishun-fsi for the reproduction steps.

I have followed the steps you listed, but I couldn't find the /packaged-classes in /library/core/buildout/intermediates/ and other libraries' output directories. Here is a list of folders under /library/core/buildout/intermediates/. Am I missing anything during the reproduction?

aapt_friendly_merged_manifests
aapt_proguard_file
aar_libs_directory
aar_main_jar
annotation_processor_list
annotations_typedef_file
annotations_zip
blame
compile_library_classes
compile_only_not_namespaced_r_class_jar
compile_symbol_list
compiled_local_resources
data_binding_layout_info_type_merge
data_binding_layout_info_type_package
incremental
jacoco_instrumented_classes
jacoco_instrumented_jars
javac
library_and_local_jars_jni
library_assets
library_manifest
local_only_symbol_list
manifest_merge_blame_file
merged_consumer_proguard_file
merged_java_res
merged_jni_libs
merged_manifests
merged_native_libs
merged_shaders
packaged_res
proguard-files
public_res
res
shader_assets
shrunk_classes
shrunk_java_res
stripped_native_libs
symbol_list_with_package_name

Findings

When trying to reproduce the issue, I also found some records about Firebase Performance Gradle Plugin when it tries to instrument two classes with same names (regardless of cases) on a case-insensitive machine (See reference https://buganizer.corp.google.com/issues/139268403).

By looking at ExoPlayer repository, I found a class called C.java which is -keep in your proguard-rules.txt file as below:

-keep class com.google.android.exoplayer2.C { *; }

Since the R8 is obfuscating other classes in alphabetical order, one of the other file under the same directory was converted to c.class (In this case, it is BasePlayer.java which is converted to c.class). It might be the reason of crash when Firebase Performance is instrumenting obfuscated classes with same name (single character names with different cases).

Next steps

  • Would you like to confirm the machine you used to build and run test app? It will help us determine whether it is related to case-insensitive machines. (For example, Windows and MacOS are case-insensitive)
  • What is the gradle version you used? It will help us follow the same reproduction steps you have provided. And feel free to point out if there is anything we should note when reproducing.
  • Would you like to try the suggestion on https://buganizer.corp.google.com/issues/139268403 to include -dontusemixedcaseclassnames in proguard configuration file, see whether it resolves your issue? It will help us identify the root cause while reproducing.

@google-oss-bot
Copy link
Contributor

Hey @rishun-fsi. We need more information to resolve this issue but there hasn't been an update in 5 weekdays. I'm marking the issue as stale and if there are no new updates in the next 5 days I will close it automatically.

If you have more information that will help us get to the bottom of this, just add a comment!

@mkj-gram
Copy link
Contributor

mkj-gram commented May 7, 2020

R8 has been updated to account for existing mixed names such that clashing should no longer occur if using the keep rule-dontusemixedcaseclassnames.

To use the R8 version, add the following to your build.gradle file:

buildscript {

    repositories {
        maven {
            url 'https://storage.googleapis.com/r8-releases/raw'
        }
    }

    dependencies {
        classpath 'com.android.tools:r8:2.0.72'          // Must be before the Gradle Plugin for Android.
        classpath 'com.android.tools.build:gradle:X.Y.Z' // Your current AGP version.
     }
}

If you see other issues, please let us know.

@rishun-fsi
Copy link
Author

Thank you @zijianjoy for the response.

Steps

For example run core with assembleRelease.
$ ./gradlew library-core:assembleRelease

I could get a list of folders under /library/core/buildout/intermediates/ that includes packaged-classes

aapt_friendly_merged_manifests
annotation_processor_list
annotations_typedef_file
annotations_zip
blame
check_manifest_result
compile_only_not_namespaced_r_class_jar
consumer_proguard_file
generated_proguard_file
incremental
jacoco_instrumented_classes
jacoco_instrumented_jars
javac
library_and_local_jars_jni
library_assets
library_manifest
lint_publish_jar
local_only_symbol_list
manifest_merge_blame_file
merged_java_res
merged_jni_libs
merged_manifests
merged_native_libs
merged_shaders
packaged-classes
packaged_res
public_res
res
shader_assets
stripped_native_libs
symbols

The next step answer

The machine used to build and run the test app

  • Windows 10 pro

Gradle version

  • Android Gradle Plug Version: 3.4.1
  • Gradle Version 5.4.1

Resolves

Thank you @mkj-gram for the quick response. you saved my day.

@zijianjoy
Copy link

Glad that the issue is resolved now, thank you @mkj-gram for committing the fix and @rishun-fsi for confirming!

For future reference, Firebase Performance Monitoring gradle plugin cannot instrument classes with only case difference in class name (for example, className.class vs classname.class), they will cause one class overwriting another, and it happens on case-insensitive machine.

The current workaround is to apply -dontusemixedcaseclassnames in proguard configuration, so class names can avoid this conflict by not having mixed case during obfuscation. However, ExoPlayer core library has a class called C.java, and this class is -keep in the proguard configuration provided in this thread, for exposing API C.class to be used by other ExoPlayer packages. R8 obfuscation previously generated c.class even with -dontusemixedcaseclassnames. With @mkj-gram 's fix, it now takes this C.class in consideration, therefore resolved the crash.

@firebase firebase locked and limited conversation to collaborators Jun 13, 2020
@ramanpreetSinghKhinda
Copy link
Contributor

The root cause (perf-plugin processing on case sensitive file systems / case sensitive files with similar names) has been now been fixed (internally tracked at b/120025646).

The changes will come in the upcoming release of perf-plugin. Please keep an eye on https://firebase.google.com/support/release-notes/android for the latest release info.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants