home-assistant / android

:iphone: Home Assistant Companion for Android
https://companion.home-assistant.io/
Apache License 2.0
2.15k stars 609 forks source link

Wear OS home screen occasionally crashes with concurrent modification exception #3325

Open dshokouhi opened 1 year ago

dshokouhi commented 1 year ago

Home Assistant Android app version(s): latest beta for both

Android version(s):

Phone: 13 Watch: 11

Device model(s):

Pixel 7 pro Pixel watch

Home Assistant version:

2023.1.x

Last working Home Assistant release (if known):

n/a

Description of problem, include YAML if issue is related to notifications:

Sometimes the wear OS app will load and then crash within a few seconds after showing the areas.

Companion App Logs:

2023-02-09 09:40:54.755 16190-16190 AndroidRuntime          io....stant.companion.android.debug  E  FATAL EXCEPTION: main
                                                                                                    Process: io.homeassistant.companion.android.debug, PID: 16190
                                                                                                    java.util.ConcurrentModificationException
                                                                                                        at androidx.compose.runtime.snapshots.StateListIterator.validateModification(SnapshotStateList.kt:278)
                                                                                                        at androidx.compose.runtime.snapshots.StateListIterator.next(SnapshotStateList.kt:257)
                                                                                                        at io.homeassistant.companion.android.home.views.MainViewKt$MainView$1$3$1.invoke(MainView.kt:259)
                                                                                                        at io.homeassistant.companion.android.home.views.MainViewKt$MainView$1$3$1.invoke(MainView.kt:76)
                                                                                                        at androidx.wear.compose.material.ScalingLazyColumnKt$ScalingLazyColumn$1$1$2$1.invoke(ScalingLazyColumn.kt:425)
                                                                                                        at androidx.wear.compose.material.ScalingLazyColumnKt$ScalingLazyColumn$1$1$2$1.invoke(ScalingLazyColumn.kt:410)
                                                                                                        at androidx.compose.foundation.lazy.LazyListItemProviderKt$rememberLazyListItemProvider$1$itemProviderState$1.invoke(LazyListItemProvider.kt:53)
                                                                                                        at androidx.compose.foundation.lazy.LazyListItemProviderKt$rememberLazyListItemProvider$1$itemProviderState$1.invoke(LazyListItemProvider.kt:52)
                                                                                                        at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:2139)
                                                                                                        at androidx.compose.runtime.DerivedSnapshotState.currentRecord(DerivedState.kt:161)
                                                                                                        at androidx.compose.runtime.DerivedSnapshotState.getCurrentValue(DerivedState.kt:231)
                                                                                                        at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.recordInvalidation(SnapshotStateObserver.kt:419)
                                                                                                        at androidx.compose.runtime.snapshots.SnapshotStateObserver$applyObserver$1.invoke(SnapshotStateObserver.kt:42)
                                                                                                        at androidx.compose.runtime.snapshots.SnapshotStateObserver$applyObserver$1.invoke(SnapshotStateObserver.kt:38)
                                                                                                        at androidx.compose.runtime.snapshots.MutableSnapshot.apply(Snapshot.kt:748)
                                                                                                        at androidx.compose.runtime.Recomposer.applyAndCheck(Recomposer.kt:1108)
                                                                                                        at androidx.compose.runtime.Recomposer.performRecompose(Recomposer.kt:1476)
                                                                                                        at androidx.compose.runtime.Recomposer.access$performRecompose(Recomposer.kt:125)
                                                                                                        at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:534)
                                                                                                        at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:503)
                                                                                                        at androidx.compose.ui.platform.AndroidUiFrameClock$withFrameNanos$2$callback$1.doFrame(AndroidUiFrameClock.android.kt:34)
                                                                                                        at androidx.compose.ui.platform.AndroidUiDispatcher.performFrameDispatch(AndroidUiDispatcher.android.kt:109)
                                                                                                        at androidx.compose.ui.platform.AndroidUiDispatcher.access$performFrameDispatch(AndroidUiDispatcher.android.kt:41)
                                                                                                        at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.doFrame(AndroidUiDispatcher.android.kt:69)
                                                                                                        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:970)
                                                                                                        at android.view.Choreographer.doCallbacks(Choreographer.java:796)
                                                                                                        at android.view.Choreographer.doFrame(Choreographer.java:727)
                                                                                                        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:957)
                                                                                                        at android.os.Handler.handleCallback(Handler.java:938)
                                                                                                        at android.os.Handler.dispatchMessage(Handler.java:99)
                                                                                                        at android.os.Looper.loop(Looper.java:223)
                                                                                                        at android.app.ActivityThread.main(ActivityThread.java:7651)
                                                                                                        at java.lang.reflect.Method.invoke(Native Method)
                                                                                                        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
                                                                                                        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
                                                                                                        Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [androidx.compose.runtime.PausableMonotonicFrameClock@21b070f, androidx.compose.ui.platform.MotionDurationScaleImpl@642c39c, StandaloneCoroutine{Cancelling}@8af01a5, AndroidUiDispatcher@fa2297a]

Screenshot or video of problem:

Additional information:

This does not occur when Show Only Favorites is enabled. The problem area is suspected to be in updateEntityDomains as that method is not used as often when the show only favorites option is enabled.

Using androids layout inspector I can see the recomposition counts for everything increases anytime there is any update which might be a bit inefficient

image

dshokouhi commented 1 year ago

Looks like this error is also in the play console. We have a few thousand users getting impacted by this. first appearance of this error is Beta 3044

dshokouhi commented 11 months ago

Most recent crash log

Exception java.util.ConcurrentModificationException:
  at androidx.compose.runtime.snapshots.StateListIterator.validateModification (SnapshotStateList.kt:295)
  at androidx.compose.runtime.snapshots.StateListIterator.next (SnapshotStateList.kt:274)
  at io.homeassistant.companion.android.home.views.MainViewKt$MainView$1$3$1.invoke (MainView.kt:382)
  at io.homeassistant.companion.android.home.views.MainViewKt$MainView$1$3$1.invoke (MainView.kt:77)
  at androidx.wear.compose.material.ScalingLazyColumnKt$ScalingLazyColumn$1$1$2$1.invoke (ScalingLazyColumn.kt:425)
  at androidx.wear.compose.material.ScalingLazyColumnKt$ScalingLazyColumn$1$1$2$1.invoke (ScalingLazyColumn.kt:410)
  at androidx.compose.foundation.lazy.LazyListItemProviderKt$rememberLazyListItemProvider$1$itemProviderState$1.invoke (LazyListItemProvider.kt:54)
  at androidx.compose.foundation.lazy.LazyListItemProviderKt$rememberLazyListItemProvider$1$itemProviderState$1.invoke (LazyListItemProvider.kt:53)
  at androidx.compose.runtime.snapshots.Snapshot$Companion.observe (Snapshot.kt:2200)
  at androidx.compose.runtime.DerivedSnapshotState.currentRecord (DerivedState.kt:161)
  at androidx.compose.runtime.DerivedSnapshotState.getCurrentValue (DerivedState.kt:231)
  at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.recordInvalidation (SnapshotStateObserver.kt:523)
  at androidx.compose.runtime.snapshots.SnapshotStateObserver.drainChanges (SnapshotStateObserver.kt:66)
  at androidx.compose.runtime.snapshots.SnapshotStateObserver.access$drainChanges (SnapshotStateObserver.kt:38)
  at androidx.compose.runtime.snapshots.SnapshotStateObserver$applyObserver$1.invoke (SnapshotStateObserver.kt:45)
  at androidx.compose.runtime.snapshots.SnapshotStateObserver$applyObserver$1.invoke (SnapshotStateObserver.kt:43)
  at androidx.compose.runtime.snapshots.MutableSnapshot.apply (Snapshot.kt:748)
  at androidx.compose.runtime.Recomposer.applyAndCheck (Recomposer.kt:1124)
  at androidx.compose.runtime.Recomposer.performRecompose (Recomposer.kt:1483)
  at androidx.compose.runtime.Recomposer.access$performRecompose (Recomposer.kt:124)
  at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke (Recomposer.kt:541)
  at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke (Recomposer.kt:510)
  at androidx.compose.ui.platform.AndroidUiFrameClock$withFrameNanos$2$callback$1.doFrame (AndroidUiFrameClock.android.kt:34)
  at androidx.compose.ui.platform.AndroidUiDispatcher.performFrameDispatch (AndroidUiDispatcher.android.kt:109)
  at androidx.compose.ui.platform.AndroidUiDispatcher.access$performFrameDispatch (AndroidUiDispatcher.android.kt:41)
  at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.doFrame (AndroidUiDispatcher.android.kt:69)
  at android.view.Choreographer$CallbackRecord.run (Choreographer.java:996)
  at android.view.Choreographer.doCallbacks (Choreographer.java:796)
  at android.view.Choreographer.doFrame (Choreographer.java:727)
  at android.view.Choreographer$FrameDisplayEventReceiver.run (Choreographer.java:983)
  at android.os.Handler.handleCallback (Handler.java:938)
  at android.os.Handler.dispatchMessage (Handler.java:99)
  at android.os.Looper.loop (Looper.java:246)
  at android.app.ActivityThread.main (ActivityThread.java:7690)
  at java.lang.reflect.Method.invoke
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:593)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:995)
slovdahl commented 10 months ago

Happened for me in the development environment too:

2023-08-17 22:15:21.478 21395-21395 AndroidRuntime          io....stant.companion.android.debug  E  FATAL EXCEPTION: main
                                                                                                    Process: io.homeassistant.companion.android.debug, PID: 21395
                                                                                                    java.util.ConcurrentModificationException
                                                                                                        at androidx.compose.runtime.snapshots.StateListIterator.validateModification(SnapshotStateList.kt:295)
                                                                                                        at androidx.compose.runtime.snapshots.StateListIterator.next(SnapshotStateList.kt:274)
                                                                                                        at io.homeassistant.companion.android.home.views.MainViewKt$MainView$1$3$1.invoke(MainView.kt:263)
                                                                                                        at io.homeassistant.companion.android.home.views.MainViewKt$MainView$1$3$1.invoke(MainView.kt:77)
                                                                                                        at androidx.wear.compose.material.ScalingLazyColumnKt$ScalingLazyColumn$1$1$2$1.invoke(ScalingLazyColumn.kt:425)
                                                                                                        at androidx.wear.compose.material.ScalingLazyColumnKt$ScalingLazyColumn$1$1$2$1.invoke(ScalingLazyColumn.kt:410)
                                                                                                        at androidx.compose.foundation.lazy.LazyListItemProviderKt$rememberLazyListItemProvider$1$itemProviderState$1.invoke(LazyListItemProvider.kt:54)
                                                                                                        at androidx.compose.foundation.lazy.LazyListItemProviderKt$rememberLazyListItemProvider$1$itemProviderState$1.invoke(LazyListItemProvider.kt:53)
                                                                                                        at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:2200)
                                                                                                        at androidx.compose.runtime.DerivedSnapshotState.currentRecord(DerivedState.kt:161)
                                                                                                        at androidx.compose.runtime.DerivedSnapshotState.getCurrentValue(DerivedState.kt:231)
                                                                                                        at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.recordInvalidation(SnapshotStateObserver.kt:523)
                                                                                                        at androidx.compose.runtime.snapshots.SnapshotStateObserver.drainChanges(SnapshotStateObserver.kt:66)
                                                                                                        at androidx.compose.runtime.snapshots.SnapshotStateObserver.access$drainChanges(SnapshotStateObserver.kt:38)
                                                                                                        at androidx.compose.runtime.snapshots.SnapshotStateObserver$applyObserver$1.invoke(SnapshotStateObserver.kt:45)
                                                                                                        at androidx.compose.runtime.snapshots.SnapshotStateObserver$applyObserver$1.invoke(SnapshotStateObserver.kt:43)
                                                                                                        at androidx.compose.runtime.snapshots.SnapshotKt.advanceGlobalSnapshot(Snapshot.kt:1768)
                                                                                                        at androidx.compose.runtime.snapshots.SnapshotKt.advanceGlobalSnapshot(Snapshot.kt:1779)
                                                                                                        at androidx.compose.runtime.snapshots.SnapshotKt.access$advanceGlobalSnapshot(Snapshot.kt:1)
                                                                                                        at androidx.compose.runtime.snapshots.GlobalSnapshot.notifyObjectsInitialized$runtime_release(Snapshot.kt:1337)
                                                                                                        at androidx.compose.runtime.snapshots.Snapshot$Companion.notifyObjectsInitialized(Snapshot.kt:551)
                                                                                                        at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:641)
                                                                                                        at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:510)
                                                                                                        at androidx.compose.ui.platform.AndroidUiFrameClock$withFrameNanos$2$callback$1.doFrame(AndroidUiFrameClock.android.kt:34)
                                                                                                        at androidx.compose.ui.platform.AndroidUiDispatcher.performFrameDispatch(AndroidUiDispatcher.android.kt:109)
                                                                                                        at androidx.compose.ui.platform.AndroidUiDispatcher.access$performFrameDispatch(AndroidUiDispatcher.android.kt:41)
                                                                                                        at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.doFrame(AndroidUiDispatcher.android.kt:69)
                                                                                                        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:996)
                                                                                                        at android.view.Choreographer.doCallbacks(Choreographer.java:796)
                                                                                                        at android.view.Choreographer.doFrame(Choreographer.java:727)
                                                                                                        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:983)
                                                                                                        at android.os.Handler.handleCallback(Handler.java:938)
                                                                                                        at android.os.Handler.dispatchMessage(Handler.java:99)
                                                                                                        at android.os.Looper.loop(Looper.java:246)
                                                                                                        at android.app.ActivityThread.main(ActivityThread.java:7690)
                                                                                                        at java.lang.reflect.Method.invoke(Native Method)
                                                                                                        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:593)
                                                                                                        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:995)
                                                                                                        Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [androidx.compose.runtime.PausableMonotonicFrameClock@8127c81, androidx.compose.ui.platform.MotionDurationScaleImpl@e948126, StandaloneCoroutine{Cancelling}@df8c167, AndroidUiDispatcher@a81eb14]
JBou commented 9 months ago

This problem is already reported in the compose issue tracker: https://issuetracker.google.com/issues/272334463

Maybe you can add a +1 vote there in the upper right, or even better add a comment that it affects this app and a few thousand users, so the issue gets a bit more attention and the suggested solution gets implemented.