maui icon indicating copy to clipboard operation
maui copied to clipboard

Java.Lang.IllegalStateException after MainPage swap

Open czmirek opened this issue 1 year ago • 13 comments

Description

Exception Java.Lang.IllegalStateException is thrown when I try to change the Application.Current.MainPage after the browser opened with WebAuthenticator.Default.AuthenticateAsync is closed without any login.

Steps to Reproduce

  • Checkout the attached repo
  • Go to the example OIDC server https://openidconnect.net/ and copy the generated URL
  • Paste the URL into MainPage.xaml.cs line 16
  • Run app
  • Click the "Login" button
  • Close the browser, do not attempt to login at all
  • Exception is thrown on line 25

Link to public reproduction project repository

https://github.com/czmirek/maui_auth_mainpage_fail

Version with bug

7.0 (current)

Last version that worked well

Unknown/Other

Affected platforms

Android

Affected platform versions

Android 12, API 31, x86_64

Did you find any workaround?

Yes, see this commit in another branch of the repo. I figured out that MainActivity.OnResume must run first before MainPage is swapped. It looks like a race condition. The workaround is simple with TaskCompletionSource but I'm not sure if this is going to work in all cases.

Relevant log output

{Java.Lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at Java.Interop.JniEnvironment.InstanceMethods.CallIntMethod(JniObjectReference instance, JniMethodInfo method, JniArgumentValue* args) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/obj/Release/net7.0/JniEnvironment.g.cs:line 11725
at Java.Interop.JniPeerMembers.JniInstanceMethods.InvokeAbstractInt32Method(String encodedMember, IJavaPeerable self, JniArgumentValue* parameters) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods_Invoke.cs:line 492
at AndroidX.Fragment.App.BackStackRecord.Commit() in /Users/runner/work/1/s/generated/androidx.fragment.fragment/obj/Release/net6.0-android/generated/src/AndroidX.Fragment.App.BackStackRecord.cs:line 131
at Microsoft.Maui.Platform.NavigationRootManager.SetContentView(IView view) in D:\a\_work\1\s\src\Core\src\Platform\Android\Navigation\NavigationRootManager.cs:line 147
at Microsoft.Maui.Platform.NavigationRootManager.Connect(IView view, IMauiContext mauiContext) in D:\a\_work\1\s\src\Core\src\Platform\Android\Navigation\NavigationRootManager.cs:line 88
at Microsoft.Maui.Handlers.WindowHandler.CreateRootViewFromContent(IWindowHandler handler, IWindow window) in D:\a\_work\1\s\src\Core\src\Handlers\Window\WindowHandler.Android.cs:line 79
at Microsoft.Maui.Handlers.WindowHandler.MapContent(IWindowHandler handler, IWindow window) in D:\a\_work\1\s\src\Core\src\Handlers\Window\WindowHandler.Android.cs:line 24
at Microsoft.Maui.PropertyMapper`2.<>c__DisplayClass5_0[[Microsoft.Maui.IWindow, Microsoft.Maui, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[Microsoft.Maui.Handlers.IWindowHandler, Microsoft.Maui, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].<Add>b__0(IElementHandler h, IElement v) in D:\a\_work\1\s\src\Core\src\PropertyMapper.cs:line 172
at Microsoft.Maui.PropertyMapper.UpdatePropertyCore(String key, IElementHandler viewHandler, IElement virtualView) in D:\a\_work\1\s\src\Core\src\PropertyMapper.cs:line 47
at Microsoft.Maui.PropertyMapper.UpdateProperty(IElementHandler viewHandler, IElement virtualView, String property) in D:\a\_work\1\s\src\Core\src\PropertyMapper.cs:line 72
at Microsoft.Maui.Handlers.ElementHandler.UpdateValue(String property) in D:\a\_work\1\s\src\Core\src\Handlers\Element\ElementHandler.cs:line 87
at Microsoft.Maui.Controls.Window.OnPropertyChanged(String propertyName) in D:\a\_work\1\s\src\Controls\src\Core\HandlerImpl\Window\Window.Impl.cs:line 253
at Microsoft.Maui.Controls.BindableObject.SetValueActual(BindableProperty property, BindablePropertyContext context, Object value, Boolean currentlyApplying, SetValueFlags attributes, Boolean silent) in D:\a\_work\1\s\src\Controls\src\Core\BindableObject.cs:line 533
at Microsoft.Maui.Controls.BindableObject.SetValueCore(BindableProperty property, Object value, SetValueFlags attributes, SetValuePrivateFlags privateAttributes) in D:\a\_work\1\s\src\Controls\src\Core\BindableObject.cs:line 469
at Microsoft.Maui.Controls.BindableObject.SetValue(BindableProperty property, Object value, Boolean fromStyle, Boolean checkAccess) in D:\a\_work\1\s\src\Controls\src\Core\BindableObject.cs:line 391
at Microsoft.Maui.Controls.BindableObject.SetValue(BindableProperty property, Object value) in D:\a\_work\1\s\src\Controls\src\Core\BindableObject.cs:line 365
at Microsoft.Maui.Controls.Window.set_Page(Page value) in D:\a\_work\1\s\src\Controls\src\Core\HandlerImpl\Window\Window.Impl.cs:line 113
at Microsoft.Maui.Controls.Application.set_MainPage(Page value) in D:\a\_work\1\s\src\Controls\src\Core\Application.cs:line 107
at MauiApp1.MainPage.OnCounterClicked(Object sender, EventArgs e) in C:\Users\lesar\source\repos\maui_auth_mainpage_fail\MauiApp1\MainPage.xaml.cs:line 27  --- End of managed Java.Lang.IllegalStateException stack trace ---java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState	at androidx.fragment.app.FragmentManager.checkStateLoss(FragmentManager.java:1601)	at androidx.fragment.app.FragmentManager.enqueueAction(FragmentManager.java:1641)	at androidx.fragment.app.BackStackRecord.commitInternal(BackStackRecord.java:341)	at androidx.fragment.app.BackStackRecord.commit(BackStackRecord.java:306)	at crc6468b6408a11370c2f.WebAuthenticatorIntermediateActivity.n_onResume(Native Method)	at crc6468b6408a11370c2f.WebAuthenticatorIntermediateActivity.onResume(WebAuthenticatorIntermediateActivity.java:41)	at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1476)	at android.app.Activity.performResume(Activity.java:8191)	at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4747)	at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4790)	at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:54)	at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)	at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)	at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2210)	at android.os.Handler.dispatchMessage(Handler.java:106)	at android.os.Looper.loopOnce(Looper.java:201)	at android.os.Looper.loop(Looper.java:288)	at android.app.ActivityThread.main(ActivityThread.java:7839)	at java.lang.reflect.Method.invoke(Native Method)	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)  --- End of managed Java.Lang.IllegalStateException stack trace ---java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState	at androidx.fragment.app.FragmentManager.checkStateLoss(FragmentManager.java:1601)	at androidx.fragment.app.FragmentManager.enqueueAction(FragmentManager.java:1641)	at androidx.fragment.app.BackStackRecord.commitInternal(BackStackRecord.java:341)	at androidx.fragment.app.BackStackRecord.commit(BackStackRecord.java:306)	at crc6468b6408a11370c2f.WebAuthenticatorIntermediateActivity.n_onResume(Native Method)	at crc6468b6408a11370c2f.WebAuthenticatorIntermediateActivity.onResume(WebAuthenticatorIntermediateActivity.java:41)	at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1476)	at android.app.Activity.performResume(Activity.java:8191)	at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4747)	at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4790)	at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:54)	at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)	at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)	at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2210)	at android.os.Handler.dispatchMessage(Handler.java:106)	at android.os.Looper.loopOnce(Looper.java:201)	at android.os.Looper.loop(Looper.java:288)	at android.app.ActivityThread.main(ActivityThread.java:7839)	at java.lang.reflect.Method.invoke(Native Method)	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)}

czmirek avatar Nov 19 '22 16:11 czmirek

We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.

ghost avatar Nov 21 '22 15:11 ghost

For anyone finding this, I found this issue is also related to https://github.com/dotnet/sdk/issues/28880 and for the code to work in release, you need to add <AndroidLinkResources>false</AndroidLinkResources> to your MAUI csproj.

czmirek avatar Nov 23 '22 20:11 czmirek

Missclick closed, sorry :(

czmirek avatar Nov 23 '22 20:11 czmirek

I have the same issue, when setting a new mainpage when a timer ticks and the user need to re-authenticate.

rkops-bd avatar Dec 01 '22 16:12 rkops-bd

Looks very similar to #11259

jfversluis avatar Dec 02 '22 08:12 jfversluis

@rkops-bd can you attach a repro? I'm not able to reproduce this with the original posters sample.

PureWeen avatar Dec 10 '22 17:12 PureWeen

@czmirek I was able to reproduce your issue. From my reproduction, it seems like the issue happens because the MainPage swap is happening while the app is stopped so your workaround is waiting for the app to re-resume. That's something we should fix but I'm just leaving a note here with my observations for other people that come across this.

PureWeen avatar Dec 10 '22 18:12 PureWeen

Verified this on Visual Studio Enterprise 17.7.0 Preview 1.0. Repro on Android 13.0-API33 with provided Project: MauiApp1.zip

XamlTest avatar Jun 07 '23 06:06 XamlTest

Anybody who's having trouble with this, here is a workaround, it's not great, but app stopped crashing:

public class App : Microsoft.Maui.Controls.Application
{
    public static bool IsSleeping { get; set; }
    
    protected override void OnStart()
    {
        IsSleeping = false;
        base.OnStart();
    }

    protected override void OnSleep()
    {
        IsSleeping = true;
        base.OnSleep();
    }

    protected override void OnResume()
    {
        IsSleeping = false;
        base.OnResume();
    }

    public void SetMainPage()
    {
       MainThread.BeginInvokeOnMainThread(async () =>
       {
           while (IsSleeping)
           {
               await Task.Delay(500);
           }

           Current.MainPage = new MainPage();
      });
    }
}

r-work avatar Nov 16 '23 16:11 r-work

FYI: https://github.com/dotnet/maui/issues/19304

I think this issue invokes the same codepaths as whatever is causing this to fail. Once this issue gets addressed, we should reassess the above issue to verify if it's fixed too (My guess is that it should, since I think it's the same underlying cause). If it's not, we should reopen it.

drasticactions avatar Dec 08 '23 04:12 drasticactions

I found out that the only workaround to temporarily "fix" it now is setting LaunchMode = LaunchMode.SingleInstance instead of SingleTop.

datvm avatar Dec 08 '23 16:12 datvm

Possible related https://github.com/dotnet/maui/issues/15788

PureWeen avatar Jan 10 '24 19:01 PureWeen

Forwarding a comment PureWeen made in a possibly related issue https://github.com/dotnet/maui/issues/18159#issuecomment-1883645003

Gekidoku avatar Jan 12 '24 10:01 Gekidoku

I found the cause in Android. https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/app/FragmentManager.java

Error Message : "Can not perform this action after onSaveInstanceState"

private void checkStateLoss() {
    if (mStateSaved) {
        throw new IllegalStateException(
                "Can not perform this action after onSaveInstanceState");
    }
    if (mNoTransactionsBecause != null) {
        throw new IllegalStateException(
                "Can not perform this action inside of " + mNoTransactionsBecause);
    }
}

pulmuone avatar Feb 01 '24 05:02 pulmuone

Even if you just minimize the app while it is on splash screen it will crash (just setting the main page inside of App's constructor. Same if you set it inside IWindowCreator).

VNGames avatar Feb 08 '24 15:02 VNGames

This seems to only happen when a debugger is attached?

danielftz avatar Feb 21 '24 18:02 danielftz

This seems to only happen when a debugger is attached?

No. It crashes on release builds too. and on debug without debugger attached too. Just open the app and while on the splashscreen minimize it and then return back (maybe wait a few sec before returning back).

It can be fixed by changing commit() to commitAllowingStateLoss() when fragments are switched. And I remember I saw Xamarin does exactly that and it doesn't have such problem. I don't know the consequences of such fix though.

VNGames avatar Feb 21 '24 18:02 VNGames