Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android P displaying API compatibility error message

Running an app built against SDK level 27 on Android P somewhat unpredictably displays the following dialog (the dialog title is the name of the application):

Mysterious dialog

Detected problems with API compatibility (visit g.co/dev/appcompat for more info)

The URL leads to this page about restrictions on non-SDK interfaces. My application doesn't use reflection itself, but it does use Gson.

There are no immediately obvious log messages in Logcat, except may messages such as:

Accessing hidden field Landroid/widget/AbsListView;->mIsChildViewEnabled:Z (light greylist, reflection)

like image 208
Paul Lammertsma Avatar asked Jul 06 '18 12:07

Paul Lammertsma


2 Answers

Turns out that one of my Gson models exposed a getter that returned File. Gson uses reflection to recursively inspect the fields of classes, and in doing so, violates the reflection of disallowed SDK interfaces.

Reading the restriction document linked in the question got me to take a closer look at the log messages, and sure enough, one caught my attention:

Accessing hidden field [...] (dark greylist, reflection)

I don't recall exactly the message, but the point here is that it was in the dark greylist.

I discovered this by targeting SDK level 28 and enabling the new StrictMode feature detectNonSdkApiUsage(), upon which my application would crash with a stack trace:

if (BuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
            .detectNonSdkApiUsage()
            .penaltyLog()
            .build());
}

The stack trace wasn't immediately insightful, but it pointed me in the right direction.

like image 150
Paul Lammertsma Avatar answered Oct 22 '22 15:10

Paul Lammertsma


in you application class in the method onCreate() init this method can be close this dialog.

private void closeAndroidPDialog(){
    try {
        Class aClass = Class.forName("android.content.pm.PackageParser$Package");
        Constructor declaredConstructor = aClass.getDeclaredConstructor(String.class);
        declaredConstructor.setAccessible(true);
    } catch (Exception e) {
        e.printStackTrace();
    }
    try {
        Class cls = Class.forName("android.app.ActivityThread");
        Method declaredMethod = cls.getDeclaredMethod("currentActivityThread");
        declaredMethod.setAccessible(true);
        Object activityThread = declaredMethod.invoke(null);
        Field mHiddenApiWarningShown = cls.getDeclaredField("mHiddenApiWarningShown");
        mHiddenApiWarningShown.setAccessible(true);
        mHiddenApiWarningShown.setBoolean(activityThread, true);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

but this is dangerous this way is just make you can't see the dialog.

like image 2
Ven Ren Avatar answered Oct 22 '22 16:10

Ven Ren