One thing is true for all developers - that is, with wide enough distribution, if a user can do something with your app, they will. If you’re building apps for Android, you will eventually have users running your app on their Chromebook. This is great for those looking for a laptop-like experience with your Android app, but what do you do when your app crashes only on Chromebook?

The official ARC developer docs have some great materials on troubleshooting, involving putting the device into “Developer mode” and connecting adb. While that may be great for your development environment, it’s clearly not practical for end users. As it turns out, there’s a much easier way. Here’s what I did:

Have the user reproduce the issue. When the app crashes, they should select the “Send feedback” option. The feedback mechanism may not complete, but that’s okay since it will save feedback to the Chromebook logs.

Next, have the user put the following in the address bar of Chrome chrome://net-internals and then select “ChromeOS”.

Chrome net-internals

They can then click the “Store System and User Logs” button. After a few moments, a tarball containing logs will show up in their Downloads folder, e.g. combined-logs_20200222-081143.tar.gz Have them send that to you. In my case, the file was too big for email so I used a file sharing service (e.g. via Google Drive, Dropbox, etc.)

When you receive the tarball, extract it and open the file feedback/arc-bugreport. If you’re lucky, you’ll find a stack trace of your app crash in there. Mine looked like this:

02-22 08:04:13.732  1608  1608 I art     : Rejecting re-init on previously-failed class java.lang.Class<com.greensopinion.rideweather.activity.StatusActivity>: java.lang.LinkageError: Method boolean com.greensopinion.rideweather.activity.StatusActivity.isResumed() overrides final method in class Landroid/app/Activity; (declaration of 'com.greensopinion.rideweather.activity.StatusActivity' appears in /data/app/com.greensopinion.rideweather-1/base.apk:classes2.dex)
02-22 08:04:13.732  1608  1608 I art     :   at java.lang.Class java.lang.VMClassLoader.findLoadedClass!(java.lang.ClassLoader, java.lang.String) (VMClassLoader.java:-2)
02-22 08:04:13.732  1608  1608 I art     :   at java.lang.Class java.lang.ClassLoader.findLoadedClass(java.lang.String) (ClassLoader.java:742)
02-22 08:04:13.732  1608  1608 I art     :   at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String, boolean) (ClassLoader.java:362)
02-22 08:04:13.732  1608  1608 I art     :   at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String) (ClassLoader.java:312)
02-22 08:04:13.732  1608  1608 I art     :   at android.app.Activity android.app.Instrumentation.newActivity(java.lang.ClassLoader, java.lang.String, android.content.Intent) (Instrumentation.java:1078)
02-22 08:04:13.732  1608  1608 I art     :   at android.app.Activity android.app.ActivityThread.performLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent) (ActivityThread.java:2626)
02-22 08:04:13.732  1608  1608 I art     :   at void android.app.ActivityThread.handleLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent, java.lang.String) (ActivityThread.java:2800)
02-22 08:04:13.732  1608  1608 I art     :   at void android.app.ActivityThread.-wrap12(android.app.ActivityThread, android.app.ActivityThread$ActivityClientRecord, android.content.Intent, java.lang.String) (ActivityThread.java:-1)
02-22 08:04:13.732  1608  1608 I art     :   at void android.app.ActivityThread$H.handleMessage(android.os.Message) (ActivityThread.java:1527)
02-22 08:04:13.732  1608  1608 I art     :   at void android.os.Handler.dispatchMessage(android.os.Message) (Handler.java:102)
02-22 08:04:13.732  1608  1608 I art     :   at void android.os.Looper.loop() (Looper.java:154)
02-22 08:04:13.732  1608  1608 I art     :   at void android.app.ActivityThread.main(java.lang.String[]) (ActivityThread.java:6320)
02-22 08:04:13.732  1608  1608 I art     :   at java.lang.Object java.lang.reflect.Method.invoke!(java.lang.Object, java.lang.Object[]) (Method.java:-2)
02-22 08:04:13.732  1608  1608 I art     :   at void com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run() (ZygoteInit.java:891)
02-22 08:04:13.732  1608  1608 I art     :   at void com.android.internal.os.ZygoteInit.main(java.lang.String[]) (ZygoteInit.java:781)

You might wonder why I’d override a final method in android.app.Activity. Well, the short answer is that I made a mistake. For me the method only showed up on the Chromebook - i.e. not in the Android SDK, in the emulator or on my two Android test devices. It turns out that I should have known, see Fragment.isResumed()

With the stack trace in-hand, it’s a trivial matter to resolve the issue. Best of all, no “Developer mode” required on the Chromebook!