Usage of Non-SDK interfaces. Desugaring

190 Views Asked by At

I am checking my app in Firebase, before sending it to Play Store, and I receive this warnings:

Usage of non-SDK interfaces
Ljava/util/Collections$SynchronizedCollection;-><init>(Ljava/util/Collection;Ljava/lang/Object;)V
Ljava/util/Collections$SynchronizedCollection;->mutex:Ljava/lang/Object;
Ljava/util/Collections$SynchronizedCollection;->c:Ljava/util/Collection;
Ljava/util/Collections$SynchronizedSet;-><init>(Ljava/util/Set;Ljava/lang/Object;)V

They are marked in hiddenapi_flags as "greylist-max-o". I have isolated the problem and noticed than the problem is when using Desugaring library (I included it to use some Time features as ZonedDateTime, ZoneOffset,etc in Api<26). My question is: With these warnings can I update the app to Google Play? Is there any alternative, in older Apis, to use similar functions to ZonedDateTime.getAvailableZoneIds and so? Thank you.

1

There are 1 best solutions below

0
On

I sent the question to Issuetracker.google.com and they are investigating the issue. This is their answer:

COMMENTS All comments [email protected][email protected] #2May 17, 2023 11:02AM

This is caused by the code in DesugarCollections.java. The code is trying to acquire the lock of the underlying SynchronizedCollection through reflection of fields and methods which are internal to the platform.

If this reflection does not succeed, then the methods removeIf, forEach, replaceAll and sort will access the internal collection field directly.

According to the hiddenapi-flags.csv the mutex field is not accessible after Android 8.

This is not optimal, and could lead to undefined behavior. I suggest that you avoid using SynchronizedCollection and instead use concurrent data structures if at all possible.

[email protected][email protected] #3May 17, 2023 11:04AM

Tested the following code on emulators, and it succeeds on Android O_MR1 and fails on Android P. ALso tested on an Android U phone where it also fails.

public class SynchronizedCollectionReflection {
    public static String test() {
        Class<? extends Collection> SYNCHRONIZED_COLLECTION;
        SYNCHRONIZED_COLLECTION = Collections.synchronizedCollection(new ArrayList<>()).getClass();
        Class<? extends List> SYNCHRONIZED_LIST;
        SYNCHRONIZED_LIST = Collections.synchronizedList(new LinkedList<>()).getClass();
        Field MUTEX_FIELD;
        MUTEX_FIELD = getField(SYNCHRONIZED_COLLECTION, "mutex");
        if (MUTEX_FIELD != null) {
            MUTEX_FIELD.setAccessible(true);
        } else {
            return "Failed MUTEX_FIELD";
        }
        Field COLLECTION_FIELD;
        COLLECTION_FIELD = getField(SYNCHRONIZED_COLLECTION, "c");
        if (COLLECTION_FIELD != null) {
            COLLECTION_FIELD.setAccessible(true);
        } else {
            return "Failed COLLECTION_FIELD";
        }
        Class<? extends Set> synchronizedSet = Collections.synchronizedSet(new HashSet<>()).getClass();
        Constructor<? extends Set> SYNCHRONIZED_SET_CONSTRUCTOR;
        SYNCHRONIZED_SET_CONSTRUCTOR = getConstructor(synchronizedSet, Set.class, Object.class);
        if (SYNCHRONIZED_SET_CONSTRUCTOR != null) {
            SYNCHRONIZED_SET_CONSTRUCTOR.setAccessible(true);
        } else {
            return "Failed SYNCHRONIZED_SET_CONSTRUCTOR";
        }
        Constructor<? extends Collection> SYNCHRONIZED_COLLECTION_CONSTRUCTOR;
        SYNCHRONIZED_COLLECTION_CONSTRUCTOR =
                getConstructor(SYNCHRONIZED_COLLECTION, Collection.class, Object.class);
        if (SYNCHRONIZED_COLLECTION_CONSTRUCTOR != null) {
            SYNCHRONIZED_COLLECTION_CONSTRUCTOR.setAccessible(true);
        } else {
            return "Failed SYNCHRONIZED_COLLECTION_CONSTRUCTOR";
        }
        return "SUCCESS!";
    }

    private static Field getField(Class<?> clazz, String name) {
        try {
            return clazz.getDeclaredField(name);
        } catch (NoSuchFieldException e) {
            // For Desugar: Some fields are not available on instrumented devices.
            return null;
        }
    }

    private static <E> Constructor<? extends E> getConstructor(
            Class<? extends E> clazz, Class<?>... parameterTypes) {
        try {
            return clazz.getDeclaredConstructor(parameterTypes);
        } catch (NoSuchMethodException e) {
            // For Desugar: Some constructors are not available on instrumented devices.
            return null;
        }
    }
}

[email protected][email protected] #4May 17, 2023 11:05AM Reassigned to [email protected].

We should take another look at this, and consider failing the operations which cannot be safely desugared.