Reapply "Desugaring of AutoCloseable#close everywhere"

This reverts commit c44098b28724a38894a9276d8dfab7f4e32b7163.

Bug: b/369520931
Change-Id: I0035c5db369d521df2f85e9fdb5f5c4b974e1949
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
index 2deffd2..1836cf4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.ir.desugar;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryAPICallbackSynthesizer;
@@ -12,9 +13,11 @@
 import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterPostProcessor;
 import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade;
 import com.android.tools.r8.ir.desugar.records.RecordClassDesugaring;
+import com.android.tools.r8.utils.CollectionUtils;
 import com.android.tools.r8.utils.Timing;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Comparator;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
@@ -67,7 +70,7 @@
           && !appView.options().getLibraryDesugaringOptions().isDesugaredLibraryCompilation()) {
         desugarings.add(new DesugaredLibraryRetargeterPostProcessor(appView));
       }
-      if (appView.options().testing.enableAutoCloseableDesugaring) {
+      if (appView.options().shouldDesugarAutoCloseable()) {
         desugarings.add(new AutoCloseableRetargeterPostProcessor(appView));
       }
       if (interfaceMethodProcessorFacade != null) {
@@ -104,8 +107,11 @@
         ExecutorService executorService,
         Timing timing)
         throws ExecutionException {
+      Collection<DexProgramClass> sortedProgramClasses =
+          CollectionUtils.sort(programClasses, Comparator.comparing(DexClass::getType));
       for (CfPostProcessingDesugaring desugaring : desugarings) {
-        desugaring.postProcessingDesugaring(programClasses, eventConsumer, executorService, timing);
+        desugaring.postProcessingDesugaring(
+            sortedProgramClasses, eventConsumer, executorService, timing);
       }
     }
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index e8080f8..81173c6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -121,8 +121,12 @@
     if (desugaredLibraryRetargeter != null) {
       desugarings.add(desugaredLibraryRetargeter);
     }
-    if (appView.options().shouldDesugarAutoCloseable()) {
-      desugarings.add(new AutoCloseableRetargeter(appView));
+    AutoCloseableRetargeter autoCloseableRetargeter =
+        appView.options().shouldDesugarAutoCloseable()
+            ? new AutoCloseableRetargeter(appView)
+            : null;
+    if (autoCloseableRetargeter != null) {
+      desugarings.add(autoCloseableRetargeter);
     }
     disableDesugarer = DesugaredLibraryDisableDesugarer.create(appView);
     if (disableDesugarer != null) {
@@ -149,6 +153,7 @@
             SetUtils.newImmutableSetExcludingNullItems(
                 alwaysThrowingInstructionDesugaring,
                 backportedMethodRewriter,
+                autoCloseableRetargeter,
                 desugaredLibraryRetargeter),
             SetUtils.newImmutableSetExcludingNullItems(
                 lambdaDesugaring, stringConcatDesugaring, recordRewriter));
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/AutoCloseableRetargeterPostProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/AutoCloseableRetargeterPostProcessor.java
index e8019a1..2037f21 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/AutoCloseableRetargeterPostProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/AutoCloseableRetargeterPostProcessor.java
@@ -24,8 +24,10 @@
 import com.android.tools.r8.utils.Timing;
 import com.android.tools.r8.utils.WorkList;
 import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
 import java.util.concurrent.ExecutorService;
 
 // The rewrite of virtual calls requires to go through emulate dispatch. This class is responsible
@@ -50,23 +52,32 @@
       ExecutorService executorService,
       Timing timing) {
     try (Timing t0 = timing.begin("Auto closeable retargeter post processor")) {
-      ensureInterfacesAndForwardingMethodsSynthesized(programClasses, eventConsumer);
-    }
-  }
-
-  @SuppressWarnings("ReferenceEquality")
-  private void ensureInterfacesAndForwardingMethodsSynthesized(
-      Collection<DexProgramClass> programClasses,
-      AutoCloseableRetargeterPostProcessingEventConsumer eventConsumer) {
-    ProcessorContext processorContext = appView.createProcessorContext();
-    MainThreadContext mainThreadContext = processorContext.createMainThreadContext();
-    for (DexProgramClass clazz : programClasses) {
-      if (clazz.superType == null) {
-        assert clazz.type == appView.dexItemFactory().objectType : clazz.type.toSourceString();
-        continue;
+      ProcessorContext processorContext = appView.createProcessorContext();
+      MainThreadContext mainThreadContext = processorContext.createMainThreadContext();
+      List<ProgramMethod> bridges = new ArrayList<>();
+      for (DexProgramClass clazz : programClasses) {
+        if (clazz.superType == null) {
+          assert clazz.type.isIdenticalTo(appView.dexItemFactory().objectType)
+              : clazz.type.toSourceString();
+          continue;
+        }
+        if (clazz.isInterface() && appView.options().isInterfaceMethodDesugaringEnabled()) {
+          // We cannot add default methods on interfaces if interface method desugaring is enabled.
+          continue;
+        }
+        if (implementsAutoCloseableAtLibraryBoundary(clazz)) {
+          ProgramMethod bridge =
+              ensureInterfacesAndForwardingMethodsSynthesized(
+                  eventConsumer, clazz, mainThreadContext);
+          if (bridge != null) {
+            bridges.add(bridge);
+          }
+        }
       }
-      if (implementsAutoCloseableAtLibraryBoundary(clazz)) {
-        ensureInterfacesAndForwardingMethodsSynthesized(eventConsumer, clazz, mainThreadContext);
+      // We add the bridges in the end so they don't interfer with lookups in between.
+      for (ProgramMethod bridge : bridges) {
+        bridge.getHolder().addVirtualMethod(bridge.getDefinition());
+        eventConsumer.acceptAutoCloseableForwardingMethod(bridge, bridge.getHolder());
       }
     }
   }
@@ -75,46 +86,53 @@
     if (clazz.interfaces.contains(appView.dexItemFactory().autoCloseableType)) {
       return true;
     }
-    WorkList<DexType> workList = collectLibrarySuperTypeAndInterfaces(clazz);
+    WorkList<DexClass> workList = collectLibrarySuperTypeAndInterfaces(clazz);
     return libraryTypesImplementsAutoCloseable(workList, clazz);
   }
 
-  private WorkList<DexType> collectLibrarySuperTypeAndInterfaces(DexProgramClass clazz) {
-    WorkList<DexType> workList = WorkList.newIdentityWorkList();
+  private WorkList<DexClass> collectLibrarySuperTypeAndInterfaces(DexProgramClass clazz) {
+    WorkList<DexClass> workList = WorkList.newIdentityWorkList();
     DexClass superclass = appView.definitionFor(clazz.superType);
-    // Only performs computation if superclass is a library class, but not object to filter out
-    // the most common case.
-    if (superclass != null
-        && superclass.isLibraryClass()
-        && !superclass.type.isIdenticalTo(appView.dexItemFactory().objectType)) {
-      workList.addIfNotSeen(superclass.type);
+    // R8 only performs the computation if the superclass is a library class.
+    if (superclass != null && superclass.isLibraryClass()) {
+      workList.addIfNotSeen(superclass);
     }
     for (DexType itf : clazz.interfaces) {
       DexClass superItf = appView.definitionFor(itf);
       if (superItf != null) {
-        workList.addIfNotSeen(superItf.type);
+        // R8 only performs the computation if the super interface is a library class. If interface
+        // method desugaring is enabled, we need to go through all interfaces to insert the bridges
+        // on the classes and not the interfaces.
+        if (appView.options().isInterfaceMethodDesugaringEnabled() || superItf.isLibraryClass()) {
+          workList.addIfNotSeen(superItf);
+        }
       }
     }
     return workList;
   }
 
   private boolean libraryTypesImplementsAutoCloseable(
-      WorkList<DexType> workList, DexProgramClass clazz) {
+      WorkList<DexClass> workList, DexProgramClass clazz) {
     while (workList.hasNext()) {
-      DexType current = workList.next();
-      if (current.isIdenticalTo(appView.dexItemFactory().objectType)) {
+      DexClass current = workList.next();
+      if (current.getType().isIdenticalTo(appView.dexItemFactory().objectType)) {
         continue;
       }
-      DexClass currentClass = appView.definitionFor(current);
-      if (currentClass == null) {
-        reportInvalidSupertype(current, clazz);
-        continue;
-      }
-      if (currentClass.interfaces.contains(appView.dexItemFactory().autoCloseableType)) {
+      if (current.interfaces.contains(appView.dexItemFactory().autoCloseableType)) {
         return true;
       }
-      workList.addIfNotSeen(currentClass.superType);
-      workList.addIfNotSeen(currentClass.interfaces);
+      DexClass superClass = appView.definitionFor(current.superType);
+      if (superClass == null) {
+        reportInvalidSupertype(current.superType, clazz);
+      } else {
+        workList.addIfNotSeen(superClass);
+      }
+      for (DexType itf : current.interfaces) {
+        DexClass superItf = appView.definitionFor(itf);
+        if (superItf != null) {
+          workList.addIfNotSeen(superItf);
+        }
+      }
     }
     return false;
   }
@@ -130,7 +148,7 @@
             ImmutableSet.of(getClose(origin.type)));
   }
 
-  private void ensureInterfacesAndForwardingMethodsSynthesized(
+  private ProgramMethod ensureInterfacesAndForwardingMethodsSynthesized(
       AutoCloseableRetargeterPostProcessingEventConsumer eventConsumer,
       DexProgramClass clazz,
       MainThreadContext mainThreadContext) {
@@ -139,8 +157,9 @@
     // We cannot use the ClassProcessor since this applies up to 26, while the ClassProcessor
     // applies up to 24.
     if (appView.isAlreadyLibraryDesugared(clazz)) {
-      return;
+      return null;
     }
+    ProgramMethod bridge = null;
     DexMethod close = getClose(clazz.type);
     if (clazz.lookupVirtualMethod(close) == null) {
       DexEncodedMethod newMethod =
@@ -148,20 +167,20 @@
       if (newMethod == null) {
         // We don't support desugaring on all subtypes, in which case there is not need to inject
         // the interface.
-        return;
+        return bridge;
       }
-      clazz.addVirtualMethod(newMethod);
-      eventConsumer.acceptAutoCloseableForwardingMethod(new ProgramMethod(clazz, newMethod), clazz);
+      bridge = new ProgramMethod(clazz, newMethod);
     }
     DexType autoCloseableType = appView.dexItemFactory().autoCloseableType;
     if (clazz.interfaces.contains(autoCloseableType)) {
-      return;
+      return bridge;
     }
     clazz.addExtraInterfaces(
         Collections.singletonList(new ClassTypeSignature(autoCloseableType)),
         appView.dexItemFactory());
     eventConsumer.acceptAutoCloseableInterfaceInjection(
         clazz, appView.definitionFor(autoCloseableType));
+    return bridge;
   }
 
   @SuppressWarnings("ReferenceEquality")
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/EmulateDispatchSyntheticCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/EmulateDispatchSyntheticCfCodeProvider.java
index 2604cef..7f80f81 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/EmulateDispatchSyntheticCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/EmulateDispatchSyntheticCfCodeProvider.java
@@ -123,9 +123,17 @@
     assert dispatchType == AUTO_CLOSEABLE;
     instructions.add(new CfCheckCast(method.holder));
     loadExtraParameters(instructions);
-    if (appView.definitionFor(method.getHolderType()).isInterface()) {
+    // The type method.getHolderType() may not resolve if compiled without android library, for
+    // example, with the jdk as android.jar.
+    if (method
+        .getHolderType()
+        .isIdenticalTo(appView.dexItemFactory().javaUtilConcurrentExecutorServiceType)) {
+      assert appView.definitionFor(method.getHolderType()) == null
+          || appView.definitionFor(method.getHolderType()).isInterface();
       instructions.add(new CfInvoke(Opcodes.INVOKEINTERFACE, method, true));
     } else {
+      assert appView.definitionFor(method.getHolderType()) == null
+          || !appView.definitionFor(method.getHolderType()).isInterface();
       instructions.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, method, false));
     }
   }
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index b7da0fc..0081000 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -2129,7 +2129,7 @@
         System.getProperty("com.android.tools.r8.enableKeepAnnotations") != null;
     public boolean reverseClassSortingForDeterminism = false;
 
-    public boolean enableAutoCloseableDesugaring = false;
+    public boolean enableAutoCloseableDesugaring = true;
     public boolean enableNumberUnboxer = false;
     public boolean printNumberUnboxed = false;
     public boolean roundtripThroughLir = false;
@@ -2709,8 +2709,7 @@
   public boolean shouldDesugarAutoCloseable() {
     return desugarState.isOn()
         && getMinApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.K)
-        && canHaveMissingImplementsAutoCloseableInterface()
-        && testing.enableAutoCloseableDesugaring;
+        && canHaveMissingImplementsAutoCloseableInterface();
   }
 
   public boolean isSwitchRewritingEnabled() {
diff --git a/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterAndroidSubtypeTest.java b/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterAndroidSubtypeTest.java
index ad0d144..eed8542 100644
--- a/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterAndroidSubtypeTest.java
+++ b/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterAndroidSubtypeTest.java
@@ -8,17 +8,16 @@
 import static com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.getAutoCloseableAndroidClassData;
 import static org.hamcrest.CoreMatchers.containsString;
 
-import com.android.tools.r8.D8TestBuilder;
 import com.android.tools.r8.D8TestCompileResult;
 import com.android.tools.r8.TestBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.ContentProviderClient;
-import com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.DrmManagerClient;
-import com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.MediaMetadataRetriever;
-import com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.TypedArray;
+import com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.ContentProviderClientApiLevel24;
+import com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.DrmManagerClientApiLevel24;
+import com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.MediaMetadataRetrieverApiLevel29;
+import com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.TypedArrayAndroidApiLevel31;
 import com.android.tools.r8.desugar.backports.AbstractBackportTest;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.utils.AndroidApiLevel;
@@ -82,11 +81,6 @@
   }
 
   @Override
-  protected void configureD8Options(D8TestBuilder d8TestBuilder) throws IOException {
-    d8TestBuilder.addOptionsModification(opt -> opt.testing.enableAutoCloseableDesugaring = true);
-  }
-
-  @Override
   protected void configureProgram(TestBuilder<?, ?> builder) throws Exception {
     super.configureProgram(builder);
     if (builder.isJvmTestBuilder()) {
@@ -153,22 +147,22 @@
   private static byte[] getTestRunner() throws IOException {
     return transformer(TestRunner.class)
         .replaceClassDescriptorInMethodInstructions(
-            descriptor(ContentProviderClient.class),
+            descriptor(ContentProviderClientApiLevel24.class),
             DexItemFactory.androidContentContentProviderClientDescriptorString)
         .replaceClassDescriptorInMethodInstructions(
-            descriptor(DrmManagerClient.class),
+            descriptor(DrmManagerClientApiLevel24.class),
             DexItemFactory.androidDrmDrmManagerClientDescriptorString)
         .replaceClassDescriptorInMethodInstructions(
-            descriptor(MediaMetadataRetriever.class),
+            descriptor(MediaMetadataRetrieverApiLevel29.class),
             DexItemFactory.androidMediaMediaMetadataRetrieverDescriptorString)
         .replaceClassDescriptorInMethodInstructions(
-            descriptor(TypedArray.class),
+            descriptor(TypedArrayAndroidApiLevel31.class),
             DexItemFactory.androidContentResTypedArrayDescriptorString)
         .clearNest()
         .transform();
   }
 
-  public static class ContentProviderClientOverride extends ContentProviderClient {
+  public static class ContentProviderClientOverride extends ContentProviderClientApiLevel24 {
 
     public void close() {
       super.close();
@@ -176,9 +170,9 @@
     }
   }
 
-  public static class ContentProviderClientSub extends ContentProviderClient {}
+  public static class ContentProviderClientSub extends ContentProviderClientApiLevel24 {}
 
-  public static class DrmManagerClientOverride extends DrmManagerClient {
+  public static class DrmManagerClientOverride extends DrmManagerClientApiLevel24 {
 
     public void close() {
       super.close();
@@ -186,9 +180,9 @@
     }
   }
 
-  public static class DrmManagerClientSub extends DrmManagerClient {}
+  public static class DrmManagerClientSub extends DrmManagerClientApiLevel24 {}
 
-  public static class MediaMetadataRetrieverOverride extends MediaMetadataRetriever {
+  public static class MediaMetadataRetrieverOverride extends MediaMetadataRetrieverApiLevel29 {
 
     public void close() {
       super.close();
@@ -196,9 +190,9 @@
     }
   }
 
-  public static class MediaMetadataRetrieverSub extends MediaMetadataRetriever {}
+  public static class MediaMetadataRetrieverSub extends MediaMetadataRetrieverApiLevel29 {}
 
-  public static class TypedArrayOverride extends TypedArray {
+  public static class TypedArrayOverride extends TypedArrayAndroidApiLevel31 {
 
     public void close() {
       super.close();
@@ -206,7 +200,7 @@
     }
   }
 
-  public static class TypedArraySub extends TypedArray {}
+  public static class TypedArraySub extends TypedArrayAndroidApiLevel31 {}
 
   public static class TestRunner extends MiniAssert {
 
diff --git a/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterAndroidSubtypeTwrTest.java b/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterAndroidSubtypeTwrTest.java
index efad24f..c6b4632 100644
--- a/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterAndroidSubtypeTwrTest.java
+++ b/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterAndroidSubtypeTwrTest.java
@@ -8,11 +8,11 @@
 import static com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.getAutoCloseableAndroidClassData;
 import static org.hamcrest.CoreMatchers.containsString;
 
-import com.android.tools.r8.D8TestBuilder;
 import com.android.tools.r8.D8TestCompileResult;
 import com.android.tools.r8.TestBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.ContentProviderClientApiLevel24;
 import com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.DrmManagerClientApiLevel24;
@@ -36,6 +36,7 @@
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
     return getTestParameters()
+        .withCfRuntime(CfVm.JDK21)
         .withDexRuntimes()
         .withApiLevelsStartingAtIncluding(AndroidApiLevel.K)
         .enableApiLevelsForCf()
@@ -82,11 +83,6 @@
   }
 
   @Override
-  protected void configureD8Options(D8TestBuilder d8TestBuilder) throws IOException {
-    d8TestBuilder.addOptionsModification(opt -> opt.testing.enableAutoCloseableDesugaring = true);
-  }
-
-  @Override
   protected void configureProgram(TestBuilder<?, ?> builder) throws Exception {
     super.configureProgram(builder);
     if (builder.isJvmTestBuilder()) {
@@ -321,10 +317,5 @@
       }
       MiniAssert.assertTrue(box2[0].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/examplesJava21/autocloseable/AutoCloseableRetargeterAndroidTest.java b/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterAndroidTest.java
index 5668685..885be86 100644
--- a/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterAndroidTest.java
+++ b/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterAndroidTest.java
@@ -8,7 +8,6 @@
 import static com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.getAutoCloseableAndroidClassData;
 import static org.hamcrest.CoreMatchers.containsString;
 
-import com.android.tools.r8.D8TestBuilder;
 import com.android.tools.r8.D8TestCompileResult;
 import com.android.tools.r8.TestBuilder;
 import com.android.tools.r8.TestParameters;
@@ -54,12 +53,6 @@
     registerTarget(AndroidApiLevel.B, 5);
   }
 
-  @Override
-  protected void configureD8Options(D8TestBuilder d8TestBuilder) throws IOException {
-    d8TestBuilder.addOptionsModification(opt -> opt.testing.enableAutoCloseableDesugaring = true);
-  }
-
-  @Override
   protected void configureProgram(TestBuilder<?, ?> builder) throws Exception {
     super.configureProgram(builder);
     if (builder.isJvmTestBuilder()) {
diff --git a/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterExecutorServiceSubtypeTest.java b/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterExecutorServiceSubtypeTest.java
index d9c567c..f80a7a5 100644
--- a/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterExecutorServiceSubtypeTest.java
+++ b/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterExecutorServiceSubtypeTest.java
@@ -49,7 +49,23 @@
   }
 
   public static String EXPECTED_OUTPUT =
-      StringUtils.lines("close", "close", "close", "close", "close", "close", "close", "SUCCESS");
+      StringUtils.lines(
+          "close",
+          "close",
+          "close",
+          "close",
+          "close",
+          "close",
+          "close",
+          "close itf",
+          "close itf",
+          "close",
+          "close",
+          "close itf",
+          "close itf",
+          "close ac",
+          "close ac direct",
+          "SUCCESS");
 
   @Test
   public void testJvm() throws Exception {
@@ -66,7 +82,6 @@
     assumeTrue(parameters.isDexRuntime());
     testForD8(parameters.getBackend())
         .addInnerClassesAndStrippedOuter(getClass())
-        .addOptionsModification(opt -> opt.testing.enableAutoCloseableDesugaring = true)
         .setMinApi(parameters)
         .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.BAKLAVA))
         .addLibraryClassFileData(getAutoCloseableAndroidClassData(parameters))
@@ -104,7 +119,6 @@
         .addKeepMainRule(Main.class)
         .addInnerClassesAndStrippedOuter(getClass())
         .addInliningAnnotations()
-        .addOptionsModification(opt -> opt.testing.enableAutoCloseableDesugaring = true)
         .setMinApi(parameters)
         .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.BAKLAVA))
         .addLibraryClassFileData(getAutoCloseableAndroidClassData(parameters))
@@ -282,6 +296,340 @@
     public void execute(Runnable command) {}
   }
 
+  public interface ExecutorItfOverride extends ExecutorService {
+    default void close() {
+      ExecutorService.super.close();
+      System.out.println("close itf");
+    }
+  }
+
+  public interface ExecutorItfSub extends ExecutorService {}
+
+  public static class ExecOverride1 implements ExecutorItfOverride {
+    boolean done = false;
+
+    @Override
+    public void shutdown() {
+      done = true;
+    }
+
+    @Override
+    public List<Runnable> shutdownNow() {
+      done = true;
+      return null;
+    }
+
+    @Override
+    public boolean isShutdown() {
+      return done;
+    }
+
+    @Override
+    public boolean isTerminated() {
+      return done;
+    }
+
+    @Override
+    public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+      return true;
+    }
+
+    @Override
+    public <T> Future<T> submit(Callable<T> task) {
+      return null;
+    }
+
+    @Override
+    public <T> Future<T> submit(Runnable task, T result) {
+      return null;
+    }
+
+    @Override
+    public Future<?> submit(Runnable task) {
+      return null;
+    }
+
+    @Override
+    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
+        throws InterruptedException {
+      return null;
+    }
+
+    @Override
+    public <T> List<Future<T>> invokeAll(
+        Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+        throws InterruptedException {
+      return null;
+    }
+
+    @Override
+    public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
+        throws InterruptedException, ExecutionException {
+      return null;
+    }
+
+    @Override
+    public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+        throws InterruptedException, ExecutionException, TimeoutException {
+      return null;
+    }
+
+    @Override
+    public void execute(Runnable command) {}
+  }
+
+  public static class ExecOverride2 implements ExecutorItfOverride {
+    boolean done = false;
+
+    @Override
+    public void shutdown() {
+      done = true;
+    }
+
+    @Override
+    public List<Runnable> shutdownNow() {
+      done = true;
+      return null;
+    }
+
+    public void close() {
+      ExecutorItfOverride.super.close();
+      System.out.println("close");
+    }
+
+    @Override
+    public boolean isShutdown() {
+      return done;
+    }
+
+    @Override
+    public boolean isTerminated() {
+      return done;
+    }
+
+    @Override
+    public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+      return true;
+    }
+
+    @Override
+    public <T> Future<T> submit(Callable<T> task) {
+      return null;
+    }
+
+    @Override
+    public <T> Future<T> submit(Runnable task, T result) {
+      return null;
+    }
+
+    @Override
+    public Future<?> submit(Runnable task) {
+      return null;
+    }
+
+    @Override
+    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
+        throws InterruptedException {
+      return null;
+    }
+
+    @Override
+    public <T> List<Future<T>> invokeAll(
+        Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+        throws InterruptedException {
+      return null;
+    }
+
+    @Override
+    public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
+        throws InterruptedException, ExecutionException {
+      return null;
+    }
+
+    @Override
+    public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+        throws InterruptedException, ExecutionException, TimeoutException {
+      return null;
+    }
+
+    @Override
+    public void execute(Runnable command) {}
+  }
+
+  public static class ExecSub1 implements ExecutorItfSub {
+    boolean done = false;
+
+    @Override
+    public void shutdown() {
+      done = true;
+    }
+
+    @Override
+    public List<Runnable> shutdownNow() {
+      done = true;
+      return null;
+    }
+
+    @Override
+    public boolean isShutdown() {
+      return done;
+    }
+
+    @Override
+    public boolean isTerminated() {
+      return done;
+    }
+
+    @Override
+    public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+      return true;
+    }
+
+    @Override
+    public <T> Future<T> submit(Callable<T> task) {
+      return null;
+    }
+
+    @Override
+    public <T> Future<T> submit(Runnable task, T result) {
+      return null;
+    }
+
+    @Override
+    public Future<?> submit(Runnable task) {
+      return null;
+    }
+
+    @Override
+    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
+        throws InterruptedException {
+      return null;
+    }
+
+    @Override
+    public <T> List<Future<T>> invokeAll(
+        Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+        throws InterruptedException {
+      return null;
+    }
+
+    @Override
+    public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
+        throws InterruptedException, ExecutionException {
+      return null;
+    }
+
+    @Override
+    public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+        throws InterruptedException, ExecutionException, TimeoutException {
+      return null;
+    }
+
+    @Override
+    public void execute(Runnable command) {}
+  }
+
+  public static class ExecSub2 implements ExecutorItfSub {
+    boolean done = false;
+
+    @Override
+    public void shutdown() {
+      done = true;
+    }
+
+    @Override
+    public List<Runnable> shutdownNow() {
+      done = true;
+      return null;
+    }
+
+    public void close() {
+      ExecutorItfSub.super.close();
+      System.out.println("close");
+    }
+
+    @Override
+    public boolean isShutdown() {
+      return done;
+    }
+
+    @Override
+    public boolean isTerminated() {
+      return done;
+    }
+
+    @Override
+    public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+      return true;
+    }
+
+    @Override
+    public <T> Future<T> submit(Callable<T> task) {
+      return null;
+    }
+
+    @Override
+    public <T> Future<T> submit(Runnable task, T result) {
+      return null;
+    }
+
+    @Override
+    public Future<?> submit(Runnable task) {
+      return null;
+    }
+
+    @Override
+    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
+        throws InterruptedException {
+      return null;
+    }
+
+    @Override
+    public <T> List<Future<T>> invokeAll(
+        Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+        throws InterruptedException {
+      return null;
+    }
+
+    @Override
+    public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
+        throws InterruptedException, ExecutionException {
+      return null;
+    }
+
+    @Override
+    public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+        throws InterruptedException, ExecutionException, TimeoutException {
+      return null;
+    }
+
+    @Override
+    public void execute(Runnable command) {}
+  }
+
+  public interface AutoCloseableOverride extends AutoCloseable {
+    default void close() {
+      System.out.println("close itf");
+    }
+  }
+
+  public interface AutoCloseablesub extends AutoCloseable {}
+
+  public static class AutoCloseableOverride1 implements AutoCloseableOverride {}
+
+  public static class AutoCloseableOverride2 implements AutoCloseableOverride {
+    public void close() {
+      AutoCloseableOverride.super.close();
+      System.out.println("close ac");
+    }
+  }
+
+  public static class AutoCloseableOverride3 implements AutoCloseablesub {
+    public void close() {
+      System.out.println("close ac direct");
+    }
+  }
+
   public static class Main {
 
     public static void main(String[] args) throws Exception {
@@ -333,6 +681,22 @@
       ExecutorService executor22 = new Executor2();
       close(executor22);
 
+      ExecOverride1 execOverride1 = new ExecOverride1();
+      execOverride1.close();
+      ExecOverride2 execOverride2 = new ExecOverride2();
+      execOverride2.close();
+      ExecSub1 execSub1 = new ExecSub1();
+      execSub1.close();
+      ExecSub2 execSub2 = new ExecSub2();
+      execSub2.close();
+
+      AutoCloseableOverride1 autoCloseableOverride1 = new AutoCloseableOverride1();
+      autoCloseableOverride1.close();
+      AutoCloseableOverride2 autoCloseableOverride2 = new AutoCloseableOverride2();
+      autoCloseableOverride2.close();
+      AutoCloseableOverride3 autoCloseableOverride3 = new AutoCloseableOverride3();
+      autoCloseableOverride3.close();
+
       System.out.println("SUCCESS");
     }
 
diff --git a/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterExecutorServiceSubtypeTwrTest.java b/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterExecutorServiceSubtypeTwrTest.java
new file mode 100644
index 0000000..5a41819
--- /dev/null
+++ b/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterExecutorServiceSubtypeTwrTest.java
@@ -0,0 +1,357 @@
+// Copyright (c) 2025, 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 autocloseable;
+
+import static com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.getAutoCloseableAndroidClassData;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DeterminismChecker;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import org.junit.Assert;
+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 AutoCloseableRetargeterExecutorServiceSubtypeTwrTest extends TestBase {
+
+  @Parameter public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters()
+        .withCfRuntime(CfVm.JDK21)
+        .withDexRuntimes()
+        .withApiLevelsStartingAtIncluding(AndroidApiLevel.L)
+        .build();
+  }
+
+  public static String EXPECTED_OUTPUT =
+      StringUtils.lines("close", "close", "close", "close", "close", "SUCCESS");
+
+  @Test
+  public void testJvm() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    testForJvm(parameters)
+        .addInnerClassesAndStrippedOuter(getClass())
+        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.BAKLAVA))
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    testForD8(parameters.getBackend())
+        .addInnerClassesAndStrippedOuter(getClass())
+        .setMinApi(parameters)
+        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.BAKLAVA))
+        .addLibraryClassFileData(getAutoCloseableAndroidClassData(parameters))
+        .compile()
+        .addRunClasspathClassFileData((getAutoCloseableAndroidClassData(parameters)))
+        .inspect(this::assertCloseMethodsAndTags)
+        .run(
+            parameters.getRuntime(),
+            Main.class,
+            String.valueOf(parameters.getApiLevel().getLevel()))
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  private void assertCloseMethodsAndTags(CodeInspector inspector) {
+    for (Class<? extends ExecutorService> clazz :
+        ImmutableList.of(
+            PrintForkJoinPool.class,
+            OverrideForkJoinPool.class,
+            Executor1.class,
+            Executor2.class)) {
+      ClassSubject subj = inspector.clazz(clazz);
+      Assert.assertTrue(subj.isPresent());
+      Assert.assertTrue(subj.allMethods().stream().anyMatch(m -> m.getFinalName().equals("close")));
+      Assert.assertTrue(
+          subj.getDexProgramClass()
+              .getInterfaces()
+              .contains(inspector.getFactory().autoCloseableType));
+    }
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    testForR8(parameters.getBackend())
+        .addKeepMainRule(Main.class)
+        .addInnerClassesAndStrippedOuter(getClass())
+        .addInliningAnnotations()
+        .setMinApi(parameters)
+        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.BAKLAVA))
+        .addLibraryClassFileData(getAutoCloseableAndroidClassData(parameters))
+        .compile()
+        .addRunClasspathClassFileData((getAutoCloseableAndroidClassData(parameters)))
+        .run(
+            parameters.getRuntime(),
+            Main.class,
+            String.valueOf(parameters.getApiLevel().getLevel()))
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testD8Determinism() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    Path logDirectory = temp.newFolder().toPath();
+    Path ref = compileWithD8Determinism(logDirectory);
+    Path next = compileWithD8Determinism(logDirectory);
+    assertProgramsEqual(ref, next);
+    // Check that setting the determinism checker wrote a log file.
+    assertTrue(Files.exists(logDirectory.resolve("0.log")));
+  }
+
+  private Path compileWithD8Determinism(Path logDirectory) throws Exception {
+    return testForD8(parameters.getBackend())
+        .addInnerClassesAndStrippedOuter(getClass())
+        .setMinApi(parameters)
+        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.BAKLAVA))
+        .addLibraryClassFileData(getAutoCloseableAndroidClassData(parameters))
+        .allowStdoutMessages()
+        .addOptionsModification(
+            options ->
+                options
+                    .getTestingOptions()
+                    .setDeterminismChecker(DeterminismChecker.createWithFileBacking(logDirectory)))
+        .compile()
+        .writeToZip();
+  }
+
+  public static class PrintForkJoinPool extends ForkJoinPool {
+
+    public void close() {
+      super.close();
+      System.out.println("close");
+    }
+  }
+
+  public static class OverrideForkJoinPool extends ForkJoinPool {}
+
+  public static class OverrideForkJoinPool2 extends OverrideForkJoinPool {}
+
+  public static class Executor1 implements ExecutorService {
+
+    boolean done = false;
+
+    @Override
+    public void shutdown() {
+      done = true;
+    }
+
+    @Override
+    public List<Runnable> shutdownNow() {
+      done = true;
+      return null;
+    }
+
+    @Override
+    public boolean isShutdown() {
+      return done;
+    }
+
+    @Override
+    public boolean isTerminated() {
+      return done;
+    }
+
+    @Override
+    public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+      return true;
+    }
+
+    @Override
+    public <T> Future<T> submit(Callable<T> task) {
+      return null;
+    }
+
+    @Override
+    public <T> Future<T> submit(Runnable task, T result) {
+      return null;
+    }
+
+    @Override
+    public Future<?> submit(Runnable task) {
+      return null;
+    }
+
+    @Override
+    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
+        throws InterruptedException {
+      return null;
+    }
+
+    @Override
+    public <T> List<Future<T>> invokeAll(
+        Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+        throws InterruptedException {
+      return null;
+    }
+
+    @Override
+    public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
+        throws InterruptedException, ExecutionException {
+      return null;
+    }
+
+    @Override
+    public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+        throws InterruptedException, ExecutionException, TimeoutException {
+      return null;
+    }
+
+    @Override
+    public void execute(Runnable command) {}
+  }
+
+  public static class Executor2 implements ExecutorService {
+
+    boolean done = false;
+
+    @Override
+    public void shutdown() {
+      done = true;
+    }
+
+    @Override
+    public List<Runnable> shutdownNow() {
+      done = true;
+      return null;
+    }
+
+    @Override
+    public boolean isShutdown() {
+      return done;
+    }
+
+    @Override
+    public boolean isTerminated() {
+      return done;
+    }
+
+    public void close() {
+      ExecutorService.super.close();
+      System.out.println("close");
+    }
+
+    @Override
+    public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+      return true;
+    }
+
+    @Override
+    public <T> Future<T> submit(Callable<T> task) {
+      return null;
+    }
+
+    @Override
+    public <T> Future<T> submit(Runnable task, T result) {
+      return null;
+    }
+
+    @Override
+    public Future<?> submit(Runnable task) {
+      return null;
+    }
+
+    @Override
+    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
+        throws InterruptedException {
+      return null;
+    }
+
+    @Override
+    public <T> List<Future<T>> invokeAll(
+        Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+        throws InterruptedException {
+      return null;
+    }
+
+    @Override
+    public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
+        throws InterruptedException, ExecutionException {
+      return null;
+    }
+
+    @Override
+    public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+        throws InterruptedException, ExecutionException, TimeoutException {
+      return null;
+    }
+
+    @Override
+    public void execute(Runnable command) {}
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) throws Exception {
+      raw();
+      subtypes();
+      System.out.println("SUCCESS");
+    }
+
+    private static void subtypes() throws Exception {
+      try (PrintForkJoinPool forkJoinPool = new PrintForkJoinPool()) {}
+
+      try (ForkJoinPool forkJoinPool = new PrintForkJoinPool()) {}
+
+      try (ExecutorService executorService = new PrintForkJoinPool()) {}
+
+      try (OverrideForkJoinPool overrideForkJoinPool = new OverrideForkJoinPool()) {}
+
+      try (ForkJoinPool overrideForkJoinPool1 = new OverrideForkJoinPool()) {}
+
+      try (ExecutorService executorService = new OverrideForkJoinPool()) {}
+
+      try (OverrideForkJoinPool2 overrideForkJoinPool = new OverrideForkJoinPool2()) {}
+
+      try (OverrideForkJoinPool overrideForkJoinPool = new OverrideForkJoinPool2()) {}
+
+      try (ForkJoinPool overrideForkJoinPool1 = new OverrideForkJoinPool2()) {}
+
+      try (ExecutorService executorService = new OverrideForkJoinPool2()) {}
+
+      try (Executor1 executorService = new Executor1()) {}
+
+      try (ExecutorService executorService = new Executor1()) {}
+
+      try (Executor2 executorService = new Executor2()) {}
+
+      try (ExecutorService executorService = new Executor2()) {}
+    }
+
+    private static void raw() throws Exception {
+      try (ForkJoinPool forkJoinPool = new ForkJoinPool()) {}
+
+      try (ExecutorService forkJoinPool = new ForkJoinPool()) {}
+    }
+  }
+}
diff --git a/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterExecutorServiceTest.java b/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterExecutorServiceTest.java
index ae043fd..5a47d26 100644
--- a/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterExecutorServiceTest.java
+++ b/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterExecutorServiceTest.java
@@ -49,7 +49,6 @@
   public void testD8() throws Exception {
     testForD8(parameters.getBackend())
         .addInnerClassesAndStrippedOuter(getClass())
-        .addOptionsModification(opt -> opt.testing.enableAutoCloseableDesugaring = true)
         .setMinApi(parameters)
         .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.BAKLAVA))
         .addLibraryClassFileData(getAutoCloseableAndroidClassData(parameters))
@@ -68,7 +67,6 @@
         .addKeepMainRule(Main.class)
         .addInnerClassesAndStrippedOuter(getClass())
         .addInliningAnnotations()
-        .addOptionsModification(opt -> opt.testing.enableAutoCloseableDesugaring = true)
         .setMinApi(parameters)
         .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.BAKLAVA))
         .addLibraryClassFileData(getAutoCloseableAndroidClassData(parameters))
diff --git a/src/test/examplesJava21/twr/ExecutorServiceBackportTest.java b/src/test/examplesJava21/twr/ExecutorServiceBackportTest.java
index a57d54b..a38220c 100644
--- a/src/test/examplesJava21/twr/ExecutorServiceBackportTest.java
+++ b/src/test/examplesJava21/twr/ExecutorServiceBackportTest.java
@@ -30,6 +30,7 @@
         .withCfRuntimesStartingFromIncluding(CfVm.JDK21)
         .withDexRuntimesStartingFromExcluding(Version.V4_4_4)
         .withAllApiLevelsAlsoForCf()
+        .withApiLevel(AndroidApiLevel.K)
         .build();
   }
 
@@ -60,11 +61,6 @@
     }
   }
 
-  @Override
-  public void testD8Cf() throws Exception {
-    super.testD8Cf();
-  }
-
   public static class Main {
 
     public static void main(String[] args) {
diff --git a/src/test/examplesJava9/twr/twraddsuppressed/TwrSuppressedExceptionsTest.java b/src/test/examplesJava9/twr/twraddsuppressed/TwrSuppressedExceptionsTest.java
index 573fc99..7efe94e 100644
--- a/src/test/examplesJava9/twr/twraddsuppressed/TwrSuppressedExceptionsTest.java
+++ b/src/test/examplesJava9/twr/twraddsuppressed/TwrSuppressedExceptionsTest.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.IntBox;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -18,6 +19,7 @@
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import com.google.common.collect.ImmutableList;
 import java.util.List;
+import java.util.concurrent.ForkJoinPool;
 import java.util.stream.Collectors;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -114,11 +116,15 @@
         .addProgramClassFileData(getProgramInputs())
         .setMinApi(parameters)
         .addKeepMainRule(TEST_CLASS)
-        // TODO(b/214250388): Don't warn about AutoClosable in synthesized code.
+        // TODO(b/214250388): Don't warn about AutoClosable and ForkJoinPool in synthesized code.
         .apply(
             b -> {
               if (!apiLevelHasTwrCloseResourceSupport(parameters.isDexRuntime())) {
                 b.addDontWarn(AutoCloseable.class);
+              } else if (parameters
+                  .getApiLevel()
+                  .isBetweenBothIncluded(AndroidApiLevel.K, AndroidApiLevel.K_WATCH)) {
+                b.addDontWarn(ForkJoinPool.class);
               }
             })
         .run(parameters.getRuntime(), TEST_CLASS)
diff --git a/src/test/examplesJava9/twr/twrcloseresourceduplication/TwrCloseResourceDuplicationProfileRewritingTest.java b/src/test/examplesJava9/twr/twrcloseresourceduplication/TwrCloseResourceDuplicationProfileRewritingTest.java
index 4cad84a..1713cf4 100644
--- a/src/test/examplesJava9/twr/twrcloseresourceduplication/TwrCloseResourceDuplicationProfileRewritingTest.java
+++ b/src/test/examplesJava9/twr/twrcloseresourceduplication/TwrCloseResourceDuplicationProfileRewritingTest.java
@@ -18,11 +18,13 @@
 import com.android.tools.r8.references.TypeReference;
 import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.InternalOptions.InlinerOptions;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import com.google.common.collect.ImmutableList;
+import java.util.Arrays;
 import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -137,6 +139,11 @@
     if (hasTwrCloseResourceApiOutlines()) {
       expectedClassCount += 4;
     }
+    InternalOptions options = inspector.getApplication().options;
+    options.setMinApiLevel(parameters.getApiLevel());
+    if (options.shouldDesugarAutoCloseable()) {
+      expectedClassCount += 12;
+    }
     inspector
         .allClasses()
         .forEach(c -> System.out.println(c.getDexProgramClass().toSourceString()));
@@ -240,6 +247,52 @@
                       syntheticTwrCloseResourceClassSubject5.uniqueMethod()));
     }
 
+    profileInspector.applyIf(
+        options.shouldDesugarAutoCloseable(),
+        i ->
+            i.assertContainsClassRules(getCloseDispatcherSyntheticClasses(inspector))
+                .assertContainsMethodRules(
+                    Arrays.stream(getCloseDispatcherSyntheticClasses(inspector))
+                        .map(ClassSubject::uniqueMethod)
+                        .toArray(MethodSubject[]::new)));
+
     profileInspector.assertContainsNoOtherRules();
   }
+
+  private static ClassSubject[] getCloseDispatcherSyntheticClasses(CodeInspector inspector) {
+    return new ClassSubject[] {
+      inspector.clazz(
+          SyntheticItemsTestUtils.syntheticAutoCloseableDispatcherClass(
+              Reference.classFromTypeName(FOO), 0)),
+      inspector.clazz(
+          SyntheticItemsTestUtils.syntheticAutoCloseableDispatcherClass(
+              Reference.classFromTypeName(FOO), 1)),
+      inspector.clazz(
+          SyntheticItemsTestUtils.syntheticAutoCloseableDispatcherClass(
+              Reference.classFromTypeName(BAR), 0)),
+      inspector.clazz(
+          SyntheticItemsTestUtils.syntheticAutoCloseableDispatcherClass(
+              Reference.classFromTypeName(BAR), 1)),
+      inspector.clazz(
+          SyntheticItemsTestUtils.syntheticAutoCloseableForwarderClass(
+              Reference.classFromTypeName(FOO), 2)),
+      inspector.clazz(
+          SyntheticItemsTestUtils.syntheticAutoCloseableForwarderClass(
+              Reference.classFromTypeName(FOO), 3)),
+      inspector.clazz(
+          SyntheticItemsTestUtils.syntheticAutoCloseableForwarderClass(
+              Reference.classFromTypeName(BAR), 2)),
+      inspector.clazz(
+          SyntheticItemsTestUtils.syntheticAutoCloseableForwarderClass(
+              Reference.classFromTypeName(BAR), 3)),
+      inspector.clazz(
+          SyntheticItemsTestUtils.syntheticThrowIAEClass(Reference.classFromTypeName(FOO), 4)),
+      inspector.clazz(
+          SyntheticItemsTestUtils.syntheticThrowIAEClass(Reference.classFromTypeName(FOO), 5)),
+      inspector.clazz(
+          SyntheticItemsTestUtils.syntheticThrowIAEClass(Reference.classFromTypeName(BAR), 4)),
+      inspector.clazz(
+          SyntheticItemsTestUtils.syntheticThrowIAEClass(Reference.classFromTypeName(BAR), 5))
+    };
+  }
 }
diff --git a/src/test/examplesJava9/twr/twrcloseresourceduplication/TwrCloseResourceDuplicationTest.java b/src/test/examplesJava9/twr/twrcloseresourceduplication/TwrCloseResourceDuplicationTest.java
index 930ae37..471dffb 100644
--- a/src/test/examplesJava9/twr/twrcloseresourceduplication/TwrCloseResourceDuplicationTest.java
+++ b/src/test/examplesJava9/twr/twrcloseresourceduplication/TwrCloseResourceDuplicationTest.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.ZipUtils;
 import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
@@ -127,6 +128,11 @@
               if (hasTwrCloseResourceApiOutlines()) {
                 expectedSynthetics += 1;
               }
+              InternalOptions options = inspector.getApplication().options;
+              options.setMinApiLevel(parameters.getApiLevel());
+              if (options.shouldDesugarAutoCloseable()) {
+                expectedSynthetics += 3;
+              }
               assertEquals(INPUT_CLASSES + expectedSynthetics, inspector.allClasses().size());
             });
   }
@@ -161,7 +167,19 @@
                         .getTypeName());
                 assertEquals(classOutputWithSynthetics, foundClasses);
               } else {
-                assertEquals(nonSyntheticClassOutput, foundClasses);
+                Set<String> classOutputWithSynthetics = new HashSet<>(nonSyntheticClassOutput);
+                if (parameters.getApiLevel().isLessThan(AndroidApiLevel.N)) {
+                  // Above N, the forwarder is inlined in the dispatcher.
+                  classOutputWithSynthetics.add(
+                      SyntheticItemsTestUtils.syntheticAutoCloseableForwarderClass(
+                              Reference.classFromTypeName(BAR), 1)
+                          .getTypeName());
+                }
+                classOutputWithSynthetics.add(
+                    SyntheticItemsTestUtils.syntheticAutoCloseableDispatcherClass(
+                            Reference.classFromTypeName(BAR), 0)
+                        .getTypeName());
+                assertEquals(classOutputWithSynthetics, foundClasses);
               }
             });
   }
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
index 8d3b5f2..f666284 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ContentProviderClientBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ContentProviderClientBackportTest.java
@@ -4,12 +4,20 @@
 
 package com.android.tools.r8.desugar.backports;
 
+import static com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.compileAutoCloseableAndroidLibraryClasses;
+import static com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.getAutoCloseableAndroidClassData;
 import static org.hamcrest.CoreMatchers.containsString;
 
+import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.TestBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.ContentProviderClient;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DescriptorUtils;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
 import org.junit.Test;
@@ -22,16 +30,19 @@
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+    return getTestParameters()
+        .withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+        .withDexRuntimes()
+        .withAllApiLevelsAlsoForCf()
+        .build();
   }
 
   public ContentProviderClientBackportTest(TestParameters parameters) throws IOException {
     super(
         parameters,
-        ContentProviderClientBackportTest.getContentProviderClient(parameters),
-        ImmutableList.of(
-            ContentProviderClientBackportTest.getTestRunner(),
-            ContentProviderClientBackportTest.getContentProviderClient(parameters)));
+        DescriptorUtils.descriptorToJavaType(
+            DexItemFactory.androidContentContentProviderClientDescriptorString),
+        ImmutableList.of(ContentProviderClientBackportTest.getTestRunner()));
 
     // The constructor is used by the test and release has been available since API 5 and is the
     // method close is rewritten to.
@@ -42,6 +53,27 @@
     registerTarget(AndroidApiLevel.N, 1);
   }
 
+  @Override
+  protected void configureProgram(TestBuilder<?, ?> builder) throws Exception {
+    super.configureProgram(builder);
+    if (builder.isJvmTestBuilder()) {
+      builder.addProgramClassFileData(getAutoCloseableAndroidClassData(parameters));
+    } else {
+      builder
+          .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.BAKLAVA))
+          .addLibraryClassFileData(getAutoCloseableAndroidClassData(parameters));
+    }
+  }
+
+  @Override
+  protected void configure(D8TestCompileResult builder) throws Exception {
+    if (parameters.isDexRuntime()) {
+      builder.addBootClasspathFiles(compileAutoCloseableAndroidLibraryClasses(this, parameters));
+    } else {
+      builder.addRunClasspathClassFileData(getAutoCloseableAndroidClassData(parameters));
+    }
+  }
+
   @Test
   public void testJvm() throws Exception {
     parameters.assumeJvmTestParameters();
@@ -49,19 +81,7 @@
         .apply(this::configureProgram)
         .run(parameters.getRuntime(), getTestClassName())
         // Fails when not desugared.
-        .assertFailureWithErrorThatMatches(containsString("Failed: close should not be called"));
-  }
-
-  private static byte[] getContentProviderClient(TestParameters parameters) throws IOException {
-    if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)) {
-      return transformer(ContentProviderClientApiLevel24.class)
-          .setClassDescriptor(DexItemFactory.androidContentContentProviderClientDescriptorString)
-          .transform();
-    } else {
-      return transformer(ContentProviderClient.class)
-          .setClassDescriptor(DexItemFactory.androidContentContentProviderClientDescriptorString)
-          .transform();
-    }
+        .assertFailureWithErrorThatMatches(containsString("close should not be called"));
   }
 
   private static byte[] getTestRunner() throws IOException {
@@ -72,32 +92,6 @@
         .transform();
   }
 
-  public static class ContentProviderClient {
-    public boolean wasClosed = false;
-
-    public void close() {
-      TestRunner.doFail("close should not be called");
-    }
-
-    public boolean release() {
-      wasClosed = true;
-      return wasClosed;
-    }
-  }
-
-  public static class ContentProviderClientApiLevel24 {
-    public boolean wasClosed = false;
-
-    public void close() {
-      wasClosed = true;
-    }
-
-    public boolean release() {
-      TestRunner.doFail("release should not be called");
-      return true;
-    }
-  }
-
   public static class TestRunner extends MiniAssert {
 
     public static void main(String[] args) {
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
index 5d3ee55..ab89582 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/DrmManagerClientBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/DrmManagerClientBackportTest.java
@@ -4,12 +4,20 @@
 
 package com.android.tools.r8.desugar.backports;
 
+import static com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.compileAutoCloseableAndroidLibraryClasses;
+import static com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.getAutoCloseableAndroidClassData;
 import static org.hamcrest.CoreMatchers.containsString;
 
+import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.TestBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.DrmManagerClient;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DescriptorUtils;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
 import org.junit.Test;
@@ -22,16 +30,19 @@
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+    return getTestParameters()
+        .withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+        .withDexRuntimes()
+        .withAllApiLevelsAlsoForCf()
+        .build();
   }
 
   public DrmManagerClientBackportTest(TestParameters parameters) throws IOException {
     super(
         parameters,
-        DrmManagerClientBackportTest.getDrmManagerClient(parameters),
-        ImmutableList.of(
-            DrmManagerClientBackportTest.getTestRunner(),
-            DrmManagerClientBackportTest.getDrmManagerClient(parameters)));
+        DescriptorUtils.descriptorToJavaType(
+            DexItemFactory.androidDrmDrmManagerClientDescriptorString),
+        ImmutableList.of(DrmManagerClientBackportTest.getTestRunner()));
 
     // The constructor is used by the test and release has been available since API 5 and is the
     // method close is rewritten to.
@@ -42,6 +53,27 @@
     registerTarget(AndroidApiLevel.N, 1);
   }
 
+  @Override
+  protected void configureProgram(TestBuilder<?, ?> builder) throws Exception {
+    super.configureProgram(builder);
+    if (builder.isJvmTestBuilder()) {
+      builder.addProgramClassFileData(getAutoCloseableAndroidClassData(parameters));
+    } else {
+      builder
+          .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.BAKLAVA))
+          .addLibraryClassFileData(getAutoCloseableAndroidClassData(parameters));
+    }
+  }
+
+  @Override
+  protected void configure(D8TestCompileResult builder) throws Exception {
+    if (parameters.isDexRuntime()) {
+      builder.addBootClasspathFiles(compileAutoCloseableAndroidLibraryClasses(this, parameters));
+    } else {
+      builder.addRunClasspathClassFileData(getAutoCloseableAndroidClassData(parameters));
+    }
+  }
+
   @Test
   public void testJvm() throws Exception {
     parameters.assumeJvmTestParameters();
@@ -49,19 +81,7 @@
         .apply(this::configureProgram)
         .run(parameters.getRuntime(), getTestClassName())
         // Fails when not desugared.
-        .assertFailureWithErrorThatMatches(containsString("Failed: close should not be called"));
-  }
-
-  private static byte[] getDrmManagerClient(TestParameters parameters) throws IOException {
-    if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)) {
-      return transformer(DrmManagerClientApiLevel24.class)
-          .setClassDescriptor(DexItemFactory.androidDrmDrmManagerClientDescriptorString)
-          .transform();
-    } else {
-      return transformer(DrmManagerClient.class)
-          .setClassDescriptor(DexItemFactory.androidDrmDrmManagerClientDescriptorString)
-          .transform();
-    }
+        .assertFailureWithErrorThatMatches(containsString("close should not be called"));
   }
 
   private static byte[] getTestRunner() throws IOException {
@@ -72,30 +92,6 @@
         .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) {
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
index 144d841..3584912 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/MediaDrmBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MediaDrmBackportTest.java
@@ -4,12 +4,20 @@
 
 package com.android.tools.r8.desugar.backports;
 
+import static com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.compileAutoCloseableAndroidLibraryClasses;
+import static com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.getAutoCloseableAndroidClassData;
 import static org.hamcrest.CoreMatchers.containsString;
 
+import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.TestBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.MediaDrm;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DescriptorUtils;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
 import org.junit.Test;
@@ -22,15 +30,18 @@
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+    return getTestParameters()
+        .withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+        .withDexRuntimes()
+        .withAllApiLevelsAlsoForCf()
+        .build();
   }
 
   public MediaDrmBackportTest(TestParameters parameters) throws IOException {
     super(
         parameters,
-        MediaDrmBackportTest.getMediaDrm(parameters),
-        ImmutableList.of(
-            MediaDrmBackportTest.getTestRunner(), MediaDrmBackportTest.getMediaDrm(parameters)));
+        DescriptorUtils.descriptorToJavaType(DexItemFactory.androidMediaMediaDrmDescriptorString),
+        ImmutableList.of(MediaDrmBackportTest.getTestRunner()));
 
     // The constructor is used by the test and release has been available since API 18 and is the
     // method close is rewritten to.
@@ -41,6 +52,27 @@
     registerTarget(AndroidApiLevel.P, 1);
   }
 
+  @Override
+  protected void configureProgram(TestBuilder<?, ?> builder) throws Exception {
+    super.configureProgram(builder);
+    if (builder.isJvmTestBuilder()) {
+      builder.addProgramClassFileData(getAutoCloseableAndroidClassData(parameters));
+    } else {
+      builder
+          .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.BAKLAVA))
+          .addLibraryClassFileData(getAutoCloseableAndroidClassData(parameters));
+    }
+  }
+
+  @Override
+  protected void configure(D8TestCompileResult builder) throws Exception {
+    if (parameters.isDexRuntime()) {
+      builder.addBootClasspathFiles(compileAutoCloseableAndroidLibraryClasses(this, parameters));
+    } else {
+      builder.addRunClasspathClassFileData(getAutoCloseableAndroidClassData(parameters));
+    }
+  }
+
   @Test
   public void testJvm() throws Exception {
     parameters.assumeJvmTestParameters();
@@ -48,19 +80,7 @@
         .apply(this::configureProgram)
         .run(parameters.getRuntime(), getTestClassName())
         // Fails when not desugared.
-        .assertFailureWithErrorThatMatches(containsString("Failed: close should not be called"));
-  }
-
-  private static byte[] getMediaDrm(TestParameters parameters) throws IOException {
-    if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.P)) {
-      return transformer(MediaDrmApiLevel28.class)
-          .setClassDescriptor(DexItemFactory.androidMediaMediaDrmDescriptorString)
-          .transform();
-    } else {
-      return transformer(MediaDrm.class)
-          .setClassDescriptor(DexItemFactory.androidMediaMediaDrmDescriptorString)
-          .transform();
-    }
+        .assertFailureWithErrorThatMatches(containsString("close should not be called"));
   }
 
   private static byte[] getTestRunner() throws IOException {
@@ -70,30 +90,6 @@
         .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) {
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
index d2cb8f8..9b324ef 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/MediaMetadataRetrieverBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MediaMetadataRetrieverBackportTest.java
@@ -4,12 +4,20 @@
 
 package com.android.tools.r8.desugar.backports;
 
+import static com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.compileAutoCloseableAndroidLibraryClasses;
+import static com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.getAutoCloseableAndroidClassData;
 import static org.hamcrest.CoreMatchers.containsString;
 
+import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.TestBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.MediaMetadataRetriever;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DescriptorUtils;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
 import org.junit.Test;
@@ -22,16 +30,19 @@
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+    return getTestParameters()
+        .withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+        .withDexRuntimes()
+        .withAllApiLevelsAlsoForCf()
+        .build();
   }
 
   public MediaMetadataRetrieverBackportTest(TestParameters parameters) throws IOException {
     super(
         parameters,
-        MediaMetadataRetrieverBackportTest.getMediaMetadataRetriever(parameters),
-        ImmutableList.of(
-            MediaMetadataRetrieverBackportTest.getTestRunner(),
-            MediaMetadataRetrieverBackportTest.getMediaMetadataRetriever(parameters)));
+        DescriptorUtils.descriptorToJavaType(
+            DexItemFactory.androidMediaMediaMetadataRetrieverDescriptorString),
+        ImmutableList.of(MediaMetadataRetrieverBackportTest.getTestRunner()));
 
     // The constructor is used by the test and release has been available since API 10 and is the
     // method close is rewritten to.
@@ -42,6 +53,27 @@
     registerTarget(AndroidApiLevel.Q, 1);
   }
 
+  @Override
+  protected void configureProgram(TestBuilder<?, ?> builder) throws Exception {
+    super.configureProgram(builder);
+    if (builder.isJvmTestBuilder()) {
+      builder.addProgramClassFileData(getAutoCloseableAndroidClassData(parameters));
+    } else {
+      builder
+          .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.BAKLAVA))
+          .addLibraryClassFileData(getAutoCloseableAndroidClassData(parameters));
+    }
+  }
+
+  @Override
+  protected void configure(D8TestCompileResult builder) throws Exception {
+    if (parameters.isDexRuntime()) {
+      builder.addBootClasspathFiles(compileAutoCloseableAndroidLibraryClasses(this, parameters));
+    } else {
+      builder.addRunClasspathClassFileData(getAutoCloseableAndroidClassData(parameters));
+    }
+  }
+
   @Test
   public void testJvm() throws Exception {
     parameters.assumeJvmTestParameters();
@@ -49,19 +81,7 @@
         .apply(this::configureProgram)
         .run(parameters.getRuntime(), getTestClassName())
         // Fails when not desugared.
-        .assertFailureWithErrorThatMatches(containsString("Failed: close should not be called"));
-  }
-
-  private static byte[] getMediaMetadataRetriever(TestParameters parameters) throws IOException {
-    if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.Q)) {
-      return transformer(MediaMetadataRetrieverApiLevel29.class)
-          .setClassDescriptor(DexItemFactory.androidMediaMediaMetadataRetrieverDescriptorString)
-          .transform();
-    } else {
-      return transformer(MediaMetadataRetriever.class)
-          .setClassDescriptor(DexItemFactory.androidMediaMediaMetadataRetrieverDescriptorString)
-          .transform();
-    }
+        .assertFailureWithErrorThatMatches(containsString("close should not be called"));
   }
 
   private static byte[] getTestRunner() throws IOException {
@@ -72,30 +92,6 @@
         .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) {
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
index 1b7e834..ab6ddb3 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/TypedArrayBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/TypedArrayBackportTest.java
@@ -4,12 +4,20 @@
 
 package com.android.tools.r8.desugar.backports;
 
+import static com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.compileAutoCloseableAndroidLibraryClasses;
+import static com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.getAutoCloseableAndroidClassData;
 import static org.hamcrest.CoreMatchers.containsString;
 
+import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.TestBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.TypedArray;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DescriptorUtils;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
 import org.junit.Test;
@@ -22,16 +30,19 @@
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+    return getTestParameters()
+        .withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+        .withDexRuntimes()
+        .withAllApiLevelsAlsoForCf()
+        .build();
   }
 
   public TypedArrayBackportTest(TestParameters parameters) throws IOException {
     super(
         parameters,
-        TypedArrayBackportTest.getTypedArray(parameters),
-        ImmutableList.of(
-            TypedArrayBackportTest.getTestRunner(),
-            TypedArrayBackportTest.getTypedArray(parameters)));
+        DescriptorUtils.descriptorToJavaType(
+            DexItemFactory.androidContentResTypedArrayDescriptorString),
+        ImmutableList.of(TypedArrayBackportTest.getTestRunner()));
 
     // The constructor is used by the test and recycle has been available since API 1 and is the
     // method close is rewritten to.
@@ -42,6 +53,27 @@
     registerTarget(AndroidApiLevel.S, 1);
   }
 
+  @Override
+  protected void configureProgram(TestBuilder<?, ?> builder) throws Exception {
+    super.configureProgram(builder);
+    if (builder.isJvmTestBuilder()) {
+      builder.addProgramClassFileData(getAutoCloseableAndroidClassData(parameters));
+    } else {
+      builder
+          .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.BAKLAVA))
+          .addLibraryClassFileData(getAutoCloseableAndroidClassData(parameters));
+    }
+  }
+
+  @Override
+  protected void configure(D8TestCompileResult builder) throws Exception {
+    if (parameters.isDexRuntime()) {
+      builder.addBootClasspathFiles(compileAutoCloseableAndroidLibraryClasses(this, parameters));
+    } else {
+      builder.addRunClasspathClassFileData(getAutoCloseableAndroidClassData(parameters));
+    }
+  }
+
   @Test
   public void testJvm() throws Exception {
     parameters.assumeJvmTestParameters();
@@ -49,19 +81,7 @@
         .apply(this::configureProgram)
         .run(parameters.getRuntime(), getTestClassName())
         // Fails when not desugared.
-        .assertFailureWithErrorThatMatches(containsString("Failed: close should not be called"));
-  }
-
-  private static byte[] getTypedArray(TestParameters parameters) throws IOException {
-    if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.S)) {
-      return transformer(TypedArrayAndroidApiLevel31.class)
-          .setClassDescriptor(DexItemFactory.androidContentResTypedArrayDescriptorString)
-          .transform();
-    } else {
-      return transformer(TypedArray.class)
-          .setClassDescriptor(DexItemFactory.androidContentResTypedArrayDescriptorString)
-          .transform();
-    }
+        .assertFailureWithErrorThatMatches(containsString("close should not be called"));
   }
 
   private static byte[] getTestRunner() throws IOException {
@@ -72,30 +92,6 @@
         .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) {
diff --git a/src/test/testbase/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java b/src/test/testbase/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
index cdbf02f..f2c3ee2 100644
--- a/src/test/testbase/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
+++ b/src/test/testbase/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
@@ -242,7 +242,6 @@
         .compileWithExpectedDiagnostics(this::checkDiagnostics)
         .apply(this::configure)
         .inspect(this::assertDesugaring)
-        .apply(this::configure)
         .run(parameters.getRuntime(), testClassName)
         .apply(runResultConsumer);
   }
diff --git a/src/test/testbase/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java b/src/test/testbase/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
index da0c617..899a073 100644
--- a/src/test/testbase/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
+++ b/src/test/testbase/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
@@ -155,6 +155,20 @@
     return syntheticClass(reference, naming.TWR_CLOSE_RESOURCE, id);
   }
 
+  public static ClassReference syntheticAutoCloseableDispatcherClass(
+      ClassReference classReference, int id) {
+    return syntheticClass(classReference, naming.AUTOCLOSEABLE_DISPATCHER, id);
+  }
+
+  public static ClassReference syntheticAutoCloseableForwarderClass(
+      ClassReference classReference, int id) {
+    return syntheticClass(classReference, naming.AUTOCLOSEABLE_FORWARDER, id);
+  }
+
+  public static ClassReference syntheticThrowIAEClass(ClassReference classReference, int id) {
+    return syntheticClass(classReference, naming.THROW_IAE, id);
+  }
+
   public static MethodReference syntheticLambdaMethod(Class<?> clazz, int id, Method method) {
     ClassReference syntheticHolder = syntheticLambdaClass(clazz, id);
     MethodReference originalMethod = Reference.methodFromMethod(method);