Merge commit '70ebc812a8092a8b760c05682c8eb7d16b2cfab2' into dev-release
diff --git a/src/library_desugar/desugar_jdk_libs.json b/src/library_desugar/desugar_jdk_libs.json
index 795458a..67139d3 100644
--- a/src/library_desugar/desugar_jdk_libs.json
+++ b/src/library_desugar/desugar_jdk_libs.json
@@ -2,7 +2,7 @@
   "configuration_format_version": 3,
   "group_id" : "com.tools.android",
   "artifact_id" : "desugar_jdk_libs",
-  "version": "1.1.8",
+  "version": "1.1.9",
   "required_compilation_api_level": 26,
   "synthesized_library_classes_package_prefix": "j$.",
   "support_all_callbacks_from_library": true,
@@ -258,4 +258,4 @@
     "-keepattributes InnerClasses",
     "-dontwarn sun.misc.Unsafe"
   ]
-}
\ No newline at end of file
+}
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs.json b/src/library_desugar/jdk11/desugar_jdk_libs.json
index 0180c03..05b48f0 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs.json
@@ -1,5 +1,5 @@
 {
-  "identifier": "com.tools.android:desugar_jdk_libs_configuration:2.0.2",
+  "identifier": "com.tools.android:desugar_jdk_libs_configuration:2.0.3",
   "configuration_format_version": 100,
   "required_compilation_api_level": 30,
   "synthesized_library_classes_package_prefix": "j$.",
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_minimal.json b/src/library_desugar/jdk11/desugar_jdk_libs_minimal.json
index fb728c1..03edf18 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_minimal.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_minimal.json
@@ -1,5 +1,5 @@
 {
-  "identifier": "com.tools.android:desugar_jdk_libs_configuration_minimal:2.0.2",
+  "identifier": "com.tools.android:desugar_jdk_libs_configuration_minimal:2.0.3",
   "configuration_format_version": 100,
   "required_compilation_api_level": 24,
   "synthesized_library_classes_package_prefix": "j$.",
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
index 787243a..667ecc9 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
@@ -1,5 +1,5 @@
 {
-  "identifier": "com.tools.android:desugar_jdk_libs_configuration_nio:2.0.2",
+  "identifier": "com.tools.android:desugar_jdk_libs_configuration_nio:2.0.3",
   "configuration_format_version": 100,
   "required_compilation_api_level": 30,
   "synthesized_library_classes_package_prefix": "j$.",
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java b/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
index 7599bb6..03ae2fd 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
@@ -22,6 +22,7 @@
 
   protected static final String ART_PROFILE_FLAG = "--art-profile";
   protected static final String MIN_API_FLAG = "--min-api";
+  protected static final String STARTUP_PROFILE_FLAG = "--startup-profile";
   protected static final String THREAD_COUNT_FLAG = "--thread-count";
   protected static final String MAP_DIAGNOSTICS = "--map-diagnostics";
   protected static final String DUMP_INPUT_TO_FILE = "--dumpinputtofile";
diff --git a/src/main/java/com/android/tools/r8/D8CommandParser.java b/src/main/java/com/android/tools/r8/D8CommandParser.java
index 4cf6711..7485cc8 100644
--- a/src/main/java/com/android/tools/r8/D8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/D8CommandParser.java
@@ -27,8 +27,6 @@
 
 public class D8CommandParser extends BaseCompilerCommandParser<D8Command, D8Command.Builder> {
 
-  static final String STARTUP_PROFILE_FLAG = "--startup-profile";
-
   private static final Set<String> OPTIONS_WITH_ONE_PARAMETER =
       ImmutableSet.of(
           "--output",
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 7a28ef3..1edde59 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -122,8 +122,8 @@
         mapIdProvider,
         null,
         false,
-        null,
-        null,
+        Collections.emptyList(),
+        Collections.emptyList(),
         classConflictResolver,
         cancelCompilationChecker);
     this.d8Command = d8Command;
diff --git a/src/main/java/com/android/tools/r8/R8CommandParser.java b/src/main/java/com/android/tools/r8/R8CommandParser.java
index f7db94e..f3fd1b8 100644
--- a/src/main/java/com/android/tools/r8/R8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/R8CommandParser.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.profile.art.ArtProfileConsumerUtils;
 import com.android.tools.r8.profile.art.ArtProfileProviderUtils;
+import com.android.tools.r8.profile.startup.StartupProfileProviderUtils;
 import com.android.tools.r8.utils.FlagFile;
 import com.android.tools.r8.utils.MapIdTemplateProvider;
 import com.android.tools.r8.utils.SourceFileTemplateProvider;
@@ -47,6 +48,7 @@
           "--map-id-template",
           "--source-file-template",
           ART_PROFILE_FLAG,
+          STARTUP_PROFILE_FLAG,
           THREAD_COUNT_FLAG);
 
   // Note: this must be a subset of OPTIONS_WITH_ONE_PARAMETER.
@@ -312,6 +314,10 @@
         builder.addArtProfileForRewriting(
             ArtProfileProviderUtils.createFromHumanReadableArtProfile(artProfilePath),
             ArtProfileConsumerUtils.create(rewrittenArtProfilePath));
+      } else if (arg.equals(STARTUP_PROFILE_FLAG)) {
+        Path startupProfilePath = Paths.get(nextArg);
+        builder.addStartupProfileProviders(
+            StartupProfileProviderUtils.createFromHumanReadableArtProfile(startupProfilePath));
       } else if (arg.startsWith("--")) {
         if (tryParseAssertionArgument(builder, arg, argsOrigin)) {
           continue;
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 919de23..5ebfb20 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -85,6 +85,7 @@
   private GraphLens codeLens = GraphLens.getIdentityLens();
   private GraphLens graphLens = GraphLens.getIdentityLens();
   private InitClassLens initClassLens;
+  private GraphLens kotlinMetadataLens = GraphLens.getIdentityLens();
   private NamingLens namingLens = NamingLens.getIdentityLens();
   private ProguardCompatibilityActions proguardCompatibilityActions;
   private RootSet rootSet;
@@ -594,6 +595,14 @@
     this.initClassLens = initClassLens;
   }
 
+  public GraphLens getKotlinMetadataLens() {
+    return kotlinMetadataLens;
+  }
+
+  public void setKotlinMetadataLens(GraphLens kotlinMetadataLens) {
+    this.kotlinMetadataLens = kotlinMetadataLens;
+  }
+
   public void setInitializedClassesInInstanceMethods(
       InitializedClassesInInstanceMethods initializedClassesInInstanceMethods) {
     this.initializedClassesInInstanceMethods = initializedClassesInInstanceMethods;
@@ -939,6 +948,7 @@
     firstUnappliedLens.withAlternativeParentLens(
         newMemberRebindingLens,
         () -> {
+          GraphLens appliedLensInModifiedLens = GraphLens.getIdentityLens();
           if (appView.hasLiveness()) {
             appView
                 .withLiveness()
@@ -957,7 +967,9 @@
           appView.setArtProfileCollection(
               appView.getArtProfileCollection().rewrittenWithLens(appView, lens));
           appView.setAssumeInfoCollection(
-              appView.getAssumeInfoCollection().rewrittenWithLens(appView, lens));
+              appView
+                  .getAssumeInfoCollection()
+                  .rewrittenWithLens(appView, lens, appliedLensInModifiedLens));
           if (appView.hasInitClassLens()) {
             appView.setInitClassLens(appView.initClassLens().rewrittenWithLens(lens));
           }
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index 9807531..2b7a222 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.analysis.value.SingleValue;
@@ -449,6 +450,13 @@
                   .recordFieldHasAbstractValue(fixedUpField, appView, abstractValue));
     }
 
+    public Builder clearDynamicType() {
+      return addBuildConsumer(
+          fixedUpField ->
+              OptimizationFeedbackSimple.getInstance()
+                  .markFieldHasDynamicType(fixedUpField, DynamicType.unknown()));
+    }
+
     public Builder clearAnnotations() {
       return setAnnotations(DexAnnotationSet.empty());
     }
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 579b21f..2dfb637 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -85,6 +85,16 @@
       "Ljava/lang/invoke/MethodHandles$Lookup;";
   public static final String dalvikAnnotationOptimizationPrefixString =
       "Ldalvik/annotation/optimization/";
+  public static final String androidUtilSparseArrayDescriptorString = "Landroid/util/SparseArray;";
+  public static final String androidContentResTypedArrayDescriptorString =
+      "Landroid/content/res/TypedArray;";
+  public static final String androidContentContentProviderClientDescriptorString =
+      "Landroid/content/ContentProviderClient;";
+  public static final String androidDrmDrmManagerClientDescriptorString =
+      "Landroid/drm/DrmManagerClient;";
+  public static final String androidMediaMediaDrmDescriptorString = "Landroid/media/MediaDrm;";
+  public static final String androidMediaMediaMetadataRetrieverDescriptorString =
+      "Landroid/media/MediaMetadataRetriever;";
 
   /** Set of types that may be synthesized during compilation. */
   private final Set<DexType> possibleCompilerSynthesizedTypes = Sets.newIdentityHashSet();
@@ -608,7 +618,17 @@
       createStaticallyKnownType("Landroid/util/Property;");
   public final DexType androidViewViewType = createStaticallyKnownType("Landroid/view/View;");
   public final DexType androidUtilSparseArrayType =
-      createStaticallyKnownType("Landroid/util/SparseArray;");
+      createStaticallyKnownType(androidUtilSparseArrayDescriptorString);
+  public final DexType androidContentResTypedArrayType =
+      createStaticallyKnownType(androidContentResTypedArrayDescriptorString);
+  public final DexType androidContentContentProviderClientType =
+      createStaticallyKnownType(androidContentContentProviderClientDescriptorString);
+  public final DexType androidDrmDrmManagerClientType =
+      createStaticallyKnownType(androidDrmDrmManagerClientDescriptorString);
+  public final DexType androidMediaMediaDrmType =
+      createStaticallyKnownType(androidMediaMediaDrmDescriptorString);
+  public final DexType androidMediaMediaMetadataRetrieverType =
+      createStaticallyKnownType(androidMediaMediaMetadataRetrieverDescriptorString);
 
   public final StringBuildingMethods stringBuilderMethods =
       new StringBuildingMethods(stringBuilderType);
@@ -662,6 +682,16 @@
   public final AndroidViewViewMembers androidViewViewMembers = new AndroidViewViewMembers();
   public final AndroidUtilSparseArrayMembers androidUtilSparseArrayMembers =
       new AndroidUtilSparseArrayMembers();
+  public final AndroidContentResTypedArrayMembers androidContentResTypedArrayMembers =
+      new AndroidContentResTypedArrayMembers();
+  public final AndroidContentContentProviderClientMembers
+      androidContentContentProviderClientMembers = new AndroidContentContentProviderClientMembers();
+  public final AndroidDrmDrmManagerClientMembers androidDrmDrmManagerClientMembers =
+      new AndroidDrmDrmManagerClientMembers();
+  public final AndroidMediaMediaDrmMembers androidMediaMediaDrmMembers =
+      new AndroidMediaMediaDrmMembers();
+  public final AndroidMediaMetadataRetrieverMembers androidMediaMetadataRetrieverMembers =
+      new AndroidMediaMetadataRetrieverMembers();
 
   // java.**
   public final JavaIoFileMembers javaIoFileMembers = new JavaIoFileMembers();
@@ -1135,6 +1165,7 @@
     }
   }
 
+  // android.util.SparseArray
   public class AndroidUtilSparseArrayMembers extends LibraryMembers {
     public final DexMethod put =
         createMethod(androidUtilSparseArrayType, createProto(voidType, intType, objectType), "put");
@@ -1143,6 +1174,46 @@
             androidUtilSparseArrayType, createProto(voidType, intType, objectType), setString);
   }
 
+  // android.content.res.TypedArray
+  public class AndroidContentResTypedArrayMembers extends LibraryMembers {
+    public final DexMethod recycle =
+        createMethod(androidContentResTypedArrayType, createProto(voidType), "recycle");
+    public final DexMethod close =
+        createMethod(androidContentResTypedArrayType, createProto(voidType), "close");
+  }
+
+  // android.content.ContentProviderClient
+  public class AndroidContentContentProviderClientMembers extends LibraryMembers {
+    public final DexMethod release =
+        createMethod(androidContentContentProviderClientType, createProto(voidType), "release");
+    public final DexMethod close =
+        createMethod(androidContentContentProviderClientType, createProto(voidType), "close");
+  }
+
+  // android.drm.DrmManagerClient
+  public class AndroidDrmDrmManagerClientMembers extends LibraryMembers {
+    public final DexMethod release =
+        createMethod(androidDrmDrmManagerClientType, createProto(voidType), "release");
+    public final DexMethod close =
+        createMethod(androidDrmDrmManagerClientType, createProto(voidType), "close");
+  }
+
+  // android.media.MediaDrm
+  public class AndroidMediaMediaDrmMembers extends LibraryMembers {
+    public final DexMethod release =
+        createMethod(androidMediaMediaDrmType, createProto(voidType), "release");
+    public final DexMethod close =
+        createMethod(androidMediaMediaDrmType, createProto(voidType), "close");
+  }
+
+  // android.media.MediaMetadataRetriever
+  public class AndroidMediaMetadataRetrieverMembers extends LibraryMembers {
+    public final DexMethod release =
+        createMethod(androidMediaMediaMetadataRetrieverType, createProto(voidType), "release");
+    public final DexMethod close =
+        createMethod(androidMediaMediaMetadataRetrieverType, createProto(voidType), "close");
+  }
+
   public class BooleanMembers extends BoxedPrimitiveMembers {
 
     public final DexField FALSE = createField(boxedBooleanType, boxedBooleanType, "FALSE");
diff --git a/src/main/java/com/android/tools/r8/graph/lens/GraphLens.java b/src/main/java/com/android/tools/r8/graph/lens/GraphLens.java
index bd71700..6f292cb 100644
--- a/src/main/java/com/android/tools/r8/graph/lens/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/lens/GraphLens.java
@@ -152,10 +152,11 @@
 
   public abstract DexField getRenamedFieldSignature(DexField originalField, GraphLens codeLens);
 
-  public final DexMember<?, ?> getRenamedMemberSignature(DexMember<?, ?> originalMember) {
+  public final DexMember<?, ?> getRenamedMemberSignature(
+      DexMember<?, ?> originalMember, GraphLens codeLens) {
     return originalMember.isDexField()
-        ? getRenamedFieldSignature(originalMember.asDexField())
-        : getRenamedMethodSignature(originalMember.asDexMethod());
+        ? getRenamedFieldSignature(originalMember.asDexField(), codeLens)
+        : getRenamedMethodSignature(originalMember.asDexMethod(), codeLens);
   }
 
   public final DexMethod getRenamedMethodSignature(DexMethod originalMethod) {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
index 55aad86d..a12baa2 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
@@ -139,7 +139,7 @@
       for (DexProgramClass root : subtypingForrest.getProgramRoots()) {
         subtypingForrest.traverseNodeDepthFirst(root, HashBiMap.create(), this::fixupProgramClass);
       }
-      new AnnotationFixer(lens).run(appView.appInfo().classes());
+      new AnnotationFixer(lens, appView.graphLens()).run(appView.appInfo().classes());
     }
     return lens;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
index 2a543a8..4df6b75 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
@@ -37,6 +37,7 @@
 import com.android.tools.r8.ir.optimize.ClassInitializerDefaultsOptimization.ClassInitializerDefaultsResult;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.ArrayUtils;
 import com.android.tools.r8.utils.Timing;
 import java.util.IdentityHashMap;
 import java.util.List;
@@ -262,13 +263,14 @@
       return null;
     }
 
+    DexType[] valuesTypes = new DexType[valuesSize];
     ObjectState[] valuesState = new ObjectState[valuesSize];
 
     if (invokeNewArray != null) {
       // Populate array values from filled-new-array values.
       List<Value> inValues = invokeNewArray.inValues();
       for (int i = 0; i < valuesSize; ++i) {
-        if (!updateEnumValueState(valuesState, i, inValues.get(i))) {
+        if (!updateEnumValueState(valuesState, valuesTypes, i, inValues.get(i))) {
           return null;
         }
       }
@@ -291,7 +293,7 @@
           if (index < 0 || index >= valuesSize) {
             return null;
           }
-          if (!updateEnumValueState(valuesState, index, arrayPut.value())) {
+          if (!updateEnumValueState(valuesState, valuesTypes, index, arrayPut.value())) {
             return null;
           }
           break;
@@ -323,18 +325,20 @@
       return null;
     }
 
-    for (ObjectState objectState : valuesState) {
-      if (objectState == null) {
-        return null;
-      }
+    if (ArrayUtils.contains(valuesState, null)) {
+      return null;
     }
+    // This should be guaranteed since valuesState and valuesTypes are updated at the same time.
+    assert !ArrayUtils.contains(valuesTypes, null);
 
     return appView
         .abstractValueFactory()
-        .createSingleFieldValue(valuesField.getReference(), new EnumValuesObjectState(valuesState));
+        .createSingleFieldValue(
+            valuesField.getReference(), new EnumValuesObjectState(valuesState, valuesTypes));
   }
 
-  private boolean updateEnumValueState(ObjectState[] valuesState, int index, Value value) {
+  private boolean updateEnumValueState(
+      ObjectState[] valuesState, DexType[] valuesTypes, int index, Value value) {
     Value root = value.getAliasedValue();
     if (root.isPhi()) {
       return false;
@@ -360,6 +364,8 @@
     if (valuesState[index] != null) {
       return false;
     }
+    assert definition.isNewInstance();
+    valuesTypes[index] = definition.asNewInstance().getType();
     valuesState[index] = objectState;
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/EnumValuesObjectState.java b/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/EnumValuesObjectState.java
index 3a50ca7..722dccc 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/EnumValuesObjectState.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/EnumValuesObjectState.java
@@ -7,10 +7,12 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.lens.GraphLens;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.analysis.value.UnknownValue;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.ArrayUtils;
 import java.util.Arrays;
 import java.util.Objects;
 import java.util.function.BiConsumer;
@@ -18,11 +20,20 @@
 public class EnumValuesObjectState extends ObjectState {
 
   private final ObjectState[] state;
+  // Contains information about the class of each enum instance.
+  private final ObjectClassForOrdinal objectClassForOrdinal;
 
-  public EnumValuesObjectState(ObjectState[] state) {
+  public EnumValuesObjectState(ObjectState[] state, DexType[] valuesTypes) {
     assert state.length > 0;
+    assert valuesTypes.length == state.length;
     assert Arrays.stream(state).noneMatch(Objects::isNull);
     this.state = state;
+    this.objectClassForOrdinal = ObjectClassForOrdinal.create(valuesTypes);
+  }
+
+  EnumValuesObjectState(ObjectState[] state, ObjectClassForOrdinal objectClassForOrdinal) {
+    this.state = state;
+    this.objectClassForOrdinal = objectClassForOrdinal;
   }
 
   @Override
@@ -40,6 +51,13 @@
     return state[ordinal];
   }
 
+  public DexType getObjectClassForOrdinal(int ordinal) {
+    if (ordinal < 0 || ordinal >= state.length) {
+      return null;
+    }
+    return objectClassForOrdinal.getObjectClassForOrdinal(ordinal);
+  }
+
   public int getEnumValuesSize() {
     return state.length;
   }
@@ -77,7 +95,8 @@
     for (int i = 0; i < state.length; i++) {
       newState[i] = state[i].rewrittenWithLens(appView, lens, codeLens);
     }
-    return new EnumValuesObjectState(newState);
+    return new EnumValuesObjectState(
+        newState, objectClassForOrdinal.rewrittenWithLens(appView, lens, codeLens));
   }
 
   @Override
@@ -89,16 +108,124 @@
     if (state.length != other.state.length) {
       return false;
     }
-    for (int i = 0; i < state.length; i++) {
-      if (!state[i].equals(other.state[i])) {
-        return false;
-      }
+    if (!Arrays.equals(state, other.state)) {
+      return false;
     }
-    return true;
+    return objectClassForOrdinal.equals(other.objectClassForOrdinal);
   }
 
   @Override
   public int hashCode() {
     return Arrays.hashCode(state);
   }
+
+  abstract static class ObjectClassForOrdinal {
+
+    static ObjectClassForOrdinal create(DexType[] valuesClass) {
+      return sameType(valuesClass)
+          ? new UniformObjectClassForOrdinal(valuesClass[0])
+          : new VariableObjectClassForOrdinal(valuesClass);
+    }
+
+    static boolean sameType(DexType[] valuesClass) {
+      DexType defaultType = valuesClass[0];
+      for (DexType type : valuesClass) {
+        if (type != defaultType) {
+          return false;
+        }
+      }
+      return true;
+    }
+
+    abstract DexType getObjectClassForOrdinal(int ordinal);
+
+    abstract ObjectClassForOrdinal rewrittenWithLens(
+        AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens);
+
+    @Override
+    public abstract int hashCode();
+
+    @Override
+    public abstract boolean equals(Object obj);
+  }
+
+  static class UniformObjectClassForOrdinal extends ObjectClassForOrdinal {
+    private final DexType type;
+
+    UniformObjectClassForOrdinal(DexType type) {
+      assert type != null;
+      this.type = type;
+    }
+
+    @Override
+    DexType getObjectClassForOrdinal(int ordinal) {
+      return type;
+    }
+
+    @Override
+    ObjectClassForOrdinal rewrittenWithLens(
+        AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) {
+      DexType rewrittenType = lens.lookupType(type, codeLens);
+      assert rewrittenType.isClassType();
+      return new UniformObjectClassForOrdinal(rewrittenType);
+    }
+
+    @Override
+    public int hashCode() {
+      return type.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (!(obj instanceof UniformObjectClassForOrdinal)) {
+        return false;
+      }
+      UniformObjectClassForOrdinal other = (UniformObjectClassForOrdinal) obj;
+      return type == other.type;
+    }
+  }
+
+  static class VariableObjectClassForOrdinal extends ObjectClassForOrdinal {
+    private final DexType[] types;
+
+    VariableObjectClassForOrdinal(DexType[] types) {
+      assert Arrays.stream(types).noneMatch(Objects::isNull);
+      this.types = types;
+    }
+
+    @Override
+    DexType getObjectClassForOrdinal(int ordinal) {
+      assert ordinal >= 0 && ordinal < types.length;
+      return types[ordinal];
+    }
+
+    @Override
+    ObjectClassForOrdinal rewrittenWithLens(
+        AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) {
+      DexType[] newTypes =
+          ArrayUtils.map(
+              types,
+              type -> {
+                DexType rewrittenType = lens.lookupType(type, codeLens);
+                assert rewrittenType.isClassType();
+                return rewrittenType;
+              },
+              DexType.EMPTY_ARRAY);
+      return create(newTypes);
+    }
+
+    @Override
+    public int hashCode() {
+      return Arrays.hashCode(types);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (!(obj instanceof VariableObjectClassForOrdinal)) {
+        return false;
+      }
+      VariableObjectClassForOrdinal other = (VariableObjectClassForOrdinal) obj;
+      return Arrays.equals(types, other.types);
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/NonEmptyObjectState.java b/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/NonEmptyObjectState.java
index b9a9933..2b6f00d 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/NonEmptyObjectState.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/NonEmptyObjectState.java
@@ -55,7 +55,7 @@
 
   @Override
   public boolean equals(Object o) {
-    if (getClass() != o.getClass()) {
+    if (o == null || getClass() != o.getClass()) {
       return false;
     }
     NonEmptyObjectState other = (NonEmptyObjectState) o;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 47d762c..4643e0a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -45,12 +45,17 @@
 import com.android.tools.r8.ir.desugar.backports.BooleanMethodRewrites;
 import com.android.tools.r8.ir.desugar.backports.CollectionMethodGenerators;
 import com.android.tools.r8.ir.desugar.backports.CollectionMethodRewrites;
+import com.android.tools.r8.ir.desugar.backports.ContentProviderClientMethodRewrites;
+import com.android.tools.r8.ir.desugar.backports.DrmManagerClientMethodRewrites;
 import com.android.tools.r8.ir.desugar.backports.FloatMethodRewrites;
 import com.android.tools.r8.ir.desugar.backports.LongMethodRewrites;
+import com.android.tools.r8.ir.desugar.backports.MediaDrmMethodRewrites;
+import com.android.tools.r8.ir.desugar.backports.MediaMetadataRetrieverMethodRewrites;
 import com.android.tools.r8.ir.desugar.backports.NumericMethodRewrites;
 import com.android.tools.r8.ir.desugar.backports.ObjectsMethodRewrites;
 import com.android.tools.r8.ir.desugar.backports.OptionalMethodRewrites;
 import com.android.tools.r8.ir.desugar.backports.SparseArrayMethodRewrites;
+import com.android.tools.r8.ir.desugar.backports.TypedArrayMethodRewrites;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeter;
 import com.android.tools.r8.position.MethodPosition;
 import com.android.tools.r8.synthesis.SyntheticItems.GlobalSyntheticsStrategy;
@@ -235,6 +240,12 @@
           initializeAndroidOThreadLocalMethodProviderWithSupplier(factory);
         }
       }
+      if (options.getMinApiLevel().isLessThan(AndroidApiLevel.P)) {
+        initializeAndroidPMethodProviders(factory);
+      }
+      if (options.getMinApiLevel().isLessThan(AndroidApiLevel.Q)) {
+        initializeAndroidQMethodProviders(factory);
+      }
       if (options.getMinApiLevel().isLessThan(AndroidApiLevel.R)) {
         if (options.testing.alwaysBackportListSetMapMethods
             || typeIsPresentWithoutBackportsFrom(factory.setType, AndroidApiLevel.R)) {
@@ -813,6 +824,22 @@
       }
 
       initializeMathExactApis(factory, factory.mathType);
+
+      // android.content.res.ContentProviderClient
+
+      // void android.content.ContentProviderClient.close()
+      addProvider(
+          new InvokeRewriter(
+              factory.androidContentContentProviderClientMembers.close,
+              ContentProviderClientMethodRewrites.rewriteClose()));
+
+      // android.drm.DrmManagerClient
+
+      // void android.drm.DrmManagerClient.close()
+      addProvider(
+          new InvokeRewriter(
+              factory.androidDrmDrmManagerClientMembers.close,
+              DrmManagerClientMethodRewrites.rewriteClose()));
     }
 
     /**
@@ -1051,6 +1078,21 @@
               method, BackportedMethods::StringMethods_joinIterable, "joinIterable"));
     }
 
+    private void initializeAndroidPMethodProviders(DexItemFactory factory) {
+      // void android.drm.DrmManagerClient.close()
+      addProvider(
+          new InvokeRewriter(
+              factory.androidMediaMediaDrmMembers.close, MediaDrmMethodRewrites.rewriteClose()));
+    }
+
+    private void initializeAndroidQMethodProviders(DexItemFactory factory) {
+      // void android.drm.DrmManagerClient.close()
+      addProvider(
+          new InvokeRewriter(
+              factory.androidMediaMetadataRetrieverMembers.close,
+              MediaMetadataRetrieverMethodRewrites.rewriteClose()));
+    }
+
     private void initializeAndroidRObjectsMethodProviderWithSupplier(DexItemFactory factory) {
       // Objects
       DexType type = factory.objectsType;
@@ -1286,10 +1328,18 @@
 
       // android.util.SparseArray
 
-      // void android.util.SparseArray.set(int, Object))
+      // void android.util.SparseArray.set(int, Object)
       addProvider(
           new InvokeRewriter(
               factory.androidUtilSparseArrayMembers.set, SparseArrayMethodRewrites.rewriteSet()));
+
+      // android.content.res.TypedArray
+
+      // void android.content.res.TypedArray.close()
+      addProvider(
+          new InvokeRewriter(
+              factory.androidContentResTypedArrayMembers.close,
+              TypedArrayMethodRewrites.rewriteClose()));
     }
 
     private void initializeAndroidSv2MethodProviders(DexItemFactory factory) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/ContentProviderClientMethodRewrites.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/ContentProviderClientMethodRewrites.java
new file mode 100644
index 0000000..8881483
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/ContentProviderClientMethodRewrites.java
@@ -0,0 +1,24 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.backports;
+
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.MethodInvokeRewriter;
+import org.objectweb.asm.Opcodes;
+
+public final class ContentProviderClientMethodRewrites {
+
+  private ContentProviderClientMethodRewrites() {}
+
+  public static MethodInvokeRewriter rewriteClose() {
+    // Rewrite android/content/ContentProviderClient#close to
+    // android/content/ContentProviderClient#recycle
+    return (invoke, factory) ->
+        new CfInvoke(
+            Opcodes.INVOKEVIRTUAL,
+            factory.androidContentContentProviderClientMembers.release,
+            false);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/DrmManagerClientMethodRewrites.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/DrmManagerClientMethodRewrites.java
new file mode 100644
index 0000000..408f560
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/DrmManagerClientMethodRewrites.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.backports;
+
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.MethodInvokeRewriter;
+import org.objectweb.asm.Opcodes;
+
+public final class DrmManagerClientMethodRewrites {
+
+  private DrmManagerClientMethodRewrites() {}
+
+  public static MethodInvokeRewriter rewriteClose() {
+    // Rewrite android/drm/DrmManagerClient#close to android/drm/DrmManagerClient#release
+    return (invoke, factory) ->
+        new CfInvoke(
+            Opcodes.INVOKEVIRTUAL, factory.androidDrmDrmManagerClientMembers.release, false);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/MediaDrmMethodRewrites.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/MediaDrmMethodRewrites.java
new file mode 100644
index 0000000..57b0fe2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/MediaDrmMethodRewrites.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.backports;
+
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.MethodInvokeRewriter;
+import org.objectweb.asm.Opcodes;
+
+public final class MediaDrmMethodRewrites {
+
+  private MediaDrmMethodRewrites() {}
+
+  public static MethodInvokeRewriter rewriteClose() {
+    // Rewrite android/media/MediaDrm#close to android/media/MediaDrm#release
+    return (invoke, factory) ->
+        new CfInvoke(Opcodes.INVOKEVIRTUAL, factory.androidMediaMediaDrmMembers.release, false);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/MediaMetadataRetrieverMethodRewrites.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/MediaMetadataRetrieverMethodRewrites.java
new file mode 100644
index 0000000..07aec55
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/MediaMetadataRetrieverMethodRewrites.java
@@ -0,0 +1,22 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.backports;
+
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.MethodInvokeRewriter;
+import org.objectweb.asm.Opcodes;
+
+public final class MediaMetadataRetrieverMethodRewrites {
+
+  private MediaMetadataRetrieverMethodRewrites() {}
+
+  public static MethodInvokeRewriter rewriteClose() {
+    // Rewrite android/media/MediaMetadataRetriever#close to
+    // android/media/MediaMetadataRetriever#release
+    return (invoke, factory) ->
+        new CfInvoke(
+            Opcodes.INVOKEVIRTUAL, factory.androidMediaMetadataRetrieverMembers.release, false);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/TypedArrayMethodRewrites.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/TypedArrayMethodRewrites.java
new file mode 100644
index 0000000..941f0ba
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/TypedArrayMethodRewrites.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.backports;
+
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.MethodInvokeRewriter;
+import org.objectweb.asm.Opcodes;
+
+public final class TypedArrayMethodRewrites {
+
+  private TypedArrayMethodRewrites() {}
+
+  public static MethodInvokeRewriter rewriteClose() {
+    // Rewrite android/content/res/TypedArray#close to android/content/res/TypedArray#recycle
+    return (invoke, factory) ->
+        new CfInvoke(
+            Opcodes.INVOKEVIRTUAL, factory.androidContentResTypedArrayMembers.recycle, false);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumDataMap.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumDataMap.java
index 00c4f50..4a91dc0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumDataMap.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumDataMap.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
@@ -109,6 +110,8 @@
 
     // Map each enum instance field to the list of field known data.
     final ImmutableMap<DexField, EnumInstanceFieldKnownData> instanceFieldMap;
+    // Map each ordinal to their original type. This is recorded *only* if the enum has subtypes.
+    final Int2ReferenceMap<DexType> valuesTypes;
     // Map each enum instance (static field) to the unboxed integer value.
     final ImmutableMap<DexField, Integer> unboxedValues;
     // Fields matching the $VALUES content and type, usually one.
@@ -118,10 +121,12 @@
 
     public EnumData(
         ImmutableMap<DexField, EnumInstanceFieldKnownData> instanceFieldMap,
+        Int2ReferenceMap<DexType> valuesTypes,
         ImmutableMap<DexField, Integer> unboxedValues,
         ImmutableSet<DexField> valuesFields,
         int valuesSize) {
       this.instanceFieldMap = instanceFieldMap;
+      this.valuesTypes = valuesTypes;
       this.unboxedValues = unboxedValues;
       this.valuesFields = valuesFields;
       this.valuesSize = valuesSize;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
index 4666036..5f9e4f2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
@@ -50,6 +50,7 @@
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues.EnumStaticFieldValues;
 import com.android.tools.r8.ir.analysis.type.ArrayTypeElement;
 import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.analysis.value.objectstate.EnumValuesObjectState;
@@ -92,6 +93,7 @@
 import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.IllegalInvokeWithImpreciseParameterTypeReason;
 import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.MissingContentsForEnumValuesArrayReason;
 import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.MissingEnumStaticFieldValuesReason;
+import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.MissingExactDynamicEnumTypeForEnumWithSubtypesReason;
 import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.MissingInstanceFieldValueForEnumInstanceReason;
 import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.MissingObjectStateForEnumInstanceReason;
 import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.UnsupportedInstanceFieldValueForEnumInstanceReason;
@@ -762,8 +764,8 @@
           @Override
           public void fixup(DexEncodedField field, MutableFieldOptimizationInfo optimizationInfo) {
             optimizationInfo
-                .fixupClassTypeReferences(appView, graphLens)
-                .fixupAbstractValue(appView, graphLens, codeLens);
+                .fixupAbstractValue(appView, graphLens, codeLens)
+                .fixupClassTypeReferences(appView, graphLens);
           }
 
           @Override
@@ -827,12 +829,16 @@
 
   private EnumData buildData(DexProgramClass enumClass, Set<DexField> instanceFields) {
     if (!enumClass.hasStaticFields()) {
-      return new EnumData(ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of(), -1);
+      return new EnumData(ImmutableMap.of(), null, ImmutableMap.of(), ImmutableSet.of(), -1);
     }
 
     // This map holds all the accessible fields to their unboxed value, so we can remap the field
     // read to the unboxed value.
     ImmutableMap.Builder<DexField, Integer> unboxedValues = ImmutableMap.builder();
+    // This maps the ordinal to their original type so that enum with subtypes can be correctly
+    // handled.
+    Int2ReferenceMap<DexType> valueTypes = new Int2ReferenceArrayMap<>();
+    boolean isEnumWithSubtypes = enumUnboxingCandidatesInfo.hasSubtypes(enumClass.getType());
     // This maps the ordinal to the object state, note that some fields may have been removed,
     // hence the entry is in this map but not the enumToOrdinalMap.
     Int2ReferenceMap<ObjectState> ordinalToObjectState = new Int2ReferenceArrayMap<>();
@@ -874,6 +880,18 @@
         int ordinal = optionalOrdinal.getAsInt();
         unboxedValues.put(staticField.getReference(), ordinalToUnboxedInt(ordinal));
         ordinalToObjectState.put(ordinal, enumState);
+        if (isEnumWithSubtypes) {
+          DynamicType dynamicType = staticField.getOptimizationInfo().getDynamicType();
+          if (dynamicType.isExactClassType()) {
+            valueTypes.put(ordinal, dynamicType.getExactClassType().getClassType());
+          } else {
+            reportFailure(
+                enumClass,
+                new MissingExactDynamicEnumTypeForEnumWithSubtypesReason(
+                    staticField.getReference()));
+            return null;
+          }
+        }
       } else if (factory.enumMembers.isValuesFieldCandidate(staticField, enumClass.type)) {
         ObjectState valuesState =
             enumStaticFieldValues.getObjectStateForPossiblyPinnedField(staticField.getReference());
@@ -905,11 +923,24 @@
           ObjectState enumState = valuesContents.getObjectStateForOrdinal(ordinal);
           if (enumState.isEmpty()) {
             // If $VALUES is used, we need data for all enums, at least the ordinal.
+            reportFailure(
+                enumClass,
+                new MissingInstanceFieldValueForEnumInstanceReason(
+                    factory.enumMembers.ordinalField, ordinal));
             return null;
           }
           assert getOrdinal(enumState).isPresent();
           assert getOrdinal(enumState).getAsInt() == ordinal;
           ordinalToObjectState.put(ordinal, enumState);
+          if (isEnumWithSubtypes) {
+            DexType type = valuesContents.getObjectClassForOrdinal(ordinal);
+            if (type == null) {
+              reportFailure(
+                  enumClass, new MissingExactDynamicEnumTypeForEnumWithSubtypesReason(ordinal));
+              return null;
+            }
+            valueTypes.put(ordinal, type);
+          }
         }
       }
     }
@@ -925,6 +956,7 @@
 
     return new EnumData(
         instanceFieldsData,
+        isEnumWithSubtypes ? valueTypes : null,
         unboxedValues.build(),
         valuesField.build(),
         valuesContents == null ? EnumData.INVALID_VALUES_SIZE : valuesContents.getEnumValuesSize());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java
index 9feeed2..bfeb83b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java
@@ -38,6 +38,10 @@
         new EnumUnboxingCandidateInfo(appView, enumClass, graphLensForPrimaryOptimizationPass));
   }
 
+  public boolean hasSubtypes(DexType enumType) {
+    return !enumTypeToInfo.get(enumType).getSubclasses().isEmpty();
+  }
+
   public void setEnumSubclasses(DexType superEnum, Set<DexProgramClass> subclasses) {
     enumTypeToInfo.get(superEnum).setSubclasses(subclasses);
   }
@@ -159,6 +163,10 @@
               graphLensForPrimaryOptimizationPass);
     }
 
+    public Set<DexProgramClass> getSubclasses() {
+      return subclasses;
+    }
+
     public void setSubclasses(Set<DexProgramClass> subclasses) {
       this.subclasses = subclasses;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
index 67efedd..738ed1e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedField.Builder;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -623,12 +624,7 @@
     DexField newField = field.withType(newType, factory);
     lensBuilder.move(field, newField);
     DexEncodedField newEncodedField =
-        encodedField.toTypeSubstitutedField(
-            appView,
-            newField,
-            builder ->
-                builder.setAbstractValue(
-                    encodedField.getOptimizationInfo().getAbstractValue(), appView));
+        encodedField.toTypeSubstitutedField(appView, newField, Builder::clearDynamicType);
     if (encodedField.isStatic() && encodedField.hasExplicitStaticValue()) {
       assert encodedField.getStaticValue() == DexValue.DexValueNull.NULL;
       newEncodedField.setStaticValue(DexValue.DexValueInt.DEFAULT);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java
index 5f31298..26c35b2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java
@@ -124,6 +124,34 @@
     }
   }
 
+  public static class MissingExactDynamicEnumTypeForEnumWithSubtypesReason extends Reason {
+
+    private final DexField enumField;
+    private final int ordinal;
+
+    public MissingExactDynamicEnumTypeForEnumWithSubtypesReason(DexField enumField) {
+      this.enumField = enumField;
+      this.ordinal = -1;
+    }
+
+    public MissingExactDynamicEnumTypeForEnumWithSubtypesReason(int ordinal) {
+      this.ordinal = ordinal;
+      this.enumField = null;
+    }
+
+    @Override
+    public Object getKind() {
+      return getClass();
+    }
+
+    @Override
+    public String toString() {
+      return "MissingDynamicType("
+          + (enumField != null ? enumField.toSourceString() : "ordinal=" + ordinal)
+          + ")";
+    }
+  }
+
   public static class MissingInstanceFieldValueForEnumInstanceReason extends Reason {
 
     private final DexField enumField;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java
index 89b97ad..406aa24 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java
@@ -64,9 +64,10 @@
     this.abstractValue = abstractValue;
   }
 
-  public void fixupAbstractValue(
+  public MutableFieldOptimizationInfo fixupAbstractValue(
       AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) {
     setAbstractValue(abstractValue.rewrittenWithLens(appView, lens, codeLens));
+    return this;
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
index c766497..3fdb8b4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
@@ -53,7 +53,7 @@
 
   @Override
   public void markFieldHasDynamicType(DexEncodedField field, DynamicType dynamicType) {
-    // Ignored.
+    field.getMutableOptimizationInfo().setDynamicType(dynamicType);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataMembersTracker.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataMembersTracker.java
index 88691a9..98dc167 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataMembersTracker.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataMembersTracker.java
@@ -6,7 +6,6 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexMember;
-import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.utils.IterableUtils;
 import com.google.common.collect.Sets;
@@ -48,7 +47,10 @@
         // underlying types are changed.
         diffComparedToRewritten.forEach(
             diff -> {
-              DexReference rewrittenReference = appView.graphLens().lookupReference(diff);
+              DexMember<?, ?> rewrittenReference =
+                  appView
+                      .graphLens()
+                      .getRenamedMemberSignature(diff, appView.getKotlinMetadataLens());
               assert diffComparedToOriginal.contains(rewrittenReference);
               assert IterableUtils.findOrDefault(
                       diff.getReferencedTypes(), type -> isKotlinJvmType(appView, type), null)
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
index 858a57c..04392f3 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
@@ -20,6 +20,7 @@
 import com.android.tools.r8.graph.DexValue.DexValueArray;
 import com.android.tools.r8.graph.DexValue.DexValueInt;
 import com.android.tools.r8.graph.DexValue.DexValueString;
+import com.android.tools.r8.graph.lens.GraphLens;
 import com.android.tools.r8.utils.BooleanBox;
 import com.android.tools.r8.utils.ConsumerUtils;
 import com.android.tools.r8.utils.Pair;
@@ -84,7 +85,10 @@
   }
 
   public void runForR8(ExecutorService executorService) throws ExecutionException {
-    DexType rewrittenMetadataType = appView.graphLens().lookupClassType(factory.kotlinMetadataType);
+    GraphLens graphLens = appView.graphLens();
+    GraphLens kotlinMetadataLens = appView.getKotlinMetadataLens();
+    DexType rewrittenMetadataType =
+        graphLens.lookupClassType(factory.kotlinMetadataType, kotlinMetadataLens);
     DexClass kotlinMetadata = appView.definitionFor(rewrittenMetadataType);
     WriteMetadataFieldInfo writeMetadataFieldInfo =
         new WriteMetadataFieldInfo(
@@ -124,6 +128,7 @@
           writeKotlinInfoToAnnotation(clazz, kotlinInfo, oldMeta, writeMetadataFieldInfo);
         },
         executorService);
+    appView.setKotlinMetadataLens(appView.graphLens());
   }
 
   public void runForD8(ExecutorService executorService) throws ExecutionException {
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfile.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfile.java
index 1455800..cc12b86 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfile.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfile.java
@@ -297,15 +297,25 @@
 
     @Override
     public Builder addClassRule(ArtProfileClassRule classRule) {
-      assert !rules.containsKey(classRule.getReference());
       rules.put(classRule.getType(), classRule);
       return this;
     }
 
     @Override
     public Builder addMethodRule(ArtProfileMethodRule methodRule) {
-      assert !rules.containsKey(methodRule.getReference());
-      rules.put(methodRule.getMethod(), methodRule);
+      rules.compute(
+          methodRule.getReference(),
+          (reference, existingRule) -> {
+            if (existingRule == null) {
+              return methodRule;
+            }
+            ArtProfileMethodRule existingMethodRule = (ArtProfileMethodRule) existingRule;
+            return ArtProfileMethodRule.builder()
+                .setMethod(methodRule.getMethod())
+                .join(methodRule)
+                .join(existingMethodRule)
+                .build();
+          });
       return this;
     }
 
diff --git a/src/main/java/com/android/tools/r8/repackaging/Repackaging.java b/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
index 00e55a7..cffb1cf 100644
--- a/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
+++ b/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
@@ -151,7 +151,7 @@
             repackagingTreeFixer.fixupClasses(appView.appInfo().classesWithDeterministicOrder()));
     appBuilder.replaceProgramClasses(newProgramClasses);
     RepackagingLens lens = lensBuilder.build(appView, packageMappings);
-    new AnnotationFixer(lens).run(appBuilder.getProgramClasses());
+    new AnnotationFixer(lens, appView.graphLens()).run(appBuilder.getProgramClasses());
     return lens;
   }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationFixer.java b/src/main/java/com/android/tools/r8/shaking/AnnotationFixer.java
index c1a8677..dc9c163 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationFixer.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationFixer.java
@@ -26,9 +26,15 @@
 public class AnnotationFixer {
 
   private final GraphLens lens;
+  private final GraphLens annotationLens;
 
-  public AnnotationFixer(GraphLens lens) {
+  public AnnotationFixer(GraphLens lens, GraphLens annotationLens) {
     this.lens = lens;
+    this.annotationLens = annotationLens;
+  }
+
+  private DexType lookupType(DexType type) {
+    return lens.lookupType(type, annotationLens);
   }
 
   public void run(Iterable<DexProgramClass> classes) {
@@ -54,7 +60,7 @@
 
   private DexEncodedAnnotation rewriteEncodedAnnotation(DexEncodedAnnotation original) {
     DexEncodedAnnotation rewritten =
-        original.rewrite(lens::lookupType, this::rewriteAnnotationElement);
+        original.rewrite(this::lookupType, this::rewriteAnnotationElement);
     assert rewritten != null;
     return rewritten;
   }
@@ -96,7 +102,7 @@
       }
     } else if (value.isDexValueEnum()) {
       DexField original = value.asDexValueEnum().value;
-      DexField rewritten = lens.lookupField(original);
+      DexField rewritten = lens.lookupField(original, annotationLens);
       if (original != rewritten) {
         return new DexValueEnum(rewritten);
       }
@@ -112,7 +118,7 @@
       // If we identified references in the string it would be a DexItemBasedValueString.
     } else if (value.isDexValueType()) {
       DexType originalType = value.asDexValueType().value;
-      DexType rewrittenType = lens.lookupType(originalType);
+      DexType rewrittenType = lookupType(originalType);
       if (rewrittenType != originalType) {
         return new DexValueType(rewrittenType);
       }
diff --git a/src/main/java/com/android/tools/r8/shaking/AssumeInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/AssumeInfoCollection.java
index bc3f728..75d7671 100644
--- a/src/main/java/com/android/tools/r8/shaking/AssumeInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/shaking/AssumeInfoCollection.java
@@ -60,11 +60,13 @@
     return isSideEffectFree(member.getReference());
   }
 
-  public AssumeInfoCollection rewrittenWithLens(AppView<?> appView, GraphLens graphLens) {
+  public AssumeInfoCollection rewrittenWithLens(
+      AppView<?> appView, GraphLens graphLens, GraphLens appliedLens) {
     Map<DexMember<?, ?>, AssumeInfo> rewrittenCollection = new IdentityHashMap<>();
     backing.forEach(
         (reference, info) -> {
-          DexMember<?, ?> rewrittenReference = graphLens.getRenamedMemberSignature(reference);
+          DexMember<?, ?> rewrittenReference =
+              graphLens.getRenamedMemberSignature(reference, appliedLens);
           AssumeInfo rewrittenInfo = info.rewrittenWithLens(appView, graphLens);
           assert !rewrittenInfo.isEmpty();
           rewrittenCollection.put(rewrittenReference, rewrittenInfo);
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 91c86e3..2397d02 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -1690,7 +1690,7 @@
       }
       VerticalClassMergerGraphLens lens = lensBuilder.build(appView, mergedClasses);
       if (lens != null) {
-        new AnnotationFixer(lens).run(appView.appInfo().classes());
+        new AnnotationFixer(lens, appView.graphLens()).run(appView.appInfo().classes());
       }
       return lens;
     }
diff --git a/src/main/java/com/android/tools/r8/utils/ArrayUtils.java b/src/main/java/com/android/tools/r8/utils/ArrayUtils.java
index b8d37cb..3e5104a 100644
--- a/src/main/java/com/android/tools/r8/utils/ArrayUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ArrayUtils.java
@@ -7,6 +7,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.function.Function;
 import java.util.function.IntPredicate;
@@ -75,20 +76,21 @@
    * @param emptyArray an empty array
    * @return an array with written elements
    */
-  public static <T> T[] map(T[] original, Function<T, T> mapper, T[] emptyArray) {
+  @SuppressWarnings("unchecked")
+  public static <S, T> T[] map(S[] original, Function<S, T> mapper, T[] emptyArray) {
     ArrayList<T> results = null;
     for (int i = 0; i < original.length; i++) {
-      T oldOne = original[i];
+      S oldOne = original[i];
       T newOne = mapper.apply(oldOne);
       if (newOne == oldOne) {
         if (results != null) {
-          results.add(oldOne);
+          results.add((T) oldOne);
         }
       } else {
         if (results == null) {
           results = new ArrayList<>(original.length);
           for (int j = 0; j < i; j++) {
-            results.add(original[j]);
+            results.add((T) original[j]);
           }
         }
         if (newOne != null) {
@@ -96,7 +98,7 @@
         }
       }
     }
-    return results != null ? results.toArray(emptyArray) : original;
+    return results != null ? results.toArray(emptyArray) : (T[]) original;
   }
 
   /** Rewrites the input array to the output array unconditionally. */
@@ -137,7 +139,7 @@
 
   public static <T> boolean contains(T[] elements, T elementToLookFor) {
     for (Object element : elements) {
-      if (element.equals(elementToLookFor)) {
+      if (Objects.equals(element, elementToLookFor)) {
         return true;
       }
     }
diff --git a/src/test/java/com/android/tools/r8/CommandTestBase.java b/src/test/java/com/android/tools/r8/CommandTestBase.java
index 1ea2212..886aeba 100644
--- a/src/test/java/com/android/tools/r8/CommandTestBase.java
+++ b/src/test/java/com/android/tools/r8/CommandTestBase.java
@@ -13,17 +13,29 @@
 import com.android.tools.r8.profile.art.ArtProfileConsumer;
 import com.android.tools.r8.profile.art.ArtProfileForRewriting;
 import com.android.tools.r8.profile.art.ArtProfileProvider;
+import com.android.tools.r8.profile.startup.profile.StartupProfile;
+import com.android.tools.r8.profile.startup.profile.StartupProfileRule;
+import com.android.tools.r8.startup.StartupProfileProvider;
+import com.android.tools.r8.startup.diagnostic.MissingStartupProfileItemsDiagnostic;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.IntBox;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
 import java.nio.file.Path;
+import java.util.Collection;
 import java.util.List;
 import org.junit.Test;
 
 public abstract class CommandTestBase<C extends BaseCompilerCommand> extends TestBase {
+
+  public boolean isL8() {
+    return false;
+  }
+
   private void mapDiagnosticsMissingArguments(String... args) {
     try {
       DiagnosticsChecker.checkErrorsContains(
@@ -375,6 +387,69 @@
     }
   }
 
+  @Test
+  public void startupProfileFlagAbsentTest() throws Exception {
+    assertTrue(parseWithRequiredArgs().getStartupProfileProviders().isEmpty());
+  }
+
+  @Test
+  public void startupProfileFlagPresentTest() throws Exception {
+    // Create a simple profile.
+    Path profile = temp.newFile("profile.txt").toPath();
+    String profileRule = "Lfoo/bar/Baz;->qux()V";
+    FileUtils.writeTextFile(profile, profileRule);
+
+    // Pass the profile on the command line.
+    List<StartupProfileProvider> startupProfileProviders;
+    try {
+      startupProfileProviders =
+          parseWithRequiredArgs(
+                  "--min-api",
+                  Integer.toString(AndroidApiLevel.L.getLevel()),
+                  "--startup-profile",
+                  profile.toString())
+              .getStartupProfileProviders();
+    } catch (CompilationFailedException e) {
+      assertTrue(isL8());
+      return;
+    }
+
+    assertEquals(1, startupProfileProviders.size());
+
+    // Construct the internal profile representation using the provider.
+    InternalOptions options = new InternalOptions();
+    MissingStartupProfileItemsDiagnostic.Builder missingStartupProfileItemsDiagnosticBuilder =
+        MissingStartupProfileItemsDiagnostic.Builder.nop();
+    StartupProfileProvider startupProfileProvider = startupProfileProviders.get(0);
+    StartupProfile.Builder startupProfileBuilder =
+        StartupProfile.builder(
+            options, missingStartupProfileItemsDiagnosticBuilder, startupProfileProvider);
+    startupProfileProvider.getStartupProfile(startupProfileBuilder);
+
+    // Verify we found the same rule.
+    StartupProfile startupProfile = startupProfileBuilder.build();
+    Collection<StartupProfileRule> startupItems =
+        ListUtils.newArrayList(consumer -> startupProfile.forEachRule(consumer::accept));
+    assertEquals(1, startupItems.size());
+    StartupProfileRule startupItem = startupItems.iterator().next();
+    startupItem.accept(
+        startupClass -> fail(),
+        startupMethod -> assertEquals(profileRule, startupMethod.getReference().toSmaliString()));
+  }
+
+  @Test
+  public void startupProfileFlagMissingParameterTest() {
+    String expectedErrorContains =
+        isL8() ? "Unknown option: --startup-profile" : "Missing parameter for --startup-profile.";
+    try {
+      DiagnosticsChecker.checkErrorsContains(
+          expectedErrorContains, handler -> parseWithRequiredArgs(handler, "--startup-profile"));
+      fail("Expected failure");
+    } catch (CompilationFailedException e) {
+      // Expected.
+    }
+  }
+
   private String[] prepareArgs(String[] args) {
     String[] actualTestArgs;
     String[] additionalTestArgs = requiredArgsForTest();
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index f4cbb5d..a81451f 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -779,61 +779,6 @@
     assertTrue(parse("--android-platform-build").getAndroidPlatformBuild());
   }
 
-  @Test
-  public void startupProfileFlagAbsentTest() throws Exception {
-    assertTrue(parse().getStartupProfileProviders().isEmpty());
-  }
-
-  @Test
-  public void startupProfileFlagPresentTest() throws Exception {
-    // Create a simple profile.
-    Path profile = temp.newFile("profile.txt").toPath();
-    String profileRule = "Lfoo/bar/Baz;->qux()V";
-    FileUtils.writeTextFile(profile, profileRule);
-
-    // Pass the profile on the command line.
-    List<StartupProfileProvider> startupProfileProviders =
-        parse(
-                "--min-api",
-                Integer.toString(AndroidApiLevel.L.getLevel()),
-                "--startup-profile",
-                profile.toString())
-            .getStartupProfileProviders();
-    assertEquals(1, startupProfileProviders.size());
-
-    // Construct the internal profile representation using the provider.
-    InternalOptions options = new InternalOptions();
-    MissingStartupProfileItemsDiagnostic.Builder missingStartupProfileItemsDiagnosticBuilder =
-        MissingStartupProfileItemsDiagnostic.Builder.nop();
-    StartupProfileProvider startupProfileProvider = startupProfileProviders.get(0);
-    StartupProfile.Builder startupProfileBuilder =
-        StartupProfile.builder(
-            options, missingStartupProfileItemsDiagnosticBuilder, startupProfileProvider);
-    startupProfileProvider.getStartupProfile(startupProfileBuilder);
-
-    // Verify we found the same rule.
-    StartupProfile startupProfile = startupProfileBuilder.build();
-    Collection<StartupProfileRule> startupItems =
-        ListUtils.newArrayList(consumer -> startupProfile.forEachRule(consumer::accept));
-    assertEquals(1, startupItems.size());
-    StartupProfileRule startupItem = startupItems.iterator().next();
-    startupItem.accept(
-        startupClass -> fail(),
-        startupMethod -> assertEquals(profileRule, startupMethod.getReference().toSmaliString()));
-  }
-
-  @Test
-  public void startupProfileFlagMissingParameterTest() {
-    String expectedErrorContains = "Missing parameter for --startup-profile.";
-    try {
-      DiagnosticsChecker.checkErrorsContains(
-          expectedErrorContains, handler -> parse(handler, "--startup-profile"));
-      fail("Expected failure");
-    } catch (CompilationFailedException e) {
-      // Expected.
-    }
-  }
-
   @Override
   String[] requiredArgsForTest() {
     return new String[0];
diff --git a/src/test/java/com/android/tools/r8/L8CommandTest.java b/src/test/java/com/android/tools/r8/L8CommandTest.java
index 4b42d31..6f5f226 100644
--- a/src/test/java/com/android/tools/r8/L8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/L8CommandTest.java
@@ -65,6 +65,11 @@
     return StringResource.fromFile(libraryDesugaringSpecification.getSpecification());
   }
 
+  @Override
+  public boolean isL8() {
+    return true;
+  }
+
   @Test(expected = CompilationFailedException.class)
   public void emptyBuilder() throws Throwable {
     verifyEmptyCommand(L8Command.builder().build());
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ContentProviderClientBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/ContentProviderClientBackportTest.java
new file mode 100644
index 0000000..e15b419
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ContentProviderClientBackportTest.java
@@ -0,0 +1,104 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.backports;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ContentProviderClientBackportTest extends AbstractBackportTest {
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDexRuntimesAndAllApiLevels().build();
+  }
+
+  public ContentProviderClientBackportTest(TestParameters parameters) throws IOException {
+    super(
+        parameters,
+        ContentProviderClientBackportTest.getContentProviderClient(parameters),
+        ImmutableList.of(
+            ContentProviderClientBackportTest.getTestRunner(),
+            ContentProviderClientBackportTest.getContentProviderClient(parameters)));
+
+    // The constructor is used by the test and release has been available since API 5 and is the
+    // method close is rewritten to.
+    ignoreInvokes("<init>");
+    ignoreInvokes("release");
+
+    // android.content.ContentProviderClient.close added in API 24.
+    registerTarget(AndroidApiLevel.N, 1);
+  }
+
+  private static byte[] getContentProviderClient(TestParameters parameters) throws IOException {
+    if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)) {
+      assertTrue(parameters.getRuntime().asDex().getVm().isNewerThanOrEqual(DexVm.ART_7_0_0_HOST));
+      return transformer(ContentProviderClientApiLevel24.class)
+          .setClassDescriptor(DexItemFactory.androidContentContentProviderClientDescriptorString)
+          .transform();
+    } else {
+      return transformer(ContentProviderClient.class)
+          .setClassDescriptor(DexItemFactory.androidContentContentProviderClientDescriptorString)
+          .transform();
+    }
+  }
+
+  private static byte[] getTestRunner() throws IOException {
+    return transformer(TestRunner.class)
+        .replaceClassDescriptorInMethodInstructions(
+            descriptor(ContentProviderClient.class),
+            DexItemFactory.androidContentContentProviderClientDescriptorString)
+        .transform();
+  }
+
+  public static class ContentProviderClient {
+    public boolean wasClosed = false;
+
+    public void close() {
+      TestRunner.doFail("close should not be called");
+    }
+
+    public void release() {
+      wasClosed = true;
+    }
+  }
+
+  public static class ContentProviderClientApiLevel24 {
+    public boolean wasClosed = false;
+
+    public void close() {
+      wasClosed = true;
+    }
+
+    public void release() {
+      TestRunner.doFail("release should not be called");
+    }
+  }
+
+  public static class TestRunner extends MiniAssert {
+
+    public static void main(String[] args) {
+      ContentProviderClient contentProviderClient = new ContentProviderClient();
+      MiniAssert.assertFalse(contentProviderClient.wasClosed);
+      contentProviderClient.close();
+      MiniAssert.assertTrue(contentProviderClient.wasClosed);
+    }
+
+    // Forwards to MiniAssert to avoid having to make it public.
+    public static void doFail(String message) {
+      MiniAssert.fail(message);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/DrmManagerClientBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/DrmManagerClientBackportTest.java
new file mode 100644
index 0000000..6b16c6c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/DrmManagerClientBackportTest.java
@@ -0,0 +1,104 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.backports;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class DrmManagerClientBackportTest extends AbstractBackportTest {
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDexRuntimesAndAllApiLevels().build();
+  }
+
+  public DrmManagerClientBackportTest(TestParameters parameters) throws IOException {
+    super(
+        parameters,
+        DrmManagerClientBackportTest.getDrmManagerClient(parameters),
+        ImmutableList.of(
+            DrmManagerClientBackportTest.getTestRunner(),
+            DrmManagerClientBackportTest.getDrmManagerClient(parameters)));
+
+    // The constructor is used by the test and release has been available since API 5 and is the
+    // method close is rewritten to.
+    ignoreInvokes("<init>");
+    ignoreInvokes("release");
+
+    // android.drm.DrmManagerClient.close added in API 24.
+    registerTarget(AndroidApiLevel.N, 1);
+  }
+
+  private static byte[] getDrmManagerClient(TestParameters parameters) throws IOException {
+    if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)) {
+      assertTrue(parameters.getRuntime().asDex().getVm().isNewerThanOrEqual(DexVm.ART_7_0_0_HOST));
+      return transformer(DrmManagerClientApiLevel24.class)
+          .setClassDescriptor(DexItemFactory.androidDrmDrmManagerClientDescriptorString)
+          .transform();
+    } else {
+      return transformer(DrmManagerClient.class)
+          .setClassDescriptor(DexItemFactory.androidDrmDrmManagerClientDescriptorString)
+          .transform();
+    }
+  }
+
+  private static byte[] getTestRunner() throws IOException {
+    return transformer(TestRunner.class)
+        .replaceClassDescriptorInMethodInstructions(
+            descriptor(DrmManagerClient.class),
+            DexItemFactory.androidDrmDrmManagerClientDescriptorString)
+        .transform();
+  }
+
+  public static class DrmManagerClient {
+    public boolean wasClosed = false;
+
+    public void close() {
+      TestRunner.doFail("close should not be called");
+    }
+
+    public void release() {
+      wasClosed = true;
+    }
+  }
+
+  public static class DrmManagerClientApiLevel24 {
+    public boolean wasClosed = false;
+
+    public void close() {
+      wasClosed = true;
+    }
+
+    public void release() {
+      TestRunner.doFail("release should not be called");
+    }
+  }
+
+  public static class TestRunner extends MiniAssert {
+
+    public static void main(String[] args) {
+      DrmManagerClient drmManagerClient = new DrmManagerClient();
+      MiniAssert.assertFalse(drmManagerClient.wasClosed);
+      drmManagerClient.close();
+      MiniAssert.assertTrue(drmManagerClient.wasClosed);
+    }
+
+    // Forwards to MiniAssert to avoid having to make it public.
+    public static void doFail(String message) {
+      MiniAssert.fail(message);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/MediaDrmBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/MediaDrmBackportTest.java
new file mode 100644
index 0000000..500308b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MediaDrmBackportTest.java
@@ -0,0 +1,102 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.backports;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class MediaDrmBackportTest extends AbstractBackportTest {
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDexRuntimesAndAllApiLevels().build();
+  }
+
+  public MediaDrmBackportTest(TestParameters parameters) throws IOException {
+    super(
+        parameters,
+        MediaDrmBackportTest.getMediaDrm(parameters),
+        ImmutableList.of(
+            MediaDrmBackportTest.getTestRunner(), MediaDrmBackportTest.getMediaDrm(parameters)));
+
+    // The constructor is used by the test and release has been available since API 18 and is the
+    // method close is rewritten to.
+    ignoreInvokes("<init>");
+    ignoreInvokes("release");
+
+    // android.media.MediaDrm.close added in API 28.
+    registerTarget(AndroidApiLevel.P, 1);
+  }
+
+  private static byte[] getMediaDrm(TestParameters parameters) throws IOException {
+    if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.P)) {
+      assertTrue(parameters.getRuntime().asDex().getVm().isNewerThanOrEqual(DexVm.ART_8_1_0_HOST));
+      return transformer(MediaDrmApiLevel28.class)
+          .setClassDescriptor(DexItemFactory.androidMediaMediaDrmDescriptorString)
+          .transform();
+    } else {
+      return transformer(MediaDrm.class)
+          .setClassDescriptor(DexItemFactory.androidMediaMediaDrmDescriptorString)
+          .transform();
+    }
+  }
+
+  private static byte[] getTestRunner() throws IOException {
+    return transformer(TestRunner.class)
+        .replaceClassDescriptorInMethodInstructions(
+            descriptor(MediaDrm.class), DexItemFactory.androidMediaMediaDrmDescriptorString)
+        .transform();
+  }
+
+  public static class MediaDrm {
+    public boolean wasClosed = false;
+
+    public void close() {
+      TestRunner.doFail("close should not be called");
+    }
+
+    public void release() {
+      wasClosed = true;
+    }
+  }
+
+  public static class MediaDrmApiLevel28 {
+    public boolean wasClosed = false;
+
+    public void close() {
+      wasClosed = true;
+    }
+
+    public void release() {
+      TestRunner.doFail("release should not be called");
+    }
+  }
+
+  public static class TestRunner extends MiniAssert {
+
+    public static void main(String[] args) {
+      MediaDrm mediaDrm = new MediaDrm();
+      MiniAssert.assertFalse(mediaDrm.wasClosed);
+      mediaDrm.close();
+      MiniAssert.assertTrue(mediaDrm.wasClosed);
+    }
+
+    // Forwards to MiniAssert to avoid having to make it public.
+    public static void doFail(String message) {
+      MiniAssert.fail(message);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/MediaMetadataRetrieverBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/MediaMetadataRetrieverBackportTest.java
new file mode 100644
index 0000000..b3cbfe2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MediaMetadataRetrieverBackportTest.java
@@ -0,0 +1,104 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.backports;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class MediaMetadataRetrieverBackportTest extends AbstractBackportTest {
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDexRuntimesAndAllApiLevels().build();
+  }
+
+  public MediaMetadataRetrieverBackportTest(TestParameters parameters) throws IOException {
+    super(
+        parameters,
+        MediaMetadataRetrieverBackportTest.getMediaMetadataRetriever(parameters),
+        ImmutableList.of(
+            MediaMetadataRetrieverBackportTest.getTestRunner(),
+            MediaMetadataRetrieverBackportTest.getMediaMetadataRetriever(parameters)));
+
+    // The constructor is used by the test and release has been available since API 10 and is the
+    // method close is rewritten to.
+    ignoreInvokes("<init>");
+    ignoreInvokes("release");
+
+    // android.media.MediaMetadataRetriever.close added in API 29.
+    registerTarget(AndroidApiLevel.Q, 1);
+  }
+
+  private static byte[] getMediaMetadataRetriever(TestParameters parameters) throws IOException {
+    if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.Q)) {
+      assertTrue(parameters.getRuntime().asDex().getVm().isNewerThanOrEqual(DexVm.ART_10_0_0_HOST));
+      return transformer(MediaMetadataRetrieverApiLevel29.class)
+          .setClassDescriptor(DexItemFactory.androidMediaMediaMetadataRetrieverDescriptorString)
+          .transform();
+    } else {
+      return transformer(MediaMetadataRetriever.class)
+          .setClassDescriptor(DexItemFactory.androidMediaMediaMetadataRetrieverDescriptorString)
+          .transform();
+    }
+  }
+
+  private static byte[] getTestRunner() throws IOException {
+    return transformer(TestRunner.class)
+        .replaceClassDescriptorInMethodInstructions(
+            descriptor(MediaMetadataRetriever.class),
+            DexItemFactory.androidMediaMediaMetadataRetrieverDescriptorString)
+        .transform();
+  }
+
+  public static class MediaMetadataRetriever {
+    public boolean wasClosed = false;
+
+    public void close() {
+      TestRunner.doFail("close should not be called");
+    }
+
+    public void release() {
+      wasClosed = true;
+    }
+  }
+
+  public static class MediaMetadataRetrieverApiLevel29 {
+    public boolean wasClosed = false;
+
+    public void close() {
+      wasClosed = true;
+    }
+
+    public void release() {
+      TestRunner.doFail("release should not be called");
+    }
+  }
+
+  public static class TestRunner extends MiniAssert {
+
+    public static void main(String[] args) {
+      MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
+      MiniAssert.assertFalse(mediaMetadataRetriever.wasClosed);
+      mediaMetadataRetriever.close();
+      MiniAssert.assertTrue(mediaMetadataRetriever.wasClosed);
+    }
+
+    // Forwards to MiniAssert to avoid having to make it public.
+    public static void doFail(String message) {
+      MiniAssert.fail(message);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/TypedArrayBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/TypedArrayBackportTest.java
new file mode 100644
index 0000000..06e759d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/TypedArrayBackportTest.java
@@ -0,0 +1,104 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.backports;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TypedArrayBackportTest extends AbstractBackportTest {
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDexRuntimesAndAllApiLevels().build();
+  }
+
+  public TypedArrayBackportTest(TestParameters parameters) throws IOException {
+    super(
+        parameters,
+        TypedArrayBackportTest.getTypedArray(parameters),
+        ImmutableList.of(
+            TypedArrayBackportTest.getTestRunner(),
+            TypedArrayBackportTest.getTypedArray(parameters)));
+
+    // The constructor is used by the test and recycle has been available since API 1 and is the
+    // method close is rewritten to.
+    ignoreInvokes("<init>");
+    ignoreInvokes("recycle");
+
+    // android.content.res.TypedArray.close added in API 31.
+    registerTarget(AndroidApiLevel.S, 1);
+  }
+
+  private static byte[] getTypedArray(TestParameters parameters) throws IOException {
+    if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.S)) {
+      assertTrue(parameters.getRuntime().asDex().getVm().isNewerThanOrEqual(DexVm.ART_12_0_0_HOST));
+      return transformer(TypedArrayAndroidApiLevel31.class)
+          .setClassDescriptor(DexItemFactory.androidContentResTypedArrayDescriptorString)
+          .transform();
+    } else {
+      return transformer(TypedArray.class)
+          .setClassDescriptor(DexItemFactory.androidContentResTypedArrayDescriptorString)
+          .transform();
+    }
+  }
+
+  private static byte[] getTestRunner() throws IOException {
+    return transformer(TestRunner.class)
+        .replaceClassDescriptorInMethodInstructions(
+            descriptor(TypedArray.class),
+            DexItemFactory.androidContentResTypedArrayDescriptorString)
+        .transform();
+  }
+
+  public static class TypedArray {
+    public boolean wasClosed = false;
+
+    public void close() {
+      TestRunner.doFail("close should not be called");
+    }
+
+    public void recycle() {
+      wasClosed = true;
+    }
+  }
+
+  public static class TypedArrayAndroidApiLevel31 {
+    public boolean wasClosed = false;
+
+    public void close() {
+      wasClosed = true;
+    }
+
+    public void recycle() {
+      TestRunner.doFail("recycle should not be called");
+    }
+  }
+
+  public static class TestRunner extends MiniAssert {
+
+    public static void main(String[] args) {
+      TypedArray typedArray = new TypedArray();
+      MiniAssert.assertFalse(typedArray.wasClosed);
+      typedArray.close();
+      MiniAssert.assertTrue(typedArray.wasClosed);
+    }
+
+    // Forwards to MiniAssert to avoid having to make it public.
+    public static void doFail(String message) {
+      MiniAssert.fail(message);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/DuplicateDescriptorsInArtProfileTest.java b/src/test/java/com/android/tools/r8/profile/art/DuplicateDescriptorsInArtProfileTest.java
new file mode 100644
index 0000000..fac8889
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/DuplicateDescriptorsInArtProfileTest.java
@@ -0,0 +1,90 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile.art;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class DuplicateDescriptorsInArtProfileTest extends TestBase {
+
+  static final ClassReference MAIN_CLASS_REFERENCE = Reference.classFromClass(Main.class);
+  static final MethodReference MAIN_METHOD_REFERENCE = MethodReferenceUtils.mainMethod(Main.class);
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(Backend.DEX)
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addArtProfileForRewriting(
+            new ArtProfileProvider() {
+              @Override
+              public void getArtProfile(ArtProfileBuilder profileBuilder) {
+                profileBuilder.addClassRule(
+                    classRuleBuilder -> classRuleBuilder.setClassReference(MAIN_CLASS_REFERENCE));
+                profileBuilder.addClassRule(
+                    classRuleBuilder -> classRuleBuilder.setClassReference(MAIN_CLASS_REFERENCE));
+                profileBuilder.addMethodRule(
+                    methodRuleBuilder ->
+                        methodRuleBuilder
+                            .setMethodReference(MAIN_METHOD_REFERENCE)
+                            .setMethodRuleInfo(
+                                methodRuleInfoBuilder -> methodRuleInfoBuilder.setIsHot(true)));
+                profileBuilder.addMethodRule(
+                    methodRuleBuilder ->
+                        methodRuleBuilder
+                            .setMethodReference(MAIN_METHOD_REFERENCE)
+                            .setMethodRuleInfo(
+                                methodRuleInfoBuilder -> methodRuleInfoBuilder.setIsStartup(true)));
+                profileBuilder.addMethodRule(
+                    methodRuleBuilder ->
+                        methodRuleBuilder
+                            .setMethodReference(MAIN_METHOD_REFERENCE)
+                            .setMethodRuleInfo(
+                                methodRuleInfoBuilder ->
+                                    methodRuleInfoBuilder.setIsPostStartup(true)));
+              }
+
+              @Override
+              public Origin getOrigin() {
+                return Origin.unknown();
+              }
+            })
+        .compile()
+        .inspectResidualArtProfile(
+            profileInspector ->
+                profileInspector
+                    .assertContainsClassRule(MAIN_CLASS_REFERENCE)
+                    .inspectMethodRule(
+                        MAIN_METHOD_REFERENCE,
+                        methodInspector ->
+                            methodInspector.assertIsHot().assertIsStartup().assertIsPostStartup())
+                    .assertContainsNoOtherRules());
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {}
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/startup/DuplicateDescriptorsInStartupProfileTest.java b/src/test/java/com/android/tools/r8/startup/DuplicateDescriptorsInStartupProfileTest.java
new file mode 100644
index 0000000..668c2a3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/startup/DuplicateDescriptorsInStartupProfileTest.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.startup;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.startup.profile.ExternalStartupClass;
+import com.android.tools.r8.startup.profile.ExternalStartupMethod;
+import com.android.tools.r8.startup.utils.StartupTestingUtils;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class DuplicateDescriptorsInStartupProfileTest extends TestBase {
+
+  static final ClassReference MAIN_CLASS_REFERENCE = Reference.classFromClass(Main.class);
+  static final MethodReference MAIN_METHOD_REFERENCE = MethodReferenceUtils.mainMethod(Main.class);
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(Backend.DEX)
+        .addInnerClasses(getClass())
+        .addKeepAllClassesRule()
+        .apply(
+            testBuilder ->
+                StartupTestingUtils.addStartupProfile(
+                    testBuilder,
+                    ImmutableList.of(
+                        ExternalStartupClass.builder()
+                            .setClassReference(MAIN_CLASS_REFERENCE)
+                            .build(),
+                        ExternalStartupMethod.builder()
+                            .setMethodReference(MAIN_METHOD_REFERENCE)
+                            .build())))
+        .apply(
+            testBuilder ->
+                StartupTestingUtils.addStartupProfile(
+                    testBuilder,
+                    ImmutableList.of(
+                        ExternalStartupClass.builder()
+                            .setClassReference(MAIN_CLASS_REFERENCE)
+                            .build(),
+                        ExternalStartupClass.builder()
+                            .setClassReference(MAIN_CLASS_REFERENCE)
+                            .build(),
+                        ExternalStartupMethod.builder()
+                            .setMethodReference(MAIN_METHOD_REFERENCE)
+                            .build(),
+                        ExternalStartupMethod.builder()
+                            .setMethodReference(MAIN_METHOD_REFERENCE)
+                            .build())))
+        .setMinApi(AndroidApiLevel.L)
+        .compile()
+        .inspectMultiDex(
+            primaryInspector -> assertThat(primaryInspector.clazz(Main.class), isPresent()),
+            secondaryInspector ->
+                assertThat(secondaryInspector.clazz(PostStartup.class), isPresent()));
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {}
+  }
+
+  static class PostStartup {}
+}
diff --git a/src/test/java/com/android/tools/r8/startup/InliningOutOfStartupPartitionTest.java b/src/test/java/com/android/tools/r8/startup/InliningOutOfStartupPartitionTest.java
index e1a415d..ba065b7 100644
--- a/src/test/java/com/android/tools/r8/startup/InliningOutOfStartupPartitionTest.java
+++ b/src/test/java/com/android/tools/r8/startup/InliningOutOfStartupPartitionTest.java
@@ -50,8 +50,7 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
-        .apply(
-            testBuilder -> StartupTestingUtils.setStartupConfiguration(testBuilder, startupItems))
+        .apply(testBuilder -> StartupTestingUtils.addStartupProfile(testBuilder, startupItems))
         .setMinApi(parameters)
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/startup/MinimalStartupDexFromStartupMethodRuleTest.java b/src/test/java/com/android/tools/r8/startup/MinimalStartupDexFromStartupMethodRuleTest.java
index ae0fe36..20e850f 100644
--- a/src/test/java/com/android/tools/r8/startup/MinimalStartupDexFromStartupMethodRuleTest.java
+++ b/src/test/java/com/android/tools/r8/startup/MinimalStartupDexFromStartupMethodRuleTest.java
@@ -69,7 +69,7 @@
   }
 
   private void configureStartupConfiguration(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) {
-    StartupTestingUtils.setStartupConfiguration(
+    StartupTestingUtils.addStartupProfile(
         testBuilder,
         ImmutableList.of(
             ExternalStartupMethod.builder()
diff --git a/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java b/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java
index 53c02be..91f9d7d 100644
--- a/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java
+++ b/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java
@@ -67,7 +67,7 @@
                     .setEnableMinimalStartupDex(true)
                     .setEnableStartupCompletenessCheckForTesting())
         .enableInliningAnnotations()
-        .apply(testBuilder -> StartupTestingUtils.setStartupConfiguration(testBuilder, startupList))
+        .apply(testBuilder -> StartupTestingUtils.addStartupProfile(testBuilder, startupList))
         .setMinApi(parameters)
         .compile()
         .inspectMultiDex(
diff --git a/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java b/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
index 92d3129..697aa2d 100644
--- a/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
+++ b/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
@@ -196,7 +196,7 @@
                   .setMixedSectionLayoutStrategyInspector(
                       getMixedSectionLayoutInspector(inspector, testBuilder.isD8TestBuilder()));
             })
-        .apply(ignore -> StartupTestingUtils.setStartupConfiguration(testBuilder, startupList));
+        .apply(ignore -> StartupTestingUtils.addStartupProfile(testBuilder, startupList));
   }
 
   private List<String> getExpectedOutput() {
diff --git a/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java b/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java
index 14a1e1b..6b21589 100644
--- a/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java
+++ b/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java
@@ -92,7 +92,7 @@
                   .getTestingOptions()
                   .setMixedSectionLayoutStrategyInspector(getMixedSectionLayoutInspector());
             })
-        .apply(testBuilder -> StartupTestingUtils.setStartupConfiguration(testBuilder, startupList))
+        .apply(testBuilder -> StartupTestingUtils.addStartupProfile(testBuilder, startupList))
         .setMinApi(parameters)
         .compile()
         .inspectMultiDex(this::inspectPrimaryDex, this::inspectSecondaryDex)
diff --git a/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java b/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
index 3ee4a45..8805931 100644
--- a/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
+++ b/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
@@ -158,7 +158,7 @@
     runResult.getResult().setStdout(stdoutBuilder.toString());
   }
 
-  public static void setStartupConfiguration(
+  public static void addStartupProfile(
       TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder,
       Collection<ExternalStartupItem> startupItems) {
     StartupProfileProvider startupProfileProvider =
diff --git a/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1 b/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1
index 8fc0af6..c845f51 100644
--- a/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1
+++ b/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1
@@ -1 +1 @@
-91bdabe13dd74051654146af7f9b8f87b332f198
\ No newline at end of file
+e01c698069f2b52bd80864b70adfeaf0c27e5f4f
\ No newline at end of file