Skip to content

fix: added null check for mediaNotificationControllerInfo in BaseSess…#752

Open
eddyizm wants to merge 2 commits into
developmentfrom
crash/NPE-mediaNotificationControllerInfo
Open

fix: added null check for mediaNotificationControllerInfo in BaseSess…#752
eddyizm wants to merge 2 commits into
developmentfrom
crash/NPE-mediaNotificationControllerInfo

Conversation

@eddyizm

@eddyizm eddyizm commented Jun 11, 2026

Copy link
Copy Markdown
Owner

resolves #751

  1. Fixed NullPointerException: Added a null check for mediaNotificationControllerInfo in BaseSessionCallback.updateMediaNotificationCustomLayout to prevent crashes during race conditions
    where a player event fires while no notification controller is connected.
  2. Resolved Listener Leak: refactored BaseSessionCallback to use a single Player.Listener instance that is registered only once during the first onConnect call, preventing the accumulation of redundant listeners.
  3. Established Testing Foundation:
    • Added junit, mockito-core, and mockito-kotlin dependencies to app/build.gradle.
    • Created BaseSessionCallbackTest.kt with unit tests that reproduce the reported NPE and verify the listener management logic.

Summary by CodeRabbit

  • Bug Fixes

    • Improved null safety when handling media session controller information.
    • Enhanced player switching logic for seamless transitions.
  • Tests

    • Added comprehensive unit tests for media session callback handling.
  • Chores

    • Updated build configuration to Java 17.
    • Added unit testing dependencies.

…ionCallback.updateMediaNotificationCustomLayout
@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0886da0a-2a12-4d5d-a971-9026d89f8eed

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch crash/NPE-mediaNotificationControllerInfo

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
app/build.gradle (1)

137-140: Validate security/compatibility of added test deps

  • junit:junit:4.13.2 is outside the reported vulnerable range (>= 4.7, < 4.13.1); no advisory for 4.13.2.
  • No security advisories were returned for org.mockito:mockito-core:5.12.0 or org.mockito.kotlin:mockito-kotlin:5.4.0.
  • mockito-kotlin:5.4.0 is compatible with mockito-core:5.12.0 (it declares that dependency).
  • Optional upgrade: Mockito core has a newer 5.x (5.23.0); upgrading may require coordinating the mockito-kotlin version (latest major is 6.x).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/build.gradle` around lines 137 - 140, The added test dependencies
(junit:junit:4.13.2, org.mockito:mockito-core:5.12.0,
org.mockito.kotlin:mockito-kotlin:5.4.0) are not flagged as vulnerable but
Mockito core has a newer 5.x (5.23.0); either explicitly upgrade
org.mockito:mockito-core to 5.23.0 and bump org.mockito.kotlin:mockito-kotlin to
a mutually compatible release (ensure mockito-kotlin declares or supports that
mockito-core version), or keep the current coordinates but add a short comment
in build.gradle explaining the versions were validated against advisories and
are intentionally retained; reference the dependency coordinates to locate and
change the declarations.
app/src/test/java/com/cappielloantonio/tempo/service/BaseSessionCallbackTest.kt (1)

70-87: ⚡ Quick win

Add test coverage for handlePlayerChanged called before onConnect.

The existing test verifies that handlePlayerChanged moves the listener between players, but it doesn't cover the initialization scenario where handlePlayerChanged might be called before any controller has connected (i.e., before onConnect sets currentSession).

Add a test to verify that handlePlayerChanged does not add the listener when currentSession is null:

🧪 Suggested test
`@Test`
fun handlePlayerChanged_doesNotAddListenerBeforeOnConnect() {
    val context = mock<Context>()
    val service = mock<BaseMediaService>()
    val player = mock<Player>()

    whenever(context.getString(anyInt())).thenReturn("mock_string")

    mockConstruction(SessionCommand::class.java).use {
        val callback = BaseSessionCallback(context, service)
        
        // Call handlePlayerChanged before onConnect (currentSession is null)
        callback.handlePlayerChanged(null, player)
        
        // Should NOT add listener yet
        verify(player, times(0)).addListener(any())
    }
}

This test will pass once the fix for handlePlayerChanged (returning early when currentSession == null) is applied.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@app/src/test/java/com/cappielloantonio/tempo/service/BaseSessionCallbackTest.kt`
around lines 70 - 87, handlePlayerChanged currently moves listeners between
players even when no controller session exists; update
BaseSessionCallback.handlePlayerChanged to return early if currentSession is
null (i.e., don't add a listener to the newPlayer when no session has been set
by onConnect), while still safely removing a listener from oldPlayer if
provided; reference the BaseSessionCallback.handlePlayerChanged method and the
currentSession field and ensure onConnect is the only place that sets
currentSession before listeners are added.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/main/java/com/cappielloantonio/tempo/service/BaseSessionCallback.kt`:
- Around line 157-160: handlePlayerChanged currently always adds playerListener
to newPlayer causing duplicate registrations when invoked before a controller
connects; change handlePlayerChanged (in BaseSessionCallback) to only move the
listener if currentSession is non-null (i.e., a controller has connected):
always remove playerListener from oldPlayer (if non-null), but only call
newPlayer.addListener(playerListener) when currentSession != null (so onConnect
remains responsible for the initial add to session.player). Reference symbols:
handlePlayerChanged, currentSession, onConnect, playerListener.

---

Nitpick comments:
In `@app/build.gradle`:
- Around line 137-140: The added test dependencies (junit:junit:4.13.2,
org.mockito:mockito-core:5.12.0, org.mockito.kotlin:mockito-kotlin:5.4.0) are
not flagged as vulnerable but Mockito core has a newer 5.x (5.23.0); either
explicitly upgrade org.mockito:mockito-core to 5.23.0 and bump
org.mockito.kotlin:mockito-kotlin to a mutually compatible release (ensure
mockito-kotlin declares or supports that mockito-core version), or keep the
current coordinates but add a short comment in build.gradle explaining the
versions were validated against advisories and are intentionally retained;
reference the dependency coordinates to locate and change the declarations.

In
`@app/src/test/java/com/cappielloantonio/tempo/service/BaseSessionCallbackTest.kt`:
- Around line 70-87: handlePlayerChanged currently moves listeners between
players even when no controller session exists; update
BaseSessionCallback.handlePlayerChanged to return early if currentSession is
null (i.e., don't add a listener to the newPlayer when no session has been set
by onConnect), while still safely removing a listener from oldPlayer if
provided; reference the BaseSessionCallback.handlePlayerChanged method and the
currentSession field and ensure onConnect is the only place that sets
currentSession before listeners are added.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a06edb33-c790-4667-abbd-b436d7d9530d

📥 Commits

Reviewing files that changed from the base of the PR and between 288e7e3 and df4cc88.

📒 Files selected for processing (5)
  • app/build.gradle
  • app/src/main/java/com/cappielloantonio/tempo/service/BaseMediaService.kt
  • app/src/main/java/com/cappielloantonio/tempo/service/BaseSessionCallback.kt
  • app/src/tempus/java/com/cappielloantonio/tempo/service/MediaService.kt
  • app/src/test/java/com/cappielloantonio/tempo/service/BaseSessionCallbackTest.kt

Comment thread app/build.gradle
Comment on lines 78 to 81
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I vaguely remember reading about this section when creating a new project. Unlike the kotlinOptions from below, this has to do with the compatibility of some libraries.

What worries me is that this change may bump the minSdk a few versions.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

For Java, "compatibility" just means "unwrap this nice method into the most rudimentary, backward-compatible byte code for the JVM."

If nothing happens, we are all good. But if something behaves strange or starts complaining about compatibility, this may be the reason.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Yeah it concerned me too but it was needed in order to implement the testing framework. I could have went down the wrong path as I am not very familiar with java testing myself.

This seemed to be the easiest path forward, tests succeed and I was able to do a successful build sequence. Currently testing this PR. A merge into dev and pre-release would be the next step to have more testers.

I am open to other options if changing this was the wrong way to get the tests working.
./gradlew test

I will also open another PR to add the tests to the release workflows as well.

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.

2 participants