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

Fixed leak of CarAppLifecycleOwner #7669

Merged
merged 4 commits into from
Feb 15, 2024
Merged

Conversation

VysotskiVadim
Copy link
Contributor

Description

A customer complained that they see 14 log entries like

[nav-sdk]: [CarAppLifecycleOwner] app onActivityPaused

every time activity is paused. The same for resume.

During investigation I found out that every time MapboxCopilot starts, it creates a new instance of CarAppLifecycleOwner, and subscribes for all activities changes without desubscribing from it. This PR added desubscription.

@VysotskiVadim VysotskiVadim requested a review from a team as a code owner December 12, 2023 14:52
Copy link

github-actions bot commented Dec 12, 2023

Changelog

Features

  • Added billing explanation logs. Now Navigation SDK explains in the logs why certain Active Guidance or Free Drive Trip session started/stopped/paused/resumed. Billing explanations have [BillingExplanation] prefix in the logcat. [#7710](https://github.com/mapbox/mapbox-navigation-android/pull/7710)

Bug fixes and improvements

  • Fixed leak of CarAppLifecycleOwner on every copilot start. [#7669](https://github.com/mapbox/mapbox-navigation-android/pull/7669)

Known issues ⚠️

Other changes

  • Added Polish translation for UI elements.
Android Auto Changelog

Features

Bug fixes and improvements

  • The app is now considered as the one in active navigation only when an active route is set to MapboxNavigation. Previously it was always considered active. [#7366](https://github.com/mapbox/mapbox-navigation-android/pull/7366)
  • When Android Auto host tells the app to stop active navigation because another app starts navigating, now SDK will enter FreeDrive mode instead of stopping trip session completely. [#7366](https://github.com/mapbox/mapbox-navigation-android/pull/7366)

@@ -172,6 +178,7 @@ internal class MapboxCopilotImpl(
fun stop() {
unregisterUserFeedbackCallback(userFeedbackCallback)
appLifecycleOwner.lifecycle.removeObserver(foregroundBackgroundLifecycleObserver)
appLifecycleOwnerCleanupAction()
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's also nullify it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

appLifecycleOwnerCleanupAction is val and created once per MapboxCopilotInstance to cleanup resources acquired in constructor.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, but no one stops you from invoking stop twice. Now doing this action the second time is a no-op, but what if it become asymmetrical? I don't see any potential side-effects of nullifying it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@dzinad , do I understand your worries correctly: somebody can call internal method onStop a few times, which is not currently possible via public API, so that listener of activity lifecycle will be unregistered twice, where second unregistration does nothing?
I don't see any dangerous consequences with current approach. Can you please help me understand what exactly can go wrong there?

Copy link
Contributor

Choose a reason for hiding this comment

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

I have a little bit another concern here. In case if MapboxNavigationApp.isSetup() is false, we call attachAllActivities only on MapboxCopilotImpl initialization, but call detachAllActivities when MapboxCopilotImpl.stop() is called. So there should be assumption that stop can be called only once. However, we're not protected from api misuse (even if it's internal class).

What if we will be assigning appLifecycleOwnerCleanupAction on start() call, and assign null on stop()?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What if we will be assigning appLifecycleOwnerCleanupAction on start() call, and assign null on stop()?

In that case a different misuse is possible. If onStart is called twice, one of cleanup actions will be lost.

I don't think it's worth putting effort in protecting internal API from misuse if the price is code complexity.

Copy link
Contributor

Choose a reason for hiding this comment

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

so that listener of activity lifecycle will be unregistered twice, where second unregistration does nothing?

It currently doesn't do nothing, it detaches all activities. It may result in being nothing, but I don't see any strong reason why we should rely on that. We can also add something else to appLifecycleOwnerCleanupAction, which won't be a no-op.

Ideally I'd move the setup to start and nullify it in stop (sth like what Dima suggested). And also add a check that start/stop is not invoked twice in a row (make the second invocation no-op).
But I don't find this critical. If we do that, we will reduce the risk of making an error in the future. But it's up to you whether you find this risk significant.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ideally I'd move the setup to start and nullify it in stop (sth like what Dima suggested). And also add a check that start/stop is not invoked twice in a row (make the second invocation no-op).

It's already implemented in MapboxCopilot. MapboxCopilot won't let MapboxCopilotImpl#onStart to be called 2 times. MapboxCopilotImpl could have been a private class if not unit tests which use it directly.

val application = applicationContext as Application
attachAllActivities(application)
appLifecycleOwnerCleanupAction = {
detachAllActivities(application)
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we can also add tests to make sure nothing is leaked anymore.

@VysotskiVadim VysotskiVadim requested a review from dzinad January 3, 2024 15:02
Copy link

codecov bot commented Jan 3, 2024

Codecov Report

Attention: 3 lines in your changes are missing coverage. Please review.

Comparison is base (659d1ed) 74.12% compared to head (25c0597) 74.12%.

Impacted file tree graph

@@             Coverage Diff              @@
##               main    #7669      +/-   ##
============================================
- Coverage     74.12%   74.12%   -0.01%     
  Complexity     6252     6252              
============================================
  Files           852      852              
  Lines         33696    33705       +9     
  Branches       4012     4012              
============================================
+ Hits          24977    24983       +6     
- Misses         7169     7172       +3     
  Partials       1550     1550              
Files Coverage Δ
...com/mapbox/navigation/copilot/MapboxCopilotImpl.kt 88.68% <100.00%> (+0.25%) ⬆️
...on/core/internal/lifecycle/CarAppLifecycleOwner.kt 82.24% <0.00%> (-2.38%) ⬇️

@VysotskiVadim VysotskiVadim added the needs backporting Requires cherry-picking to a currently running release branch label Jan 4, 2024
@VysotskiVadim VysotskiVadim removed the needs backporting Requires cherry-picking to a currently running release branch label Feb 15, 2024
@VysotskiVadim VysotskiVadim force-pushed the vv-cleanup-carapplifecycleowner branch from c184c4d to 25c0597 Compare February 15, 2024 17:02
@VysotskiVadim VysotskiVadim enabled auto-merge (squash) February 15, 2024 17:06
@VysotskiVadim VysotskiVadim merged commit 9e5e7ce into main Feb 15, 2024
38 of 44 checks passed
@VysotskiVadim VysotskiVadim deleted the vv-cleanup-carapplifecycleowner branch February 15, 2024 17:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants