firebase / firebase-android-sdk

Firebase Android SDK
https://firebase.google.com
Apache License 2.0
2.26k stars 573 forks source link

Firestore crashing on Android 4 in offline mode #904

Closed MattSkala closed 4 years ago

MattSkala commented 4 years ago

Steps to reproduce:

  1. Add a snapshot listener to a collection/document reference
  2. Launch the app on Android 4.x
  3. Turn the flight mode on
  4. The app crashes

The app works perfectly fine as long as the internet connection is available, so it should not be a TLS issue as suggested by the exception message.

Relevant Code:

10-12 21:31:55.423 3498-3575/com.fitifyworkouts.bodyweight.workoutapp.debug W/Firestore: (21.1.1) [OnlineStateTracker]: Could not reach Cloud Firestore backend. Connection failed 1 times. Most recent error: Status{code=UNAVAILABLE, description=null, cause=java.net.ConnectException: failed to connect to firestore.googleapis.com/2a00:1450:400e:809::200a (port 443): connect failed: EHOSTUNREACH (No route to host)
        at libcore.io.IoBridge.connect(IoBridge.java:114)
        at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:192)
        at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
        at java.net.Socket.startupSocket(Socket.java:567)
        at java.net.Socket.<init>(Socket.java:226)
        at javax.net.DefaultSocketFactory.createSocket(DefaultSocketFactory.java:51)
        at io.grpc.okhttp.OkHttpClientTransport$4.run(OkHttpClientTransport.java:541)
        at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:123)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
        at java.lang.Thread.run(Thread.java:841)
     Caused by: libcore.io.ErrnoException: connect failed: EHOSTUNREACH (No route to host)
        at libcore.io.Posix.connect(Native Method)
        at libcore.io.BlockGuardOs.connect(BlockGuardOs.java:85)
        at libcore.io.IoBridge.connectErrno(IoBridge.java:127)
        at libcore.io.IoBridge.connect(IoBridge.java:112)
        at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:192) 
        at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172) 
        at java.net.Socket.startupSocket(Socket.java:567) 
        at java.net.Socket.<init>(Socket.java:226) 
        at javax.net.DefaultSocketFactory.createSocket(DefaultSocketFactory.java:51) 
        at io.grpc.okhttp.OkHttpClientTransport$4.run(OkHttpClientTransport.java:541) 
        at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:123) 
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587) 
        at java.lang.Thread.run(Thread.java:841) 
    }
    This typically indicates that your device does not have a healthy Internet connection at the moment. The client will operate in offline mode until it is able to successfully connect to the backend.
10-12 21:31:55.743 3498-3498/com.fitifyworkouts.bodyweight.workoutapp.debug D/AndroidRuntime: Shutting down VM
10-12 21:31:55.743 3498-3498/com.fitifyworkouts.bodyweight.workoutapp.debug W/dalvikvm: threadid=1: thread exiting with uncaught exception (group=0x9ccbbb20)
10-12 21:31:55.753 3498-3498/com.fitifyworkouts.bodyweight.workoutapp.debug E/main: java.lang.IllegalStateException: The Cloud Firestore client failed to establish a secure connection. This is likely a problem with your app, rather than with Cloud Firestore itself. See https://bit.ly/2XFpdma for instructions on how to enable TLS on Android 4.x devices.
        at com.google.firebase.firestore.remote.AbstractStream.close(com.google.firebase:firebase-firestore@@21.1.1:278)
        at com.google.firebase.firestore.remote.AbstractStream.handleServerClose(com.google.firebase:firebase-firestore@@21.1.1:387)
        at com.google.firebase.firestore.remote.AbstractStream$StreamObserver.lambda$onClose$3(com.google.firebase:firebase-firestore@@21.1.1:151)
        at com.google.firebase.firestore.remote.AbstractStream$StreamObserver$$Lambda$4.run(com.google.firebase:firebase-firestore@@21.1.1)
        at com.google.firebase.firestore.remote.AbstractStream$CloseGuardedRunner.run(com.google.firebase:firebase-firestore@@21.1.1:67)
        at com.google.firebase.firestore.remote.AbstractStream$StreamObserver.onClose(com.google.firebase:firebase-firestore@@21.1.1:137)
        at com.google.firebase.firestore.remote.FirestoreChannel$1.onClose(com.google.firebase:firebase-firestore@@21.1.1:131)
        at io.grpc.PartialForwardingClientCallListener.onClose(PartialForwardingClientCallListener.java:39)
        at io.grpc.ForwardingClientCallListener.onClose(ForwardingClientCallListener.java:23)
        at io.grpc.ForwardingClientCallListener$SimpleForwardingClientCallListener.onClose(ForwardingClientCallListener.java:40)
        at io.grpc.internal.CensusStatsModule$StatsClientInterceptor$1$1.onClose(CensusStatsModule.java:700)
        at io.grpc.PartialForwardingClientCallListener.onClose(PartialForwardingClientCallListener.java:39)
        at io.grpc.ForwardingClientCallListener.onClose(ForwardingClientCallListener.java:23)
        at io.grpc.ForwardingClientCallListener$SimpleForwardingClientCallListener.onClose(ForwardingClientCallListener.java:40)
        at io.grpc.internal.CensusTracingModule$TracingClientInterceptor$1$1.onClose(CensusTracingModule.java:399)
        at io.grpc.internal.ClientCallImpl.closeObserver(ClientCallImpl.java:500)
        at io.grpc.internal.ClientCallImpl.access$300(ClientCallImpl.java:65)
        at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl.close(ClientCallImpl.java:592)
        at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl.access$700(ClientCallImpl.java:508)
        at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInContext(ClientCallImpl.java:632)
        at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37)
        at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:123)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
        at java.util.concurrent.FutureTask.run(FutureTask.java:237)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:152)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:265)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
        at com.google.firebase.firestore.util.AsyncQueue$SynchronizedShutdownAwareExecutor$DelayedStartFactory.run(com.google.firebase:firebase-firestore@@21.1.1:224)
        at java.lang.Thread.run(Thread.java:841)
     Caused by: java.net.ConnectException: failed to connect to firestore.googleapis.com/2a00:1450:400e:809::200a (port 443): connect failed: EHOSTUNREACH (No route to host)
        at libcore.io.IoBridge.connect(IoBridge.java:114)
        at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:192)
        at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
        at java.net.Socket.startupSocket(Socket.java:567)
        at java.net.Socket.<init>(Socket.java:226)
        at javax.net.DefaultSocketFactory.createSocket(DefaultSocketFactory.java:51)
        at io.grpc.okhttp.OkHttpClientTransport$4.run(OkHttpClientTransport.java:541)
        at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:123)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExec
MattSkala commented 4 years ago

The issue is likely in the following condition, specifically the check for ConnectException:

https://github.com/firebase/firebase-android-sdk/blob/76ffd966d4c677dd97883f53118e3becfc2f76c0/firebase-firestore/src/main/java/com/google/firebase/firestore/remote/Datastore.java#L244-L252

var-const commented 4 years ago

Thank you for the bug report! I'll try to reproduce, but upon a glance, it does seem that this error handler is overly broad.