Android WebView causing RuntimeException at WebViewDelegate.getPackageId

Viewed 1146

I have a WebView in the layout xml of my MainActivity, to which I setWebViewClient(new WebViewClient()), followed by loadUrl(...) in onCreate.

Most of the time the app runs fine and the Web content is displayed correctly.

But in some cases, opening the app causes a crash. I've noticed that it happens when the app scheduled a PendingIntent broadcast with AlarmManager, which triggers a Notification whose contentIntent is a PendingIntent.getActivity set to launch MainActivity. But it happens only in the case when the user has removed the app from the stack of active apps in the meantime (Notification is visible, not yet clicked, and stack if apps cleared. So, app process probably stopped?).

Seemingly no other system modifications in between (in particular no app/system update, no playing around with user profiles or Chrome app.)

Stack trace:

java.lang.RuntimeException: 
  at android.webkit.WebViewDelegate.getPackageId (WebViewDelegate.java:164)
  at yj.a (PG:16)
  at xH.run (PG:14)
  at java.lang.Thread.run (Thread.java:764)

Occurs with Android 7.0 thru 9. Also, seems to have started to occur when I upgraded target SDK to 28.

I don't use explicitly a WebViewDelegate. It must be internal system code (hence the obfuscation).

By reading the source code of AOSP, it seems that the WebView fails to retrieve the package to which it belongs -- but why sometimes only!?

Any help appreciated! Thanks.

2 Answers

It has taken weeks of investigation on and off, but I've finally found why I'm seeing this issue. For me, it was just because I'd overridden the getResources() method in my application scope to use the current activity. Something like this:

public class MyApplication extends MultiDexApplication {
    private static MyApplication sInstance = null;
    private WeakReference<Activity> mCurrentActivity;

    public static MyApplication getInstance() {
        return sInstance;
    }

    public void setCurrentActivity(Activity activity) {
        mCurrentActivity = new WeakReference<>(activity);
    }

    public Activity getCurrentActivity() {
        return mCurrentActivity == null ? null : mCurrentActivity.get();
    }

    @Override
    public Resources getResources() {
        // This is a very BAD thing to do
        Activity activity = getCurrentActivity();
        if (activity != null) {
            return activity.getResources();
        }
        return super.getResources();
    }
}

This was done as a shortcut as I often wanted to get strings that were activity-specific, so I was calling MyApplication.getInstance().getResources().getString(). I now know this was a bad thing to do - removing my override of this method instantly fixed it.

So the key takeaway from this for me is that when the WebView is initialising, it MUST be able to get hold of the application context, so that the resources passed into WebViewDelegate.getPackageId() are at the application level - the activity context isn't enough, and causes this error.

As a side note - I wasn't even trying to add a WebView to my application. I was only actually using the following:

String userAgent = WebSettings.getDefaultUserAgent(this);

I was then passing this value into a custom media player that I'm using. Passing "this" as either application or activity scope always failed, due to my override.

Looking through documentation,you can see that error is thrown when package can't be found.Check your syntax ,package name and try again.

https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/webkit/WebViewDelegate.java (Line 164)

 /**
     * Returns the package id of the given {@code packageName}.
     */
    public int getPackageId(Resources resources, String packageName) {
        SparseArray<String> packageIdentifiers =
                resources.getAssets().getAssignedPackageIdentifiers();
        for (int i = 0; i < packageIdentifiers.size(); i++) {
            final String name = packageIdentifiers.valueAt(i);
            if (packageName.equals(name)) {
                return packageIdentifiers.keyAt(i);
            }
        }
        throw new RuntimeException("Package not found: " + packageName);
    }
Related