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

Fix potential crash in connection state flow #6563

Conversation

Pururun
Copy link
Contributor

@Pururun Pururun commented Aug 5, 2024

I am not sure this happens in the actual app, but it is good to prevent future issues.


This change is Reviewable

@Pururun Pururun added the Android Issues related to Android label Aug 5, 2024
@Pururun Pururun requested review from Rawa and albin-mullvad August 5, 2024 08:10
Copy link

linear bot commented Aug 5, 2024

Copy link
Contributor

@Rawa Rawa left a comment

Choose a reason for hiding this comment

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

Reviewable status: 0 of 1 files reviewed, 1 unresolved discussion (waiting on @Pururun)


android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/util/ManagedChannel.kt line 26 at r1 (raw file):

        }

        awaitClose { cancel() }

I don't think this should be necessary? awaitClose block is called when the flow is cancelled, I believe this block should rather be to used to remove listeners etc.

I believe the problem we faced is that we don't have a way to unregister the notifyWhenStateChanged callback. Thus it would invoke it.resume(...) and possibly send(currentState) on a already cancelled flow.

So I believe the correct solution would be something along the lines of:

internal fun ManagedChannel.connectivityFlow(): Flow<ConnectivityState> {
    return callbackFlow {
        var currentState = getState(false)

        while (isActive) {
            // Check that we are active before sending
            send(currentState)
            currentState = suspendCancellableCoroutine {
                notifyWhenStateChanged(currentState) {
                    // If we are cancelled we don't try and invoke the it.resume
                    if (it.isActive) {
                        it.resume(getState(false))
                    }
                }
            }
        }
    }
}

Alternatively I believe the callback could possibly be, where lambda is the onCancellation callback:

                        it.resume(getState(false), { _ -> })

Copy link
Contributor

@Rawa Rawa left a comment

Choose a reason for hiding this comment

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

Reviewable status: 0 of 1 files reviewed, 1 unresolved discussion (waiting on @Pururun)


android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/util/ManagedChannel.kt line 26 at r1 (raw file):

Previously, Rawa (David Göransson) wrote…

I don't think this should be necessary? awaitClose block is called when the flow is cancelled, I believe this block should rather be to used to remove listeners etc.

I believe the problem we faced is that we don't have a way to unregister the notifyWhenStateChanged callback. Thus it would invoke it.resume(...) and possibly send(currentState) on a already cancelled flow.

So I believe the correct solution would be something along the lines of:

internal fun ManagedChannel.connectivityFlow(): Flow<ConnectivityState> {
    return callbackFlow {
        var currentState = getState(false)

        while (isActive) {
            // Check that we are active before sending
            send(currentState)
            currentState = suspendCancellableCoroutine {
                notifyWhenStateChanged(currentState) {
                    // If we are cancelled we don't try and invoke the it.resume
                    if (it.isActive) {
                        it.resume(getState(false))
                    }
                }
            }
        }
    }
}

Alternatively I believe the callback could possibly be, where lambda is the onCancellation callback:

                        it.resume(getState(false), { _ -> })

Clarification: With second alternative I don't believe the if (it.isActive) is needed.

@Pururun Pururun force-pushed the fix-potential-crash-in-connection-state-function-droid-1224 branch from a2649ec to 068354d Compare August 9, 2024 12:37
Copy link
Contributor

@Rawa Rawa left a comment

Choose a reason for hiding this comment

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

Reviewed 1 of 1 files at r2, all commit messages.
Reviewable status: all files reviewed, 1 unresolved discussion (waiting on @Pururun)

@Pururun Pururun requested a review from Rawa August 9, 2024 13:06
Copy link
Contributor

@Rawa Rawa left a comment

Choose a reason for hiding this comment

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

Reviewable status: :shipit: complete! all files reviewed, all discussions resolved

Copy link
Collaborator

@albin-mullvad albin-mullvad left a comment

Choose a reason for hiding this comment

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

:lgtm:

Reviewed all commit messages.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved

@albin-mullvad albin-mullvad force-pushed the fix-potential-crash-in-connection-state-function-droid-1224 branch from 068354d to 0f02d8f Compare August 13, 2024 08:45
@albin-mullvad albin-mullvad force-pushed the fix-potential-crash-in-connection-state-function-droid-1224 branch from 0f02d8f to 62e16e8 Compare August 15, 2024 08:27
@albin-mullvad albin-mullvad merged commit 90cb3c3 into main Aug 15, 2024
24 checks passed
@albin-mullvad albin-mullvad deleted the fix-potential-crash-in-connection-state-function-droid-1224 branch August 15, 2024 08:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Android Issues related to Android
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants