Avoid synthesizing reference to Build.SDK_INT in desugaring when not needed

This adds a new closeExecutorService helper that is specialized to API level >= 24. When the min API is >= 24, the new helper can be used to avoid that closeExecutorService synthesizes a referenced to Build.SDK_INT, which may lead to missing class errors.

Bug: b/415072833
Change-Id: I3aca9b10b04794cf40eaad4847a6b7af57c7f5ef
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 6399485..d7aff3c 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
@@ -1875,7 +1875,9 @@
         addProvider(
             new StatifyingMethodGenerator(
                 method,
-                BackportedMethods::ExecutorServiceMethods_closeExecutorService,
+                appView.options().getMinApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)
+                    ? BackportedMethods::ExecutorServiceMethods_closeExecutorServiceNPlus
+                    : BackportedMethods::ExecutorServiceMethods_closeExecutorService,
                 "closeExecutorService",
                 type));
       }
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 02eda10..6c3f005 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
@@ -2613,6 +2613,197 @@
         ImmutableList.of());
   }
 
+  public static CfCode ExecutorServiceMethods_closeExecutorServiceNPlus(
+      DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    CfLabel label9 = new CfLabel();
+    CfLabel label10 = new CfLabel();
+    CfLabel label11 = new CfLabel();
+    CfLabel label12 = new CfLabel();
+    CfLabel label13 = new CfLabel();
+    CfLabel label14 = new CfLabel();
+    CfLabel label15 = new CfLabel();
+    CfLabel label16 = new CfLabel();
+    CfLabel label17 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        4,
+        4,
+        ImmutableList.of(
+            label0,
+            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, label2),
+            label1,
+            new CfReturnVoid(),
+            label2,
+            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,
+                factory.createMethod(
+                    factory.createType("Ljava/util/concurrent/ExecutorService;"),
+                    factory.createProto(factory.booleanType),
+                    factory.createString("isTerminated")),
+                true),
+            new CfStore(ValueType.INT, 1),
+            label3,
+            new CfLoad(ValueType.INT, 1),
+            new CfIf(IfType.NE, ValueType.INT, label16),
+            label4,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfInvoke(
+                185,
+                factory.createMethod(
+                    factory.createType("Ljava/util/concurrent/ExecutorService;"),
+                    factory.createProto(factory.voidType),
+                    factory.createString("shutdown")),
+                true),
+            label5,
+            new CfConstNumber(0, ValueType.INT),
+            new CfStore(ValueType.INT, 2),
+            label6,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(
+                          factory.createType("Ljava/util/concurrent/ExecutorService;")),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 1),
+            new CfIf(IfType.NE, ValueType.INT, label14),
+            label7,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfConstNumber(1, ValueType.LONG),
+            new CfStaticFieldRead(
+                factory.createField(
+                    factory.createType("Ljava/util/concurrent/TimeUnit;"),
+                    factory.createType("Ljava/util/concurrent/TimeUnit;"),
+                    factory.createString("DAYS"))),
+            new CfInvoke(
+                185,
+                factory.createMethod(
+                    factory.createType("Ljava/util/concurrent/ExecutorService;"),
+                    factory.createProto(
+                        factory.booleanType,
+                        factory.longType,
+                        factory.createType("Ljava/util/concurrent/TimeUnit;")),
+                    factory.createString("awaitTermination")),
+                true),
+            new CfStore(ValueType.INT, 1),
+            label8,
+            new CfGoto(label6),
+            label9,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(
+                          factory.createType("Ljava/util/concurrent/ExecutorService;")),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    }),
+                new ArrayDeque<>(
+                    Arrays.asList(
+                        FrameType.initializedNonNullReference(
+                            factory.createType("Ljava/lang/InterruptedException;"))))),
+            new CfStore(ValueType.OBJECT, 3),
+            label10,
+            new CfLoad(ValueType.INT, 2),
+            new CfIf(IfType.NE, ValueType.INT, label13),
+            label11,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfInvoke(
+                185,
+                factory.createMethod(
+                    factory.createType("Ljava/util/concurrent/ExecutorService;"),
+                    factory.createProto(factory.createType("Ljava/util/List;")),
+                    factory.createString("shutdownNow")),
+                true),
+            new CfStackInstruction(CfStackInstruction.Opcode.Pop),
+            label12,
+            new CfConstNumber(1, ValueType.INT),
+            new CfStore(ValueType.INT, 2),
+            label13,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(
+                          factory.createType("Ljava/util/concurrent/ExecutorService;")),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfGoto(label6),
+            label14,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(
+                          factory.createType("Ljava/util/concurrent/ExecutorService;")),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 2),
+            new CfIf(IfType.EQ, ValueType.INT, label16),
+            label15,
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Thread;"),
+                    factory.createProto(factory.createType("Ljava/lang/Thread;")),
+                    factory.createString("currentThread")),
+                false),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Thread;"),
+                    factory.createProto(factory.voidType),
+                    factory.createString("interrupt")),
+                false),
+            label16,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(
+                          factory.createType("Ljava/util/concurrent/ExecutorService;")),
+                      FrameType.intType()
+                    })),
+            new CfReturnVoid(),
+            label17),
+        ImmutableList.of(
+            new CfTryCatch(
+                label7,
+                label8,
+                ImmutableList.of(factory.createType("Ljava/lang/InterruptedException;")),
+                ImmutableList.of(label9))),
+        ImmutableList.of());
+  }
+
   public static CfCode FloatMethods_isFinite(DexItemFactory factory, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
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 c0382b0..9beb64b 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
@@ -35,4 +35,33 @@
       }
     }
   }
+
+  /**
+   * Similar to {@link #closeExecutorService(ExecutorService)}, but assumes SDK_INT is > 23. This
+   * avoids synthesizing a reference to android.os.Build.SDK_INT when compiling libcore. See also
+   * b/415072833.
+   */
+  public static void closeExecutorServiceNPlus(ExecutorService executorService) {
+    if (executorService == ForkJoinPool.commonPool()) {
+      return;
+    }
+    boolean terminated = executorService.isTerminated();
+    if (!terminated) {
+      executorService.shutdown();
+      boolean interrupted = false;
+      while (!terminated) {
+        try {
+          terminated = executorService.awaitTermination(1L, TimeUnit.DAYS);
+        } catch (InterruptedException e) {
+          if (!interrupted) {
+            executorService.shutdownNow();
+            interrupted = true;
+          }
+        }
+      }
+      if (interrupted) {
+        Thread.currentThread().interrupt();
+      }
+    }
+  }
 }