Reapply "AutoCloseable Retargeter"

This reverts commit f47bed9866d57634e314baf8adecbf2a506ba81e.

Bug: b/369520931
Change-Id: I787d97ce1678bbde172ae9aa5d07bd8e052294cc
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index d60a682..0dc6918 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -1274,7 +1274,11 @@
   }
 
   public static DexEncodedMethod createDesugaringForwardingMethod(
-      DexClassAndMethod target, DexClass clazz, DexMethod forwardMethod, DexItemFactory factory) {
+      DexClassAndMethod target,
+      DexClass clazz,
+      DexMethod forwardMethod,
+      DexItemFactory factory,
+      boolean targetIsStatic) {
     assert forwardMethod != null;
     // New method will have the same name, proto, and also all the flags of the
     // default method, including bridge flag.
@@ -1285,16 +1289,23 @@
     newFlags.unsetAbstract();
     // Holder is companion class, or retarget method, not an interface.
     boolean isInterfaceMethodReference = false;
+    ForwardMethodBuilder builder =
+        ForwardMethodBuilder.builder(factory).setNonStaticSource(newMethod);
+    if (targetIsStatic) {
+      builder.setStaticTarget(forwardMethod, isInterfaceMethodReference);
+    } else {
+      builder.setVirtualTarget(forwardMethod, isInterfaceMethodReference);
+    }
+    if (forwardMethod.getReturnType().isNotIdenticalTo(target.getReturnType())) {
+      assert target.getReturnType().isVoidType();
+      builder.setIgnoreTargetResult();
+    }
     return syntheticBuilder()
         .setMethod(newMethod)
         .setAccessFlags(newFlags)
         .setGenericSignature(MethodTypeSignature.noSignature())
         .setAnnotations(DexAnnotationSet.empty())
-        .setCode(
-            ForwardMethodBuilder.builder(factory)
-                .setNonStaticSource(newMethod)
-                .setStaticTarget(forwardMethod, isInterfaceMethodReference)
-                .buildCf())
+        .setCode(builder.buildCf())
         .setApiLevelForDefinition(target.getDefinition().getApiLevelForDefinition())
         .setApiLevelForCode(target.getDefinition().getApiLevelForCode())
         .build();
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 d02a4cd..27f31fa 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -73,6 +73,7 @@
   public static final String throwableDescriptorString = "Ljava/lang/Throwable;";
   public static final String dalvikAnnotationSignatureString = "Ldalvik/annotation/Signature;";
   public static final String recordTagDescriptorString = "Lcom/android/tools/r8/RecordTag;";
+  public static final String autoCloseableTagString = "Lcom/android/tools/r8/AutoCloseableTag;";
   public static final String recordDescriptorString = "Ljava/lang/Record;";
   public static final String desugarVarHandleDescriptorString =
       "Lcom/android/tools/r8/DesugarVarHandle;";
@@ -260,6 +261,7 @@
   public final DexString objectDescriptor = createString("Ljava/lang/Object;");
   public final DexString recordDescriptor = createString(recordDescriptorString);
   public final DexString recordTagDescriptor = createString(recordTagDescriptorString);
+  public final DexString autoCloseableTagDescriptor = createString(autoCloseableTagString);
   public final DexString objectArrayDescriptor = createString("[Ljava/lang/Object;");
   public final DexString classDescriptor = createString("Ljava/lang/Class;");
   public final DexString classLoaderDescriptor = createString("Ljava/lang/ClassLoader;");
@@ -441,6 +443,7 @@
   public final DexType objectType = createStaticallyKnownType(objectDescriptor);
   public final DexType recordType = createStaticallyKnownType(recordDescriptor);
   public final DexType recordTagType = createStaticallyKnownType(recordTagDescriptor);
+  public final DexType autoCloseableTagType = createStaticallyKnownType(autoCloseableTagDescriptor);
   public final DexType objectArrayType = createStaticallyKnownType(objectArrayDescriptor);
   public final DexType classArrayType = createStaticallyKnownType(classArrayDescriptor);
   public final DexType enumType = createStaticallyKnownType(enumDescriptor);
@@ -665,6 +668,7 @@
       createStaticallyKnownType(androidContentContentProviderClientDescriptorString);
   public final DexType androidDrmDrmManagerClientType =
       createStaticallyKnownType(androidDrmDrmManagerClientDescriptorString);
+  public final DexType androidMediaMediaDrm = createStaticallyKnownType("Landroid/media/MediaDrm;");
   public final DexType androidMediaMediaDrmType =
       createStaticallyKnownType(androidMediaMediaDrmDescriptorString);
   public final DexType androidMediaMediaMetadataRetrieverType =
@@ -894,6 +898,11 @@
       createStaticallyKnownType(desugarMethodHandlesLookupDescriptorString);
   public final DexType mockitoType = createStaticallyKnownType("Lorg/mockito/Mockito;");
 
+  public final DexType javaUtilConcurrentExecutorServiceType =
+      createStaticallyKnownType("Ljava/util/concurrent/ExecutorService;");
+  public final DexType javaUtilConcurrentForkJoinPoolType =
+      createStaticallyKnownType("Ljava/util/concurrent/ForkJoinPool;");
+
   public final ObjectMethodsMembers objectMethodsMembers = new ObjectMethodsMembers();
   public final ServiceLoaderMethods serviceLoaderMethods = new ServiceLoaderMethods();
   public final IteratorMethods iteratorMethods = new IteratorMethods();
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/InterfaceCollection.java b/src/main/java/com/android/tools/r8/ir/analysis/type/InterfaceCollection.java
index 62462bd..ae47e34 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/InterfaceCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/InterfaceCollection.java
@@ -5,6 +5,8 @@
 
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.AutoCloseableRetargeterHelper;
+import com.android.tools.r8.utils.BooleanBox;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.OptionalBool;
 import com.android.tools.r8.utils.Pair;
@@ -21,14 +23,27 @@
 
 public class InterfaceCollection {
 
-  @SuppressWarnings("ReferenceEquality")
   public static boolean isKnownToImplement(
       DexType iface, DexType implementor, InternalOptions options) {
     if (options.canHaveZipFileWithMissingCloseableBug()
-        && implementor == options.dexItemFactory().zipFileType
-        && iface == options.dexItemFactory().closeableType) {
+        && implementor.isIdenticalTo(options.dexItemFactory().zipFileType)
+        && iface.isIdenticalTo(options.dexItemFactory().closeableType)) {
       return false;
     }
+    if (options.canHaveMissingImplementsAutoCloseableInterface()
+        && iface.isIdenticalTo(options.dexItemFactory().autoCloseableType)) {
+      BooleanBox booleanBox = new BooleanBox(true);
+      AutoCloseableRetargeterHelper.forEachAutoCloseableMissingSubimplementation(
+          type -> {
+            if (type.isIdenticalTo(implementor)) {
+              booleanBox.set(false);
+            }
+          },
+          options.getMinApiLevel(),
+          options.dexItemFactory(),
+          true);
+      return booleanBox.get();
+    }
     return true;
   }
 
@@ -44,10 +59,6 @@
               || isKnownToImplement(iface, implementor.getType(), options));
     }
 
-    public Builder addInterface(DexType iface, DexType implementor, InternalOptions options) {
-      return addInterface(iface, isKnownToImplement(iface, implementor, options));
-    }
-
     public Builder addInterface(DexType type, boolean isKnown) {
       interfaces.compute(
           type,
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/TrivialCheckCastAndInstanceOfRemover.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/TrivialCheckCastAndInstanceOfRemover.java
index 855b473..52386c8 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/TrivialCheckCastAndInstanceOfRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/TrivialCheckCastAndInstanceOfRemover.java
@@ -311,6 +311,13 @@
               .isPossiblyFalse()) {
         return false;
       }
+      if (appView.options().canHaveMissingImplementsAutoCloseableInterface()
+          && instanceOfBaseType.isIdenticalTo(appView.dexItemFactory().autoCloseableType)
+          && instanceOfClass.isLibraryClass()) {
+        // Library classes may be messed up since they may implement AutoCloseable from a different
+        // api level than the api level they are introduced.
+        return false;
+      }
     }
 
     Value inValue = instanceOf.value();
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 ee7aa09..04fb94e 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
@@ -926,21 +926,24 @@
 
       initializeMathExactApis(factory, factory.mathType);
 
-      // android.content.res.ContentProviderClient
+      // AutoCloseable desugaring is disabled on low min-api levels, we rely on backports instead.
+      if (!appView.options().shouldDesugarAutoCloseable()) {
+        // android.content.res.ContentProviderClient
 
-      // void android.content.ContentProviderClient.close()
-      addProvider(
-          new InvokeRewriter(
-              factory.androidContentContentProviderClientMembers.close,
-              ContentProviderClientMethodRewrites.rewriteClose()));
+        // void android.content.ContentProviderClient.close()
+        addProvider(
+            new InvokeRewriter(
+                factory.androidContentContentProviderClientMembers.close,
+                ContentProviderClientMethodRewrites.rewriteClose()));
 
-      // android.drm.DrmManagerClient
+        // android.drm.DrmManagerClient
 
-      // void android.drm.DrmManagerClient.close()
-      addProvider(
-          new InvokeRewriter(
-              factory.androidDrmDrmManagerClientMembers.close,
-              DrmManagerClientMethodRewrites.rewriteClose()));
+        // void android.drm.DrmManagerClient.close()
+        addProvider(
+            new InvokeRewriter(
+                factory.androidDrmDrmManagerClientMembers.close,
+                DrmManagerClientMethodRewrites.rewriteClose()));
+      }
     }
 
     /**
@@ -1197,11 +1200,14 @@
               "stripTrailingZeros",
               bigDecimal));
 
-      // void android.drm.DrmManagerClient.close()
-      addProvider(
-          new InvokeRewriter(
-              factory.androidMediaMetadataRetrieverMembers.close,
-              MediaMetadataRetrieverMethodRewrites.rewriteClose()));
+      // AutoCloseable desugaring is disabled on low min-api levels, we rely on backports instead.
+      if (!appView.options().shouldDesugarAutoCloseable()) {
+        // void android.media.MetadataRetriever.close()
+        addProvider(
+            new InvokeRewriter(
+                factory.androidMediaMetadataRetrieverMembers.close,
+                MediaMetadataRetrieverMethodRewrites.rewriteClose()));
+      }
     }
 
     private void initializeAndroidRObjectsMethodProviderWithSupplier(DexItemFactory factory) {
@@ -1448,13 +1454,16 @@
           new InvokeRewriter(
               factory.androidUtilSparseArrayMembers.set, SparseArrayMethodRewrites.rewriteSet()));
 
-      // android.content.res.TypedArray
+      // AutoCloseable desugaring is disabled on low min-api levels, we rely on backports instead.
+      if (!appView.options().shouldDesugarAutoCloseable()) {
+        // android.content.res.TypedArray
 
-      // void android.content.res.TypedArray.close()
-      addProvider(
-          new InvokeRewriter(
-              factory.androidContentResTypedArrayMembers.close,
-              TypedArrayMethodRewrites.rewriteClose()));
+        // void android.content.res.TypedArray.close()
+        addProvider(
+            new InvokeRewriter(
+                factory.androidContentResTypedArrayMembers.close,
+                TypedArrayMethodRewrites.rewriteClose()));
+      }
     }
 
     private void initializeAndroidSv2MethodProviders(DexItemFactory factory) {
@@ -1874,17 +1883,20 @@
                     (Integer) versionCodeFull[1])));
       }
 
-      // void java.util.concurrent.ExecutorService.close()
-      type = factory.createType("Ljava/util/concurrent/ExecutorService;");
-      name = factory.createString("close");
-      proto = factory.createProto(factory.voidType);
-      method = factory.createMethod(type, proto, name);
-      addProvider(
-          new StatifyingMethodGenerator(
-              method,
-              BackportedMethods::ExecutorServiceMethods_closeExecutorService,
-              "closeExecutorService",
-              type));
+      // AutoCloseable desugaring is disabled on low min-api levels, we rely on backports instead.
+      if (!appView.options().shouldDesugarAutoCloseable()) {
+        // void java.util.concurrent.ExecutorService.close()
+        type = factory.createType("Ljava/util/concurrent/ExecutorService;");
+        name = factory.createString("close");
+        proto = factory.createProto(factory.voidType);
+        method = factory.createMethod(type, proto, name);
+        addProvider(
+            new StatifyingMethodGenerator(
+                method,
+                BackportedMethods::ExecutorServiceMethods_closeExecutorService,
+                "closeExecutorService",
+                type));
+      }
     }
 
     private void initializeAndroidUMethodProviders(DexItemFactory factory) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
index ace4c1e..2363fe4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
@@ -26,6 +26,7 @@
 import com.android.tools.r8.ir.desugar.constantdynamic.ConstantDynamicClass;
 import com.android.tools.r8.ir.desugar.constantdynamic.ConstantDynamicDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryAPIConverterEventConsumer;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.AutoCloseableRetargeterEventConsumer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibRewriterEventConsumer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterSynthesizerEventConsumer.DesugaredLibraryRetargeterInstructionEventConsumer;
 import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialBridgeInfo;
@@ -77,7 +78,8 @@
         ApiInvokeOutlinerDesugaringEventConsumer,
         VarHandleDesugaringEventConsumer,
         DesugaredLibRewriterEventConsumer,
-        TypeSwitchDesugaringEventConsumer {
+        TypeSwitchDesugaringEventConsumer,
+        AutoCloseableRetargeterEventConsumer {
 
   public static CfInstructionDesugaringEventConsumer createForD8(
       AppView<?> appView,
@@ -259,6 +261,11 @@
     }
 
     @Override
+    public void acceptAutoCloseableDispatchMethod(ProgramMethod method, ProgramDefinition context) {
+      methodProcessor.scheduleDesugaredMethodForProcessing(method);
+    }
+
+    @Override
     public void acceptVarHandleDesugaringClass(DexProgramClass clazz) {
       clazz
           .programMethods()
@@ -558,6 +565,11 @@
     }
 
     @Override
+    public void acceptAutoCloseableDispatchMethod(ProgramMethod method, ProgramDefinition context) {
+      // Intentionally empty. The method will be hit by tracing if required.
+    }
+
+    @Override
     public void acceptVarHandleDesugaringClass(DexProgramClass clazz) {
       // Intentionally empty. The class will be hit by tracing if required.
     }
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 f31a4a7..dd8e727 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
@@ -8,6 +8,7 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryAPICallbackSynthesizer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.disabledesugarer.DesugaredLibraryDisableDesugarerPostProcessor;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.AutoCloseableRetargeterPostProcessor;
 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;
@@ -64,6 +65,9 @@
           && !appView.options().getLibraryDesugaringOptions().isDesugaredLibraryCompilation()) {
         desugarings.add(new DesugaredLibraryRetargeterPostProcessor(appView));
       }
+      if (appView.options().testing.enableAutoCloseableDesugaring) {
+        desugarings.add(new AutoCloseableRetargeterPostProcessor(appView));
+      }
       if (interfaceMethodProcessorFacade != null) {
         desugarings.add(interfaceMethodProcessorFacade);
       }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
index 9cdb6e6..e1a3cd9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
@@ -11,10 +11,12 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.MethodResolutionResult.FailedResolutionResult;
+import com.android.tools.r8.graph.ProgramDefinition;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.conversion.D8MethodProcessor;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryAPICallbackSynthesizorEventConsumer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.AutoCloseableRetargeterEventConsumer.AutoCloseableRetargeterPostProcessingEventConsumer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterSynthesizerEventConsumer.DesugaredLibraryRetargeterPostProcessingEventConsumer;
 import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper;
 import com.android.tools.r8.ir.desugar.itf.InterfaceProcessingDesugaringEventConsumer;
@@ -35,7 +37,8 @@
 public abstract class CfPostProcessingDesugaringEventConsumer
     implements DesugaredLibraryRetargeterPostProcessingEventConsumer,
         InterfaceProcessingDesugaringEventConsumer,
-        DesugaredLibraryAPICallbackSynthesizorEventConsumer {
+        DesugaredLibraryAPICallbackSynthesizorEventConsumer,
+        AutoCloseableRetargeterPostProcessingEventConsumer {
 
   public static CfPostProcessingDesugaringEventConsumer createForD8(
       AppView<?> appView,
@@ -108,6 +111,12 @@
     }
 
     @Override
+    public void acceptAutoCloseableInterfaceInjection(
+        DexProgramClass clazz, DexClass newInterface) {
+      // Intentionally empty.
+    }
+
+    @Override
     public void acceptEmulatedInterfaceMarkerInterface(
         DexProgramClass clazz, DexClasspathClass newInterface) {
       // Intentionally empty.
@@ -169,6 +178,17 @@
     public void acceptGenericApiConversionStub(DexClasspathClass dexClasspathClass) {
       // Intentionally empty.
     }
+
+    @Override
+    public void acceptAutoCloseableDispatchMethod(ProgramMethod method, ProgramDefinition context) {
+      addMethodToReprocess(method);
+    }
+
+    @Override
+    public void acceptAutoCloseableForwardingMethod(
+        ProgramMethod method, ProgramDefinition context) {
+      addMethodToReprocess(method);
+    }
   }
 
   public static class R8PostProcessingDesugaringEventConsumer
@@ -218,6 +238,12 @@
     }
 
     @Override
+    public void acceptAutoCloseableInterfaceInjection(
+        DexProgramClass clazz, DexClass newInterface) {
+      additions.injectInterface(clazz, newInterface);
+    }
+
+    @Override
     public void acceptDesugaredLibraryRetargeterDispatchClasspathClass(DexClasspathClass clazz) {
       additions.addLiveClasspathClass(clazz);
     }
@@ -271,5 +297,16 @@
     public void acceptGenericApiConversionStub(DexClasspathClass clazz) {
       additions.addLiveClasspathClass(clazz);
     }
+
+    @Override
+    public void acceptAutoCloseableDispatchMethod(ProgramMethod method, ProgramDefinition context) {
+      additions.addLiveMethod(method);
+    }
+
+    @Override
+    public void acceptAutoCloseableForwardingMethod(
+        ProgramMethod method, ProgramDefinition context) {
+      additions.addLiveMethod(method);
+    }
   }
 }
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 4442dfe..604920f 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
@@ -20,6 +20,7 @@
 import com.android.tools.r8.ir.desugar.constantdynamic.ConstantDynamicInstructionDesugaring;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryAPIConverter;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.disabledesugarer.DesugaredLibraryDisableDesugarer;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.AutoCloseableRetargeter;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryLibRewriter;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeter;
 import com.android.tools.r8.ir.desugar.icce.AlwaysThrowingInstructionDesugaring;
@@ -120,6 +121,9 @@
     if (desugaredLibraryRetargeter != null) {
       desugarings.add(desugaredLibraryRetargeter);
     }
+    if (appView.options().shouldDesugarAutoCloseable()) {
+      desugarings.add(new AutoCloseableRetargeter(appView));
+    }
     disableDesugarer = DesugaredLibraryDisableDesugarer.create(appView);
     if (disableDesugarer != null) {
       desugarings.add(disableDesugarer);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
index 24224c7..02eda10 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
@@ -106,6 +106,7 @@
     factory.createSynthesizedType("Ljava/util/OptionalLong;");
     factory.createSynthesizedType("Ljava/util/Set;");
     factory.createSynthesizedType("Ljava/util/concurrent/ExecutorService;");
+    factory.createSynthesizedType("Ljava/util/concurrent/ForkJoinPool;");
     factory.createSynthesizedType("Ljava/util/concurrent/TimeUnit;");
     factory.createSynthesizedType("Ljava/util/concurrent/atomic/AtomicReference;");
     factory.createSynthesizedType("Ljava/util/concurrent/atomic/AtomicReferenceArray;");
@@ -2430,12 +2431,42 @@
     CfLabel label13 = new CfLabel();
     CfLabel label14 = new CfLabel();
     CfLabel label15 = new CfLabel();
+    CfLabel label16 = new CfLabel();
+    CfLabel label17 = new CfLabel();
+    CfLabel label18 = new CfLabel();
     return new CfCode(
         method.holder,
         4,
         4,
         ImmutableList.of(
             label0,
+            new CfStaticFieldRead(
+                factory.createField(
+                    factory.createType("Landroid/os/Build$VERSION;"),
+                    factory.intType,
+                    factory.createString("SDK_INT"))),
+            new CfConstNumber(23, ValueType.INT),
+            new CfIfCmp(IfType.LE, ValueType.INT, label3),
+            label1,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/util/concurrent/ForkJoinPool;"),
+                    factory.createProto(factory.createType("Ljava/util/concurrent/ForkJoinPool;")),
+                    factory.createString("commonPool")),
+                false),
+            new CfIfCmp(IfType.NE, ValueType.OBJECT, label3),
+            label2,
+            new CfReturnVoid(),
+            label3,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(
+                          factory.createType("Ljava/util/concurrent/ExecutorService;"))
+                    })),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInvoke(
                 185,
@@ -2445,10 +2476,10 @@
                     factory.createString("isTerminated")),
                 true),
             new CfStore(ValueType.INT, 1),
-            label1,
+            label4,
             new CfLoad(ValueType.INT, 1),
-            new CfIf(IfType.NE, ValueType.INT, label14),
-            label2,
+            new CfIf(IfType.NE, ValueType.INT, label17),
+            label5,
             new CfLoad(ValueType.OBJECT, 0),
             new CfInvoke(
                 185,
@@ -2457,10 +2488,10 @@
                     factory.createProto(factory.voidType),
                     factory.createString("shutdown")),
                 true),
-            label3,
+            label6,
             new CfConstNumber(0, ValueType.INT),
             new CfStore(ValueType.INT, 2),
-            label4,
+            label7,
             new CfFrame(
                 new Int2ObjectAVLTreeMap<>(
                     new int[] {0, 1, 2},
@@ -2471,8 +2502,8 @@
                       FrameType.intType()
                     })),
             new CfLoad(ValueType.INT, 1),
-            new CfIf(IfType.NE, ValueType.INT, label12),
-            label5,
+            new CfIf(IfType.NE, ValueType.INT, label15),
+            label8,
             new CfLoad(ValueType.OBJECT, 0),
             new CfConstNumber(1, ValueType.LONG),
             new CfStaticFieldRead(
@@ -2491,9 +2522,9 @@
                     factory.createString("awaitTermination")),
                 true),
             new CfStore(ValueType.INT, 1),
-            label6,
-            new CfGoto(label4),
-            label7,
+            label9,
+            new CfGoto(label7),
+            label10,
             new CfFrame(
                 new Int2ObjectAVLTreeMap<>(
                     new int[] {0, 1, 2},
@@ -2508,10 +2539,10 @@
                         FrameType.initializedNonNullReference(
                             factory.createType("Ljava/lang/InterruptedException;"))))),
             new CfStore(ValueType.OBJECT, 3),
-            label8,
+            label11,
             new CfLoad(ValueType.INT, 2),
-            new CfIf(IfType.NE, ValueType.INT, label11),
-            label9,
+            new CfIf(IfType.NE, ValueType.INT, label14),
+            label12,
             new CfLoad(ValueType.OBJECT, 0),
             new CfInvoke(
                 185,
@@ -2521,10 +2552,10 @@
                     factory.createString("shutdownNow")),
                 true),
             new CfStackInstruction(CfStackInstruction.Opcode.Pop),
-            label10,
+            label13,
             new CfConstNumber(1, ValueType.INT),
             new CfStore(ValueType.INT, 2),
-            label11,
+            label14,
             new CfFrame(
                 new Int2ObjectAVLTreeMap<>(
                     new int[] {0, 1, 2},
@@ -2534,8 +2565,8 @@
                       FrameType.intType(),
                       FrameType.intType()
                     })),
-            new CfGoto(label4),
-            label12,
+            new CfGoto(label7),
+            label15,
             new CfFrame(
                 new Int2ObjectAVLTreeMap<>(
                     new int[] {0, 1, 2},
@@ -2546,8 +2577,8 @@
                       FrameType.intType()
                     })),
             new CfLoad(ValueType.INT, 2),
-            new CfIf(IfType.EQ, ValueType.INT, label14),
-            label13,
+            new CfIf(IfType.EQ, ValueType.INT, label17),
+            label16,
             new CfInvoke(
                 184,
                 factory.createMethod(
@@ -2562,7 +2593,7 @@
                     factory.createProto(factory.voidType),
                     factory.createString("interrupt")),
                 false),
-            label14,
+            label17,
             new CfFrame(
                 new Int2ObjectAVLTreeMap<>(
                     new int[] {0, 1},
@@ -2572,13 +2603,13 @@
                       FrameType.intType()
                     })),
             new CfReturnVoid(),
-            label15),
+            label18),
         ImmutableList.of(
             new CfTryCatch(
-                label5,
-                label6,
+                label8,
+                label9,
                 ImmutableList.of(factory.createType("Ljava/lang/InterruptedException;")),
-                ImmutableList.of(label7))),
+                ImmutableList.of(label10))),
         ImmutableList.of());
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/AutoCloseableRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/AutoCloseableRetargeter.java
new file mode 100644
index 0000000..703067b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/AutoCloseableRetargeter.java
@@ -0,0 +1,186 @@
+// 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 com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter;
+
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.AutoCloseableRetargeterHelper.createCloseMethod;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.AutoCloseableRetargeterHelper.lookupSuperIncludingInterfaces;
+
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfStackInstruction;
+import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.MethodResolutionResult;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.DesugarDescription;
+import com.android.tools.r8.ir.synthetic.EmulateDispatchSyntheticCfCodeProvider;
+import com.android.tools.r8.ir.synthetic.EmulateDispatchSyntheticCfCodeProvider.EmulateDispatchType;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.function.BiFunction;
+import java.util.function.IntConsumer;
+import org.objectweb.asm.Opcodes;
+
+public class AutoCloseableRetargeter implements CfInstructionDesugaring {
+
+  private final AppView<?> appView;
+  private final AutoCloseableRetargeterHelper data;
+
+  public AutoCloseableRetargeter(AppView<?> appView) {
+    this.appView = appView;
+    this.data =
+        new AutoCloseableRetargeterHelper(
+            appView.options().getMinApiLevel(), appView.dexItemFactory());
+  }
+
+  public DexMethod synthesizeDispatcher(
+      ProgramMethod context,
+      MethodProcessingContext methodProcessingContext,
+      AutoCloseableRetargeterEventConsumer eventConsumer) {
+    DexItemFactory factory = appView.dexItemFactory();
+    LinkedHashMap<DexType, DexMethod> dispatchCases =
+        data.synthesizeDispatchCases(appView, context, eventConsumer, methodProcessingContext);
+    ProgramMethod method =
+        appView
+            .getSyntheticItems()
+            .createMethod(
+                kinds -> kinds.AUTOCLOSEABLE_DISPATCHER,
+                methodProcessingContext.createUniqueContext(),
+                appView,
+                methodBuilder ->
+                    methodBuilder
+                        .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+                        .setProto(factory.createProto(factory.voidType, factory.objectType))
+                        .setCode(
+                            methodSig ->
+                                new EmulateDispatchSyntheticCfCodeProvider(
+                                        methodSig.getHolderType(),
+                                        data.createThrowUnsupportedException(
+                                                appView,
+                                                context,
+                                                eventConsumer,
+                                                methodProcessingContext::createUniqueContext)
+                                            .getReference(),
+                                        createCloseMethod(factory, factory.autoCloseableType),
+                                        dispatchCases,
+                                        EmulateDispatchType.AUTO_CLOSEABLE,
+                                        appView)
+                                    .generateCfCode()));
+    eventConsumer.acceptAutoCloseableDispatchMethod(method, context);
+    return method.getReference();
+  }
+
+  @Override
+  public void acceptRelevantAsmOpcodes(IntConsumer consumer) {
+    consumer.accept(Opcodes.INVOKEVIRTUAL);
+    consumer.accept(Opcodes.INVOKESPECIAL);
+    consumer.accept(Opcodes.INVOKEINTERFACE);
+  }
+
+  @Override
+  public DesugarDescription compute(CfInstruction instruction, ProgramMethod context) {
+    if (instruction.isInvoke() && !instruction.isInvokeStatic()) {
+      return computeInvokeDescription(instruction, context);
+    }
+    return DesugarDescription.nothing();
+  }
+
+  private DesugarDescription computeInvokeDescription(
+      CfInstruction instruction, ProgramMethod context) {
+    if (appView
+        .getSyntheticItems()
+        .isSyntheticOfKind(context.getHolderType(), kinds -> kinds.AUTOCLOSEABLE_DISPATCHER)) {
+      return DesugarDescription.nothing();
+    }
+    CfInvoke cfInvoke = instruction.asInvoke();
+    DexMethod invokedMethod = cfInvoke.getMethod();
+    if (!data.hasCloseMethodName(invokedMethod) || invokedMethod.getArity() > 0) {
+      return DesugarDescription.nothing();
+    }
+    AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring();
+    MethodResolutionResult resolutionResult =
+        appInfo.resolveMethodLegacy(invokedMethod, cfInvoke.isInterface());
+    if (!resolutionResult.isSingleResolution()) {
+      return DesugarDescription.nothing();
+    }
+    assert resolutionResult.getSingleTarget() != null;
+    DexMethod reference = resolutionResult.getSingleTarget().getReference();
+    if (data.shouldEmulateMethod(reference)) {
+      return computeNewTarget(reference, cfInvoke.isInvokeSuper(context.getHolderType()), context);
+    }
+    return DesugarDescription.nothing();
+  }
+
+  private DesugarDescription computeNewTarget(
+      DexMethod singleTarget, boolean superInvoke, ProgramMethod context) {
+    if (superInvoke) {
+      DexClassAndMethod superMethod =
+          lookupSuperIncludingInterfaces(appView, singleTarget, context.getContextClass());
+      if (superMethod != null && superMethod.isLibraryMethod()) {
+        DexType holderType = superMethod.getHolderType();
+        if (data.superTargetsToRewrite().contains(holderType)) {
+          return createWithTarget(
+              singleTarget,
+              (eventConsumer, methodProcessingContext) ->
+                  data.synthesizeDispatchCase(
+                      appView,
+                      holderType,
+                      context,
+                      eventConsumer,
+                      methodProcessingContext::createUniqueContext));
+        }
+      }
+      return DesugarDescription.nothing();
+    }
+    return createWithTarget(
+        singleTarget,
+        (eventConsumer, methodProcessingContext) ->
+            synthesizeDispatcher(context, methodProcessingContext, eventConsumer));
+  }
+
+  private DesugarDescription createWithTarget(
+      DexMethod target,
+      BiFunction<CfInstructionDesugaringEventConsumer, MethodProcessingContext, DexMethod>
+          methodProvider) {
+    return DesugarDescription.builder()
+        .setDesugarRewrite(
+            (position,
+                freshLocalProvider,
+                localStackAllocator,
+                desugaringInfo,
+                eventConsumer,
+                context,
+                methodProcessingContext,
+                desugarings,
+                dexItemFactory) -> {
+              DexMethod newInvokeTarget =
+                  methodProvider.apply(eventConsumer, methodProcessingContext);
+              assert appView.definitionFor(newInvokeTarget.getHolderType()) != null;
+              assert !appView.definitionFor(newInvokeTarget.getHolderType()).isInterface();
+              List<CfInstruction> instructions = new ArrayList<>();
+              if (appView.getSyntheticItems().isSynthetic(newInvokeTarget.getHolderType())) {
+                instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, newInvokeTarget, false));
+              } else {
+                instructions.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, newInvokeTarget, false));
+              }
+              if (target.getReturnType().isVoidType()
+                  && !newInvokeTarget.getReturnType().isVoidType()) {
+                instructions.add(new CfStackInstruction(Opcode.Pop));
+              }
+              return instructions;
+            })
+        .build();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/AutoCloseableRetargeterEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/AutoCloseableRetargeterEventConsumer.java
new file mode 100644
index 0000000..9a29029
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/AutoCloseableRetargeterEventConsumer.java
@@ -0,0 +1,23 @@
+// 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 com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramDefinition;
+import com.android.tools.r8.graph.ProgramMethod;
+
+public interface AutoCloseableRetargeterEventConsumer {
+
+  void acceptAutoCloseableDispatchMethod(ProgramMethod method, ProgramDefinition context);
+
+  interface AutoCloseableRetargeterPostProcessingEventConsumer
+      extends AutoCloseableRetargeterEventConsumer {
+
+    void acceptAutoCloseableForwardingMethod(ProgramMethod method, ProgramDefinition context);
+
+    void acceptAutoCloseableInterfaceInjection(DexProgramClass clazz, DexClass newInterface);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/AutoCloseableRetargeterHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/AutoCloseableRetargeterHelper.java
new file mode 100644
index 0000000..3597e57
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/AutoCloseableRetargeterHelper.java
@@ -0,0 +1,234 @@
+// 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 com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter;
+
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+import com.android.tools.r8.contexts.CompilationContext.UniqueContext;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.ProgramDefinition;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.backports.BackportedMethods;
+import com.android.tools.r8.ir.synthetic.ThrowCfCodeProvider;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableSet;
+import java.util.LinkedHashMap;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+public class AutoCloseableRetargeterHelper {
+
+  private final AndroidApiLevel minApiLevel;
+  private final DexItemFactory factory;
+  private final DexString close;
+  private final Set<DexMethod> methodsToEmulate;
+
+  public AutoCloseableRetargeterHelper(AndroidApiLevel minApiLevel, DexItemFactory factory) {
+    this.minApiLevel = minApiLevel;
+    this.factory = factory;
+    this.close = factory.createString("close");
+    this.methodsToEmulate = methodsToEmulate();
+  }
+
+  static DexMethod createCloseMethod(DexItemFactory factory, DexType holderType) {
+    return factory.createMethod(holderType, factory.createProto(factory.voidType), "close");
+  }
+
+  public boolean hasCloseMethodName(DexMethod method) {
+    return method.getName().isIdenticalTo(close);
+  }
+
+  public boolean shouldEmulateMethod(DexMethod method) {
+    return methodsToEmulate.contains(method);
+  }
+
+  // This includes all library types which implements AutoCloseable#close() and their subtypes.
+  // We exclude android.media.MediaDrm which is final and rewritten by the backportedMethodRewriter.
+  private Set<DexMethod> methodsToEmulate() {
+    ImmutableSet.Builder<DexMethod> builder = ImmutableSet.builder();
+    forEachAutoCloseableMissingSubimplementation(
+        type -> {
+          if (!type.isIdenticalTo(factory.androidMediaMediaDrmType)) {
+            builder.add(createCloseMethod(factory, type));
+          }
+        });
+    builder.add(createCloseMethod(factory, factory.autoCloseableType));
+    return builder.build();
+  }
+
+  // This includes all library types which implements directly AutoCloseable#close() including
+  // android.media.MediaDrm.
+  public static void forEachAutoCloseableMissingSubimplementation(
+      Consumer<DexType> consumer,
+      AndroidApiLevel minApiLevel,
+      DexItemFactory factory,
+      boolean withSubtypes) {
+    if (minApiLevel.isLessThanOrEqualTo(AndroidApiLevel.V)) {
+      consumer.accept(factory.javaUtilConcurrentExecutorServiceType);
+      consumer.accept(factory.javaUtilConcurrentForkJoinPoolType);
+      if (withSubtypes) {
+        consumer.accept(factory.createType("Ljava/util/concurrent/ScheduledExecutorService;"));
+        consumer.accept(factory.createType("Ljava/util/concurrent/AbstractExecutorService;"));
+        consumer.accept(factory.createType("Ljava/util/concurrent/ThreadPoolExecutor;"));
+        consumer.accept(factory.createType("Ljava/util/concurrent/ScheduledThreadPoolExecutor;"));
+      }
+    }
+    if (minApiLevel.isLessThanOrEqualTo(AndroidApiLevel.R)) {
+      consumer.accept(factory.androidContentResTypedArrayType);
+    }
+    if (minApiLevel.isLessThanOrEqualTo(AndroidApiLevel.P)) {
+      consumer.accept(factory.androidMediaMediaMetadataRetrieverType);
+    }
+    if (minApiLevel.isLessThanOrEqualTo(AndroidApiLevel.O_MR1)) {
+      consumer.accept(factory.androidMediaMediaDrmType);
+    }
+    if (minApiLevel.isLessThanOrEqualTo(AndroidApiLevel.M)) {
+      consumer.accept(factory.androidDrmDrmManagerClientType);
+      consumer.accept(factory.androidContentContentProviderClientType);
+    }
+  }
+
+  private void forEachAutoCloseableMissingSubimplementation(Consumer<DexType> consumer) {
+    forEachAutoCloseableMissingSubimplementation(consumer, minApiLevel, factory, false);
+  }
+
+  // This includes all library types which implements directly AutoCloseable#close() including
+  // android.media.MediaDrm, however, android.media.MediaDrm is final and rewritten if called
+  // directly by the backported method rewriter.
+  public LinkedHashMap<DexType, DexMethod> synthesizeDispatchCases(
+      AppView<?> appView,
+      ProgramMethod context,
+      AutoCloseableRetargeterEventConsumer eventConsumer,
+      MethodProcessingContext methodProcessingContext) {
+    LinkedHashMap<DexType, DexMethod> map = new LinkedHashMap<>();
+    forEachAutoCloseableMissingSubimplementation(
+        type -> {
+          // ForkJoinPool has an optimized version of ExecutorService.close. ForkJoinPool is not
+          // present in 19 (added in 21) so R8 cannot use instanceof ForkJoinPool in the emulated
+          // dispatch. We rely on ForkJoinPool implementing ExecutorService and use that path.
+          if (type.isNotIdenticalTo(factory.javaUtilConcurrentForkJoinPoolType)) {
+            map.put(
+                type,
+                synthesizeDispatchCase(
+                    appView,
+                    type,
+                    context,
+                    eventConsumer,
+                    methodProcessingContext::createUniqueContext));
+          }
+        });
+    return map;
+  }
+
+  public Set<DexType> superTargetsToRewrite() {
+    ImmutableSet.Builder<DexType> builder = ImmutableSet.builder();
+    forEachAutoCloseableMissingSubimplementation(builder::add);
+    return builder.build();
+  }
+
+  public DexMethod synthesizeDispatchCase(
+      AppView<?> appView,
+      DexType type,
+      ProgramDefinition context,
+      AutoCloseableRetargeterEventConsumer eventConsumer,
+      Supplier<UniqueContext> contextSupplier) {
+    assert superTargetsToRewrite().contains(type);
+    if (type.isIdenticalTo(factory.javaUtilConcurrentExecutorServiceType)
+        || type.isIdenticalTo(factory.javaUtilConcurrentForkJoinPoolType)) {
+      // For ForkJoinPool.close R8 uses the less efficient ExecutorService.close.
+      // ExecutorService.close does not however use unreachable apis and ExecutorService is present
+      // at Android api 19.
+      return synthesizeExecutorServiceDispatchCase(
+          appView, context, eventConsumer, contextSupplier);
+    }
+    if (type.isIdenticalTo(factory.androidContentResTypedArrayType)) {
+      return factory.createMethod(type, factory.createProto(factory.voidType), "recycle");
+    }
+    if (type.isIdenticalTo(factory.androidContentContentProviderClientType)) {
+      return factory.createMethod(type, factory.createProto(factory.booleanType), "release");
+    }
+    assert ImmutableSet.of(
+            factory.androidMediaMediaMetadataRetrieverType,
+            factory.androidMediaMediaDrmType,
+            factory.androidDrmDrmManagerClientType)
+        .contains(type);
+    return factory.createMethod(type, factory.createProto(factory.voidType), "release");
+  }
+
+  private DexMethod synthesizeExecutorServiceDispatchCase(
+      AppView<?> appView,
+      ProgramDefinition context,
+      AutoCloseableRetargeterEventConsumer eventConsumer,
+      Supplier<UniqueContext> contextSupplier) {
+    ProgramMethod method =
+        appView
+            .getSyntheticItems()
+            .createMethod(
+                kinds -> kinds.AUTOCLOSEABLE_FORWARDER,
+                contextSupplier.get(),
+                appView,
+                methodBuilder ->
+                    methodBuilder
+                        .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+                        .setProto(
+                            factory.createProto(
+                                factory.voidType, factory.javaUtilConcurrentExecutorServiceType))
+                        .setCode(
+                            methodSig ->
+                                BackportedMethods.ExecutorServiceMethods_closeExecutorService(
+                                    factory, methodSig)));
+    eventConsumer.acceptAutoCloseableDispatchMethod(method, context);
+    return method.getReference();
+  }
+
+  ProgramMethod createThrowUnsupportedException(
+      AppView<?> appView,
+      ProgramDefinition context,
+      AutoCloseableRetargeterEventConsumer eventConsumer,
+      Supplier<UniqueContext> contextSupplier) {
+    ProgramMethod method =
+        appView
+            .getSyntheticItems()
+            .createMethod(
+                kinds -> kinds.THROW_IAE,
+                contextSupplier.get(),
+                appView,
+                methodBuilder ->
+                    methodBuilder
+                        .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+                        .setProto(factory.createProto(factory.voidType, factory.objectType))
+                        .setCode(
+                            methodSig ->
+                                new ThrowCfCodeProvider(
+                                        appView,
+                                        methodSig.getHolderType(),
+                                        factory.illegalArgumentExceptionType,
+                                        null)
+                                    .generateCfCode()));
+    eventConsumer.acceptAutoCloseableDispatchMethod(method, context);
+    return method;
+  }
+
+  static DexClassAndMethod lookupSuperIncludingInterfaces(
+      AppView<?> appView, DexMethod target, DexProgramClass context) {
+    DexClassAndMethod superMethod =
+        appView
+            .appInfoForDesugaring()
+            .lookupSuperTarget(target, context, appView, appView.appInfoForDesugaring());
+    if (superMethod != null) {
+      return superMethod;
+    }
+    return appView
+        .appInfoForDesugaring()
+        .lookupMaximallySpecificMethod(context.getContextClass(), target);
+  }
+}
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
new file mode 100644
index 0000000..b45317b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/AutoCloseableRetargeterPostProcessor.java
@@ -0,0 +1,211 @@
+// 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 com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter;
+
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.AutoCloseableRetargeterHelper.lookupSuperIncludingInterfaces;
+
+import com.android.tools.r8.contexts.CompilationContext.MainThreadContext;
+import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaring;
+import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.AutoCloseableRetargeterEventConsumer.AutoCloseableRetargeterPostProcessingEventConsumer;
+import com.android.tools.r8.utils.OptionalBool;
+import com.android.tools.r8.utils.WorkList;
+import com.google.common.collect.ImmutableSet;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.concurrent.ExecutorService;
+
+// The rewrite of virtual calls requires to go through emulate dispatch. This class is responsible
+// for inserting interfaces on library boundaries and forwarding methods in the program, and to
+// synthesize the interfaces and emulated dispatch classes in the desugared library.
+public class AutoCloseableRetargeterPostProcessor implements CfPostProcessingDesugaring {
+
+  private final AppView<?> appView;
+  private final AutoCloseableRetargeterHelper data;
+
+  public AutoCloseableRetargeterPostProcessor(AppView<?> appView) {
+    this.appView = appView;
+    this.data =
+        new AutoCloseableRetargeterHelper(
+            appView.options().getMinApiLevel(), appView.dexItemFactory());
+  }
+
+  @Override
+  public void postProcessingDesugaring(
+      Collection<DexProgramClass> programClasses,
+      CfPostProcessingDesugaringEventConsumer eventConsumer,
+      ExecutorService executorService) {
+    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;
+      }
+      if (implementsAutoCloseableAtLibraryBoundary(clazz)) {
+        ensureInterfacesAndForwardingMethodsSynthesized(eventConsumer, clazz, mainThreadContext);
+      }
+    }
+  }
+
+  private boolean implementsAutoCloseableAtLibraryBoundary(DexProgramClass clazz) {
+    if (clazz.interfaces.contains(appView.dexItemFactory().autoCloseableType)) {
+      return true;
+    }
+    WorkList<DexType> workList = collectLibrarySuperTypeAndInterfaces(clazz);
+    return libraryTypesImplementsAutoCloseable(workList, clazz);
+  }
+
+  private WorkList<DexType> collectLibrarySuperTypeAndInterfaces(DexProgramClass clazz) {
+    WorkList<DexType> 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);
+    }
+    for (DexType itf : clazz.interfaces) {
+      DexClass superItf = appView.definitionFor(itf);
+      if (superItf != null) {
+        workList.addIfNotSeen(superItf.type);
+      }
+    }
+    return workList;
+  }
+
+  private boolean libraryTypesImplementsAutoCloseable(
+      WorkList<DexType> workList, DexProgramClass clazz) {
+    while (workList.hasNext()) {
+      DexType current = workList.next();
+      if (current.isIdenticalTo(appView.dexItemFactory().objectType)) {
+        continue;
+      }
+      DexClass currentClass = appView.definitionFor(current);
+      if (currentClass == null) {
+        reportInvalidSupertype(current, clazz);
+        continue;
+      }
+      if (currentClass.interfaces.contains(appView.dexItemFactory().autoCloseableType)) {
+        return true;
+      }
+      workList.addIfNotSeen(currentClass.superType);
+      workList.addIfNotSeen(currentClass.interfaces);
+    }
+    return false;
+  }
+
+  private void reportInvalidSupertype(DexType current, DexProgramClass origin) {
+    appView
+        .options()
+        .warningInvalidLibrarySuperclassForDesugar(
+            origin.getOrigin(),
+            origin.type,
+            current,
+            "missing",
+            ImmutableSet.of(getClose(origin.type)));
+  }
+
+  private void ensureInterfacesAndForwardingMethodsSynthesized(
+      AutoCloseableRetargeterPostProcessingEventConsumer eventConsumer,
+      DexProgramClass clazz,
+      MainThreadContext mainThreadContext) {
+    // DesugaredLibraryRetargeter emulate dispatch: insertion of a marker interface & forwarding
+    // methods.
+    // We cannot use the ClassProcessor since this applies up to 26, while the ClassProcessor
+    // applies up to 24.
+    if (appView.isAlreadyLibraryDesugared(clazz)) {
+      return;
+    }
+    DexMethod close = getClose(clazz.type);
+    if (clazz.lookupVirtualMethod(close) == null) {
+      DexEncodedMethod newMethod =
+          createForwardingMethod(close, clazz, eventConsumer, mainThreadContext);
+      if (newMethod == null) {
+        // We don't support desugaring on all subtypes, in which case there is not need to inject
+        // the interface.
+        return;
+      }
+      clazz.addVirtualMethod(newMethod);
+      eventConsumer.acceptAutoCloseableForwardingMethod(new ProgramMethod(clazz, newMethod), clazz);
+    }
+    DexType autoCloseableType = appView.dexItemFactory().autoCloseableType;
+    if (clazz.interfaces.contains(autoCloseableType)) {
+      return;
+    }
+    clazz.addExtraInterfaces(
+        Collections.singletonList(new ClassTypeSignature(autoCloseableType)),
+        appView.dexItemFactory());
+    eventConsumer.acceptAutoCloseableInterfaceInjection(
+        clazz, appView.definitionFor(autoCloseableType));
+  }
+
+  @SuppressWarnings("ReferenceEquality")
+  private DexEncodedMethod createForwardingMethod(
+      DexMethod target,
+      DexProgramClass clazz,
+      AutoCloseableRetargeterPostProcessingEventConsumer eventConsumer,
+      MainThreadContext mainThreadContext) {
+    // NOTE: Never add a forwarding method to methods of classes unknown or coming from android.jar
+    // even if this results in invalid code, these classes are never desugared.
+    // In desugared library, emulated interface methods can be overridden by retarget lib members.
+    AppInfoWithClassHierarchy appInfoForDesugaring = appView.appInfoForDesugaring();
+    assert clazz.lookupVirtualMethod(target) == null;
+    DexClassAndMethod superMethod = lookupSuperIncludingInterfaces(appView, target, clazz);
+    if (superMethod == null
+        || !data.superTargetsToRewrite().contains(superMethod.getHolderType())) {
+      return null;
+    }
+    DexMethod forwardMethod =
+        data.synthesizeDispatchCase(
+            appView,
+            superMethod.getHolderType(),
+            clazz,
+            eventConsumer,
+            () -> mainThreadContext.createUniqueContext(clazz));
+    assert forwardMethod != null && forwardMethod != target;
+    DexClassAndMethod resolvedMethod =
+        appInfoForDesugaring
+            .resolveMethodLegacy(target, target.getHolderType().isInterface(appInfoForDesugaring))
+            .getResolutionPair();
+    assert resolvedMethod != null;
+    DexEncodedMethod desugaringForwardingMethod =
+        DexEncodedMethod.createDesugaringForwardingMethod(
+            resolvedMethod,
+            clazz,
+            forwardMethod,
+            appView.dexItemFactory(),
+            appView.getSyntheticItems().isSynthetic(forwardMethod.getHolderType()));
+    desugaringForwardingMethod.setLibraryMethodOverride(OptionalBool.TRUE);
+    return desugaringForwardingMethod;
+  }
+
+  private DexMethod getClose(DexType holder) {
+    return appView
+        .dexItemFactory()
+        .createMethod(
+            holder,
+            appView.dexItemFactory().createProto(appView.dexItemFactory().voidType),
+            "close");
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java
index c4be060..fa05f19 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java
@@ -161,7 +161,7 @@
     assert resolvedMethod != null;
     DexEncodedMethod desugaringForwardingMethod =
         DexEncodedMethod.createDesugaringForwardingMethod(
-            resolvedMethod, clazz, forwardMethod, appView.dexItemFactory());
+            resolvedMethod, clazz, forwardMethod, appView.dexItemFactory(), true);
     desugaringForwardingMethod.setLibraryMethodOverride(OptionalBool.TRUE);
     return desugaringForwardingMethod;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java
index 5777971..3413d51 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java
@@ -20,6 +20,7 @@
 import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterSynthesizerEventConsumer.DesugaredLibraryRetargeterInstructionEventConsumer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterSynthesizerEventConsumer.DesugaredLibraryRetargeterL8SynthesizerEventConsumer;
 import com.android.tools.r8.ir.synthetic.EmulateDispatchSyntheticCfCodeProvider;
+import com.android.tools.r8.ir.synthetic.EmulateDispatchSyntheticCfCodeProvider.EmulateDispatchType;
 import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
 import com.android.tools.r8.synthesis.SyntheticClassBuilder;
 import com.android.tools.r8.synthesis.SyntheticItems.SyntheticKindSelector;
@@ -284,7 +285,12 @@
     DexMethod itfMethod = emulatedInterfaceDispatchMethod(itfClass, descriptor);
     assert descriptor.getDispatchCases().isEmpty();
     return new EmulateDispatchSyntheticCfCodeProvider(
-            methodSig.getHolderType(), forwardingMethod, itfMethod, new LinkedHashMap<>(), appView)
+            methodSig.getHolderType(),
+            forwardingMethod,
+            itfMethod,
+            new LinkedHashMap<>(),
+            EmulateDispatchType.ALL_STATIC,
+            appView)
         .generateCfCode();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
index c2a1998..2b89cbb 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
@@ -1038,7 +1038,7 @@
     // In desugared library, emulated interface methods can be overridden by retarget lib members.
     DexEncodedMethod desugaringForwardingMethod =
         DexEncodedMethod.createDesugaringForwardingMethod(
-            target, clazz, forwardMethod, dexItemFactory);
+            target, clazz, forwardMethod, dexItemFactory, true);
     if (!target.isProgramMethod() || target.getDefinition().isLibraryMethodOverride().isTrue()) {
       desugaringForwardingMethod.setLibraryMethodOverride(OptionalBool.TRUE);
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
index 20086db..968ecad 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedInterfaceDescriptor;
 import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceSynthesizerEventConsumer.L8ProgramEmulatedInterfaceSynthesizerEventConsumer;
 import com.android.tools.r8.ir.synthetic.EmulateDispatchSyntheticCfCodeProvider;
+import com.android.tools.r8.ir.synthetic.EmulateDispatchSyntheticCfCodeProvider.EmulateDispatchType;
 import com.android.tools.r8.synthesis.SyntheticMethodBuilder;
 import com.android.tools.r8.synthesis.SyntheticProgramClassBuilder;
 import com.android.tools.r8.utils.StringDiagnostic;
@@ -105,6 +106,7 @@
                         companionMethod,
                         itfMethod,
                         extraDispatchCases,
+                        EmulateDispatchType.ALL_STATIC,
                         appView)
                     .generateCfCode());
   }
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 9caa410..2604cef 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
@@ -4,6 +4,9 @@
 
 package com.android.tools.r8.ir.synthetic;
 
+import static com.android.tools.r8.ir.synthetic.EmulateDispatchSyntheticCfCodeProvider.EmulateDispatchType.ALL_STATIC;
+import static com.android.tools.r8.ir.synthetic.EmulateDispatchSyntheticCfCodeProvider.EmulateDispatchType.AUTO_CLOSEABLE;
+
 import com.android.tools.r8.cf.code.CfCheckCast;
 import com.android.tools.r8.cf.code.CfFrame;
 import com.android.tools.r8.cf.code.CfIf;
@@ -29,8 +32,14 @@
 
 public class EmulateDispatchSyntheticCfCodeProvider extends SyntheticCfCodeProvider {
 
+  public enum EmulateDispatchType {
+    ALL_STATIC,
+    AUTO_CLOSEABLE
+  }
+
   private final DexMethod forwardingMethod;
   private final DexMethod interfaceMethod;
+  private final EmulateDispatchType dispatchType;
   private final LinkedHashMap<DexType, DexMethod> extraDispatchCases;
 
   public EmulateDispatchSyntheticCfCodeProvider(
@@ -38,11 +47,13 @@
       DexMethod forwardingMethod,
       DexMethod interfaceMethod,
       LinkedHashMap<DexType, DexMethod> extraDispatchCases,
+      EmulateDispatchType dispatchType,
       AppView<?> appView) {
     super(appView, holder);
     this.forwardingMethod = forwardingMethod;
     this.interfaceMethod = interfaceMethod;
     this.extraDispatchCases = extraDispatchCases;
+    this.dispatchType = dispatchType;
   }
 
   @Override
@@ -89,8 +100,7 @@
       // Call basic block.
       instructions.add(new CfLoad(ValueType.fromDexType(receiverType), 0));
       instructions.add(new CfCheckCast(dispatch.getKey()));
-      loadExtraParameters(instructions);
-      instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, dispatch.getValue(), false));
+      forwardCall(instructions, dispatch.getValue());
       addReturn(instructions);
     }
 
@@ -98,12 +108,28 @@
     instructions.add(labels[nextLabel]);
     instructions.add(frame.clone());
     instructions.add(new CfLoad(ValueType.fromDexType(receiverType), 0));
-    loadExtraParameters(instructions);
-    instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, forwardingMethod, false));
+    forwardCall(instructions, forwardingMethod);
     addReturn(instructions);
     return standardCfCodeFromInstructions(instructions);
   }
 
+  private void forwardCall(List<CfInstruction> instructions, DexMethod method) {
+    if (dispatchType == ALL_STATIC
+        || appView.getSyntheticItems().isSynthetic(method.getHolderType())) {
+      loadExtraParameters(instructions);
+      instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, method, false));
+      return;
+    }
+    assert dispatchType == AUTO_CLOSEABLE;
+    instructions.add(new CfCheckCast(method.holder));
+    loadExtraParameters(instructions);
+    if (appView.definitionFor(method.getHolderType()).isInterface()) {
+      instructions.add(new CfInvoke(Opcodes.INVOKEINTERFACE, method, true));
+    } else {
+      instructions.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, method, false));
+    }
+  }
+
   private void loadExtraParameters(List<CfInstruction> instructions) {
     int index = 1;
     for (DexType type : interfaceMethod.proto.parameters.values) {
@@ -113,10 +139,10 @@
 
   @SuppressWarnings("ReferenceEquality")
   private void addReturn(List<CfInstruction> instructions) {
-    if (interfaceMethod.proto.returnType == appView.dexItemFactory().voidType) {
+    if (interfaceMethod.getReturnType().isVoidType()) {
       instructions.add(new CfReturnVoid());
     } else {
-      instructions.add(new CfReturn(ValueType.fromDexType(interfaceMethod.proto.returnType)));
+      instructions.add(new CfReturn(ValueType.fromDexType(interfaceMethod.getReturnType())));
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java
index b8c5959..77a8cd8 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java
@@ -67,6 +67,7 @@
   private InvokeType invokeType = null;
   private Boolean isInterface = null;
   private boolean castResult = false;
+  private boolean ignoreTargetResult = false;
   private boolean isConstructorDelegate = false;
   private AppInfoWithClassHierarchy appInfoForCastArguments = null;
 
@@ -157,6 +158,11 @@
     return this;
   }
 
+  public ForwardMethodBuilder setIgnoreTargetResult() {
+    ignoreTargetResult = true;
+    return this;
+  }
+
   public ForwardMethodBuilder setCastArguments(AppInfoWithClassHierarchy appInfo) {
     appInfoForCastArguments = appInfo;
     return this;
@@ -380,6 +386,9 @@
         && !targetMethod.getReturnType().isVoidType()) {
       assert ValueType.fromDexType(sourceMethod.getReturnType())
           == ValueType.fromDexType(targetMethod.getReturnType());
+    } else if (ignoreTargetResult) {
+      assert sourceMethod.getReturnType().isVoidType();
+      assert !targetMethod.getReturnType().isVoidType();
     } else {
       assert sourceMethod.getReturnType() == targetMethod.getReturnType();
     }
diff --git a/src/main/java/com/android/tools/r8/profile/rewriting/ConcreteProfileCollectionAdditions.java b/src/main/java/com/android/tools/r8/profile/rewriting/ConcreteProfileCollectionAdditions.java
index 7fa618c..2882170 100644
--- a/src/main/java/com/android/tools/r8/profile/rewriting/ConcreteProfileCollectionAdditions.java
+++ b/src/main/java/com/android/tools/r8/profile/rewriting/ConcreteProfileCollectionAdditions.java
@@ -80,6 +80,12 @@
         context, additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder()));
   }
 
+  public void addMethodAndHolderIfContextIsInProfile(
+      ProgramMethod method, ProgramDefinition context) {
+    applyIfContextIsInProfile(
+        context, additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder()));
+  }
+
   void applyIfContextIsInProfile(
       ProgramDefinition context, Consumer<ProfileAdditionsBuilder> builderConsumer) {
     if (context.isProgramClass()) {
diff --git a/src/main/java/com/android/tools/r8/profile/rewriting/ProfileRewritingCfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/profile/rewriting/ProfileRewritingCfInstructionDesugaringEventConsumer.java
index 3af5dc4..8a65064 100644
--- a/src/main/java/com/android/tools/r8/profile/rewriting/ProfileRewritingCfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/profile/rewriting/ProfileRewritingCfInstructionDesugaringEventConsumer.java
@@ -342,6 +342,12 @@
   }
 
   @Override
+  public void acceptAutoCloseableDispatchMethod(ProgramMethod method, ProgramDefinition context) {
+    additionsCollection.addMethodAndHolderIfContextIsInProfile(method, context);
+    parent.acceptAutoCloseableDispatchMethod(method, context);
+  }
+
+  @Override
   public void acceptRecordClass(DexProgramClass recordClass) {
     parent.acceptRecordClass(recordClass);
   }
diff --git a/src/main/java/com/android/tools/r8/profile/rewriting/ProfileRewritingCfPostProcessingDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/profile/rewriting/ProfileRewritingCfPostProcessingDesugaringEventConsumer.java
index 944f860..926c9d7 100644
--- a/src/main/java/com/android/tools/r8/profile/rewriting/ProfileRewritingCfPostProcessingDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/profile/rewriting/ProfileRewritingCfPostProcessingDesugaringEventConsumer.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.MethodResolutionResult.FailedResolutionResult;
+import com.android.tools.r8.graph.ProgramDefinition;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
@@ -106,11 +107,28 @@
   }
 
   @Override
+  public void acceptAutoCloseableForwardingMethod(ProgramMethod method, ProgramDefinition context) {
+    additionsCollection.addMethodAndHolderIfContextIsInProfile(method, context);
+    parent.acceptAutoCloseableForwardingMethod(method, context);
+  }
+
+  @Override
+  public void acceptAutoCloseableDispatchMethod(ProgramMethod method, ProgramDefinition context) {
+    additionsCollection.addMethodAndHolderIfContextIsInProfile(method, context);
+    parent.acceptAutoCloseableDispatchMethod(method, context);
+  }
+
+  @Override
   public void acceptInterfaceInjection(DexProgramClass clazz, DexClass newInterface) {
     parent.acceptInterfaceInjection(clazz, newInterface);
   }
 
   @Override
+  public void acceptAutoCloseableInterfaceInjection(DexProgramClass clazz, DexClass newInterface) {
+    parent.acceptAutoCloseableInterfaceInjection(clazz, newInterface);
+  }
+
+  @Override
   public void acceptInterfaceMethodDesugaringForwardingMethod(
       ProgramMethod method, DexClassAndMethod baseMethod) {
     additionsCollection.addMethodIfContextIsInProfile(method, baseMethod);
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
index 0030cec..1ef8edc 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -66,6 +66,10 @@
   public final SyntheticKind CONST_DYNAMIC = generator.forNonSharableInstanceClass("$Condy");
 
   // Method synthetics.
+  public final SyntheticKind AUTOCLOSEABLE_DISPATCHER =
+      generator.forSingleMethodWithGlobalMerging("AutoCloseableDispatcher");
+  public final SyntheticKind AUTOCLOSEABLE_FORWARDER =
+      generator.forSingleMethodWithGlobalMerging("AutoCloseableForwarder");
   public final SyntheticKind TYPE_SWITCH_HELPER =
       generator.forSingleMethodWithGlobalMerging("TypeSwitch");
   public final SyntheticKind ENUM_UNBOXING_CHECK_NOT_ZERO_METHOD =
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 6dd1d81..64de864 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -2122,6 +2122,7 @@
         System.getProperty("com.android.tools.r8.enableKeepAnnotations") != null;
     public boolean reverseClassSortingForDeterminism = false;
 
+    public boolean enableAutoCloseableDesugaring = false;
     public boolean enableNumberUnboxer = false;
     public boolean printNumberUnboxed = false;
     public boolean roundtripThroughLir = false;
@@ -2695,6 +2696,17 @@
         && !canUseDefaultAndStaticInterfaceMethods();
   }
 
+  public boolean canHaveMissingImplementsAutoCloseableInterface() {
+    return getMinApiLevel().isLessThanOrEqualTo(AndroidApiLevel.V);
+  }
+
+  public boolean shouldDesugarAutoCloseable() {
+    return desugarState.isOn()
+        && getMinApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.K)
+        && canHaveMissingImplementsAutoCloseableInterface()
+        && testing.enableAutoCloseableDesugaring;
+  }
+
   public boolean isSwitchRewritingEnabled() {
     return enableSwitchRewriting && !debug;
   }
diff --git a/src/test/examplesJava21/autocloseable/AutoCloseableDesugaringClassesPresentAtKitKat.java b/src/test/examplesJava21/autocloseable/AutoCloseableDesugaringClassesPresentAtKitKat.java
new file mode 100644
index 0000000..a6c9881
--- /dev/null
+++ b/src/test/examplesJava21/autocloseable/AutoCloseableDesugaringClassesPresentAtKitKat.java
@@ -0,0 +1,64 @@
+// 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 org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryTypeRewriter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.AutoCloseableRetargeterHelper;
+import com.android.tools.r8.synthesis.SyntheticItems.GlobalSyntheticsStrategy;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.Sets;
+import java.nio.file.Path;
+import java.util.Set;
+import org.junit.Test;
+import org.junit.runners.Parameterized.Parameters;
+
+public class AutoCloseableDesugaringClassesPresentAtKitKat extends TestBase {
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    InternalOptions options = new InternalOptions();
+    Path androidJarK = ToolHelper.getAndroidJar(AndroidApiLevel.K);
+    AndroidApp app = AndroidApp.builder().addProgramFile(androidJarK).build();
+    DirectMappedDexApplication libHolder =
+        new ApplicationReader(app, options, Timing.empty()).read().toDirect();
+    AppInfo initialAppInfo =
+        AppInfo.createInitialAppInfo(libHolder, GlobalSyntheticsStrategy.forNonSynthesizing());
+    AppView<AppInfo> appView =
+        AppView.createForD8(initialAppInfo, DesugaredLibraryTypeRewriter.empty(), Timing.empty());
+
+    AutoCloseableRetargeterHelper data =
+        new AutoCloseableRetargeterHelper(AndroidApiLevel.B, appView.dexItemFactory());
+    Set<DexType> missing = Sets.newIdentityHashSet();
+    for (DexType dexType : data.superTargetsToRewrite()) {
+      if (appView.definitionFor(dexType) == null) {
+        missing.add(dexType);
+      }
+    }
+    assertEquals(1, missing.size());
+    // ForkJoinPool is missing at Android Api level 19 but that's ok since it implements
+    // ExecutorService.close in a more optimized way. We rely on ExecutorService for the
+    // emulated dispatch.
+    assertEquals(
+        options.dexItemFactory().javaUtilConcurrentForkJoinPoolType, missing.iterator().next());
+  }
+}
diff --git a/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterAndroidSubtypeTest.java b/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterAndroidSubtypeTest.java
new file mode 100644
index 0000000..5fca53f
--- /dev/null
+++ b/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterAndroidSubtypeTest.java
@@ -0,0 +1,271 @@
+// 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.compileAutoCloseableAndroidLibraryClasses;
+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.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.backports.AbstractBackportTest;
+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;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class AutoCloseableRetargeterAndroidSubtypeTest extends AbstractBackportTest {
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters()
+        .withDexRuntimes()
+        .withApiLevelsStartingAtIncluding(AndroidApiLevel.K)
+        .enableApiLevelsForCf()
+        .build();
+  }
+
+  public AutoCloseableRetargeterAndroidSubtypeTest(TestParameters parameters) throws IOException {
+    super(
+        parameters,
+        getTestRunner(),
+        ImmutableList.of(
+            getTestRunner(),
+            getTransformedSubclass(
+                ContentProviderClientOverride.class,
+                DexItemFactory.androidContentContentProviderClientDescriptorString),
+            getTransformedSubclass(
+                ContentProviderClientSub.class,
+                DexItemFactory.androidContentContentProviderClientDescriptorString),
+            getTransformedSubclass(
+                DrmManagerClientOverride.class,
+                DexItemFactory.androidDrmDrmManagerClientDescriptorString),
+            getTransformedSubclass(
+                DrmManagerClientSub.class,
+                DexItemFactory.androidDrmDrmManagerClientDescriptorString),
+            getTransformedSubclass(
+                MediaMetadataRetrieverOverride.class,
+                DexItemFactory.androidMediaMediaMetadataRetrieverDescriptorString),
+            getTransformedSubclass(
+                MediaMetadataRetrieverSub.class,
+                DexItemFactory.androidMediaMediaMetadataRetrieverDescriptorString),
+            getTransformedSubclass(
+                TypedArrayOverride.class,
+                DexItemFactory.androidContentResTypedArrayDescriptorString),
+            getTransformedSubclass(
+                TypedArraySub.class, DexItemFactory.androidContentResTypedArrayDescriptorString)));
+
+    // 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");
+
+    registerTarget(AndroidApiLevel.B, 4);
+  }
+
+  @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()) {
+      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();
+    testForJvm(parameters)
+        .addStrippedOuter(getClass())
+        .apply(this::configureProgram)
+        .run(parameters.getRuntime(), getTestClassName())
+        // Fails when not desugared.
+        .assertFailureWithErrorThatMatches(containsString("close should not be called"));
+  }
+
+  private static byte[] getTransformedSubclass(Class<?> sub, String superDesc) throws IOException {
+    return transformer(sub)
+        .setSuper(superDesc)
+        .transformMethodInsnInMethod(
+            "<init>",
+            (opcode, owner, name, descriptor, isInterface, visitor) -> {
+              assert name.equals("<init>");
+              visitor.visitMethodInsn(
+                  opcode,
+                  DescriptorUtils.descriptorToInternalName(superDesc),
+                  name,
+                  descriptor,
+                  isInterface);
+            })
+        .transformMethodInsnInMethod(
+            "close",
+            (opcode, owner, name, descriptor, isInterface, visitor) -> {
+              if (name.equals("close")) {
+                visitor.visitMethodInsn(
+                    opcode,
+                    DescriptorUtils.descriptorToInternalName(superDesc),
+                    name,
+                    descriptor,
+                    isInterface);
+              } else {
+                visitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+              }
+            })
+        .clearNest()
+        .transform();
+  }
+
+  private static byte[] getTestRunner() throws IOException {
+    return transformer(TestRunner.class)
+        .replaceClassDescriptorInMethodInstructions(
+            descriptor(ContentProviderClient.class),
+            DexItemFactory.androidContentContentProviderClientDescriptorString)
+        .replaceClassDescriptorInMethodInstructions(
+            descriptor(DrmManagerClient.class),
+            DexItemFactory.androidDrmDrmManagerClientDescriptorString)
+        .replaceClassDescriptorInMethodInstructions(
+            descriptor(MediaMetadataRetriever.class),
+            DexItemFactory.androidMediaMediaMetadataRetrieverDescriptorString)
+        .replaceClassDescriptorInMethodInstructions(
+            descriptor(TypedArray.class),
+            DexItemFactory.androidContentResTypedArrayDescriptorString)
+        .clearNest()
+        .transform();
+  }
+
+  public static class ContentProviderClientOverride extends ContentProviderClient {
+
+    public void close() {
+      super.close();
+      System.out.println("close content provider");
+    }
+  }
+
+  public static class ContentProviderClientSub extends ContentProviderClient {}
+
+  public static class DrmManagerClientOverride extends DrmManagerClient {
+
+    public void close() {
+      super.close();
+      System.out.println("close drm manager");
+    }
+  }
+
+  public static class DrmManagerClientSub extends DrmManagerClient {}
+
+  public static class MediaMetadataRetrieverOverride extends MediaMetadataRetriever {
+
+    public void close() {
+      super.close();
+      System.out.println("close media metadata");
+    }
+  }
+
+  public static class MediaMetadataRetrieverSub extends MediaMetadataRetriever {}
+
+  public static class TypedArrayOverride extends TypedArray {
+
+    public void close() {
+      super.close();
+      System.out.println("close typed array");
+    }
+  }
+
+  public static class TypedArraySub extends TypedArray {}
+
+  public static class TestRunner extends MiniAssert {
+
+    public static void main(String[] args) {
+      contentProviderClient();
+      drmManagerClient();
+      mediaMetadataRetriever();
+      typedArray();
+    }
+
+    private static void contentProviderClient() {
+      ContentProviderClientSub cpcSub = new ContentProviderClientSub();
+      MiniAssert.assertFalse(cpcSub.wasClosed);
+      cpcSub.close();
+      MiniAssert.assertTrue(cpcSub.wasClosed);
+
+      ContentProviderClientOverride cpcOverride = new ContentProviderClientOverride();
+      MiniAssert.assertFalse(cpcOverride.wasClosed);
+      cpcOverride.close();
+      MiniAssert.assertTrue(cpcOverride.wasClosed);
+    }
+
+    public static void drmManagerClient() {
+      DrmManagerClientSub drmMC = new DrmManagerClientSub();
+      MiniAssert.assertFalse(drmMC.wasClosed);
+      drmMC.close();
+      MiniAssert.assertTrue(drmMC.wasClosed);
+
+      DrmManagerClientOverride drmMCOverride = new DrmManagerClientOverride();
+      MiniAssert.assertFalse(drmMCOverride.wasClosed);
+      drmMCOverride.close();
+      MiniAssert.assertTrue(drmMCOverride.wasClosed);
+    }
+
+    public static void mediaMetadataRetriever() {
+      MediaMetadataRetrieverSub mmrSub = new MediaMetadataRetrieverSub();
+      MiniAssert.assertFalse(mmrSub.wasClosed);
+      mmrSub.close();
+      MiniAssert.assertTrue(mmrSub.wasClosed);
+
+      MediaMetadataRetrieverOverride mmrOverride = new MediaMetadataRetrieverOverride();
+      MiniAssert.assertFalse(mmrOverride.wasClosed);
+      mmrOverride.close();
+      MiniAssert.assertTrue(mmrOverride.wasClosed);
+    }
+
+    public static void typedArray() {
+      TypedArraySub taSub = new TypedArraySub();
+      MiniAssert.assertFalse(taSub.wasClosed);
+      taSub.close();
+      MiniAssert.assertTrue(taSub.wasClosed);
+
+      TypedArrayOverride taOverride = new TypedArrayOverride();
+      MiniAssert.assertFalse(taOverride.wasClosed);
+      taOverride.close();
+      MiniAssert.assertTrue(taOverride.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/AutoCloseableRetargeterAndroidSubtypeTwrTest.java b/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterAndroidSubtypeTwrTest.java
new file mode 100644
index 0000000..efad24f
--- /dev/null
+++ b/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterAndroidSubtypeTwrTest.java
@@ -0,0 +1,330 @@
+// 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.compileAutoCloseableAndroidLibraryClasses;
+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.ToolHelper;
+import com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.ContentProviderClientApiLevel24;
+import com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.DrmManagerClientApiLevel24;
+import com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.MediaDrmApiLevel28;
+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;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class AutoCloseableRetargeterAndroidSubtypeTwrTest extends AbstractBackportTest {
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters()
+        .withDexRuntimes()
+        .withApiLevelsStartingAtIncluding(AndroidApiLevel.K)
+        .enableApiLevelsForCf()
+        .build();
+  }
+
+  public AutoCloseableRetargeterAndroidSubtypeTwrTest(TestParameters parameters)
+      throws IOException {
+    super(
+        parameters,
+        getTestRunner(),
+        ImmutableList.of(
+            getTestRunner(),
+            getTransformedSubclass(
+                ContentProviderClientOverride.class,
+                DexItemFactory.androidContentContentProviderClientDescriptorString),
+            getTransformedSubclass(
+                ContentProviderClientSub.class,
+                DexItemFactory.androidContentContentProviderClientDescriptorString),
+            getTransformedSubclass(
+                DrmManagerClientOverride.class,
+                DexItemFactory.androidDrmDrmManagerClientDescriptorString),
+            getTransformedSubclass(
+                DrmManagerClientSub.class,
+                DexItemFactory.androidDrmDrmManagerClientDescriptorString),
+            getTransformedSubclass(
+                MediaMetadataRetrieverOverride.class,
+                DexItemFactory.androidMediaMediaMetadataRetrieverDescriptorString),
+            getTransformedSubclass(
+                MediaMetadataRetrieverSub.class,
+                DexItemFactory.androidMediaMediaMetadataRetrieverDescriptorString),
+            getTransformedSubclass(
+                TypedArrayOverride.class,
+                DexItemFactory.androidContentResTypedArrayDescriptorString),
+            getTransformedSubclass(
+                TypedArraySub.class, DexItemFactory.androidContentResTypedArrayDescriptorString)));
+
+    // 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");
+
+    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()) {
+      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();
+    testForJvm(parameters)
+        .addStrippedOuter(getClass())
+        .apply(this::configureProgram)
+        .run(parameters.getRuntime(), getTestClassName())
+        // Fails when not desugared.
+        .assertFailureWithErrorThatMatches(containsString("close should not be called"));
+  }
+
+  private static byte[] getTransformedSubclass(Class<?> sub, String superDesc) throws IOException {
+    return transformer(sub)
+        .setSuper(superDesc)
+        .transformMethodInsnInMethod(
+            "<init>",
+            (opcode, owner, name, descriptor, isInterface, visitor) -> {
+              assert name.equals("<init>");
+              visitor.visitMethodInsn(
+                  opcode,
+                  DescriptorUtils.descriptorToInternalName(superDesc),
+                  name,
+                  descriptor,
+                  isInterface);
+            })
+        .transformMethodInsnInMethod(
+            "close",
+            (opcode, owner, name, descriptor, isInterface, visitor) -> {
+              if (name.equals("close")) {
+                visitor.visitMethodInsn(
+                    opcode,
+                    DescriptorUtils.descriptorToInternalName(superDesc),
+                    name,
+                    descriptor,
+                    isInterface);
+              } else {
+                visitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+              }
+            })
+        .clearNest()
+        .transform();
+  }
+
+  private static byte[] getTestRunner() throws IOException {
+    return transformer(TestRunner.class)
+        .replaceClassDescriptorInMethodInstructions(
+            descriptor(ContentProviderClientApiLevel24.class),
+            DexItemFactory.androidContentContentProviderClientDescriptorString)
+        .replaceClassDescriptorInMethodInstructions(
+            descriptor(DrmManagerClientApiLevel24.class),
+            DexItemFactory.androidDrmDrmManagerClientDescriptorString)
+        .replaceClassDescriptorInMethodInstructions(
+            descriptor(MediaMetadataRetrieverApiLevel29.class),
+            DexItemFactory.androidMediaMediaMetadataRetrieverDescriptorString)
+        .replaceClassDescriptorInMethodInstructions(
+            descriptor(TypedArrayAndroidApiLevel31.class),
+            DexItemFactory.androidContentResTypedArrayDescriptorString)
+        .replaceClassDescriptorInMethodInstructions(
+            descriptor(MediaDrmApiLevel28.class),
+            DexItemFactory.androidMediaMediaDrmDescriptorString)
+        .clearNest()
+        .transform();
+  }
+
+  public static class ContentProviderClientOverride extends ContentProviderClientApiLevel24 {
+
+    public void close() {
+      super.close();
+      System.out.println("close content provider");
+    }
+  }
+
+  public static class ContentProviderClientSub extends ContentProviderClientApiLevel24 {}
+
+  public static class DrmManagerClientOverride extends DrmManagerClientApiLevel24 {
+
+    public void close() {
+      super.close();
+      System.out.println("close drm manager");
+    }
+  }
+
+  public static class DrmManagerClientSub extends DrmManagerClientApiLevel24 {}
+
+  public static class MediaMetadataRetrieverOverride extends MediaMetadataRetrieverApiLevel29 {
+
+    public void close() {
+      super.close();
+      System.out.println("close media metadata");
+    }
+  }
+
+  public static class MediaMetadataRetrieverSub extends MediaMetadataRetrieverApiLevel29 {}
+
+  public static class TypedArrayOverride extends TypedArrayAndroidApiLevel31 {
+
+    public void close() {
+      super.close();
+      System.out.println("close typed array");
+    }
+  }
+
+  public static class TypedArraySub extends TypedArrayAndroidApiLevel31 {}
+
+  public static class TestRunner extends MiniAssert {
+
+    public static void main(String[] args) {
+      raw();
+      contentProviderClient();
+      drmManagerClient();
+      mediaMetadataRetriever();
+      typedArray();
+    }
+
+    private static void raw() {
+      ContentProviderClientApiLevel24[] box1 = new ContentProviderClientApiLevel24[1];
+      try (ContentProviderClientApiLevel24 val = new ContentProviderClientApiLevel24()) {
+        MiniAssert.assertFalse(val.wasClosed);
+        box1[0] = val;
+      }
+      MiniAssert.assertTrue(box1[0].wasClosed);
+
+      DrmManagerClientApiLevel24[] box2 = new DrmManagerClientApiLevel24[1];
+      try (DrmManagerClientApiLevel24 val = new DrmManagerClientApiLevel24()) {
+        MiniAssert.assertFalse(val.wasClosed);
+        box2[0] = val;
+      }
+      MiniAssert.assertTrue(box2[0].wasClosed);
+
+      MediaDrmApiLevel28[] box3 = new MediaDrmApiLevel28[1];
+      try (MediaDrmApiLevel28 val = new MediaDrmApiLevel28()) {
+        MiniAssert.assertFalse(val.wasClosed);
+        box3[0] = val;
+      }
+      MiniAssert.assertTrue(box3[0].wasClosed);
+
+      MediaMetadataRetrieverApiLevel29[] box4 = new MediaMetadataRetrieverApiLevel29[1];
+      try (MediaMetadataRetrieverApiLevel29 val = new MediaMetadataRetrieverApiLevel29()) {
+        MiniAssert.assertFalse(val.wasClosed);
+        box4[0] = val;
+      }
+      MiniAssert.assertTrue(box4[0].wasClosed);
+
+      TypedArrayAndroidApiLevel31[] box5 = new TypedArrayAndroidApiLevel31[1];
+      try (TypedArrayAndroidApiLevel31 val = new TypedArrayAndroidApiLevel31()) {
+        MiniAssert.assertFalse(val.wasClosed);
+        box5[0] = val;
+      }
+      MiniAssert.assertTrue(box5[0].wasClosed);
+    }
+
+    private static void contentProviderClient() {
+      ContentProviderClientSub[] box1 = new ContentProviderClientSub[1];
+      try (ContentProviderClientSub val = new ContentProviderClientSub()) {
+        MiniAssert.assertFalse(val.wasClosed);
+        box1[0] = val;
+      }
+      MiniAssert.assertTrue(box1[0].wasClosed);
+
+      ContentProviderClientOverride[] box2 = new ContentProviderClientOverride[1];
+      try (ContentProviderClientOverride val = new ContentProviderClientOverride()) {
+        MiniAssert.assertFalse(val.wasClosed);
+        box2[0] = val;
+      }
+      MiniAssert.assertTrue(box2[0].wasClosed);
+    }
+
+    public static void drmManagerClient() {
+      DrmManagerClientSub[] box1 = new DrmManagerClientSub[1];
+      try (DrmManagerClientSub val = new DrmManagerClientSub()) {
+        MiniAssert.assertFalse(val.wasClosed);
+        box1[0] = val;
+      }
+      MiniAssert.assertTrue(box1[0].wasClosed);
+
+      DrmManagerClientOverride[] box2 = new DrmManagerClientOverride[1];
+      try (DrmManagerClientOverride val = new DrmManagerClientOverride()) {
+        MiniAssert.assertFalse(val.wasClosed);
+        box2[0] = val;
+      }
+      MiniAssert.assertTrue(box2[0].wasClosed);
+    }
+
+    public static void mediaMetadataRetriever() {
+      MediaMetadataRetrieverSub[] box1 = new MediaMetadataRetrieverSub[1];
+      try (MediaMetadataRetrieverSub val = new MediaMetadataRetrieverSub()) {
+        MiniAssert.assertFalse(val.wasClosed);
+        box1[0] = val;
+      }
+      MiniAssert.assertTrue(box1[0].wasClosed);
+
+      MediaMetadataRetrieverOverride[] box2 = new MediaMetadataRetrieverOverride[1];
+      try (MediaMetadataRetrieverOverride val = new MediaMetadataRetrieverOverride()) {
+        MiniAssert.assertFalse(val.wasClosed);
+        box2[0] = val;
+      }
+      MiniAssert.assertTrue(box2[0].wasClosed);
+    }
+
+    public static void typedArray() {
+      TypedArraySub[] box1 = new TypedArraySub[1];
+      try (TypedArraySub val = new TypedArraySub()) {
+        MiniAssert.assertFalse(val.wasClosed);
+        box1[0] = val;
+      }
+      MiniAssert.assertTrue(box1[0].wasClosed);
+
+      TypedArrayOverride[] box2 = new TypedArrayOverride[1];
+      try (TypedArrayOverride val = new TypedArrayOverride()) {
+        MiniAssert.assertFalse(val.wasClosed);
+        box2[0] = val;
+      }
+      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
new file mode 100644
index 0000000..e68e264
--- /dev/null
+++ b/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterAndroidTest.java
@@ -0,0 +1,160 @@
+// 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.compileAutoCloseableAndroidLibraryClasses;
+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.ToolHelper;
+import com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.ContentProviderClient;
+import com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.DrmManagerClient;
+import com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.MediaDrm;
+import com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.MediaMetadataRetriever;
+import com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.TypedArray;
+import com.android.tools.r8.desugar.backports.AbstractBackportTest;
+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.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class AutoCloseableRetargeterAndroidTest extends AbstractBackportTest {
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters()
+        .withDexRuntimes()
+        .withApiLevelsStartingAtIncluding(AndroidApiLevel.K)
+        .enableApiLevelsForCf()
+        .build();
+  }
+
+  public AutoCloseableRetargeterAndroidTest(TestParameters parameters) throws IOException {
+    super(parameters, getTestRunner(), ImmutableList.of(getTestRunner()));
+
+    // 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");
+
+    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()) {
+      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();
+    testForJvm(parameters)
+        .addStrippedOuter(getClass())
+        .apply(this::configureProgram)
+        .run(parameters.getRuntime(), getTestClassName())
+        // Fails when not desugared.
+        .assertFailureWithErrorThatMatches(containsString("close should not be called"));
+  }
+
+  private static byte[] getTestRunner() throws IOException {
+    return transformer(TestRunner.class)
+        .replaceClassDescriptorInMethodInstructions(
+            descriptor(ContentProviderClient.class),
+            DexItemFactory.androidContentContentProviderClientDescriptorString)
+        .replaceClassDescriptorInMethodInstructions(
+            descriptor(DrmManagerClient.class),
+            DexItemFactory.androidDrmDrmManagerClientDescriptorString)
+        .replaceClassDescriptorInMethodInstructions(
+            descriptor(MediaDrm.class), DexItemFactory.androidMediaMediaDrmDescriptorString)
+        .replaceClassDescriptorInMethodInstructions(
+            descriptor(MediaMetadataRetriever.class),
+            DexItemFactory.androidMediaMediaMetadataRetrieverDescriptorString)
+        .replaceClassDescriptorInMethodInstructions(
+            descriptor(TypedArray.class),
+            DexItemFactory.androidContentResTypedArrayDescriptorString)
+        .clearNest()
+        .transform();
+  }
+
+  public static class TestRunner extends MiniAssert {
+
+    public static void main(String[] args) {
+      contentProviderClient();
+      drmManagerClient();
+      mediaDrm();
+      mediaMetadataRetriever();
+      typedArray();
+    }
+
+    private static void contentProviderClient() {
+      ContentProviderClient contentProviderClient = new ContentProviderClient();
+      MiniAssert.assertFalse(contentProviderClient.wasClosed);
+      // Loop as regression test for b/276874854.
+      for (int i = 0; i < 2; i++) {
+        contentProviderClient.close();
+        MiniAssert.assertTrue(contentProviderClient.wasClosed);
+      }
+    }
+
+    public static void drmManagerClient() {
+      DrmManagerClient drmManagerClient = new DrmManagerClient();
+      MiniAssert.assertFalse(drmManagerClient.wasClosed);
+      drmManagerClient.close();
+      MiniAssert.assertTrue(drmManagerClient.wasClosed);
+    }
+
+    public static void mediaDrm() {
+      MediaDrm mediaDrm = new MediaDrm();
+      MiniAssert.assertFalse(mediaDrm.wasClosed);
+      mediaDrm.close();
+      MiniAssert.assertTrue(mediaDrm.wasClosed);
+    }
+
+    public static void mediaMetadataRetriever() {
+      MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
+      MiniAssert.assertFalse(mediaMetadataRetriever.wasClosed);
+      mediaMetadataRetriever.close();
+      MiniAssert.assertTrue(mediaMetadataRetriever.wasClosed);
+    }
+
+    public static void typedArray() {
+      TypedArray typedArray = new TypedArray();
+      MiniAssert.assertFalse(typedArray.wasClosed);
+      typedArray.close();
+      MiniAssert.assertTrue(typedArray.wasClosed);
+    }
+  }
+}
diff --git a/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterExecutorServiceSubtypeTest.java b/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterExecutorServiceSubtypeTest.java
new file mode 100644
index 0000000..d9c567c
--- /dev/null
+++ b/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterExecutorServiceSubtypeTest.java
@@ -0,0 +1,349 @@
+// 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.Assume.assumeTrue;
+
+import com.android.tools.r8.NeverInline;
+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.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.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 AutoCloseableRetargeterExecutorServiceSubtypeTest 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", "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())
+        .addOptionsModification(opt -> opt.testing.enableAutoCloseableDesugaring = true)
+        .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()
+        .addOptionsModification(opt -> opt.testing.enableAutoCloseableDesugaring = true)
+        .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);
+  }
+
+  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 {
+      PrintForkJoinPool forkJoinPool = new PrintForkJoinPool();
+      forkJoinPool.close();
+      forkJoinPool = new PrintForkJoinPool();
+      close(forkJoinPool);
+
+      ForkJoinPool forkJoinPool2 = new PrintForkJoinPool();
+      forkJoinPool2.close();
+
+      ExecutorService executorService = new PrintForkJoinPool();
+      executorService.close();
+      executorService = new PrintForkJoinPool();
+      closeExecutorService(executorService);
+
+      OverrideForkJoinPool overrideForkJoinPool = new OverrideForkJoinPool();
+      overrideForkJoinPool.close();
+      overrideForkJoinPool = new OverrideForkJoinPool();
+      close(overrideForkJoinPool);
+
+      ForkJoinPool overrideForkJoinPool1 = new OverrideForkJoinPool();
+      overrideForkJoinPool1.close();
+
+      executorService = new OverrideForkJoinPool();
+      executorService.close();
+      executorService = new OverrideForkJoinPool();
+      closeExecutorService(executorService);
+
+      OverrideForkJoinPool2 overrideForkJoinPool2 = new OverrideForkJoinPool2();
+      overrideForkJoinPool2.close();
+      overrideForkJoinPool2 = new OverrideForkJoinPool2();
+      close(overrideForkJoinPool2);
+
+      ForkJoinPool overrideForkJoinPool22 = new OverrideForkJoinPool2();
+      overrideForkJoinPool22.close();
+
+      executorService = new OverrideForkJoinPool2();
+      executorService.close();
+      executorService = new OverrideForkJoinPool2();
+      closeExecutorService(executorService);
+
+      Executor1 executor1 = new Executor1();
+      executor1.close();
+      Executor2 executor2 = new Executor2();
+      executor2.close();
+      ExecutorService executor11 = new Executor1();
+      close(executor11);
+      ExecutorService executor22 = new Executor2();
+      close(executor22);
+
+      System.out.println("SUCCESS");
+    }
+
+    @NeverInline
+    public static void closeExecutorService(ExecutorService ac) throws Exception {
+      ac.close();
+    }
+
+    @NeverInline
+    public static void close(AutoCloseable ac) throws Exception {
+      ac.close();
+    }
+  }
+}
diff --git a/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterExecutorServiceTest.java b/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterExecutorServiceTest.java
new file mode 100644
index 0000000..ae043fd
--- /dev/null
+++ b/src/test/examplesJava21/autocloseable/AutoCloseableRetargeterExecutorServiceTest.java
@@ -0,0 +1,127 @@
+// 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 com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+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 AutoCloseableRetargeterExecutorServiceTest extends TestBase {
+
+  @Parameter public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters()
+        .withDexRuntimes()
+        .withApiLevelsStartingAtIncluding(AndroidApiLevel.K)
+        .build();
+  }
+
+  private static String EXPECTED_OUTPUT = StringUtils.lines("false", "true", "SUCCESS");
+  private static String EXPECTED_OUTPUT_24 =
+      StringUtils.lines("false", "true", "false", "false", "SUCCESS");
+
+  private String getExpectedOutput() {
+    return parameters.getApiLevel().isLessThanOrEqualTo(AndroidApiLevel.M)
+        ? EXPECTED_OUTPUT
+        : EXPECTED_OUTPUT_24;
+  }
+
+  @Test
+  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))
+        .compile()
+        .addRunClasspathClassFileData((getAutoCloseableAndroidClassData(parameters)))
+        .run(
+            parameters.getRuntime(),
+            Main.class,
+            String.valueOf(parameters.getApiLevel().getLevel()))
+        .assertSuccessWithOutput(getExpectedOutput());
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addKeepMainRule(Main.class)
+        .addInnerClassesAndStrippedOuter(getClass())
+        .addInliningAnnotations()
+        .addOptionsModification(opt -> opt.testing.enableAutoCloseableDesugaring = true)
+        .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(getExpectedOutput());
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) throws Exception {
+      int api = Integer.parseInt(args[0]);
+
+      ScheduledThreadPoolExecutor pool = new ScheduledThreadPoolExecutor(1);
+      System.out.println(pool.isTerminated());
+      pool.close();
+      System.out.println(pool.isTerminated());
+
+      if (api >= 21) {
+        ForkJoinPool forkJoinPool = new ForkJoinPool();
+        forkJoinPool.close();
+        forkJoinPool = new ForkJoinPool();
+        close(forkJoinPool);
+
+        ExecutorService executorService = new ForkJoinPool();
+        executorService.close();
+        executorService = new ForkJoinPool();
+        closeExecutorService(executorService);
+      }
+
+      if (api >= 24) {
+        ForkJoinPool commonPool = ForkJoinPool.commonPool();
+        System.out.println(commonPool.isTerminated());
+        commonPool.close();
+        // Common pool should never terminate.
+        System.out.println(commonPool.isTerminated());
+      }
+
+      System.out.println("SUCCESS");
+    }
+
+    @NeverInline
+    public static void closeExecutorService(ExecutorService ac) throws Exception {
+      ac.close();
+    }
+
+    @NeverInline
+    public static void close(AutoCloseable ac) throws Exception {
+      ac.close();
+    }
+  }
+}
diff --git a/src/test/examplesJava21/twr/ExecutorServiceBackportTest.java b/src/test/examplesJava21/twr/ExecutorServiceBackportTest.java
index c55a8e4..a57d54b 100644
--- a/src/test/examplesJava21/twr/ExecutorServiceBackportTest.java
+++ b/src/test/examplesJava21/twr/ExecutorServiceBackportTest.java
@@ -4,8 +4,14 @@
 
 package twr;
 
+import static com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.compileAutoCloseableAndroidLibraryClasses;
+import static com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.getAutoCloseableAndroidClassData;
+
+import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.TestBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.desugar.backports.AbstractBackportTest;
 import com.android.tools.r8.utils.AndroidApiLevel;
@@ -33,6 +39,32 @@
     ignoreInvokes("isTerminated");
   }
 
+  @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));
+    }
+  }
+
+  @Override
+  public void testD8Cf() throws Exception {
+    super.testD8Cf();
+  }
+
   public static class Main {
 
     public static void main(String[] args) {
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/ExecutorServiceMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/ExecutorServiceMethods.java
index 3d5a297..c0382b0 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/ExecutorServiceMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/ExecutorServiceMethods.java
@@ -1,11 +1,21 @@
 package com.android.tools.r8.ir.desugar.backports;
 
 import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ForkJoinPool;
 import java.util.concurrent.TimeUnit;
 
 public class ExecutorServiceMethods {
+  // Stub out android.os.Build$VERSION as it does not exist when building R8.
+  private static class AndroidOsBuildVersionStub {
+    public static int SDK_INT;
+  }
 
   public static void closeExecutorService(ExecutorService executorService) {
+    if (AndroidOsBuildVersionStub.SDK_INT > 23) {
+      if (executorService == ForkJoinPool.commonPool()) {
+        return;
+      }
+    }
     boolean terminated = executorService.isTerminated();
     if (!terminated) {
       executorService.shutdown();
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
index 96dd2e3..40d6b6d 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
@@ -169,7 +169,13 @@
   private static CfInstruction rewriteToAndroidOsBuildVersion(
       DexItemFactory itemFactory, CfInstruction instruction) {
     // Rewrite references to UnsafeStub to sun.misc.Unsafe.
-    if (instruction.isStaticFieldGet()) {
+    if (instruction.isStaticFieldGet()
+        && instruction
+            .asFieldInstruction()
+            .getField()
+            .getHolderType()
+            .toString()
+            .contains("Stub")) {
       CfStaticFieldRead fieldGet = instruction.asStaticFieldGet();
       return new CfStaticFieldRead(
           itemFactory.createField(
@@ -182,7 +188,10 @@
           .asFrame()
           .mapReferenceTypes(
               type -> {
-                throw new RuntimeException("Unexpected CfFrame instruction.");
+                if (type.toString().contains("Stub")) {
+                  throw new RuntimeException("Unexpected CfFrame instruction.");
+                }
+                return type;
               });
     }
     return instruction;
@@ -206,6 +215,12 @@
               .map(instruction -> rewriteToUnsafe(factory, instruction))
               .collect(Collectors.toList()));
     }
+    if (holderName.equals("ExecutorServiceMethods") && methodName.equals("closeExecutorService")) {
+      code.setInstructions(
+          code.getInstructions().stream()
+              .map(instruction -> rewriteToAndroidOsBuildVersion(factory, instruction))
+              .collect(Collectors.toList()));
+    }
     if (holderName.equals("AndroidOsBuildVersionMethods") && methodName.equals("getSdkIntFull")) {
       code.setInstructions(
           code.getInstructions().stream()
diff --git a/src/test/testbase/java/com/android/tools/r8/JvmTestBuilder.java b/src/test/testbase/java/com/android/tools/r8/JvmTestBuilder.java
index da2771f..25d4fab 100644
--- a/src/test/testbase/java/com/android/tools/r8/JvmTestBuilder.java
+++ b/src/test/testbase/java/com/android/tools/r8/JvmTestBuilder.java
@@ -46,6 +46,11 @@
   }
 
   @Override
+  public boolean isJvmTestBuilder() {
+    return true;
+  }
+
+  @Override
   JvmTestBuilder self() {
     return this;
   }
diff --git a/src/test/testbase/java/com/android/tools/r8/TestBuilder.java b/src/test/testbase/java/com/android/tools/r8/TestBuilder.java
index ea40b20..294f719 100644
--- a/src/test/testbase/java/com/android/tools/r8/TestBuilder.java
+++ b/src/test/testbase/java/com/android/tools/r8/TestBuilder.java
@@ -96,6 +96,10 @@
     return self;
   }
 
+  public boolean isJvmTestBuilder() {
+    return false;
+  }
+
   @Deprecated
   public RR run(String mainClass)
       throws CompilationFailedException, ExecutionException, IOException {
diff --git a/src/test/testbase/java/com/android/tools/r8/desugar/AutoCloseableAndroidLibraryFileData.java b/src/test/testbase/java/com/android/tools/r8/desugar/AutoCloseableAndroidLibraryFileData.java
new file mode 100644
index 0000000..ec7aa63
--- /dev/null
+++ b/src/test/testbase/java/com/android/tools/r8/desugar/AutoCloseableAndroidLibraryFileData.java
@@ -0,0 +1,240 @@
+// 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 com.android.tools.r8.desugar;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.graph.AccessFlags;
+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 java.nio.file.Path;
+
+public class AutoCloseableAndroidLibraryFileData extends TestBase {
+
+  public static Path compileAutoCloseableAndroidLibraryClasses(
+      TestBase testBase, TestParameters parameters) throws Exception {
+    return testBase
+        .testForD8()
+        .addProgramClassFileData(getAutoCloseableAndroidClassData(parameters))
+        .setMinApi(parameters)
+        .compile()
+        .writeToZip();
+  }
+
+  public static ImmutableList<byte[]> getAutoCloseableAndroidClassData(TestParameters parameters)
+      throws Exception {
+    return ImmutableList.of(
+        getMediaDrm(parameters),
+        getContentProviderClient(parameters),
+        getDrmManagerClient(parameters),
+        getMediaMetadataRetriever(parameters),
+        getTypedArray(parameters),
+        getTransformedBuildVERSIONClass());
+  }
+
+  private static byte[] getContentProviderClient(TestParameters parameters) throws IOException {
+    return getTransformedClass(
+        parameters,
+        AndroidApiLevel.N,
+        ContentProviderClientApiLevel24.class,
+        ContentProviderClient.class,
+        DexItemFactory.androidContentContentProviderClientDescriptorString);
+  }
+
+  private static byte[] getDrmManagerClient(TestParameters parameters) throws IOException {
+    return getTransformedClass(
+        parameters,
+        AndroidApiLevel.N,
+        DrmManagerClientApiLevel24.class,
+        DrmManagerClient.class,
+        DexItemFactory.androidDrmDrmManagerClientDescriptorString);
+  }
+
+  private static byte[] getMediaDrm(TestParameters parameters) throws IOException {
+    return getTransformedClass(
+        parameters,
+        AndroidApiLevel.P,
+        MediaDrmApiLevel28.class,
+        MediaDrm.class,
+        DexItemFactory.androidMediaMediaDrmDescriptorString);
+  }
+
+  private static byte[] getMediaMetadataRetriever(TestParameters parameters) throws IOException {
+    return getTransformedClass(
+        parameters,
+        AndroidApiLevel.Q,
+        MediaMetadataRetrieverApiLevel29.class,
+        MediaMetadataRetriever.class,
+        DexItemFactory.androidMediaMediaMetadataRetrieverDescriptorString);
+  }
+
+  private static byte[] getTypedArray(TestParameters parameters) throws IOException {
+    return getTransformedClass(
+        parameters,
+        AndroidApiLevel.S,
+        TypedArrayAndroidApiLevel31.class,
+        TypedArray.class,
+        DexItemFactory.androidContentResTypedArrayDescriptorString);
+  }
+
+  private static byte[] getTransformedClass(
+      TestParameters parameters,
+      AndroidApiLevel minApiLevel,
+      Class<?> lowApiClass,
+      Class<?> highApiClass,
+      String descriptorString)
+      throws IOException {
+    if (parameters.getApiLevel().isGreaterThanOrEqualTo(minApiLevel)) {
+      return transformer(lowApiClass).setClassDescriptor(descriptorString).clearNest().transform();
+    } else {
+      return transformer(highApiClass).setClassDescriptor(descriptorString).clearNest().transform();
+    }
+  }
+
+  private static byte[] getTransformedBuildVERSIONClass() throws IOException, NoSuchFieldException {
+    return transformer(VERSION.class)
+        .setClassDescriptor("Landroid/os/Build$VERSION;")
+        .setAccessFlags(VERSION.class.getDeclaredField("SDK_INT"), AccessFlags::setFinal)
+        .transform();
+  }
+
+  // Minimal android.os.Build$VERSION for runtime classpath.
+  public static class /*android.os.Build$*/ VERSION {
+
+    public static /*final*/ int SDK_INT = -1;
+  }
+
+  public static class ContentProviderClient {
+
+    public boolean wasClosed = false;
+
+    public void close() {
+      throw new AssertionError("close should not be called");
+    }
+
+    public boolean release() {
+      wasClosed = true;
+      return wasClosed;
+    }
+  }
+
+  public static class ContentProviderClientApiLevel24 implements AutoCloseable {
+
+    public boolean wasClosed = false;
+
+    public void close() {
+      wasClosed = true;
+    }
+
+    public boolean release() {
+      throw new AssertionError("release should not be called");
+    }
+  }
+
+  public static class DrmManagerClient {
+
+    public boolean wasClosed = false;
+
+    public void close() {
+      throw new AssertionError("close should not be called");
+    }
+
+    public void release() {
+      wasClosed = true;
+    }
+  }
+
+  public static class DrmManagerClientApiLevel24 implements AutoCloseable {
+
+    public boolean wasClosed = false;
+
+    public void close() {
+      wasClosed = true;
+    }
+
+    public void release() {
+      throw new AssertionError("release should not be called");
+    }
+  }
+
+  public static class MediaDrm {
+
+    public boolean wasClosed = false;
+
+    public void close() {
+      throw new AssertionError("close should not be called");
+    }
+
+    public void release() {
+      wasClosed = true;
+    }
+  }
+
+  public static class MediaDrmApiLevel28 implements AutoCloseable {
+
+    public boolean wasClosed = false;
+
+    public void close() {
+      wasClosed = true;
+    }
+
+    public void release() {
+      throw new AssertionError("release should not be called");
+    }
+  }
+
+  public static class MediaMetadataRetriever {
+
+    public boolean wasClosed = false;
+
+    public void close() {
+      throw new AssertionError("close should not be called");
+    }
+
+    public void release() {
+      wasClosed = true;
+    }
+  }
+
+  public static class MediaMetadataRetrieverApiLevel29 implements AutoCloseable {
+
+    public boolean wasClosed = false;
+
+    public void close() {
+      wasClosed = true;
+    }
+
+    public void release() {
+      throw new AssertionError("release should not be called");
+    }
+  }
+
+  public static class TypedArray {
+
+    public boolean wasClosed = false;
+
+    public void close() {
+      throw new AssertionError("close should not be called");
+    }
+
+    public void recycle() {
+      wasClosed = true;
+    }
+  }
+
+  public static class TypedArrayAndroidApiLevel31 implements AutoCloseable {
+
+    public boolean wasClosed = false;
+
+    public void close() {
+      wasClosed = true;
+    }
+
+    public void recycle() {
+      throw new AssertionError("recycle should not be called");
+    }
+  }
+}
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 8eebd4a..cdbf02f 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
@@ -189,7 +189,7 @@
     ignoredInvokes.add(methodName);
   }
 
-  protected void configureProgram(TestBuilder<?, ?> builder) throws IOException {
+  protected void configureProgram(TestBuilder<?, ?> builder) throws Exception {
     builder.addProgramClasses(MiniAssert.class, IgnoreInvokes.class);
     if (testClass != null) {
       testClass.addAsProgramClass(builder);
@@ -198,6 +198,10 @@
     }
   }
 
+  protected void configureD8Options(D8TestBuilder d8TestBuilder) throws IOException {
+    // Intentionally empty.
+  }
+
   @Test
   public void testJvm() throws Exception {
     parameters.assumeJvmTestParameters();
@@ -233,6 +237,7 @@
     testForD8()
         .setMinApi(parameters)
         .apply(this::configureProgram)
+        .apply(this::configureD8Options)
         .setIncludeClassesChecksum(true)
         .compileWithExpectedDiagnostics(this::checkDiagnostics)
         .apply(this::configure)
@@ -256,8 +261,10 @@
     testForD8(Backend.CF)
         .setMinApi(parameters)
         .apply(this::configureProgram)
+        .apply(this::configureD8Options)
         .setIncludeClassesChecksum(true)
         .compileWithExpectedDiagnostics(this::checkDiagnostics)
+        .apply(this::configure)
         .run(parameters.getRuntime(), testClassName)
         .assertSuccess()
         .inspect(this::assertDesugaring);