Backport android.media.MediaMetadataRetriever.close()

Method close was added in API level 29. Rewrite to release which has
been present since API level 10.

Fixes: b/264392341
Change-Id: Icbbd936e0003507fb0785d0906c49ac7b64c5abc
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 a07a676..2dfb637 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -93,6 +93,8 @@
   public static final String androidDrmDrmManagerClientDescriptorString =
       "Landroid/drm/DrmManagerClient;";
   public static final String androidMediaMediaDrmDescriptorString = "Landroid/media/MediaDrm;";
+  public static final String androidMediaMediaMetadataRetrieverDescriptorString =
+      "Landroid/media/MediaMetadataRetriever;";
 
   /** Set of types that may be synthesized during compilation. */
   private final Set<DexType> possibleCompilerSynthesizedTypes = Sets.newIdentityHashSet();
@@ -625,6 +627,8 @@
       createStaticallyKnownType(androidDrmDrmManagerClientDescriptorString);
   public final DexType androidMediaMediaDrmType =
       createStaticallyKnownType(androidMediaMediaDrmDescriptorString);
+  public final DexType androidMediaMediaMetadataRetrieverType =
+      createStaticallyKnownType(androidMediaMediaMetadataRetrieverDescriptorString);
 
   public final StringBuildingMethods stringBuilderMethods =
       new StringBuildingMethods(stringBuilderType);
@@ -686,6 +690,8 @@
       new AndroidDrmDrmManagerClientMembers();
   public final AndroidMediaMediaDrmMembers androidMediaMediaDrmMembers =
       new AndroidMediaMediaDrmMembers();
+  public final AndroidMediaMetadataRetrieverMembers androidMediaMetadataRetrieverMembers =
+      new AndroidMediaMetadataRetrieverMembers();
 
   // java.**
   public final JavaIoFileMembers javaIoFileMembers = new JavaIoFileMembers();
@@ -1200,6 +1206,14 @@
         createMethod(androidMediaMediaDrmType, createProto(voidType), "close");
   }
 
+  // android.media.MediaMetadataRetriever
+  public class AndroidMediaMetadataRetrieverMembers extends LibraryMembers {
+    public final DexMethod release =
+        createMethod(androidMediaMediaMetadataRetrieverType, createProto(voidType), "release");
+    public final DexMethod close =
+        createMethod(androidMediaMediaMetadataRetrieverType, createProto(voidType), "close");
+  }
+
   public class BooleanMembers extends BoxedPrimitiveMembers {
 
     public final DexField FALSE = createField(boxedBooleanType, boxedBooleanType, "FALSE");
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 892dc5a..4643e0a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -50,6 +50,7 @@
 import com.android.tools.r8.ir.desugar.backports.FloatMethodRewrites;
 import com.android.tools.r8.ir.desugar.backports.LongMethodRewrites;
 import com.android.tools.r8.ir.desugar.backports.MediaDrmMethodRewrites;
+import com.android.tools.r8.ir.desugar.backports.MediaMetadataRetrieverMethodRewrites;
 import com.android.tools.r8.ir.desugar.backports.NumericMethodRewrites;
 import com.android.tools.r8.ir.desugar.backports.ObjectsMethodRewrites;
 import com.android.tools.r8.ir.desugar.backports.OptionalMethodRewrites;
@@ -242,6 +243,9 @@
       if (options.getMinApiLevel().isLessThan(AndroidApiLevel.P)) {
         initializeAndroidPMethodProviders(factory);
       }
+      if (options.getMinApiLevel().isLessThan(AndroidApiLevel.Q)) {
+        initializeAndroidQMethodProviders(factory);
+      }
       if (options.getMinApiLevel().isLessThan(AndroidApiLevel.R)) {
         if (options.testing.alwaysBackportListSetMapMethods
             || typeIsPresentWithoutBackportsFrom(factory.setType, AndroidApiLevel.R)) {
@@ -1081,6 +1085,14 @@
               factory.androidMediaMediaDrmMembers.close, MediaDrmMethodRewrites.rewriteClose()));
     }
 
+    private void initializeAndroidQMethodProviders(DexItemFactory factory) {
+      // void android.drm.DrmManagerClient.close()
+      addProvider(
+          new InvokeRewriter(
+              factory.androidMediaMetadataRetrieverMembers.close,
+              MediaMetadataRetrieverMethodRewrites.rewriteClose()));
+    }
+
     private void initializeAndroidRObjectsMethodProviderWithSupplier(DexItemFactory factory) {
       // Objects
       DexType type = factory.objectsType;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/MediaMetadataRetrieverMethodRewrites.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/MediaMetadataRetrieverMethodRewrites.java
new file mode 100644
index 0000000..07aec55
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/MediaMetadataRetrieverMethodRewrites.java
@@ -0,0 +1,22 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.backports;
+
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.MethodInvokeRewriter;
+import org.objectweb.asm.Opcodes;
+
+public final class MediaMetadataRetrieverMethodRewrites {
+
+  private MediaMetadataRetrieverMethodRewrites() {}
+
+  public static MethodInvokeRewriter rewriteClose() {
+    // Rewrite android/media/MediaMetadataRetriever#close to
+    // android/media/MediaMetadataRetriever#release
+    return (invoke, factory) ->
+        new CfInvoke(
+            Opcodes.INVOKEVIRTUAL, factory.androidMediaMetadataRetrieverMembers.release, false);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/MediaMetadataRetrieverBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/MediaMetadataRetrieverBackportTest.java
new file mode 100644
index 0000000..b3cbfe2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MediaMetadataRetrieverBackportTest.java
@@ -0,0 +1,104 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.backports;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class MediaMetadataRetrieverBackportTest extends AbstractBackportTest {
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDexRuntimesAndAllApiLevels().build();
+  }
+
+  public MediaMetadataRetrieverBackportTest(TestParameters parameters) throws IOException {
+    super(
+        parameters,
+        MediaMetadataRetrieverBackportTest.getMediaMetadataRetriever(parameters),
+        ImmutableList.of(
+            MediaMetadataRetrieverBackportTest.getTestRunner(),
+            MediaMetadataRetrieverBackportTest.getMediaMetadataRetriever(parameters)));
+
+    // The constructor is used by the test and release has been available since API 10 and is the
+    // method close is rewritten to.
+    ignoreInvokes("<init>");
+    ignoreInvokes("release");
+
+    // android.media.MediaMetadataRetriever.close added in API 29.
+    registerTarget(AndroidApiLevel.Q, 1);
+  }
+
+  private static byte[] getMediaMetadataRetriever(TestParameters parameters) throws IOException {
+    if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.Q)) {
+      assertTrue(parameters.getRuntime().asDex().getVm().isNewerThanOrEqual(DexVm.ART_10_0_0_HOST));
+      return transformer(MediaMetadataRetrieverApiLevel29.class)
+          .setClassDescriptor(DexItemFactory.androidMediaMediaMetadataRetrieverDescriptorString)
+          .transform();
+    } else {
+      return transformer(MediaMetadataRetriever.class)
+          .setClassDescriptor(DexItemFactory.androidMediaMediaMetadataRetrieverDescriptorString)
+          .transform();
+    }
+  }
+
+  private static byte[] getTestRunner() throws IOException {
+    return transformer(TestRunner.class)
+        .replaceClassDescriptorInMethodInstructions(
+            descriptor(MediaMetadataRetriever.class),
+            DexItemFactory.androidMediaMediaMetadataRetrieverDescriptorString)
+        .transform();
+  }
+
+  public static class MediaMetadataRetriever {
+    public boolean wasClosed = false;
+
+    public void close() {
+      TestRunner.doFail("close should not be called");
+    }
+
+    public void release() {
+      wasClosed = true;
+    }
+  }
+
+  public static class MediaMetadataRetrieverApiLevel29 {
+    public boolean wasClosed = false;
+
+    public void close() {
+      wasClosed = true;
+    }
+
+    public void release() {
+      TestRunner.doFail("release should not be called");
+    }
+  }
+
+  public static class TestRunner extends MiniAssert {
+
+    public static void main(String[] args) {
+      MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
+      MiniAssert.assertFalse(mediaMetadataRetriever.wasClosed);
+      mediaMetadataRetriever.close();
+      MiniAssert.assertTrue(mediaMetadataRetriever.wasClosed);
+    }
+
+    // Forwards to MiniAssert to avoid having to make it public.
+    public static void doFail(String message) {
+      MiniAssert.fail(message);
+    }
+  }
+}