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

Fixing IllegalStateException: You should authenticate before using this when restoring app #6710

Merged
merged 6 commits into from
Aug 1, 2022

Conversation

ouchadam
Copy link
Contributor

@ouchadam ouchadam commented Aug 1, 2022

Type of change

  • Feature
  • Bugfix
  • Technical
  • Other :

Content

  • Always attempts to reinitialise the Session instance if we have a cached authenticated session.

Motivation and context

Fixes #6709 - crash when launching the app

  • Realm migration bg #6548 allowed the realm database migration to occur off the main thread but had the condition that the session must be initialised via the MainActivity, however we can't guarantee this screen will appear first as the system may attempt to restore other screens directly along with the Application at the same time

Screenshots / GIFs

*With a manual process restart after 5 seconds~

Before After
before-restart-process after-recreate-process

Tests

Add the following block to HomeActivity to force the process to be restarted with the current launch intent

fun onCreate() {
  Handler(Looper.getMainLooper()).postDelayed(Runnable {
      triggerRebirth(this@HomeActivity)
  }, 10000)
}

fun triggerRebirth() {
    val intent = Intent(this, HomeActivity::class.java)
            .apply {
                addFlags(Intent.FLAG_ACTIVITY_NEW_TASK and Intent.FLAG_ACTIVITY_CLEAR_TASK)
                putExtra(Mavericks.KEY_ARG, HomeActivityArgs(
                        clearNotification = true,
                        authenticationDescription = null,
                ))
            }
    startActivity(intent)
    Runtime.getRuntime().exit(0)
}
  • Notice the app crashes

Tested devices

  • Physical
  • Emulator
  • OS version(s):

@ouchadam ouchadam added the Z-NextRelease For issues and PRs which should be included in the NextRelease. label Aug 1, 2022
}

fun getSafeActiveSession(): Session? {
return activeSessionReference.get()
return runBlocking { getOrInitializeSession(startSync = true) }
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this is the crash fix, we'll always attempt to initialise the session (including perform a migration if needed)

the hope is that most users will only perform a migration via the MainActivity flow (tapping the launcher icon/notification) which will give them a loading bar, however it's possible the ROMs/OEMs can restore the activity and application directly

* @return the initialized Session or null when no authenticated sessions are available.
*/
suspend fun tryInitialize(readCurrentSession: () -> Session?, initializer: (Session) -> Unit): Session? {
return withContext(INITIALIZER_CONTEXT) {
Copy link
Contributor Author

@ouchadam ouchadam Aug 1, 2022

Choose a reason for hiding this comment

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

the initialisation is thread safe and will block or wait for any in progress initialisers to complete, this means that ideally all access to ActiveSessionHolder should be off the main thread to avoid possible ANRs, however this is quite a large refactor

the most common flow will be via the MainActivity which is off the main thread

Copy link
Member

Choose a reason for hiding this comment

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

Maybe safer to use a semaphore instead of a single thread dispatcher?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

will give it a try 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

works well! 💯 9114630

@@ -204,7 +198,7 @@ class VectorMessagingReceiver : MessagingReceiver() {
}
}

private fun getEventFastLane(session: Session, pushData: PushData) {
private suspend fun getEventFastLane(session: Session, pushData: PushData) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

reuses the suspension

@@ -80,18 +87,27 @@ class ActiveSessionHolder @Inject constructor(
}

fun hasActiveSession(): Boolean {
return activeSessionReference.get() != null
return activeSessionReference.get() != null || authenticationService.hasAuthenticatedSessions()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

an active session is now a in memory instance or cached authenticated session

this should become async with the hasAuthenticatedSessions being on the IO dispatcher, however as the active session is cached in memory almost straight away in most cases I've opt'd to avoid the breaking change (will hopefully be included in the realm-kotlin changes! 🤞 )

?: throw IllegalStateException("You should authenticate before using this")
}

suspend fun getOrInitializeSession(startSync: Boolean): Session? {
return activeSessionReference.get() ?: sessionInitializer.tryInitialize(readCurrentSession = { activeSessionReference.get() }) { session ->
Copy link
Contributor Author

Choose a reason for hiding this comment

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

we're eagerly reading from the in-memory cache to avoid a potential thread switch but it has the side effect of needing to query the in-memory value again within the initializer thread

@ouchadam ouchadam marked this pull request as ready for review August 1, 2022 14:57
@ouchadam ouchadam requested review from a team and ganfra and removed request for a team August 1, 2022 14:57
}

fun getSafeActiveSession(): Session? {
return activeSessionReference.get()
return runBlocking { getOrInitializeSession(startSync = true) }
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 getActiveSession and getSafeActiveSession would be suspending instead of using runBlocking however the change needed is much bigger than the scope of this PR

Copy link
Member

@ganfra ganfra left a comment

Choose a reason for hiding this comment

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

Some small remarks otherwise LGTM

@@ -55,11 +55,15 @@ class StartAppViewModel @AssistedInject constructor(
handleLongProcessing()
viewModelScope.launch(Dispatchers.IO) {
Copy link
Member

Choose a reason for hiding this comment

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

Can you take care of inject Dispatchers instead of using directly IO?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

will do 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

* @return the initialized Session or null when no authenticated sessions are available.
*/
suspend fun tryInitialize(readCurrentSession: () -> Session?, initializer: (Session) -> Unit): Session? {
return withContext(INITIALIZER_CONTEXT) {
Copy link
Member

Choose a reason for hiding this comment

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

Maybe safer to use a semaphore instead of a single thread dispatcher?

Copy link
Member

@ganfra ganfra left a comment

Choose a reason for hiding this comment

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

All good, thanks!

@ouchadam ouchadam merged commit cf5745e into hotfix/v1.4.31 Aug 1, 2022
@ouchadam ouchadam deleted the feature/adm/always-initialise-session branch August 1, 2022 16:49
@sonarqubecloud
Copy link

sonarqubecloud bot commented Aug 1, 2022

SonarCloud Quality Gate failed.    Quality Gate failed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 2 Code Smells

0.0% 0.0% Coverage
0.0% 0.0% Duplication

@bmarty bmarty mentioned this pull request Aug 19, 2022
15 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Z-Crash Z-NextRelease For issues and PRs which should be included in the NextRelease.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants