java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.PackageManager

3,067

I got a hack that fixes the symptoms of the crash, at https://github.com/fmatosqg/system_alert_window/tree/hack_fix

In short, the plugin accesses activity when it's no longer available. My quick fix stores the activity.applicationContext (guaranteed to exist while the application is alive - even if it's only an overlay) and stores it in a static variable that may outlive the object from class SystemAlertWindowPlugin.

. . .
    private static Context staticContext;

. . .
    private boolean openApp(String packageName) {
        Intent launchIntent = staticContext.getPackageManager().getLaunchIntentForPackage(packageName);

        if (launchIntent != null) {
            // null pointer check in case package name was not found
            staticContext.startActivity(launchIntent);

            return true;
        }
        return false;
    }

I'd like to explain why this is not a proper fix but I'm short on time now and will try to get back later with the reasons. But I'm not an expert on flutter plugins and I wouldn't be sure on how the proper code is without spending lots more time understanding the plugin itself, which I probably won't be able to.

I did a very similar fix with a very similar crash on another plugin, I hope it can help guide you or the plugin author: https://github.com/hnvn/flutter_image_cropper/pull/167

Last, some piece of advice. Android changes a lot from version to version and I'd urge you to try this and other fixes on emulator running android 10 AND 11, since they're getting more and more restrictive on launching activities. I know about changes from background, but I'm not up to date in all the details, or if launching from overlay constitutes foreground or background for ANDROID purposes. A taste of it: https://www.reddit.com/r/androiddev/comments/dcleem/android_10_restricts_how_to_start_activity_from/

Share:
3,067
anonymous-dev
Author by

anonymous-dev

Updated on December 22, 2022

Comments

  • anonymous-dev
    anonymous-dev over 1 year

    Introduction

    I am running flutter v1.17.5 DeviceApps v1.0.10 and SystemAlertWindow v0.2.2+3 (intentially not the latest version). And I want to open my app from a system alert window that is running in the foreground, even when the app is closed.

    I am using the SystemOverlayWindow plugin and the plugin is a activity SystemAlertWindowPlugin.java

    In my Application.kt I register the plugins and pass the registry

    public class Application: FlutterApplication(), PluginRegistrantCallback {
    
       override fun onCreate() {
         super.onCreate();
         FlutterFirebaseMessagingService.setPluginRegistrant(this);
         SystemAlertWindowPlugin.setPluginRegistrant(this);
         createNotificationChannels();
         FlutterMain.startInitialization(this);
       }
    
       override fun registerWith(registry: PluginRegistry?) {
        if (!registry!!.hasPlugin("io.flutter.plugins.firebasemessaging")) {
          FirebaseMessagingPlugin.registerWith(registry!!.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"));
        }
        if (!registry!!.hasPlugin("in.jvapps.system_alert_window")) {
          SystemAlertWindowPlugin.registerWith(registry!!.registrarFor("in.jvapps.system_alert_window"));
        }
        if (!registry!!.hasPlugin("fr.g123k.deviceapps")) {
          DeviceAppsPlugin.registerWith(registry!!.registrarFor("fr.g123k.deviceapps"));
        }
       }
    

    I also register another plugin called DeviceApps. This is the DeviceAppsPlugin DeviceAppsPlugin.java

    Problem

    In short

    So the system overlay (which runs in the foreground) calls > the dart callback > invokes a method of the DeviceApps plugin > error occcurs

    Long version

    I have a static callback that is registered here and it gets called when I have a click interaction with the system alert window. But now I wan't to call the DeviceApps plugin in my dart code from the that static callback

    So the method channel will invoke this and that will run the static callback defined in dart.

    This is the static dart callback that is registered and called with a background channel

      static Future<void> systemOverlayOnClickListner(String tag) async {
        switch (tag) {
          case 'button_app_to_foreground':
            DeviceApps.openApp('com.companyname.appname'); // this is where I try to run the plugin
            await SystemAlertWindow.closeSystemWindow();
            break;
        }
    

    The callback will invoke a method of the DeviceApps plugin. And this causes problems because this method will try to get the package manager from the activity passed in it's constructor. But according to this error the activity is null.

    E/MethodChannel#g123k/device_apps(24210): Failed to handle method call
    E/MethodChannel#g123k/device_apps(24210): java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.PackageManager android.app.Activity.getPackageManager()' on a null object reference
    E/MethodChannel#g123k/device_apps(24210):   at fr.g123k.deviceapps.DeviceAppsPlugin.openApp(DeviceAppsPlugin.java:141)
    

    So it will invoke the getPackageManager() on a null object.

    The activity is only null when it is being called from this static callback that is called by the background channel. But not when I call it normally from the app scope. Why is this the case?

    Conslusion

    So in conclusion calling the plugin works fine when I call it from my app scope. But once the callback is called trough means of the background channel all of the sudden the activity is null.

    I cannot just start a isolate in my app and send a message to that from my callback like how its done here. Because I need this code to work when the app is closed, and a app scoped isolate doesn't run in the background.

    So how can I open my app from the callback?

    This is the full stack trace

    E/flutter (26735): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: PlatformException(error, Attempt to invoke virtual method 'android.content.pm.PackageManager android.app.Activity.getPackageManager()' on a null object reference, null)
    E/flutter (26735): #0      StandardMethodCodec.decodeEnvelope 
    package:flutter/…/services/message_codecs.dart:569
    E/flutter (26735): #1      MethodChannel._invokeMethod 
    package:flutter/…/services/platform_channel.dart:156
    E/flutter (26735): <asynchronous suspension>
    E/flutter (26735): #2      MethodChannel.invokeMethod 
    package:flutter/…/services/platform_channel.dart:329
    E/flutter (26735): #3      DeviceApps.openApp 
    package:device_apps/device_apps.dart:81
    E/flutter (26735): #4      SystemOverlayController.systemOverlayOnClickListner 
    package:appname/…/singletons/system_overlay_controller.dart:51
    E/flutter (26735): #5      callbackDispatcher.<anonymous closure> 
    package:system_alert_window/system_alert_window.dart:136
    E/flutter (26735): #6      MethodChannel._handleAsMethodCall 
    package:flutter/…/services/platform_channel.dart:409
    E/flutter (26735): #7      MethodChannel.setMethodCallHandler.<anonymous closure> 
    package:flutter/…/services/platform_channel.dart:377
    E/flutter (26735): #8      _DefaultBinaryMessenger.handlePlatformMessage 
    package:flutter/…/services/binding.dart:199
    E/flutter (26735): #9      _invoke3.<anonymous closure>  (dart:ui/hooks.dart:290:15)
    E/flutter (26735): #10     _rootRun  (dart:async/zone.dart:1184:13)
    E/flutter (26735): #11     _CustomZone.run  (dart:async/zone.dart:1077:19)
    E/flutter (26735): #12     _CustomZone.runGuarded  (dart:async/zone.dart:979:7)
    E/flutter (26735): #13     _invoke3  (dart:ui/hooks.dart:289:10)
    E/flutter (26735): #14     _dispatchPlatformMessage  (dart:ui/hooks.dart:164:5
    

    Example Repo

    I even tried to add the open app method directly to a forked version of the system alert window. And implemented it into a example repo that you can find here, take the branch called my-branch.

    https://github.com/michael-ottink/system_overlay_callback_null_activity

    But it throws the exact same error. Even if I use exactly the same activity. So it has something to do with the background channel I think.

    Run the app and then click the button to get the overlay. Bring the app to the background and click on open in the overlay. The error occurs.

    Extra info

    I think this is a similar issue only here they choose to not register the plugin because it is foreground only. Where in my case I want to fork either of these plugins and modify the them so that it also works in the background. How do I do that?