Flutter Missing Plugin and Crash Remedies

Feb 10, 2021

In "The Dreaded MissingPluginException" I described one type of bug which causes the app to get stuck at a blank white screen during startup. However if there’s any other exception happening during the early initialization phase which trips Flutter then it’ll also result in the same white screen ANR (Application Not Responding), but I’ll talk about that later.

Several people asked, so first I’d want to provide a recipe which prevents MissingPluginException and other related crashes which result from R8 being too eager to ‘whack’ classes and code.

  1. Look up android/app/src/main/java/io/flutter/pluins/GeneratedPluginRegistrant.java.
  2. Focus on the only function registerWith.
  3. Iterate over all the lines of that function each one belongs to a plugin.
  4. Identify the class name of the plugin from that line. For example if the line is flutterEngine.getPlugins().add(new com.pauldemarco.flutter_blue.FlutterBluePlugin()); then the class path will be "com.pauldemarco.flutter_blue". basically you need to leave off the last camel-case part from the path.
  5. Add a keep class rule for that class path into the Pro Guard configuration file.
  6. If you don’t have a ProGuard configuration file then create the file android/app/proguard-rules.pro and that will be your rule file.
  7. Add a keepclassmembernames rule for the same class path into the Pro Guard configuration file.
  8. After the iteration is done add rules for the LifecycleObserver.
  9. Add some more extra rules.

For example here is the line in the GeneratedPluginRegistrant.java:

    flutterEngine.getPlugins().add(new com.pauldemarco.flutter_blue.FlutterBluePlugin());

then it’ll mean these ProGuard rules:

-keep class com.pauldemarco.flutter_blue.** { *; }
-keepclassmembernames class com.pauldemarco.flutter_blue.* { *; }

For the most complete example here is my full GeneratedPluginRegistrant.java:

@Keep
public final class GeneratedPluginRegistrant {
  public static void registerWith(@NonNull FlutterEngine flutterEngine) {
    ShimPluginRegistry shimPluginRegistry = new ShimPluginRegistry(flutterEngine);
    flutterEngine.getPlugins().add(new io.flutter.plugins.deviceinfo.DeviceInfoPlugin());
    flutterEngine.getPlugins().add(new com.mr.flutter.plugin.filepicker.FilePickerPlugin());
    flutterEngine.getPlugins().add(new com.pauldemarco.flutter_blue.FlutterBluePlugin());
    flutterEngine.getPlugins().add(new io.flutter.plugins.flutter_plugin_android_lifecycle.FlutterAndroidLifecyclePlugin());
    flutterEngine.getPlugins().add(new dev.flutter.plugins.integration_test.IntegrationTestPlugin());
    flutterEngine.getPlugins().add(new io.flutter.plugins.pathprovider.PathProviderPlugin());
    flutterEngine.getPlugins().add(new io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin());
    flutterEngine.getPlugins().add(new com.tekartik.sqflite.SqflitePlugin());
    flutterEngine.getPlugins().add(new name.avioli.unilinks.UniLinksPlugin());
    flutterEngine.getPlugins().add(new io.flutter.plugins.urllauncher.UrlLauncherPlugin());
    flutterEngine.getPlugins().add(new creativemaybeno.wakelock.WakelockPlugin());
      com.twwm.share_files_and_screenshot_widgets.ShareFilesAndScreenshotWidgetsPlugin.registerWith(shimPluginRegistry.registrarFor("com.twwm.share_files_and_screenshot_widgets.ShareFilesAndScreenshotWidgetsPlugin"));
  }
}

My related ProGuard configuration file:

-verbose
-keep class androidx.lifecycle.** { *; }
-keepclassmembernames class androidx.lifecycle.* { *; }
-keepclassmembers class * implements androidx.lifecycle.LifecycleObserver {
    <init>(...);
}
-keepclassmembers class * extends androidx.lifecycle.ViewModel {
    <init>(...);
}
-keepclassmembers class androidx.lifecycle.Lifecycle$State { *; }
-keepclassmembers class androidx.lifecycle.Lifecycle$Event { *; }
-keepclassmembers class * {
    @androidx.lifecycle.OnLifecycleEvent *;
}

# https://github.com/flutter/flutter/issues/78625#issuecomment-804164524
-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.** { *; }
-keep class io.flutter.util.** { *; }
-keep class io.flutter.view.** { *; }
-keep class io.flutter.** { *; }
-keep class io.flutter.plugins.** { *; }

# This is from iterating over GeneratedPLuginRegistrant.java [
-keep class com.pauldemarco.flutter_blue.** { *; }
-keepclassmembernames class com.pauldemarco.flutter_blue.* { *; }
-keep class io.flutter.plugins.deviceinfo.** { *; }
-keepclassmembernames class io.flutter.plugins.deviceinfo.** { *; }
-keep class com.mr.flutter.plugin.filepicker.** { *; }
-keepclassmembernames class com.mr.flutter.plugin.filepicker.** { *; }
-keep class io.flutter.plugins.flutter_plugin_android_lifecycle.** { *; }
-keepclassmembernames class io.flutter.plugins.flutter_plugin_android_lifecycle.** { *; }
-keep class dev.flutter.plugins.integration_test.** { *; }
-keepclassmembernames class dev.flutter.plugins.integration_test.** { *; }
-keep class io.flutter.plugins.pathprovider.** { *; }
-keepclassmembernames class io.flutter.plugins.pathprovider.** { *; }
-keep class com.twwm.share_files_and_screenshot_widgets.** { *; }
-keepclassmembernames class com.twwm.share_files_and_screenshot_widgets.** { *; }
-keep class io.flutter.plugins.sharedpreferences.** { *; }
-keepclassmembernames class io.flutter.plugins.sharedpreferences.** { *; }
-keep class com.tekartik.sqflite.** { *; }
-keepclassmembernames class com.tekartik.sqflite.** { *; }
-keep class name.avioli.unilinks.** { *; }
-keepclassmembernames class name.avioli.unilinks.** { *; }
-keep class io.flutter.plugins.urllauncher.** { *; }
-keepclassmembernames class io.flutter.plugins.urllauncher.** { *; }
-keep class creativemaybeno.wakelock.** { *; }
-keepclassmembernames class creativemaybeno.wakelock.** { *; }
# ] This is from iterating over GeneratedPLuginRegistrant.java

-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod

-keep class * extends com.google.protobuf.** { *; }
-keepclassmembernames class * extends com.google.protobuf.** { *; }

As you can see there’s quite a bit of extras there, but I rather go overkill than cause a release-only white screen ANR (Application Not Responding) again!

White Screen type ANR

Lately I also experienced a white screen type ANR (Application Not Responding) with a Huawei Mi Max 3. In that case it seems that some permission causes trouble. It didn’t matter if the bluetooth was turned on or not, but the white screen only came during the first start after the installation. Here is a related call stack from a LogCat:

04-06 22:57:04.995  1930  2164 I ActivityManager: Force stopping dev.csaba.track_my_indoor_exercise appid=10503 user=0: deletePackageX
04-06 22:57:05.601  1930  2218 I ActivityManager: Force stopping dev.csaba.track_my_indoor_exercise appid=10503 user=0: pkg removed
04-06 22:57:05.608  1930  2218 E HistoricalRegistry: Interaction before persistence initialized
04-06 22:57:05.626  1930  1930 I RoleManagerService: Granting default permissions...for user0
04-06 22:57:05.645  1930  2164 W BroadcastQueue: Background execution not allowed: receiving Intent { act=android.intent.action.PACKAGE_REMOVED dat=package:dev.csaba.track_my_indoor_exercise flg=0x4000010 (has extras) } to com.lbe.security.miui/com.lbe.security.service.BootReceiver
04-06 22:57:05.645  1930  2164 W BroadcastQueue: Background execution not allowed: receiving Intent { act=android.intent.action.PACKAGE_REMOVED dat=package:dev.csaba.track_my_indoor_exercise flg=0x4000010 (has extras) } to com.eset.ems2.gp/com.eset.commoncore.core.broadcast.CoreReceiver
04-06 22:57:05.646  1930  2218 I ActivityManager: Force stopping dev.csaba.track_my_indoor_exercise appid=10503 user=0: pkg removed
04-06 22:57:05.692  1930  2218 E AppOps  : checkOperation
04-06 22:57:05.692  1930  2218 E AppOps  : java.lang.SecurityException: Specified package dev.csaba.track_my_indoor_exercise under uid 10503 but it is really -1
04-06 22:57:05.692  1930  2218 E AppOps  : 	at com.android.server.appop.AppOpsService.verifyAndGetIsPrivileged(Unknown Source:123)
04-06 22:57:05.692  1930  2218 E AppOps  : 	at com.android.server.appop.AppOpsService.checkOperationUnchecked(Unknown Source:0)
04-06 22:57:05.692  1930  2218 E AppOps  : 	at com.android.server.appop.AppOpsService.checkOperationUnchecked(Unknown Source:6)
04-06 22:57:05.692  1930  2218 E AppOps  : 	at com.android.server.appop.AppOpsService.checkOperationImpl(Unknown Source:48)
04-06 22:57:05.692  1930  2218 E AppOps  : 	at com.android.server.appop.AppOpsService.checkOperationInternal(Unknown Source:6)
04-06 22:57:05.692  1930  2218 E AppOps  : 	at com.android.server.appop.AppOpsService.checkOperation(Unknown Source:1)
04-06 22:57:05.692  1930  2218 E AppOps  : 	at com.android.server.am.AutoStartManagerService.canRestartServiceLocked(Unknown Source:21)
04-06 22:57:05.692  1930  2218 E AppOps  : 	at com.android.server.am.AutoStartManagerService.canRestartServiceLocked(Unknown Source:1)
04-06 22:57:05.692  1930  2218 E AppOps  : 	at com.android.server.am.AutoStartManagerService.signalStopProcessesLocked(Unknown Source:6)
04-06 22:57:05.692  1930  2218 E AppOps  : 	at com.android.server.am.ProcessList.killPackageProcessesLocked(Unknown Source:194)
04-06 22:57:05.692  1930  2218 E AppOps  : 	at com.android.server.am.ActivityManagerService.forceStopPackageLocked(Unknown Source:215)
04-06 22:57:05.692  1930  2218 E AppOps  : 	at com.android.server.am.ActivityManagerService.broadcastIntentLocked(Unknown Source:1718)
04-06 22:57:05.692  1930  2218 E AppOps  : 	at com.android.server.am.ActivityManagerService.broadcastIntentLocked(Unknown Source:40)
04-06 22:57:05.692  1930  2218 E AppOps  : 	at com.android.server.am.ActivityManagerService.broadcastIntent(Unknown Source:81)
04-06 22:57:05.692  1930  2218 E AppOps  : 	at com.android.server.pm.PackageManagerService.doSendBroadcast(Unknown Source:142)
04-06 22:57:05.692  1930  2218 E AppOps  : 	at com.android.server.pm.PackageManagerService.lambda$sendPackageBroadcast$7$PackageManagerService(Unknown Source:34)
04-06 22:57:05.692  1930  2218 E AppOps  : 	at com.android.server.pm.-$$Lambda$PackageManagerService$O5iApY07YeJyXA8KUFVcxpCf1NI.run(Unknown Source:18)
04-06 22:57:05.692  1930  2218 E AppOps  : 	at android.os.Handler.handleCallback(Unknown Source:2)
04-06 22:57:05.692  1930  2218 E AppOps  : 	at android.os.Handler.dispatchMessage(Unknown Source:4)
04-06 22:57:05.692  1930  2218 E AppOps  : 	at android.os.Looper.loop(Unknown Source:242)
04-06 22:57:05.692  1930  2218 E AppOps  : 	at android.os.HandlerThread.run(Unknown Source:28)
04-06 22:57:05.692  1930  2218 E AppOps  : 	at com.android.server.ServiceThread.run(Unknown Source:12)
04-06 22:57:05.692  1930  2218 I AutoStartManagerService: MIUILOG- Reject RestartService packageName :dev.csaba.track_my_indoor_exercise uid : 10503

As we can see none of the call stack have any calls belonging to a code I could influence in any way (not even a plugin’s source I could fork and modify). I don’t have a definitive solution for this, but possibly I’ll try to use permission_handler or bluetooth_enable plugins to possibly get ahead of the race condition and secure a permission before the bug happens.

I can also see two NPEs (Null Pointer Exceptions) in the Play Store reports I haven’t ever been able to reproduce. They might be the reason for uninstalls.

Release-only NPE 1

java.lang.NullPointerException: 
  at com.pauldemarco.flutter_blue.FlutterBluePlugin$5.run (FlutterBluePlugin.java:10)
  at android.app.Activity.runOnUiThread (Activity.java:7145)
  at com.pauldemarco.flutter_blue.FlutterBluePlugin.invokeMethodUIThread (FlutterBluePlugin.java:7)
  at com.pauldemarco.flutter_blue.FlutterBluePlugin.access$400 (FlutterBluePlugin.java)
  at com.pauldemarco.flutter_blue.FlutterBluePlugin$2.onScanResult (FlutterBluePlugin.java:83)
  at android.bluetooth.le.BluetoothLeScanner$BleScanCallbackWrapper$1.run (BluetoothLeScanner.java:616)
  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:8506)
  at java.lang.reflect.Method.invoke (Method.java)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:602)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1130)

I’ll try to avoid this with a band-aid fix which aims for the effect because I’m not certain about the cause. There are some guesses and I opened up a PR (Pull Request) but I ended up forking the repository.

Release-only NPE 2

java.lang.NullPointerException: 
  at com.pauldemarco.flutter_blue.FlutterBluePlugin$5.run (FlutterBluePlugin.java:10)
  at android.os.Handler.handleCallback (Handler.java:873)
  at android.os.Handler.dispatchMessage (Handler.java:99)
  at android.os.Looper.loop (Looper.java:214)
  at android.app.ActivityThread.main (ActivityThread.java:7050)
  at java.lang.reflect.Method.invoke (Method.java)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:494)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:965)

I don’t have a clue for now how to cure that.

Comments loading...