Skip to content

Catch NPE when removing activity from frameMetricsAggregator #4184

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

Closed
wants to merge 6 commits into from

Conversation

jeremyjiang-dev
Copy link
Contributor

@jeremyjiang-dev jeremyjiang-dev commented Oct 7, 2022

Potential fix of #4146

I was not able to reproduce the issue locally. I tried with Android version 7.1 API 25 and turned off hardware acceleration, but could not reproduce the issue.

It could be a bug from older Android versions.

Reference:

Update by @leotianlizhan

Let's take a look at Android's source code, in android.view.View.
The exception was thrown from findFrameMetricsObserver, inside removeFrameMetricsListener
image
image
Since this null check didn't exists, this triggers NPE when mFrameMetricsObserver is uninitialized. As Jeremy listed in his reference, this was fixed in later Android versions. Why then, is mFrameMetricsObserver uninitialized? It's because addFrameMetricsListener fails silently!
image

Conclusion

So overall, this is the same issue caused by the view not being hardware-accelerated. The correct error message is hidden away because addFrameMetricsListener fails silently, that's why we should catch them both and use the same error message. The developer's provided crash versions also lines up with when Android fixed it: the above reference fix was made in 2019, and Android 7, 8, 9 are all before 2019, with Android 10 releasing in 2019 thus fixing the issue. There is no crash before Android 7 because as Rosario pointed out, FrameMetrics was added in Android 7 (API 24).

@google-oss-bot
Copy link
Contributor

google-oss-bot commented Oct 7, 2022

Coverage Report 1

Affected Products

  • firebase-perf

    Overall coverage changed from ? (6df2784) to 71.11% (8e6fbf4) by ?.

    102 individual files with coverage change

    FilenameBase (6df2784)Merge (8e6fbf4)Diff
    AddTrace.java?0.00%?
    AndroidApplicationInfo.java?34.71%?
    AndroidApplicationInfoOrBuilder.java?0.00%?
    AndroidLogger.java?100.00%?
    AndroidMemoryReading.java?38.36%?
    AndroidMemoryReadingOrBuilder.java?0.00%?
    ApplicationInfo.java?45.00%?
    ApplicationInfoOrBuilder.java?0.00%?
    ApplicationProcessState.java?73.91%?
    AppStartTrace.java?85.19%?
    AppStateMonitor.java?86.71%?
    AppStateUpdateHandler.java?92.86%?
    Clock.java?100.00%?
    ConfigResolver.java?93.19%?
    ConfigurationConstants.java?97.86%?
    ConfigurationFlag.java?100.00%?
    ConsoleUrlGenerator.java?37.50%?
    Constants.java?95.65%?
    Counter.java?90.91%?
    CpuGaugeCollector.java?92.77%?
    CpuMetricReading.java?39.33%?
    CpuMetricReadingOrBuilder.java?0.00%?
    DaggerFirebasePerformanceComponent.java?100.00%?
    DeviceCacheManager.java?76.03%?
    FirebasePerfApplicationInfoValidator.java?92.86%?
    FirebasePerfGaugeMetricValidator.java?100.00%?
    FirebasePerfHttpClient.java?93.85%?
    FirebasePerfMetricProto.java?0.00%?
    FirebasePerfNetworkValidator.java?86.67%?
    FirebasePerfOkHttpClient.java?44.90%?
    FirebasePerformance.java?79.12%?
    FirebasePerformanceAttributable.java?0.00%?
    FirebasePerformanceComponent.java?0.00%?
    FirebasePerformanceInitializer.java?33.33%?
    FirebasePerformanceModule.java?100.00%?
    FirebasePerformanceModule_ProvidesConfigResolverFactory.java?100.00%?
    FirebasePerformanceModule_ProvidesFirebaseAppFactory.java?100.00%?
    FirebasePerformanceModule_ProvidesFirebaseInstallationsFactory.java?100.00%?
    FirebasePerformanceModule_ProvidesRemoteConfigComponentFactory.java?100.00%?
    FirebasePerformanceModule_ProvidesRemoteConfigManagerFactory.java?100.00%?
    FirebasePerformanceModule_ProvidesSessionManagerFactory.java?100.00%?
    FirebasePerformanceModule_ProvidesTransportFactoryProviderFactory.java?100.00%?
    FirebasePerformance_Factory.java?100.00%?
    FirebasePerfProvider.java?76.92%?
    FirebasePerfRegistrar.java?100.00%?
    FirebasePerfTraceValidator.java?85.87%?
    FirebasePerfUrlConnection.java?44.26%?
    FirstDrawDoneListener.java?85.19%?
    FlgTransport.java?83.33%?
    FragmentStateMonitor.java?94.87%?
    FrameMetricsCalculator.java?96.77%?
    FrameMetricsRecorder.java?76.32%?
    GaugeManager.java?98.41%?
    GaugeMetadata.java?26.17%?
    GaugeMetadataManager.java?77.78%?
    GaugeMetadataOrBuilder.java?0.00%?
    GaugeMetric.java?39.47%?
    GaugeMetricOrBuilder.java?0.00%?
    HttpMetric.java?92.65%?
    ImmutableBundle.java?100.00%?
    InstrHttpInputStream.java?92.86%?
    InstrHttpOutputStream.java?98.00%?
    InstrHttpsURLConnection.java?94.32%?
    InstrHttpURLConnection.java?93.42%?
    InstrumentApacheHttpResponseHandler.java?100.00%?
    InstrumentOkHttpEnqueueCallback.java?100.00%?
    InstrURLConnectionBase.java?94.86%?
    LogWrapper.java?23.08%?
    MemoryGaugeCollector.java?91.38%?
    NetworkConnectionInfo.java?0.00%?
    NetworkConnectionInfoOrBuilder.java?0.00%?
    NetworkRequestMetric.java?49.16%?
    NetworkRequestMetricBuilder.java?95.97%?
    NetworkRequestMetricBuilderUtil.java?75.00%?
    NetworkRequestMetricOrBuilder.java?0.00%?
    Optional.java?86.67%?
    PendingPerfEvent.java?100.00%?
    PerfMetric.java?33.67%?
    PerfMetricOrBuilder.java?0.00%?
    PerfMetricValidator.java?93.55%?
    PerfSession.java?93.22%?
    PerfSessionOrBuilder.java?0.00%?
    Rate.java?100.00%?
    RateLimiter.java?90.77%?
    RemoteConfigManager.java?91.79%?
    ResourceType.java?0.00%?
    ScreenTraceUtil.java?94.12%?
    SessionAwareObject.java?0.00%?
    SessionManager.java?100.00%?
    SessionVerbosity.java?68.42%?
    StorageUnit.java?57.89%?
    Timer.java?90.63%?
    Trace.java?96.67%?
    TraceMetric.java?43.14%?
    TraceMetricBuilder.java?100.00%?
    TraceMetricOrBuilder.java?0.00%?
    TransportInfo.java?0.00%?
    TransportInfoOrBuilder.java?0.00%?
    TransportManager.java?94.88%?
    URLAllowlist.java?94.44%?
    URLWrapper.java?0.00%?
    Utils.java?78.57%?

Test Logs

  1. https://storage.googleapis.com/firebase-sdk-metric-reports/Euh9LHeaXn.html

@github-actions
Copy link
Contributor

github-actions bot commented Oct 7, 2022

Unit Test Results

  51 files  +  36    51 suites  +36   1m 35s ⏱️ +42s
974 tests +822  974 ✔️ +822  0 💤 ±0  0 ±0 
974 runs  +818  974 ✔️ +818  0 💤 ±0  0 ±0 

Results for commit 8d45513. ± Comparison against base commit 6df2784.

This pull request removes 152 and adds 974 tests. Note that renamed tests count towards both.
com.google.firebase.ml.modeldownloader.CustomModelDownloadConditionsTest ‑ testConfigConditions
com.google.firebase.ml.modeldownloader.CustomModelDownloadConditionsTest ‑ testDefaultBuilder
com.google.firebase.ml.modeldownloader.CustomModelDownloadConditionsTest ‑ testTwoConfiguredConditionsDifferent
com.google.firebase.ml.modeldownloader.CustomModelDownloadConditionsTest ‑ testTwoConfiguredConditionsSame
com.google.firebase.ml.modeldownloader.CustomModelDownloadConditionsTest ‑ testTwoDefaultConditionsSame
com.google.firebase.ml.modeldownloader.CustomModelTest ‑ customModel_equals
com.google.firebase.ml.modeldownloader.CustomModelTest ‑ customModel_getDownloadId
com.google.firebase.ml.modeldownloader.CustomModelTest ‑ customModel_getDownloadUrl
com.google.firebase.ml.modeldownloader.CustomModelTest ‑ customModel_getDownloadUrlExpiry
com.google.firebase.ml.modeldownloader.CustomModelTest ‑ customModel_getFileSize
…
com.google.firebase.perf.FirebasePerfRegistrarTest ‑ testGetComponents
com.google.firebase.perf.FirebasePerformanceTest ‑ firebasePerformanceInitialization_providesRcProvider_remoteConfigManagerIsSet
com.google.firebase.perf.FirebasePerformanceTest ‑ initFirebasePerformance_injectsMetadataIntoConfigResolver
com.google.firebase.perf.FirebasePerformanceTest ‑ initializeFirebasePerformance_emptyMetadataAndCache_metadataAndContextInjected
com.google.firebase.perf.FirebasePerformanceTest ‑ setDataCollectionDefaultEnabled_whenForceDisabledThenCleared_respectsGlobalFlag
com.google.firebase.perf.FirebasePerformanceTest ‑ setDataCollectionDefaultEnabled_whenForceDisabledThenCleared_respectsManifestTrue
com.google.firebase.perf.FirebasePerformanceTest ‑ setDataCollectionDefaultEnabled_whenForceEnabledThenCleared_respectsGlobalFlag
com.google.firebase.perf.FirebasePerformanceTest ‑ setDataCollectionDefaultEnabled_whenForceEnabledThenCleared_respectsManifestFalse
com.google.firebase.perf.FirebasePerformanceTest ‑ testAddingMoreThanMaxLocalAttributes
com.google.firebase.perf.FirebasePerformanceTest ‑ testBothManifestsAgree
…

♻️ This comment has been updated with latest results.

@google-oss-bot
Copy link
Contributor

google-oss-bot commented Oct 7, 2022

Size Report 1

Affected Products

  • firebase-perf

    TypeBase (6df2784)Merge (8e6fbf4)Diff
    aar311 kB311 kB+160 B (+0.1%)
    apk (aggressive)1.03 MB1.03 MB+16 B (+0.0%)

Test Logs

  1. https://storage.googleapis.com/firebase-sdk-metric-reports/wEBffJuX46.html

@visumickey visumickey requested a review from raymondlam October 7, 2022 20:40
} catch (NullPointerException err) {
frameMetricsAggregator.reset();
logger.warn(
"NullPointerException. %s", err.toString());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Does the err.toString() have enough context to understand where the issue came from? Otherwise you can be more clear and say "NullPointerException when calling FrameMetricsAggregator.remove()"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recategorized the error, please see updated description

@thatfiredev
Copy link
Member

thatfiredev commented Oct 7, 2022

Update: I took a second look at the crash log provided in the issue and it seems like the error is happening with FrameMetricsApi24Impl:

at androidx.core.app.FrameMetricsAggregator$FrameMetricsApi24Impl.remove(FrameMetricsAggregator.java:433)

So my previous comment (bellow) is not entirely true.


@jeremyjiang-dev the underlying issue seems to be the fact that the FrameMetrics API was introduced in API 24, as documented here.

The pre-24 implementation of FrameMetricsAggregator returns null values, which causes this crash: https://github.com/androidx/androidx/blob/0bc4d894a0b7d46abcbc05aedb21ed72a8e71db5/core/core/src/main/java/androidx/core/app/FrameMetricsAggregator.java#L305-L330

Instead of catching this Exception, should we maybe add that condition to FrameMetricsRecorder.isFrameMetricsRecordingSupported() so that we don't start automatic screen tracing in devices with API < 24 ?

static boolean isFrameMetricsRecordingSupported() {
    try {
      Class<?> initializerClass = Class.forName(FRAME_METRICS_AGGREGATOR_CLASSNAME);
      return Build.VERSION.SDK_INT >= 24;
    } catch (ClassNotFoundException e) {
      return false;
    }
  }

Copy link
Contributor

@leotianlizhan leotianlizhan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great investigation! I think the combination of Rosario and Jeremy's investigation is the root cause: in API < 24 devices, FrameMetricsAggregator.add does nothing, which cause NPE when trying to remove it (fixed by Android in later versions as Jeremy found)

@raymondlam
Copy link
Member

Does this mean we are not capture screen rendering when a device has API < 24?

@jeremyjiang-dev
Copy link
Contributor Author

Update: I took a second look at the crash log provided in the issue and it seems like the error is happening with FrameMetricsApi24Impl:

at androidx.core.app.FrameMetricsAggregator$FrameMetricsApi24Impl.remove(FrameMetricsAggregator.java:433)

So my previous comment (bellow) is not entirely true.

@jeremyjiang-dev the underlying issue seems to be the fact that the FrameMetrics API was introduced in API 24, as documented here.

The pre-24 implementation of FrameMetricsAggregator returns null values, which causes this crash: https://github.com/androidx/androidx/blob/0bc4d894a0b7d46abcbc05aedb21ed72a8e71db5/core/core/src/main/java/androidx/core/app/FrameMetricsAggregator.java#L305-L330

Instead of catching this Exception, should we maybe add that condition to FrameMetricsRecorder.isFrameMetricsRecordingSupported() so that we don't start automatic screen tracing in devices with API < 24 ?

static boolean isFrameMetricsRecordingSupported() {
    try {
      Class<?> initializerClass = Class.forName(FRAME_METRICS_AGGREGATOR_CLASSNAME);
      return Build.VERSION.SDK_INT >= 24;
    } catch (ClassNotFoundException e) {
      return false;
    }
  }

Thanks Rosário for the investigation! I thought about API 24 could be the issue, but the issue also happens to Android 7, 8, and 9 according to the developer. Android 7, 8, and 9 are mapped to API level 24+, so I am not sure if API 24 was the cause.
Ref: https://source.android.com/docs/setup/about/build-numbers

@leotianlizhan
Copy link
Contributor

@visumickey @raymondlam I updated this PR to categorize this NPE into the same error message "View no hardware accelerated", and a lengthy comment to explain why. I also updated the PR description with more detail explaining with Android Source Code why this is the case.

@leotianlizhan leotianlizhan self-assigned this Oct 20, 2022
@leotianlizhan leotianlizhan linked an issue Oct 20, 2022 that may be closed by this pull request
@thatfiredev
Copy link
Member

@leotianlizhan amazing job on the investigation!! Thanks for sharing the results here (publicly instead of a internal source) :)

@leotianlizhan
Copy link
Contributor

PR is moved to #4284 due to CLA issues (PR author leaving Google) blocking merge on this PR.

@firebase firebase locked and limited conversation to collaborators Dec 9, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants