diff --git a/safe-parcel/src/main/java/org/microg/safeparcel/SafeParcelUtil.java b/safe-parcel/src/main/java/org/microg/safeparcel/SafeParcelUtil.java index 7295b2c..08a684e 100644 --- a/safe-parcel/src/main/java/org/microg/safeparcel/SafeParcelUtil.java +++ b/safe-parcel/src/main/java/org/microg/safeparcel/SafeParcelUtil.java @@ -273,6 +273,9 @@ private static void writeField(SafeParcelable object, Parcel parcel, Field field case String: SafeParcelWriter.write(parcel, fieldId, (String) field.get(object), mayNull); break; + case Enum: + SafeParcelWriter.write(parcel, fieldId, field, (Enum)field.get(object), mayNull); + break; } field.setAccessible(acc); } @@ -379,15 +382,51 @@ private static void readField(SafeParcelable object, Parcel parcel, Field field, break; case Byte: break; + case Enum: + readEnum(object, parcel, field, header); + break; default: throw new IllegalStateException("Unexpected value: " + SafeParcelType.fromField(field)); } field.setAccessible(acc); } + private static final Map, Map>> statefulOrdinalsMap = new HashMap<>(); + + private static void readEnum(SafeParcelable object, Parcel parcel, Field field, int header) throws IllegalAccessException { + + Map> statefulOrdinals = null; + synchronized (SafeParcelUtil.class) { + statefulOrdinals = statefulOrdinalsMap.get(field.getType()); + if (statefulOrdinals == null) { + statefulOrdinals = new HashMap<>(); + for (Object enumObject : field.getType().getEnumConstants()) { + Enum enumConstant = (Enum) enumObject; + Field declaredField = null; + try { + declaredField = field.getType().getDeclaredField(enumConstant.name()); + } catch (NoSuchFieldException e) { + throw new IllegalStateException("Invalid enum value: " + SafeParcelType.fromField(field)); + } + SafeParcelable.Field annotation = declaredField.getAnnotation(SafeParcelable.Field.class); + if (annotation != null) { + statefulOrdinals.put(annotation.value(), enumConstant); + } else { + throw new IllegalStateException("Invalid enum value (no annotation): " + SafeParcelType.fromField(field)); + } + } + statefulOrdinalsMap.put(field.getType(), statefulOrdinals); + } + } + + int statefulOrdinal = SafeParcelReader.readInt(parcel, header); + Enum enumConstant = statefulOrdinals.get(statefulOrdinal); + field.set(object, enumConstant); + } + private enum SafeParcelType { Parcelable, Binder, StringList, List, Bundle, ParcelableArray, StringArray, ByteArray, - Interface, IntArray, Integer, Long, Boolean, Float, Double, String, Map, Byte; + Interface, IntArray, Integer, Long, Boolean, Float, Double, String, Map, Byte, Enum; public static SafeParcelType fromField(Field field) { Class clazz = field.getType(); @@ -428,6 +467,8 @@ public static SafeParcelType fromField(Field field) { return Byte; if (clazz == java.lang.String.class) return String; + if (Enum.class.isAssignableFrom(clazz)) + return Enum; throw new RuntimeException("Type is not yet usable with SafeParcelUtil: " + clazz); } } diff --git a/safe-parcel/src/main/java/org/microg/safeparcel/SafeParcelWriter.java b/safe-parcel/src/main/java/org/microg/safeparcel/SafeParcelWriter.java index 02f5ac8..7c8b41f 100644 --- a/safe-parcel/src/main/java/org/microg/safeparcel/SafeParcelWriter.java +++ b/safe-parcel/src/main/java/org/microg/safeparcel/SafeParcelWriter.java @@ -10,6 +10,7 @@ import android.os.Parcel; import android.os.Parcelable; +import java.lang.reflect.Field; import java.util.List; import java.util.Map; @@ -279,4 +280,27 @@ public static void write(Parcel parcel, int fieldId, IBinder val, boolean mayNul } } + public static void write(Parcel parcel, int fieldId, Field field, Enum val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + Field enumConstantField = null; + try { + enumConstantField = field.getType().getDeclaredField(val.name()); + } catch (NoSuchFieldException e) { + throw new RuntimeException("Invalid enum: field is absent"); + } + SafeParcelable.Field annotation = enumConstantField.getAnnotation(SafeParcelable.Field.class); + if (annotation != null) { + parcel.writeInt(annotation.value()); + } else { + throw new RuntimeException("Invalid enum: Annotation is missing"); + } + finishObjectHeader(parcel, start); + } + } + } diff --git a/safe-parcel/src/test/java/org/microg/safeparcel/test/auto/AutoTests.java b/safe-parcel/src/test/java/org/microg/safeparcel/test/auto/AutoTests.java index 3af73ad..88034bd 100644 --- a/safe-parcel/src/test/java/org/microg/safeparcel/test/auto/AutoTests.java +++ b/safe-parcel/src/test/java/org/microg/safeparcel/test/auto/AutoTests.java @@ -44,6 +44,7 @@ public void foo() { foo1.barList.add(foo1.bar); foo1.barArray = new Bar[]{foo1.bar}; foo1.intList.add(2); + foo1.enumType = Type.OFFLINE; Foo foo2 = remarshal(foo1, Foo.CREATOR); assertEquals(foo1, foo2); } diff --git a/safe-parcel/src/test/java/org/microg/safeparcel/test/auto/Foo.java b/safe-parcel/src/test/java/org/microg/safeparcel/test/auto/Foo.java index f7f3967..7fdb64f 100644 --- a/safe-parcel/src/test/java/org/microg/safeparcel/test/auto/Foo.java +++ b/safe-parcel/src/test/java/org/microg/safeparcel/test/auto/Foo.java @@ -33,6 +33,8 @@ class Foo extends AutoSafeParcelable { public Bar[] barArray = new Bar[0]; @Field(9) public List intList = new ArrayList<>(); + @Field(10) + public Type enumType = Type.OFFLINE; private Foo() { } @@ -57,6 +59,7 @@ public String toString() { ", barList=" + barList + ", barArray=" + Arrays.toString(barArray) + ", intList=" + intList + + ", enumType=" + enumType + '}'; } @@ -73,12 +76,13 @@ public boolean equals(Object o) { Objects.equals(bar, foo.bar) && Objects.equals(barList, foo.barList) && Arrays.equals(barArray, foo.barArray) && - Objects.equals(intList, foo.intList); + Objects.equals(intList, foo.intList) && + Objects.equals(enumType, foo.enumType); } @Override public int hashCode() { - int result = Objects.hash(versionCode, intPrivate, string, stringList, stringStringMap, bar, barList, intList); + int result = Objects.hash(versionCode, intPrivate, string, stringList, stringStringMap, bar, barList, intList, enumType); result = 31 * result + Arrays.hashCode(barArray); return result; } diff --git a/safe-parcel/src/test/java/org/microg/safeparcel/test/auto/Type.java b/safe-parcel/src/test/java/org/microg/safeparcel/test/auto/Type.java new file mode 100644 index 0000000..1ea5938 --- /dev/null +++ b/safe-parcel/src/test/java/org/microg/safeparcel/test/auto/Type.java @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2019, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.safeparcel.test.auto; + +import org.microg.safeparcel.SafeParcelable; + +public enum Type { + @SafeParcelable.Field(1) + ONLINE, + @SafeParcelable.Field(2) + OFFLINE +}