Introduce Path for external users

- introduce api_greater_or_equal

Bug: 222647019
Change-Id: Iacf24f8c3ad48bdc881fe32fd08abb83e1f1f2da
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_path.json b/src/library_desugar/jdk11/desugar_jdk_libs_path.json
index c2cea80..f66d02b 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_path.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_path.json
@@ -52,6 +52,44 @@
       }
     },
     {
+      "api_level_below_or_equal": 25,
+      "wrapper_conversion": [
+        "java.nio.file.Path",
+        "java.nio.file.FileSystem",
+        "java.nio.file.WatchService",
+        "java.nio.file.WatchEvent$Kind",
+        "java.nio.file.WatchKey",
+        "java.nio.file.Watchable",
+        "java.nio.file.PathMatcher",
+        "java.nio.file.WatchEvent$Modifier",
+        "java.nio.file.attribute.UserPrincipalLookupService",
+        "java.nio.file.spi.FileSystemProvider",
+        "java.nio.file.AccessMode",
+        "java.nio.file.LinkOption",
+        "java.nio.file.CopyOption",
+        "java.nio.file.attribute.GroupPrincipal",
+        "java.nio.file.attribute.FileAttribute",
+        "java.nio.file.attribute.FileAttributeView",
+        "java.nio.file.attribute.UserPrincipal",
+        "java.nio.file.FileStore",
+        "java.nio.file.attribute.FileStoreAttributeView",
+        "java.nio.file.DirectoryStream$Filter",
+        "java.nio.file.DirectoryStream",
+        "java.nio.file.OpenOption",
+        "java.nio.file.attribute.BasicFileAttributes"
+      ],
+      "custom_conversion": {
+        "java.nio.file.attribute.FileTime": "java.nio.file.attribute.FileAttributeConversions"
+      }
+    },
+    {
+      "api_level_below_or_equal": 32,
+      "api_level_greater_or_equal": 24,
+      "rewrite_prefix": {
+        "java.util.stream.Collectors": "j$.util.stream.Collectors"
+      }
+    },
+    {
       "api_level_below_or_equal": 23,
       "rewrite_prefix": {
         "java.io.DesugarBufferedReader": "j$.io.DesugarBufferedReader",
@@ -198,6 +236,16 @@
       }
     },
     {
+      "api_level_below_or_equal": 25,
+      "rewrite_prefix": {
+        "java.io.DesugarFile": "j$.io.DesugarFile",
+        "java.nio.file.": "j$.nio.file."
+      },
+      "retarget_method_with_emulated_dispatch": {
+        "java.nio.file.Path java.io.File#toPath()": "java.io.DesugarFile"
+      }
+    },
+    {
       "api_level_below_or_equal": 23,
       "retarget_method": {
         "int java.util.concurrent.atomic.AtomicInteger#accumulateAndGet(int, java.util.function.IntBinaryOperator)": "java.util.concurrent.atomic.DesugarAtomicInteger",
@@ -246,9 +294,6 @@
       "api_level_below_or_equal": 25,
       "rewrite_prefix": {
         "java.io.DesugarFile": "j$.io.DesugarFile",
-        "java.nio.channels.AsynchronousChannel": "j$.nio.channels.AsynchronousChannel",
-        "java.nio.channels.AsynchronousFileChannel": "j$.nio.channels.AsynchronousFileChannel",
-        "java.nio.channels.CompletionHandler": "j$.nio.channels.CompletionHandler",
         "java.nio.channels.Desugar": "j$.nio.channels.Desugar",
         "java.nio.file.": "j$.nio.file.",
         "jdk.internal.": "j$.jdk.internal.",
@@ -294,7 +339,6 @@
       "api_level_below_or_equal": 23,
       "rewrite_prefix": {
         "java.lang.FunctionalInterface": "j$.lang.FunctionalInterface",
-        "java.nio.channels.SeekableByteChannel": "j$.nio.channels.SeekableByteChannel",
         "java.util.AbstractList": "j$.util.AbstractList",
         "java.util.CollSer": "j$.util.CollSer",
         "java.util.Comparators": "j$.util.Comparators",
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/ApiLevelRange.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/ApiLevelRange.java
new file mode 100644
index 0000000..66a980a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/ApiLevelRange.java
@@ -0,0 +1,77 @@
+// Copyright (c) 2022, 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;
+
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.util.Objects;
+
+public class ApiLevelRange {
+
+  private final AndroidApiLevel apiLevelBelowOrEqual;
+  private final AndroidApiLevel apiLevelGreaterOrEqual;
+
+  public ApiLevelRange(int apiLevelBelowOrEqual) {
+    this(AndroidApiLevel.getAndroidApiLevel(apiLevelBelowOrEqual), null);
+  }
+
+  public ApiLevelRange(int apiLevelBelowOrEqual, int apiLevelGreaterOrEqual) {
+    this(
+        AndroidApiLevel.getAndroidApiLevel(apiLevelBelowOrEqual),
+        AndroidApiLevel.getAndroidApiLevel(apiLevelGreaterOrEqual));
+  }
+
+  public ApiLevelRange(
+      AndroidApiLevel apiLevelBelowOrEqual, AndroidApiLevel apiLevelGreaterOrEqual) {
+    this.apiLevelBelowOrEqual = apiLevelBelowOrEqual;
+    this.apiLevelGreaterOrEqual = apiLevelGreaterOrEqual;
+  }
+
+  public int getApiLevelBelowOrEqualAsInt() {
+    return apiLevelBelowOrEqual.getLevel();
+  }
+
+  public int getApiLevelGreaterOrEqualAsInt() {
+    return apiLevelGreaterOrEqual.getLevel();
+  }
+
+  public boolean hasApiLevelGreaterOrEqual() {
+    return apiLevelGreaterOrEqual != null;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (!(o instanceof ApiLevelRange)) {
+      return false;
+    }
+    ApiLevelRange that = (ApiLevelRange) o;
+    return apiLevelBelowOrEqual.equals(that.apiLevelBelowOrEqual)
+        && apiLevelGreaterOrEqual.equals(that.apiLevelGreaterOrEqual);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(apiLevelBelowOrEqual, apiLevelGreaterOrEqual);
+  }
+
+  public int deterministicOrder(ApiLevelRange other) {
+    int compare = apiLevelBelowOrEqual.compareTo(other.apiLevelBelowOrEqual);
+    if (compare != 0) {
+      return compare;
+    }
+    if (apiLevelGreaterOrEqual == null) {
+      if (other.apiLevelGreaterOrEqual == null) {
+        return 0;
+      }
+      return 1;
+    }
+    if (other.apiLevelGreaterOrEqual == null) {
+      return -1;
+    }
+    return apiLevelGreaterOrEqual.compareTo(other.apiLevelGreaterOrEqual);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java
index 79f1ff2..fa95d25 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java
@@ -287,7 +287,7 @@
                 + invokedMethod.holder
                 + "#"
                 + invokedMethod.name
-                + " may not work correctly at runtime (Cannot convert type "
+                + " may not work correctly at runtime (No conversion registered for type "
                 + desugaredType
                 + ").",
             origin,
@@ -650,7 +650,7 @@
 
   @Override
   public String uniqueIdentifier() {
-    return "$wrapper$";
+    return "$wrapper";
   }
 
   // Program wrappers are harder to deal with than classpath wrapper because generating a method's
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
index 5936d8c..1663694 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
@@ -47,6 +47,7 @@
   static final String PROGRAM_FLAGS_KEY = "program_flags";
 
   static final String API_LEVEL_BELOW_OR_EQUAL_KEY = "api_level_below_or_equal";
+  static final String API_LEVEL_GREATER_OR_EQUAL_KEY = "api_level_greater_or_equal";
   static final String WRAPPER_CONVERSION_KEY = "wrapper_conversion";
   static final String WRAPPER_CONVERSION_EXCLUDING_KEY = "wrapper_conversion_excluding";
   static final String CUSTOM_CONVERSION_KEY = "custom_conversion";
@@ -231,7 +232,13 @@
       JsonObject flag = jsonFlagSet.getAsJsonObject();
       int api_level_below_or_equal = required(flag, API_LEVEL_BELOW_OR_EQUAL_KEY).getAsInt();
       if (minAPILevel <= api_level_below_or_equal) {
-        parseFlags(flag, builder);
+        if (flag.has(API_LEVEL_GREATER_OR_EQUAL_KEY)) {
+          if (minAPILevel >= flag.get(API_LEVEL_GREATER_OR_EQUAL_KEY).getAsInt()) {
+            parseFlags(flag, builder);
+          }
+        } else {
+          parseFlags(flag, builder);
+        }
       }
     }
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecification.java
index 69c82a9..ef8be02 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecification.java
@@ -4,24 +4,24 @@
 
 package com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification;
 
+import com.android.tools.r8.ir.desugar.desugaredlibrary.ApiLevelRange;
 import com.android.tools.r8.origin.Origin;
-import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
 import java.util.Map;
 
 public class MultiAPILevelHumanDesugaredLibrarySpecification {
 
   private final Origin origin;
   private final HumanTopLevelFlags topLevelFlags;
-  private final Int2ObjectMap<HumanRewritingFlags> commonFlags;
-  private final Int2ObjectMap<HumanRewritingFlags> libraryFlags;
-  private final Int2ObjectMap<HumanRewritingFlags> programFlags;
+  private final Map<ApiLevelRange, HumanRewritingFlags> commonFlags;
+  private final Map<ApiLevelRange, HumanRewritingFlags> libraryFlags;
+  private final Map<ApiLevelRange, HumanRewritingFlags> programFlags;
 
   public MultiAPILevelHumanDesugaredLibrarySpecification(
       Origin origin,
       HumanTopLevelFlags topLevelFlags,
-      Int2ObjectMap<HumanRewritingFlags> commonFlags,
-      Int2ObjectMap<HumanRewritingFlags> libraryFlags,
-      Int2ObjectMap<HumanRewritingFlags> programFlags) {
+      Map<ApiLevelRange, HumanRewritingFlags> commonFlags,
+      Map<ApiLevelRange, HumanRewritingFlags> libraryFlags,
+      Map<ApiLevelRange, HumanRewritingFlags> programFlags) {
     this.origin = origin;
     this.topLevelFlags = topLevelFlags;
     this.commonFlags = commonFlags;
@@ -37,28 +37,15 @@
     return topLevelFlags;
   }
 
-  public Int2ObjectMap<HumanRewritingFlags> getCommonFlags() {
+  public Map<ApiLevelRange, HumanRewritingFlags> getCommonFlags() {
     return commonFlags;
   }
 
-  public Int2ObjectMap<HumanRewritingFlags> getLibraryFlags() {
+  public Map<ApiLevelRange, HumanRewritingFlags> getLibraryFlags() {
     return libraryFlags;
   }
 
-  public Int2ObjectMap<HumanRewritingFlags> getProgramFlags() {
+  public Map<ApiLevelRange, HumanRewritingFlags> getProgramFlags() {
     return programFlags;
   }
-
-  public Map<Integer, HumanRewritingFlags> getCommonFlagsForTesting() {
-    return commonFlags;
-  }
-
-  public Map<Integer, HumanRewritingFlags> getLibraryFlagsForTesting() {
-    return libraryFlags;
-  }
-
-  public Map<Integer, HumanRewritingFlags> getProgramFlagsForTesting() {
-    return programFlags;
-  }
-
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.java
index c059c27..6283419 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.java
@@ -8,10 +8,10 @@
 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.ir.desugar.desugaredlibrary.ApiLevelRange;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.Reporter;
-import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
-import it.unimi.dsi.fastutil.ints.IntArraySet;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 import java.util.function.BiConsumer;
@@ -23,12 +23,12 @@
       MultiAPILevelHumanDesugaredLibrarySpecification specification,
       Reporter reporter) {
 
-    IntArraySet apis = new IntArraySet();
+    Set<ApiLevelRange> apis = new HashSet<>();
     apis.addAll(specification.getCommonFlags().keySet());
     apis.addAll(specification.getLibraryFlags().keySet());
     apis.addAll(specification.getProgramFlags().keySet());
 
-    for (Integer api : apis) {
+    for (ApiLevelRange api : apis) {
       deduplicateFlags(specification, reporter, api);
     }
   }
@@ -36,11 +36,11 @@
   private static void deduplicateFlags(
       MultiAPILevelHumanDesugaredLibrarySpecification specification,
       Reporter reporter,
-      int api) {
+      ApiLevelRange api) {
 
-    Int2ObjectMap<HumanRewritingFlags> commonFlags = specification.getCommonFlags();
-    Int2ObjectMap<HumanRewritingFlags> libraryFlags = specification.getLibraryFlags();
-    Int2ObjectMap<HumanRewritingFlags> programFlags = specification.getProgramFlags();
+    Map<ApiLevelRange, HumanRewritingFlags> commonFlags = specification.getCommonFlags();
+    Map<ApiLevelRange, HumanRewritingFlags> libraryFlags = specification.getLibraryFlags();
+    Map<ApiLevelRange, HumanRewritingFlags> programFlags = specification.getProgramFlags();
 
     HumanRewritingFlags library = libraryFlags.get(api);
     HumanRewritingFlags program = programFlags.get(api);
@@ -68,7 +68,9 @@
   }
 
   private static void putNewFlags(
-      int api, Int2ObjectMap<HumanRewritingFlags> flags, HumanRewritingFlags.Builder builder) {
+      ApiLevelRange api,
+      Map<ApiLevelRange, HumanRewritingFlags> flags,
+      HumanRewritingFlags.Builder builder) {
     HumanRewritingFlags build = builder.build();
     if (build.isEmpty()) {
       flags.remove(api);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java
index 928eeab..d77a0bc 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java
@@ -7,6 +7,7 @@
 import static com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser.CONFIGURATION_FORMAT_VERSION_KEY;
 import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.AMEND_LIBRARY_METHOD_KEY;
 import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.API_LEVEL_BELOW_OR_EQUAL_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.API_LEVEL_GREATER_OR_EQUAL_KEY;
 import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.BACKPORT_KEY;
 import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.COMMON_FLAGS_KEY;
 import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.CURRENT_HUMAN_CONFIGURATION_FORMAT_VERSION;
@@ -35,10 +36,9 @@
 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.ir.desugar.desugaredlibrary.ApiLevelRange;
 import com.google.gson.Gson;
-import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
 import java.util.ArrayList;
-import java.util.Comparator;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -81,14 +81,17 @@
   }
 
   private List<Object> rewritingFlagsToString(
-      Int2ObjectMap<HumanRewritingFlags> rewritingFlagsMap) {
+      Map<ApiLevelRange, HumanRewritingFlags> rewritingFlagsMap) {
     ArrayList<Object> list = new ArrayList<>();
-    ArrayList<Integer> apis = new ArrayList<>(rewritingFlagsMap.keySet());
-    apis.sort(Comparator.reverseOrder());
-    for (int apiBelowOrEqual : apis) {
-      HumanRewritingFlags flags = rewritingFlagsMap.get(apiBelowOrEqual);
+    ArrayList<ApiLevelRange> apis = new ArrayList<>(rewritingFlagsMap.keySet());
+    apis.sort((x, y) -> -x.deterministicOrder(y));
+    for (ApiLevelRange range : apis) {
+      HumanRewritingFlags flags = rewritingFlagsMap.get(range);
       HashMap<String, Object> toJson = new LinkedHashMap<>();
-      toJson.put(API_LEVEL_BELOW_OR_EQUAL_KEY, apiBelowOrEqual);
+      toJson.put(API_LEVEL_BELOW_OR_EQUAL_KEY, range.getApiLevelBelowOrEqualAsInt());
+      if (range.hasApiLevelGreaterOrEqual()) {
+        toJson.put(API_LEVEL_GREATER_OR_EQUAL_KEY, range.getApiLevelGreaterOrEqualAsInt());
+      }
       if (!flags.getRewritePrefix().isEmpty()) {
         toJson.put(REWRITE_PREFIX_KEY, new TreeMap<>(flags.getRewritePrefix()));
       }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationParser.java
index 9f9fa81..2eab9cf 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationParser.java
@@ -6,11 +6,12 @@
 
 import com.android.tools.r8.StringResource;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.ApiLevelRange;
 import com.android.tools.r8.utils.Reporter;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
-import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
-import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import java.util.HashMap;
+import java.util.Map;
 
 public class MultiAPILevelHumanDesugaredLibrarySpecificationParser
     extends HumanDesugaredLibrarySpecificationParser {
@@ -27,26 +28,31 @@
 
     HumanTopLevelFlags topLevelFlags = parseTopLevelFlags(jsonConfigString, builder -> {});
 
-    Int2ObjectMap<HumanRewritingFlags> commonFlags = parseAllFlags(COMMON_FLAGS_KEY);
-    Int2ObjectMap<HumanRewritingFlags> libraryFlags = parseAllFlags(LIBRARY_FLAGS_KEY);
-    Int2ObjectMap<HumanRewritingFlags> programFlags = parseAllFlags(PROGRAM_FLAGS_KEY);
+    Map<ApiLevelRange, HumanRewritingFlags> commonFlags = parseAllFlags(COMMON_FLAGS_KEY);
+    Map<ApiLevelRange, HumanRewritingFlags> libraryFlags = parseAllFlags(LIBRARY_FLAGS_KEY);
+    Map<ApiLevelRange, HumanRewritingFlags> programFlags = parseAllFlags(PROGRAM_FLAGS_KEY);
 
     return new MultiAPILevelHumanDesugaredLibrarySpecification(
         getOrigin(), topLevelFlags, commonFlags, libraryFlags, programFlags);
   }
 
-  private Int2ObjectMap<HumanRewritingFlags> parseAllFlags(String flagKey) {
+  private Map<ApiLevelRange, HumanRewritingFlags> parseAllFlags(String flagKey) {
     JsonElement jsonFlags = required(getJsonConfig(), flagKey);
-    Int2ObjectMap<HumanRewritingFlags> flags = new Int2ObjectArrayMap<>();
+    Map<ApiLevelRange, HumanRewritingFlags> flags = new HashMap<>();
     for (JsonElement jsonFlagSet : jsonFlags.getAsJsonArray()) {
       JsonObject flag = jsonFlagSet.getAsJsonObject();
-      int api_level_below_or_equal = required(flag, API_LEVEL_BELOW_OR_EQUAL_KEY).getAsInt();
+      int apiLevelBelowOrEqual = required(flag, API_LEVEL_BELOW_OR_EQUAL_KEY).getAsInt();
+      ApiLevelRange range =
+          flag.has(API_LEVEL_GREATER_OR_EQUAL_KEY)
+              ? new ApiLevelRange(
+                  apiLevelBelowOrEqual, flag.get(API_LEVEL_GREATER_OR_EQUAL_KEY).getAsInt())
+              : new ApiLevelRange(apiLevelBelowOrEqual);
       HumanRewritingFlags.Builder builder =
-          flags.containsKey(api_level_below_or_equal)
-              ? flags.get(api_level_below_or_equal).newBuilder(reporter(), getOrigin())
+          flags.containsKey(range)
+              ? flags.get(range).newBuilder(reporter(), getOrigin())
               : HumanRewritingFlags.builder(reporter(), getOrigin());
       parseFlags(flag, builder);
-      flags.put(api_level_below_or_equal, builder.build());
+      flags.put(range, builder.build());
     }
     return flags;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterL8Synthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterL8Synthesizer.java
index 6a32412..4ef9c00 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterL8Synthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterL8Synthesizer.java
@@ -35,7 +35,7 @@
 
   @Override
   public String uniqueIdentifier() {
-    return "$retargeter$";
+    return "$retargeter";
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
index 7e0daab..baa4178 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
@@ -17,6 +17,7 @@
 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.ir.desugar.desugaredlibrary.ApiLevelRange;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanTopLevelFlags;
@@ -36,11 +37,11 @@
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
-import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
 import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
 import java.io.IOException;
 import java.nio.file.Path;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -86,11 +87,11 @@
         AppForSpecConversion.readAppForTesting(desugaredJDKLib, androidLib, options, true, timing);
 
     HumanTopLevelFlags humanTopLevelFlags = convertTopLevelFlags(legacySpec.getTopLevelFlags());
-    Int2ObjectArrayMap<HumanRewritingFlags> commonFlags =
+    Map<ApiLevelRange, HumanRewritingFlags> commonFlags =
         convertRewritingFlagMap(legacySpec.getCommonFlags(), app, origin);
-    Int2ObjectArrayMap<HumanRewritingFlags> programFlags =
+    Map<ApiLevelRange, HumanRewritingFlags> programFlags =
         convertRewritingFlagMap(legacySpec.getProgramFlags(), app, origin);
-    Int2ObjectArrayMap<HumanRewritingFlags> libraryFlags =
+    Map<ApiLevelRange, HumanRewritingFlags> libraryFlags =
         convertRewritingFlagMap(legacySpec.getLibraryFlags(), app, origin);
 
     legacyLibraryFlagHacks(libraryFlags, app, origin);
@@ -166,9 +167,9 @@
   }
 
   private void legacyLibraryFlagHacks(
-      Int2ObjectArrayMap<HumanRewritingFlags> libraryFlags, DexApplication app, Origin origin) {
-    int level = LEGACY_HACK_LEVEL.getLevel();
-    HumanRewritingFlags humanRewritingFlags = libraryFlags.get(level);
+      Map<ApiLevelRange, HumanRewritingFlags> libraryFlags, DexApplication app, Origin origin) {
+    ApiLevelRange range = new ApiLevelRange(LEGACY_HACK_LEVEL.getLevel());
+    HumanRewritingFlags humanRewritingFlags = libraryFlags.get(range);
     if (humanRewritingFlags == null) {
       // Skip CHM only configuration.
       return;
@@ -176,7 +177,7 @@
     HumanRewritingFlags.Builder builder =
         humanRewritingFlags.newBuilder(app.options.reporter, origin);
     legacyLibraryFlagHacks(app.dexItemFactory(), builder);
-    libraryFlags.put(level, builder.build());
+    libraryFlags.put(range, builder.build());
   }
 
   private void legacyLibraryFlagHacks(
@@ -210,10 +211,11 @@
     builder.retargetMethod(source, target);
   }
 
-  private Int2ObjectArrayMap<HumanRewritingFlags> convertRewritingFlagMap(
+  private Map<ApiLevelRange, HumanRewritingFlags> convertRewritingFlagMap(
       Int2ObjectMap<LegacyRewritingFlags> libFlags, DexApplication app, Origin origin) {
-    Int2ObjectArrayMap<HumanRewritingFlags> map = new Int2ObjectArrayMap<>();
-    libFlags.forEach((key, flags) -> map.put((int) key, convertRewritingFlags(flags, app, origin)));
+    Map<ApiLevelRange, HumanRewritingFlags> map = new HashMap<>();
+    libFlags.forEach(
+        (key, flags) -> map.put(new ApiLevelRange(key), convertRewritingFlags(flags, app, origin)));
     return map;
   }
 
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 b8b50d9..6a57dae 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
@@ -114,7 +114,7 @@
 
   @Override
   public String uniqueIdentifier() {
-    return "$emulatedInterface$";
+    return "$emulatedInterface";
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
index 6a84f09..366ee34 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
@@ -476,7 +476,7 @@
 
   @Override
   public String uniqueIdentifier() {
-    return "$record$";
+    return "$record";
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 1ff0dce..bf484bc 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -204,6 +204,10 @@
     return Paths.get(getDesugarLibraryJsonDir(), "desugar_jdk_libs.json");
   }
 
+  public static Path getDesugarLibJsonForTestingWithPath() {
+    return Paths.get(getDesugarLibraryJsonDir(), "desugar_jdk_libs_path.json");
+  }
+
   public static Path getCHMOnlyDesugarLibJsonForTesting() {
     return Paths.get(getDesugarLibraryJsonDir(), "chm_only_desugar_jdk_libs.json");
   }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/PathTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/PathTest.java
new file mode 100644
index 0000000..3065b0f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/PathTest.java
@@ -0,0 +1,92 @@
+// Copyright (c) 2022, 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.desugaredlibrary.jdk11;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.LibraryDesugaringTestConfiguration;
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import org.junit.Assume;
+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 PathTest extends DesugaredLibraryTestBase {
+
+  private static final String EXPECTED_RESULT = StringUtils.lines("x.txt", "dir", "dir/x.txt", "/");
+  private final TestParameters parameters;
+  private final boolean shrinkDesugaredLibrary;
+
+  @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+  }
+
+  public PathTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+    this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+    this.parameters = parameters;
+  }
+
+  private LibraryDesugaringTestConfiguration pathConfiguration() {
+    return LibraryDesugaringTestConfiguration.builder()
+        .setMinApi(parameters.getApiLevel())
+        .addDesugaredLibraryConfiguration(
+            StringResource.fromFile(ToolHelper.getDesugarLibJsonForTestingWithPath()))
+        .setMode(shrinkDesugaredLibrary ? CompilationMode.RELEASE : CompilationMode.DEBUG)
+        .withKeepRuleConsumer()
+        .build();
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    Assume.assumeTrue(isJDK11DesugaredLibrary());
+    testForD8(parameters.getBackend())
+        .addLibraryFiles(getLibraryFile())
+        .addInnerClasses(PathTest.class)
+        .setMinApi(parameters.getApiLevel())
+        .enableCoreLibraryDesugaring(pathConfiguration())
+        .compile()
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED_RESULT);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    Assume.assumeTrue(isJDK11DesugaredLibrary());
+    testForR8(Backend.DEX)
+        .addLibraryFiles(getLibraryFile())
+        .addInnerClasses(PathTest.class)
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(TestClass.class)
+        .enableCoreLibraryDesugaring(pathConfiguration())
+        .compile()
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED_RESULT);
+  }
+
+  public static class TestClass {
+
+    public static void main(String[] args) {
+      File file = new File("x.txt");
+      Path path1 = file.toPath();
+      System.out.println(path1);
+      Path path2 = Paths.get("dir/");
+      System.out.println(path2);
+      Path resolve = path2.resolve(path1);
+      System.out.println(resolve);
+      System.out.println(resolve.getFileSystem().getSeparator());
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java
index f107b2a..ff80b66 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.ApiLevelRange;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanTopLevelFlags;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.MultiAPILevelHumanDesugaredLibrarySpecification;
@@ -80,21 +81,18 @@
       MultiAPILevelHumanDesugaredLibrarySpecification humanSpec1,
       MultiAPILevelHumanDesugaredLibrarySpecification humanSpec2) {
     assertTopLevelFlagsEquals(humanSpec1.getTopLevelFlags(), humanSpec2.getTopLevelFlags());
-    assertFlagMapEquals(
-        humanSpec1.getCommonFlagsForTesting(), humanSpec2.getCommonFlagsForTesting());
-    assertFlagMapEquals(
-        humanSpec1.getLibraryFlagsForTesting(), humanSpec2.getLibraryFlagsForTesting());
-    assertFlagMapEquals(
-        humanSpec1.getProgramFlagsForTesting(), humanSpec2.getProgramFlagsForTesting());
+    assertFlagMapEquals(humanSpec1.getCommonFlags(), humanSpec2.getCommonFlags());
+    assertFlagMapEquals(humanSpec1.getLibraryFlags(), humanSpec2.getLibraryFlags());
+    assertFlagMapEquals(humanSpec1.getProgramFlags(), humanSpec2.getProgramFlags());
   }
 
   private void assertFlagMapEquals(
-      Map<Integer, HumanRewritingFlags> commonFlags1,
-      Map<Integer, HumanRewritingFlags> commonFlags2) {
+      Map<ApiLevelRange, HumanRewritingFlags> commonFlags1,
+      Map<ApiLevelRange, HumanRewritingFlags> commonFlags2) {
     assertEquals(commonFlags1.size(), commonFlags2.size());
-    for (int integer : commonFlags1.keySet()) {
-      assertTrue(commonFlags2.containsKey(integer));
-      assertFlagsEquals(commonFlags1.get(integer), commonFlags2.get(integer));
+    for (ApiLevelRange range : commonFlags1.keySet()) {
+      assertTrue(commonFlags2.containsKey(range));
+      assertFlagsEquals(commonFlags1.get(range), commonFlags2.get(range));
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/graph/InvokeFinalTest.java b/src/test/java/com/android/tools/r8/graph/InvokeFinalTest.java
index 54f2fa4..6aaf325 100644
--- a/src/test/java/com/android/tools/r8/graph/InvokeFinalTest.java
+++ b/src/test/java/com/android/tools/r8/graph/InvokeFinalTest.java
@@ -75,8 +75,6 @@
     }
 
     public void bar() {
-      // TODO(b/110175213): We cannot change this to an invoke-special since this requires a
-      //  direct bridge in DEX.
       foo();
     }
   }