Merge commit 'a9f4c6589326e50d38c30c0023ab30a5cae289dd' into dev-release
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepConstants.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepConstants.java
index ca219b2..260c31c 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepConstants.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepConstants.java
@@ -26,6 +26,14 @@
     public static final String consequences = "consequences";
   }
 
+  public static final class UsesReflection {
+    public static final Class<com.android.tools.r8.keepanno.annotations.UsesReflection> CLASS =
+        com.android.tools.r8.keepanno.annotations.UsesReflection.class;
+    public static final String DESCRIPTOR = getDescriptor(CLASS);
+    public static final String value = "value";
+    public static final String additionalPreconditions = "additionalPreconditions";
+  }
+
   // Implicit hidden item which is "super type" of Condition and Target.
   public static final class Item {
     public static final String classConstant = "classConstant";
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsesReflection.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsesReflection.java
new file mode 100644
index 0000000..22fcb36
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsesReflection.java
@@ -0,0 +1,62 @@
+// 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.keepanno.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to declare the reflective usages made by an item.
+ *
+ * <p>The annotation's 'value' is a list of targets to be kept if the annotated item is used. The
+ * annotated item is a precondition for keeping any of the specified targets. Thus, if an annotated
+ * method is determined to be unused by the program, the annotation itself will not be in effect and
+ * the targets will not be kept (assuming nothing else is otherwise keeping them).
+ *
+ * <p>The annotation's 'additionalPreconditions' is optional and can specify additional conditions
+ * that should be satisfied for the annotation to be in effect.
+ *
+ * <p>The translation of the @UsesReflection annotation into a @KeepEdge is as follows:
+ *
+ * <p>Assume the item of the annotation is denoted by 'CTX' and referred to as its context.
+ *
+ * <pre>
+ * @UsesReflection(value = targets, [additionalPreconditions = preconditions])
+ * ==>
+ * @KeepEdge(
+ *   consequences = targets,
+ *   preconditions = {createConditionFromContext(CTX)} + preconditions
+ * )
+ *
+ * where
+ *   KeepCondition createConditionFromContext(ctx) {
+ *     if (ctx.isClass()) {
+ *       return new KeepCondition(classTypeName = ctx.getClassTypeName());
+ *     }
+ *     if (ctx.isMethod()) {
+ *       return new KeepCondition(
+ *         classTypeName = ctx.getClassTypeName(),
+ *         methodName = ctx.getMethodName(),
+ *         methodReturnType = ctx.getMethodReturnType(),
+ *         methodParameterTypes = ctx.getMethodParameterTypes());
+ *     }
+ *     if (ctx.isField()) {
+ *       return new KeepCondition(
+ *         classTypeName = ctx.getClassTypeName(),
+ *         fieldName = ctx.getFieldName()
+ *         fieldType = ctx.getFieldType());
+ *     }
+ *     // unreachable
+ *   }
+ * </pre>
+ */
+@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
+@Retention(RetentionPolicy.CLASS)
+public @interface UsesReflection {
+  KeepTarget[] value();
+
+  KeepCondition[] additionalPreconditions() default {};
+}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
index dd5be82..3545a25 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.keepanno.asm;
 
+import com.android.tools.r8.keepanno.annotations.KeepConstants;
 import com.android.tools.r8.keepanno.annotations.KeepConstants.Condition;
 import com.android.tools.r8.keepanno.annotations.KeepConstants.Edge;
 import com.android.tools.r8.keepanno.annotations.KeepConstants.Item;
@@ -43,66 +44,143 @@
 
   private static class KeepEdgeClassVisitor extends ClassVisitor {
     private final Parent<KeepEdge> parent;
+    private String className;
 
     KeepEdgeClassVisitor(Parent<KeepEdge> parent) {
       super(ASM_VERSION);
       this.parent = parent;
     }
 
+    private static String binaryNameToTypeName(String binaryName) {
+      return binaryName.replace('/', '.');
+    }
+
+    @Override
+    public void visit(
+        int version,
+        int access,
+        String name,
+        String signature,
+        String superName,
+        String[] interfaces) {
+      super.visit(version, access, name, signature, superName, interfaces);
+      this.className = binaryNameToTypeName(name);
+    }
+
     @Override
     public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
+      if (visible) {
+        return null;
+      }
       // Skip any visible annotations as @KeepEdge is not runtime visible.
-      if (!visible && descriptor.equals(Edge.DESCRIPTOR)) {
+      if (descriptor.equals(Edge.DESCRIPTOR)) {
         return new KeepEdgeVisitor(parent);
       }
+      if (descriptor.equals(KeepConstants.UsesReflection.DESCRIPTOR)) {
+        KeepItemPattern classItem =
+            KeepItemPattern.builder()
+                .setClassPattern(KeepQualifiedClassNamePattern.exact(className))
+                .build();
+        return new UsesReflectionVisitor(parent, classItem);
+      }
       return null;
     }
 
     @Override
     public MethodVisitor visitMethod(
         int access, String name, String descriptor, String signature, String[] exceptions) {
-      return new KeepEdgeMethodVisitor(parent);
+      return new KeepEdgeMethodVisitor(parent, className, name, descriptor);
     }
 
     @Override
     public FieldVisitor visitField(
         int access, String name, String descriptor, String signature, Object value) {
-      return new KeepEdgeFieldVisitor(parent);
+      return new KeepEdgeFieldVisitor(parent, className, name, descriptor);
     }
   }
 
   private static class KeepEdgeMethodVisitor extends MethodVisitor {
     private final Parent<KeepEdge> parent;
+    private final String className;
+    private final String methodName;
+    private final String methodDescriptor;
 
-    KeepEdgeMethodVisitor(Parent<KeepEdge> parent) {
+    KeepEdgeMethodVisitor(
+        Parent<KeepEdge> parent, String className, String methodName, String methodDescriptor) {
       super(ASM_VERSION);
       this.parent = parent;
+      this.className = className;
+      this.methodName = methodName;
+      this.methodDescriptor = methodDescriptor;
+    }
+
+    private KeepItemPattern createItemContext() {
+      Type returnType = Type.getReturnType(methodDescriptor);
+      Type[] argumentTypes = Type.getArgumentTypes(methodDescriptor);
+      // TODO(b/248408342): Defaults are "any", support setting actual return type and params.
+      return KeepItemPattern.builder()
+          .setClassPattern(KeepQualifiedClassNamePattern.exact(className))
+          .setMemberPattern(
+              KeepMethodPattern.builder()
+                  .setNamePattern(KeepMethodNamePattern.exact(methodName))
+                  .build())
+          .build();
     }
 
     @Override
     public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
       // Skip any visible annotations as @KeepEdge is not runtime visible.
-      if (!visible && descriptor.equals(Edge.DESCRIPTOR)) {
+      if (visible) {
+        return null;
+      }
+      if (descriptor.equals(Edge.DESCRIPTOR)) {
         return new KeepEdgeVisitor(parent);
       }
+      if (descriptor.equals(KeepConstants.UsesReflection.DESCRIPTOR)) {
+        return new UsesReflectionVisitor(parent, createItemContext());
+      }
       return null;
     }
   }
 
   private static class KeepEdgeFieldVisitor extends FieldVisitor {
     private final Parent<KeepEdge> parent;
+    private final String className;
+    private final String fieldName;
+    private final String fieldDescriptor;
 
-    KeepEdgeFieldVisitor(Parent<KeepEdge> parent) {
+    KeepEdgeFieldVisitor(
+        Parent<KeepEdge> parent, String className, String fieldName, String fieldDescriptor) {
       super(ASM_VERSION);
       this.parent = parent;
+      this.className = className;
+      this.fieldName = fieldName;
+      this.fieldDescriptor = fieldDescriptor;
+    }
+
+    private KeepItemPattern createItemContext() {
+      // TODO(b/248408342): Default type is "any", support setting actual field type.
+      return KeepItemPattern.builder()
+          .setClassPattern(KeepQualifiedClassNamePattern.exact(className))
+          .setMemberPattern(
+              KeepFieldPattern.builder()
+                  .setNamePattern(KeepFieldNamePattern.exact(fieldName))
+                  .build())
+          .build();
     }
 
     @Override
     public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
       // Skip any visible annotations as @KeepEdge is not runtime visible.
-      if (!visible && descriptor.equals(Edge.DESCRIPTOR)) {
+      if (visible) {
+        return null;
+      }
+      if (descriptor.equals(Edge.DESCRIPTOR)) {
         return new KeepEdgeVisitor(parent);
       }
+      if (descriptor.equals(KeepConstants.UsesReflection.DESCRIPTOR)) {
+        return new UsesReflectionVisitor(parent, createItemContext());
+      }
       return null;
     }
   }
@@ -164,6 +242,36 @@
     }
   }
 
+  private static class UsesReflectionVisitor extends AnnotationVisitorBase {
+    private final Parent<KeepEdge> parent;
+    private final KeepEdge.Builder builder = KeepEdge.builder();
+    private final KeepPreconditions.Builder preconditions = KeepPreconditions.builder();
+
+    UsesReflectionVisitor(Parent<KeepEdge> parent, KeepItemPattern context) {
+      this.parent = parent;
+      preconditions.addCondition(KeepCondition.builder().setItem(context).build());
+    }
+
+    @Override
+    public AnnotationVisitor visitArray(String name) {
+      if (name.equals(KeepConstants.UsesReflection.value)) {
+        return new KeepConsequencesVisitor(builder::setConsequences);
+      }
+      if (name.equals(KeepConstants.UsesReflection.additionalPreconditions)) {
+        return new KeepPreconditionsVisitor(
+            additionalPreconditions -> {
+              additionalPreconditions.forEach(preconditions::addCondition);
+            });
+      }
+      return super.visitArray(name);
+    }
+
+    @Override
+    public void visitEnd() {
+      parent.accept(builder.setPreconditions(preconditions.build()).build());
+    }
+  }
+
   private static class KeepPreconditionsVisitor extends AnnotationVisitorBase {
     private final Parent<KeepPreconditions> parent;
     private final KeepPreconditions.Builder builder = KeepPreconditions.builder();
diff --git a/src/library_desugar/java/desugar/sun/nio/fs/DesugarAndroidBasicFileAttributeView.java b/src/library_desugar/java/desugar/sun/nio/fs/DesugarAndroidBasicFileAttributeView.java
new file mode 100644
index 0000000..b8be0e2
--- /dev/null
+++ b/src/library_desugar/java/desugar/sun/nio/fs/DesugarAndroidBasicFileAttributeView.java
@@ -0,0 +1,32 @@
+// 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 desugar.sun.nio.fs;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Map;
+
+class DesugarAndroidBasicFileAttributeView extends DesugarBasicFileAttributeView {
+
+  private final Path path;
+
+  public DesugarAndroidBasicFileAttributeView(Path path) {
+    super(path);
+    this.path = path;
+  }
+
+  @Override
+  public BasicFileAttributes readAttributes() throws IOException {
+    path.getFileSystem().provider().checkAccess(path);
+    return super.readAttributes();
+  }
+
+  @Override
+  public Map<String, Object> readAttributes(String[] requested) throws IOException {
+    path.getFileSystem().provider().checkAccess(path);
+    return super.readAttributes(requested);
+  }
+}
diff --git a/src/library_desugar/java/desugar/sun/nio/fs/DesugarAndroidFileSystemProvider.java b/src/library_desugar/java/desugar/sun/nio/fs/DesugarAndroidFileSystemProvider.java
index 42a3f91..6b4a062 100644
--- a/src/library_desugar/java/desugar/sun/nio/fs/DesugarAndroidFileSystemProvider.java
+++ b/src/library_desugar/java/desugar/sun/nio/fs/DesugarAndroidFileSystemProvider.java
@@ -12,12 +12,16 @@
 import java.nio.file.CopyOption;
 import java.nio.file.FileAlreadyExistsException;
 import java.nio.file.Files;
+import java.nio.file.LinkOption;
 import java.nio.file.NoSuchFileException;
 import java.nio.file.OpenOption;
 import java.nio.file.Path;
 import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.BasicFileAttributeView;
 import java.nio.file.attribute.FileAttribute;
+import java.nio.file.attribute.FileAttributeView;
 import java.nio.file.spi.FileSystemProvider;
+import java.util.Map;
 import java.util.Set;
 
 /** Linux implementation of {@link FileSystemProvider} for desugar support. */
@@ -72,6 +76,62 @@
   }
 
   @Override
+  public <V extends FileAttributeView> V getFileAttributeView(
+      Path path, Class<V> type, LinkOption... options) {
+    if (type == null) {
+      throw new NullPointerException();
+    }
+    if (type == BasicFileAttributeView.class) {
+      return type.cast(new DesugarAndroidBasicFileAttributeView(path));
+    }
+    return null;
+  }
+
+  @Override
+  public Map<String, Object> readAttributes(Path path, String attributes, LinkOption... options)
+      throws IOException {
+    int attributesTypeIndexEnd = attributes.indexOf(":");
+    final Class<? extends BasicFileAttributeView> attributeViewType;
+    final String[] requestedAttributes;
+    if (attributesTypeIndexEnd == -1) {
+      attributeViewType = BasicFileAttributeView.class;
+      requestedAttributes = attributes.split(",");
+    } else {
+      String attributeTypeSpec = attributes.substring(0, attributesTypeIndexEnd);
+      if ("basic".equals(attributeTypeSpec)) {
+        attributeViewType = BasicFileAttributeView.class;
+      } else {
+        throw new UnsupportedOperationException(
+            String.format("Requested attribute type for: %s is not available.", attributeTypeSpec));
+      }
+      requestedAttributes = attributes.substring(attributesTypeIndexEnd + 1).split(",");
+    }
+    if (attributeViewType == BasicFileAttributeView.class) {
+      DesugarBasicFileAttributeView attrView = new DesugarAndroidBasicFileAttributeView(path);
+      return attrView.readAttributes(requestedAttributes);
+    }
+    throw new AssertionError("Unexpected View '" + attributeViewType + "' requested");
+  }
+
+  private boolean exists(Path file) {
+    try {
+      checkAccess(file);
+      return true;
+    } catch (IOException ioe) {
+      return false;
+    }
+  }
+
+  @Override
+  public void delete(Path path) throws IOException {
+    if (exists(path)) {
+      deleteIfExists(path);
+      return;
+    }
+    throw new NoSuchFileException(path.toString());
+  }
+
+  @Override
   public SeekableByteChannel newByteChannel(
       Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
     if (path.toFile().isDirectory()) {
@@ -90,4 +150,17 @@
     }
     return DesugarChannels.openEmulatedFileChannel(path, options, attrs);
   }
+
+  @Override
+  public boolean isSameFile(Path path, Path path2) throws IOException {
+    // If the paths are equals, then it answers true even if they do not exist.
+    if (path.equals(path2)) {
+      return true;
+    }
+    // If the paths are not equal, they could still be equal due to symbolic link and so on, but
+    // in that case accessibility is checked.
+    checkAccess(path);
+    checkAccess(path2);
+    return super.isSameFile(path, path2);
+  }
 }
diff --git a/src/library_desugar/java/desugar/sun/nio/fs/DesugarBasicFileAttributeView.java b/src/library_desugar/java/desugar/sun/nio/fs/DesugarBasicFileAttributeView.java
new file mode 100644
index 0000000..c62a952
--- /dev/null
+++ b/src/library_desugar/java/desugar/sun/nio/fs/DesugarBasicFileAttributeView.java
@@ -0,0 +1,27 @@
+// 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 desugar.sun.nio.fs;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.FileTime;
+import java.util.Map;
+
+public class DesugarBasicFileAttributeView {
+
+  public DesugarBasicFileAttributeView(Path path) {}
+
+  public BasicFileAttributes readAttributes() throws IOException {
+    return null;
+  }
+
+  public void setTimes(FileTime lastModifiedTime, FileTime lastAccessTime, FileTime createTime)
+      throws IOException {}
+
+  public Map<String, Object> readAttributes(String[] requested) throws IOException {
+    return null;
+  }
+}
diff --git a/src/library_desugar/java/desugar/sun/nio/fs/DesugarBasicFileAttributes.java b/src/library_desugar/java/desugar/sun/nio/fs/DesugarBasicFileAttributes.java
new file mode 100644
index 0000000..8121b78
--- /dev/null
+++ b/src/library_desugar/java/desugar/sun/nio/fs/DesugarBasicFileAttributes.java
@@ -0,0 +1,61 @@
+// 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 desugar.sun.nio.fs;
+
+import java.io.File;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.FileTime;
+
+public class DesugarBasicFileAttributes implements BasicFileAttributes {
+
+  static DesugarBasicFileAttributes create(File file) {
+    return null;
+  }
+
+  @Override
+  public FileTime lastModifiedTime() {
+    return null;
+  }
+
+  @Override
+  public FileTime lastAccessTime() {
+    return null;
+  }
+
+  @Override
+  public FileTime creationTime() {
+    return null;
+  }
+
+  @Override
+  public boolean isRegularFile() {
+    return false;
+  }
+
+  @Override
+  public boolean isDirectory() {
+    return false;
+  }
+
+  @Override
+  public boolean isSymbolicLink() {
+    return false;
+  }
+
+  @Override
+  public boolean isOther() {
+    return false;
+  }
+
+  @Override
+  public long size() {
+    return 0;
+  }
+
+  @Override
+  public Object fileKey() {
+    return null;
+  }
+}
diff --git a/src/library_desugar/java/java/nio/channels/DesugarChannels.java b/src/library_desugar/java/java/nio/channels/DesugarChannels.java
index 4ca851d..15040ef 100644
--- a/src/library_desugar/java/java/nio/channels/DesugarChannels.java
+++ b/src/library_desugar/java/java/nio/channels/DesugarChannels.java
@@ -9,6 +9,8 @@
 import java.io.RandomAccessFile;
 import java.nio.ByteBuffer;
 import java.nio.MappedByteBuffer;
+import java.nio.file.FileAlreadyExistsException;
+import java.nio.file.Files;
 import java.nio.file.NoSuchFileException;
 import java.nio.file.OpenOption;
 import java.nio.file.Path;
@@ -20,17 +22,6 @@
 
 public class DesugarChannels {
 
-  /** Special conversion for Channel to answer a converted FileChannel if required. */
-  public static Channel convertMaybeLegacyChannelFromLibrary(Channel raw) {
-    if (raw == null) {
-      return null;
-    }
-    if (raw instanceof FileChannel) {
-      return convertMaybeLegacyFileChannelFromLibrary((FileChannel) raw);
-    }
-    return raw;
-  }
-
   /**
    * Below Api 24 FileChannel does not implement SeekableByteChannel. When we get one from the
    * library, we wrap it to implement the interface.
@@ -46,32 +37,41 @@
   }
 
   /**
-   * We unwrap when going to the library since we cannot intercept the calls to final methods in the
-   * library.
+   * All FileChannels below 24 are wrapped to support the new interface SeekableByteChannel.
+   * FileChannels between 24 and 26 are wrapped only to improve the emulation of program opened
+   * FileChannels, especially with the append and delete on close options.
    */
-  public static FileChannel convertMaybeLegacyFileChannelToLibrary(FileChannel raw) {
-    if (raw == null) {
-      return null;
-    }
-    if (raw instanceof WrappedFileChannel) {
-      return ((WrappedFileChannel) raw).delegate;
-    }
-    return raw;
-  }
-
   static class WrappedFileChannel extends FileChannel implements SeekableByteChannel {
 
     final FileChannel delegate;
+    final boolean deleteOnClose;
+    final boolean appendMode;
+    final Path path;
 
     public static FileChannel wrap(FileChannel channel) {
       if (channel instanceof WrappedFileChannel) {
         return channel;
       }
-      return new WrappedFileChannel(channel);
+      return new WrappedFileChannel(channel, false, false, null);
     }
 
-    private WrappedFileChannel(FileChannel delegate) {
+    public static FileChannel withExtraOptions(
+        FileChannel channel, Set<? extends OpenOption> options, Path path) {
+      FileChannel raw =
+          channel instanceof WrappedFileChannel ? ((WrappedFileChannel) channel).delegate : channel;
+      return new WrappedFileChannel(
+          raw,
+          options.contains(StandardOpenOption.DELETE_ON_CLOSE),
+          options.contains(StandardOpenOption.APPEND),
+          path);
+    }
+
+    private WrappedFileChannel(
+        FileChannel delegate, boolean deleteOnClose, boolean appendMode, Path path) {
       this.delegate = delegate;
+      this.deleteOnClose = deleteOnClose;
+      this.appendMode = appendMode;
+      this.path = deleteOnClose ? path : null;
     }
 
     @Override
@@ -86,6 +86,9 @@
 
     @Override
     public int write(ByteBuffer src) throws IOException {
+      if (appendMode) {
+        return delegate.write(src, size());
+      }
       return delegate.write(src);
     }
 
@@ -146,20 +149,57 @@
       return delegate.map(mode, position, size);
     }
 
+    // When using lock or tryLock the extra options are lost.
     @Override
     public FileLock lock(long position, long size, boolean shared) throws IOException {
-      return delegate.lock(position, size, shared);
+      return wrapLock(delegate.lock(position, size, shared));
     }
 
     @Override
     public FileLock tryLock(long position, long size, boolean shared) throws IOException {
-      return delegate.tryLock(position, size, shared);
+      return wrapLock(delegate.tryLock(position, size, shared));
+    }
+
+    private FileLock wrapLock(FileLock lock) {
+      if (lock == null) {
+        return null;
+      }
+      return new WrappedFileChannelFileLock(lock, this);
     }
 
     @Override
     public void implCloseChannel() throws IOException {
       // We cannot call the protected method, this should be effectively equivalent.
       delegate.close();
+      if (deleteOnClose) {
+        Files.deleteIfExists(path);
+      }
+    }
+  }
+
+  /**
+   * The FileLock state is final and duplicated in the wrapper, besides the FileChannel where the
+   * wrapped file channel is used. All methods in FileLock, even channel(), use the duplicated and
+   * corrected state. Only 2 methods require to dispatch to the delegate which is effectively
+   * holding the lock.
+   */
+  static class WrappedFileChannelFileLock extends FileLock {
+
+    private final FileLock delegate;
+
+    WrappedFileChannelFileLock(FileLock delegate, WrappedFileChannel wrappedFileChannel) {
+      super(wrappedFileChannel, delegate.position(), delegate.size(), delegate.isShared());
+      this.delegate = delegate;
+    }
+
+    @Override
+    public boolean isValid() {
+      return delegate.isValid();
+    }
+
+    @Override
+    public void release() throws IOException {
+      delegate.release();
     }
   }
 
@@ -196,23 +236,35 @@
 
     RandomAccessFile randomAccessFile =
         new RandomAccessFile(path.toFile(), getFileAccessModeText(openOptions));
-    if (openOptions.contains(StandardOpenOption.TRUNCATE_EXISTING)) {
+    // TRUNCATE_EXISTING is ignored if the file is not writable.
+    // TRUNCATE_EXISTING is not compatible with APPEND, so we just need to check for WRITE.
+    if (openOptions.contains(StandardOpenOption.TRUNCATE_EXISTING)
+        && openOptions.contains(StandardOpenOption.WRITE)) {
       randomAccessFile.setLength(0);
     }
 
-    if (!openOptions.contains(StandardOpenOption.APPEND)) {
+    if (!openOptions.contains(StandardOpenOption.APPEND)
+        && !openOptions.contains(StandardOpenOption.DELETE_ON_CLOSE)) {
       // This one may be retargeted, below 24, to support SeekableByteChannel.
       return randomAccessFile.getChannel();
     }
 
-    // TODO(b/259056135): Consider subclassing UnsupportedOperationException for desugared library.
-    // RandomAccessFile does not support APPEND.
-    // We could hack a wrapper to support APPEND in simple cases such as Files.write().
-    throw new UnsupportedOperationException();
+    return WrappedFileChannel.withExtraOptions(randomAccessFile.getChannel(), openOptions, path);
   }
 
   private static void validateOpenOptions(Path path, Set<? extends OpenOption> openOptions)
-      throws NoSuchFileException {
+      throws IOException {
+    if (Files.exists(path)) {
+      if (openOptions.contains(StandardOpenOption.CREATE_NEW)
+          && openOptions.contains(StandardOpenOption.WRITE)) {
+        throw new FileAlreadyExistsException(path.toString());
+      }
+    } else {
+      if (!(openOptions.contains(StandardOpenOption.CREATE)
+          || openOptions.contains(StandardOpenOption.CREATE_NEW))) {
+        throw new NoSuchFileException(path.toString());
+      }
+    }
     // Validations that resemble sun.nio.fs.UnixChannelFactory#newFileChannel.
     if (openOptions.contains(StandardOpenOption.READ)
         && openOptions.contains(StandardOpenOption.APPEND)) {
@@ -222,13 +274,11 @@
         && openOptions.contains(StandardOpenOption.TRUNCATE_EXISTING)) {
       throw new IllegalArgumentException("APPEND + TRUNCATE_EXISTING not allowed");
     }
-    if (openOptions.contains(StandardOpenOption.APPEND) && !path.toFile().exists()) {
-      throw new NoSuchFileException(path.toString());
-    }
   }
 
   private static String getFileAccessModeText(Set<? extends OpenOption> options) {
-    if (!options.contains(StandardOpenOption.WRITE)) {
+    if (!options.contains(StandardOpenOption.WRITE)
+        && !options.contains(StandardOpenOption.APPEND)) {
       return "r";
     }
     if (options.contains(StandardOpenOption.SYNC)) {
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
index f4a3aef..4bcf1a3 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
@@ -355,12 +355,9 @@
         "java.util.stream.LongStream java.util.stream.Stream#flatMapToLong(java.util.function.Function)": [0, "java.util.function.Function java.util.stream.FlatMapApiFlips#flipFunctionReturningStream(java.util.function.Function)"],
         "java.util.stream.LongStream java.util.stream.LongStream#flatMap(java.util.function.LongFunction)": [0, "java.util.function.LongFunction java.util.stream.FlatMapApiFlips#flipFunctionReturningStream(java.util.function.LongFunction)"],
         "java.lang.Object java.lang.StackWalker#walk(java.util.function.Function)": [0, "java.util.function.Function java.util.stream.StackWalkerApiFlips#flipFunctionStream(java.util.function.Function)"],
-        "java.nio.channels.FileChannel java.nio.channels.FileLock#channel()": [-1, "java.nio.channels.FileChannel java.nio.channels.DesugarChannels#convertMaybeLegacyFileChannelFromLibrary(java.nio.channels.FileChannel)"],
-        "java.nio.channels.Channel java.nio.channels.FileLock#acquiredBy()": [-1, "java.nio.channels.Channel java.nio.channels.DesugarChannels#convertMaybeLegacyChannelFromLibrary(java.nio.channels.Channel)"],
         "java.nio.channels.FileChannel java.io.RandomAccessFile#getChannel()": [-1, "java.nio.channels.FileChannel java.nio.channels.DesugarChannels#convertMaybeLegacyFileChannelFromLibrary(java.nio.channels.FileChannel)"],
         "java.nio.channels.FileChannel java.io.FileInputStream#getChannel()": [-1, "java.nio.channels.FileChannel java.nio.channels.DesugarChannels#convertMaybeLegacyFileChannelFromLibrary(java.nio.channels.FileChannel)"],
-        "java.nio.channels.FileChannel java.io.FileOutputStream#getChannel()": [-1, "java.nio.channels.FileChannel java.nio.channels.DesugarChannels#convertMaybeLegacyFileChannelFromLibrary(java.nio.channels.FileChannel)"],
-        "void java.nio.channels.FileLock#<init>(java.nio.channels.FileChannel,long, long, boolean)": [0, "java.nio.channels.FileChannel java.nio.channels.DesugarChannels#convertMaybeLegacyFileChannelToLibrary(java.nio.channels.FileChannel)"]
+        "java.nio.channels.FileChannel java.io.FileOutputStream#getChannel()": [-1, "java.nio.channels.FileChannel java.nio.channels.DesugarChannels#convertMaybeLegacyFileChannelFromLibrary(java.nio.channels.FileChannel)"]
       },
       "never_outline_api": [
         "java.lang.Object java.lang.StackWalker#walk(java.util.function.Function)"
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriterExperimental.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriterExperimental.java
index 8c90b56..826b8dc 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriterExperimental.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriterExperimental.java
@@ -119,6 +119,9 @@
       VirtualFile virtualFile = virtualFiles.get(i);
       Timing fileTiming = Timing.create("VirtualFile " + virtualFile.getId(), options);
       assert forcedStrings.size() == 0;
+      if (virtualFile.isEmpty()) {
+        continue;
+      }
       DexContainerSection section =
           writeVirtualFileSection(
               virtualFile,
@@ -146,11 +149,15 @@
       fileTiming.end();
       timings.add(fileTiming);
     }
-    updateStringIdsSizeAndOffset(dexOutputBuffer, sections);
-
     merger.add(timings);
     merger.end();
 
+    if (sections.isEmpty()) {
+      return;
+    }
+
+    updateStringIdsSizeAndOffset(dexOutputBuffer, sections);
+
     ByteBufferResult result =
         new ByteBufferResult(
             dexOutputBuffer.stealByteBuffer(),
@@ -205,9 +212,7 @@
       int offset,
       DexOutputBuffer outputBuffer,
       boolean last) {
-    if (virtualFile.isEmpty()) {
-      return null;
-    }
+    assert !virtualFile.isEmpty();
     printItemUseInfo(virtualFile);
 
     timing.begin("Reindex for lazy strings");
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaringMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaringMethods.java
index 78f3611..e32fa57 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaringMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaringMethods.java
@@ -1296,10 +1296,32 @@
     CfLabel label1 = new CfLabel();
     return new CfCode(
         method.holder,
-        2,
+        4,
         2,
         ImmutableList.of(
-            label0, new CfConstNumber(-1, ValueType.LONG), new CfReturn(ValueType.LONG), label1),
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfInstanceFieldRead(
+                factory.createField(
+                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Lsun/misc/Unsafe;"),
+                    factory.createString("U"))),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfInstanceFieldRead(
+                factory.createField(
+                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.longType,
+                    factory.createString("offset"))),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.createType("Lsun/misc/Unsafe;"),
+                    factory.createProto(factory.longType, factory.objectType, factory.longType),
+                    factory.createString("getLong")),
+                false),
+            new CfReturn(ValueType.LONG),
+            label1),
         ImmutableList.of(),
         ImmutableList.of());
   }
@@ -1310,6 +1332,8 @@
     CfLabel label2 = new CfLabel();
     CfLabel label3 = new CfLabel();
     CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
     return new CfCode(
         method.holder,
         5,
@@ -1347,7 +1371,7 @@
                     factory.createProto(factory.voidType, factory.objectType, factory.intType),
                     factory.createString("set")),
                 false),
-            new CfGoto(label3),
+            new CfGoto(label5),
             label2,
             new CfFrame(
                 new Int2ObjectAVLTreeMap<>(
@@ -1362,6 +1386,48 @@
             new CfInstanceFieldRead(
                 factory.createField(
                     factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.classType,
+                    factory.createString("type"))),
+            new CfStaticFieldRead(
+                factory.createField(
+                    factory.createType("Ljava/lang/Long;"),
+                    factory.classType,
+                    factory.createString("TYPE"))),
+            new CfIfCmp(If.Type.NE, ValueType.OBJECT, label4),
+            label3,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.OBJECT, 2),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createProto(factory.longType, factory.objectType),
+                    factory.createString("toLongIfPossible")),
+                false),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createProto(factory.voidType, factory.objectType, factory.longType),
+                    factory.createString("set")),
+                false),
+            new CfGoto(label5),
+            label4,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(
+                          factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+                      FrameType.initializedNonNullReference(factory.objectType),
+                      FrameType.initializedNonNullReference(factory.objectType)
+                    })),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfInstanceFieldRead(
+                factory.createField(
+                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
                     factory.createType("Lsun/misc/Unsafe;"),
                     factory.createString("U"))),
             new CfLoad(ValueType.OBJECT, 1),
@@ -1380,7 +1446,7 @@
                         factory.voidType, factory.objectType, factory.longType, factory.objectType),
                     factory.createString("putObject")),
                 false),
-            label3,
+            label5,
             new CfFrame(
                 new Int2ObjectAVLTreeMap<>(
                     new int[] {0, 1, 2},
@@ -1391,7 +1457,7 @@
                       FrameType.initializedNonNullReference(factory.objectType)
                     })),
             new CfReturnVoid(),
-            label4),
+            label6),
         ImmutableList.of(),
         ImmutableList.of());
   }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index 5e95467..e46ea51 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -354,7 +354,7 @@
           assert !prefix.contains(SyntheticNaming.getPhaseSeparator(Phase.INTERNAL));
           DexType context =
               dexItemFactory.createType(DescriptorUtils.getDescriptorFromClassBinaryName(prefix));
-          assert isNotSyntheticType(context) || context == dexItemFactory.desugarVarHandleType;
+          assert isNotSyntheticType(context) || synthetics.isGlobalSyntheticClass(context);
         });
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index 323c745..4ea11dc 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -401,12 +401,16 @@
     return isSyntheticClass(clazz.type);
   }
 
-  public boolean isGlobalSyntheticClass(DexProgramClass clazz) {
-    SyntheticDefinition<?, ?, ?> definition = pending.definitions.get(clazz.type);
+  public boolean isGlobalSyntheticClass(DexType type) {
+    SyntheticDefinition<?, ?, ?> definition = pending.definitions.get(type);
     if (definition != null) {
       return definition.getKind().isGlobal();
     }
-    return isGlobalReferences(committed.getClasses().get(clazz.type));
+    return isGlobalReferences(committed.getClasses().get(type));
+  }
+
+  public boolean isGlobalSyntheticClass(DexProgramClass clazz) {
+    return isGlobalSyntheticClass(clazz.getType());
   }
 
   private static boolean isGlobalReferences(List<SyntheticProgramClassReference> references) {
diff --git a/src/test/examplesJava9/varhandle/InstanceLongField.java b/src/test/examplesJava9/varhandle/InstanceLongField.java
index dcca70e..fac0ef3 100644
--- a/src/test/examplesJava9/varhandle/InstanceLongField.java
+++ b/src/test/examplesJava9/varhandle/InstanceLongField.java
@@ -10,14 +10,69 @@
 
   private long field;
 
+  private static void checkJavaLangInvokeWrongMethodTypeException(RuntimeException e) {
+    if (e.getClass().getCanonicalName().equals("java.lang.invoke.WrongMethodTypeException")
+        || e.getMessage().equals("java.lang.invoke.WrongMethodTypeException")) {
+      return;
+    }
+    throw e;
+  }
+
   public static void testSet(VarHandle varHandle) {
-    System.out.println("testGet");
+    System.out.println("testSet");
 
     InstanceLongField instance = new InstanceLongField();
+    System.out.println((long) varHandle.get(instance));
 
+    // Long value.
+    varHandle.set(instance, (long) 1);
+    System.out.println((long) varHandle.get(instance));
+    varHandle.set(instance, Long.valueOf(2));
     System.out.println(varHandle.get(instance));
-    varHandle.set(instance, 1);
-    System.out.println(varHandle.get(instance));
+
+    // Long compatible values.
+    varHandle.set(instance, (byte) 3);
+    System.out.println((long) varHandle.get(instance));
+    varHandle.set(instance, Byte.valueOf((byte) 4));
+    System.out.println((long) varHandle.get(instance));
+    varHandle.set(instance, '0');
+    System.out.println((long) varHandle.get(instance));
+    varHandle.set(instance, Character.valueOf('1'));
+    System.out.println((long) varHandle.get(instance));
+    varHandle.set(instance, (short) 5);
+    System.out.println((long) varHandle.get(instance));
+    varHandle.set(instance, Short.valueOf((short) 6));
+    System.out.println((long) varHandle.get(instance));
+    varHandle.set(instance, (int) 7);
+    System.out.println((long) varHandle.get(instance));
+    varHandle.set(instance, Integer.valueOf(8));
+    System.out.println((long) varHandle.get(instance));
+
+    // Long non-compatible values.
+    try {
+      varHandle.set(instance, true);
+    } catch (RuntimeException e) {
+      checkJavaLangInvokeWrongMethodTypeException(e);
+      System.out.println(varHandle.get(instance));
+    }
+    try {
+      varHandle.set(instance, "3");
+    } catch (RuntimeException e) {
+      checkJavaLangInvokeWrongMethodTypeException(e);
+      System.out.println(varHandle.get(instance));
+    }
+    try {
+      varHandle.set(instance, 3.0f);
+    } catch (RuntimeException e) {
+      checkJavaLangInvokeWrongMethodTypeException(e);
+      System.out.println(varHandle.get(instance));
+    }
+    try {
+      varHandle.set(instance, 3.0);
+    } catch (RuntimeException e) {
+      checkJavaLangInvokeWrongMethodTypeException(e);
+      System.out.println(varHandle.get(instance));
+    }
   }
 
   public static void testCompareAndSet(VarHandle varHandle) {
@@ -25,10 +80,108 @@
 
     InstanceLongField instance = new InstanceLongField();
 
-    varHandle.compareAndSet(instance, 1, 2);
+    // Long value.
+    varHandle.compareAndSet(instance, 1L, 2L);
+    System.out.println((long) varHandle.get(instance));
+    varHandle.compareAndSet(instance, 0L, 1L);
+    System.out.println((long) varHandle.get(instance));
+    varHandle.compareAndSet(instance, Long.valueOf(1), 2);
+    System.out.println((long) varHandle.get(instance));
+    varHandle.compareAndSet(instance, 2, Long.valueOf(3));
+    System.out.println((long) varHandle.get(instance));
+    varHandle.compareAndSet(instance, Long.valueOf(3), Long.valueOf(4));
+    System.out.println((long) varHandle.get(instance));
+
+    // Long compatible values.
+    varHandle.compareAndSet(instance, (byte) 4, 5);
     System.out.println(varHandle.get(instance));
-    varHandle.compareAndSet(instance, 0, 1);
+    varHandle.compareAndSet(instance, 5, (byte) 6);
     System.out.println(varHandle.get(instance));
+    varHandle.compareAndSet(instance, (byte) 6, (byte) 7);
+    System.out.println(varHandle.get(instance));
+    varHandle.compareAndSet(instance, Byte.valueOf((byte) 7), (byte) 8);
+    System.out.println(varHandle.get(instance));
+    varHandle.compareAndSet(instance, (byte) 8, Byte.valueOf((byte) 9));
+    System.out.println(varHandle.get(instance));
+    varHandle.compareAndSet(instance, Byte.valueOf((byte) 9), Byte.valueOf((byte) 10));
+    System.out.println(varHandle.get(instance));
+    varHandle.compareAndSet(instance, 10, '0');
+    System.out.println(varHandle.get(instance));
+    varHandle.compareAndSet(instance, '0', 49);
+    System.out.println(varHandle.get(instance));
+    varHandle.compareAndSet(instance, '1', '2');
+    System.out.println(varHandle.get(instance));
+    varHandle.compareAndSet(instance, (byte) 50, '3');
+    System.out.println(varHandle.get(instance));
+    varHandle.compareAndSet(instance, '3', (byte) 52);
+    System.out.println(varHandle.get(instance));
+    varHandle.compareAndSet(instance, '4', '5');
+    System.out.println(varHandle.get(instance));
+    varHandle.compareAndSet(instance, '5', (int) 11);
+    System.out.println(varHandle.get(instance));
+    varHandle.compareAndSet(instance, (int) 11, (int) 12);
+    System.out.println(varHandle.get(instance));
+    varHandle.compareAndSet(instance, (int) 12, Integer.valueOf(13));
+    System.out.println(varHandle.get(instance));
+    varHandle.compareAndSet(instance, Integer.valueOf(13), Integer.valueOf(14));
+    System.out.println(varHandle.get(instance));
+
+    // Long non-compatible values.
+    try {
+      varHandle.compareAndSet(instance, 6, 7.0f);
+    } catch (RuntimeException e) {
+      checkJavaLangInvokeWrongMethodTypeException(e);
+      System.out.println(varHandle.get(instance));
+    }
+    try {
+      varHandle.compareAndSet(instance, 6.0f, 7);
+    } catch (RuntimeException e) {
+      checkJavaLangInvokeWrongMethodTypeException(e);
+      System.out.println(varHandle.get(instance));
+    }
+    try {
+      varHandle.compareAndSet(instance, 6.0f, 7.0f);
+    } catch (RuntimeException e) {
+      checkJavaLangInvokeWrongMethodTypeException(e);
+      System.out.println(varHandle.get(instance));
+    }
+    try {
+      varHandle.compareAndSet(instance, 6, 7.0);
+    } catch (RuntimeException e) {
+      checkJavaLangInvokeWrongMethodTypeException(e);
+      System.out.println(varHandle.get(instance));
+    }
+    try {
+      varHandle.compareAndSet(instance, 6.0, 7);
+    } catch (RuntimeException e) {
+      checkJavaLangInvokeWrongMethodTypeException(e);
+      System.out.println(varHandle.get(instance));
+    }
+    try {
+      varHandle.compareAndSet(instance, 6.0, 7.0);
+    } catch (RuntimeException e) {
+      checkJavaLangInvokeWrongMethodTypeException(e);
+      System.out.println(varHandle.get(instance));
+    }
+
+    try {
+      varHandle.compareAndSet(instance, 6, "7");
+    } catch (RuntimeException e) {
+      checkJavaLangInvokeWrongMethodTypeException(e);
+      System.out.println(varHandle.get(instance));
+    }
+    try {
+      varHandle.compareAndSet(instance, "6", 7);
+    } catch (RuntimeException e) {
+      checkJavaLangInvokeWrongMethodTypeException(e);
+      System.out.println(varHandle.get(instance));
+    }
+    try {
+      varHandle.compareAndSet(instance, "6", "7");
+    } catch (RuntimeException e) {
+      checkJavaLangInvokeWrongMethodTypeException(e);
+      System.out.println(varHandle.get(instance));
+    }
   }
 
   public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
diff --git a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceLongFieldTest.java b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceLongFieldTest.java
index 0584f1c..c50db54 100644
--- a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceLongFieldTest.java
+++ b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceLongFieldTest.java
@@ -13,7 +13,54 @@
 public class VarHandleDesugaringInstanceLongFieldTest extends VarHandleDesugaringTestBase {
 
   private static final String EXPECTED_OUTPUT =
-      StringUtils.lines("testGet", "0", "1", "testCompareAndSet", "0", "1");
+      StringUtils.lines(
+          "testSet",
+          "0",
+          "1",
+          "2",
+          "3",
+          "4",
+          "48",
+          "49",
+          "5",
+          "6",
+          "7",
+          "8",
+          "8",
+          "8",
+          "8",
+          "8",
+          "testCompareAndSet",
+          "0",
+          "1",
+          "2",
+          "3",
+          "4",
+          "5",
+          "6",
+          "7",
+          "8",
+          "9",
+          "10",
+          "48",
+          "49",
+          "50",
+          "51",
+          "52",
+          "53",
+          "11",
+          "12",
+          "13",
+          "14",
+          "14",
+          "14",
+          "14",
+          "14",
+          "14",
+          "14",
+          "14",
+          "14",
+          "14");
   private static final String MAIN_CLASS = VarHandle.InstanceLongField.typeName();
   private static final String JAR_ENTRY = "varhandle/InstanceLongField.class";
 
@@ -36,4 +83,9 @@
   protected String getExpectedOutput() {
     return EXPECTED_OUTPUT;
   }
+
+  @Override
+  protected boolean getTestWithDesugaring() {
+    return true;
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FileLockTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FileLockTest.java
new file mode 100644
index 0000000..ae36385
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FileLockTest.java
@@ -0,0 +1,163 @@
+// 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 static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.DEFAULT_SPECIFICATIONS;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_PATH;
+
+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.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
+import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+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 FileLockTest extends DesugaredLibraryTestBase {
+
+  private static final String EXPECTED_RESULT =
+      StringUtils.lines(
+          "true",
+          "true",
+          "pos:2;sz:7;sh:false",
+          "true",
+          "true",
+          "true",
+          "pos:2;sz:7;sh:false",
+          "false",
+          "true",
+          "true",
+          "pos:2;sz:7;sh:false",
+          "true");
+
+  private final TestParameters parameters;
+  private final LibraryDesugaringSpecification libraryDesugaringSpecification;
+  private final CompilationSpecification compilationSpecification;
+
+  @Parameters(name = "{0}, spec: {1}, {2}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        // Skip Android 4.4.4 due to missing libjavacrypto.
+        getTestParameters()
+            .withCfRuntime(CfVm.JDK11)
+            .withDexRuntime(Version.V4_0_4)
+            .withDexRuntimesStartingFromIncluding(Version.V5_1_1)
+            .withAllApiLevels()
+            .build(),
+        ImmutableList.of(JDK11_PATH),
+        DEFAULT_SPECIFICATIONS);
+  }
+
+  public FileLockTest(
+      TestParameters parameters,
+      LibraryDesugaringSpecification libraryDesugaringSpecification,
+      CompilationSpecification compilationSpecification) {
+    this.parameters = parameters;
+    this.libraryDesugaringSpecification = libraryDesugaringSpecification;
+    this.compilationSpecification = compilationSpecification;
+  }
+
+  @Test
+  public void test() throws Throwable {
+    if (parameters.isCfRuntime()) {
+      // Reference runtime, we use Jdk 11 since this is Jdk 11 desugared library, not that Jdk 8
+      // behaves differently on this test.
+      Assume.assumeTrue(parameters.isCfRuntime(CfVm.JDK11) && !ToolHelper.isWindows());
+      testForJvm()
+          .addInnerClasses(getClass())
+          .run(parameters.getRuntime(), TestClass.class, "10000")
+          .assertSuccessWithOutput(EXPECTED_RESULT);
+      return;
+    }
+    testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+        .addInnerClasses(getClass())
+        .addKeepMainRule(TestClass.class)
+        .compile()
+        .withArt6Plus64BitsLib()
+        .run(
+            parameters.getRuntime(),
+            TestClass.class,
+            String.valueOf(parameters.getApiLevel().getLevel()))
+        .assertSuccessWithOutput(EXPECTED_RESULT);
+  }
+
+  public static class TestClass {
+
+    private static int minApi;
+
+    public static void main(String[] args) throws Throwable {
+      minApi = Integer.parseInt(args[0]);
+      appendTest();
+      deleteTest();
+      fosTest();
+    }
+
+    private static void checkChannel(FileChannel channel) throws IOException {
+      FileLock lock = channel.lock(2, 7, false);
+      System.out.println(lock.channel() == channel);
+      checkAcquiredBy(lock, channel);
+      System.out.println(
+          "pos:" + lock.position() + ";sz:" + lock.size() + ";sh:" + lock.isShared());
+      lock.release();
+      channel.close();
+    }
+
+    private static void checkAcquiredBy(FileLock lock, FileChannel channel) {
+      // The method acquiredBy was introduced in 24, we do not backport or check below.
+      if (minApi >= 24) {
+        System.out.println((lock.acquiredBy() == channel));
+      } else {
+        System.out.println("true");
+      }
+    }
+
+    private static void appendTest() throws IOException {
+      Path tmpAppend = Files.createTempFile("tmp_append", ".txt");
+      Files.write(tmpAppend, "There will be dragons!".getBytes(StandardCharsets.UTF_8));
+      FileChannel appendChannel =
+          FileChannel.open(tmpAppend, StandardOpenOption.APPEND, StandardOpenOption.WRITE);
+      checkChannel(appendChannel);
+      System.out.println(Files.exists(tmpAppend));
+      Files.delete(tmpAppend);
+    }
+
+    private static void deleteTest() throws IOException {
+      Path tmpDelete = Files.createTempFile("tmp_delete", ".txt");
+      Files.write(tmpDelete, "There will be dragons!".getBytes(StandardCharsets.UTF_8));
+      FileChannel deleteChannel =
+          FileChannel.open(tmpDelete, StandardOpenOption.DELETE_ON_CLOSE, StandardOpenOption.WRITE);
+      checkChannel(deleteChannel);
+      System.out.println(Files.exists(tmpDelete));
+      Files.deleteIfExists(tmpDelete);
+    }
+
+    private static void fosTest() throws IOException {
+      Path tmpFOS = Files.createTempFile("tmp_fos", ".txt");
+      Files.write(tmpFOS, "There will be dragons!".getBytes(StandardCharsets.UTF_8));
+      FileOutputStream fileOutputStream = new FileOutputStream(tmpFOS.toFile());
+      FileChannel fosChannel = fileOutputStream.getChannel();
+      checkChannel(fosChannel);
+      System.out.println(Files.exists(tmpFOS));
+      Files.delete(tmpFOS);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesAttributes2Test.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesAttributes2Test.java
new file mode 100644
index 0000000..8bbd564
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesAttributes2Test.java
@@ -0,0 +1,318 @@
+// 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 static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.DEFAULT_SPECIFICATIONS;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_PATH;
+
+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.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
+import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.PosixFilePermission;
+import java.util.List;
+import java.util.Set;
+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 FilesAttributes2Test extends DesugaredLibraryTestBase {
+
+  private static final String EXPECTED_RESULT =
+      StringUtils.lines(
+          "PRESENT FILE",
+          "fileStore:%s",
+          "lastModifiedTime:true",
+          "lastModifiedTime:true",
+          "owner:%s",
+          "owner:%s",
+          "posix:%s",
+          "posix:%s",
+          "dir:false",
+          "dir:false",
+          "hidden:false",
+          "readable:true",
+          "writable:true",
+          "executable:false",
+          "regular:true",
+          "regular:true",
+          "same:true",
+          "same:false",
+          "symlink:%s",
+          "PRESENT DIR",
+          "fileStore:%s",
+          "lastModifiedTime:true",
+          "lastModifiedTime:true",
+          "owner:%s",
+          "owner:%s",
+          "posix:%s",
+          "posix:%s",
+          "dir:true",
+          "dir:true",
+          "hidden:false",
+          "readable:true",
+          "writable:true",
+          "executable:true",
+          "regular:false",
+          "regular:false",
+          "same:true",
+          "same:false",
+          "symlink:%s",
+          "ABSENT FILE",
+          "fileStore:%s",
+          "lastModifiedTime:class java.nio.file.NoSuchFileException :: notExisting.txt",
+          "lastModifiedTime:class java.nio.file.NoSuchFileException :: notExisting.txt",
+          "owner:%s",
+          "owner:%s",
+          "posix:%s",
+          "posix:%s",
+          "dir:false",
+          "dir:false",
+          "hidden:false",
+          "readable:false",
+          "writable:false",
+          "executable:false",
+          "regular:false",
+          "regular:false",
+          "same:true",
+          "same:class java.nio.file.NoSuchFileException :: notExisting.txt",
+          "symlink:false");
+
+  private final TestParameters parameters;
+  private final LibraryDesugaringSpecification libraryDesugaringSpecification;
+  private final CompilationSpecification compilationSpecification;
+
+  @Parameters(name = "{0}, spec: {1}, {2}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        // Skip Android 4.4.4 due to missing libjavacrypto.
+        getTestParameters()
+            .withCfRuntime(CfVm.JDK11)
+            .withDexRuntime(Version.V4_0_4)
+            .withDexRuntimesStartingFromIncluding(Version.V5_1_1)
+            .withAllApiLevels()
+            .build(),
+        ImmutableList.of(JDK11_PATH),
+        DEFAULT_SPECIFICATIONS);
+  }
+
+  public FilesAttributes2Test(
+      TestParameters parameters,
+      LibraryDesugaringSpecification libraryDesugaringSpecification,
+      CompilationSpecification compilationSpecification) {
+    this.parameters = parameters;
+    this.libraryDesugaringSpecification = libraryDesugaringSpecification;
+    this.compilationSpecification = compilationSpecification;
+  }
+
+  private String getExpectedResult() {
+    // On Android 24, everything seems to be considered a symlink due to canonicalFile being
+    // invalid. It seems it does not reproduce on real device, so this may be an issue from our
+    // test set-up.
+    boolean invalidSymlink =
+        parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V7_0_0);
+    // On Dex, the security manager is not working in our test set-up.
+    boolean invalidFileStore = parameters.isDexRuntime();
+    // Posix attributes are not available on desugared nio.
+    boolean invalidPosix =
+        parameters.isDexRuntime()
+            && !libraryDesugaringSpecification.usesPlatformFileSystem(parameters);
+
+    String invalidFileStoreString = "class java.lang.SecurityException :: getFileStore";
+    String invalidPosixString = "class java.lang.UnsupportedOperationException :: no-message";
+    String missingFile = "class java.nio.file.NoSuchFileException :: notExisting.txt";
+    String success = "true";
+
+    Object[] vars = {
+      invalidFileStore ? invalidFileStoreString : success,
+      invalidPosix ? invalidPosixString : success,
+      invalidPosix ? invalidPosixString : success,
+      invalidPosix ? invalidPosixString : success,
+      invalidPosix ? invalidPosixString : success,
+      String.valueOf(invalidSymlink),
+      invalidFileStore ? invalidFileStoreString : success,
+      invalidPosix ? invalidPosixString : success,
+      invalidPosix ? invalidPosixString : success,
+      invalidPosix ? invalidPosixString : success,
+      invalidPosix ? invalidPosixString : success,
+      String.valueOf(invalidSymlink),
+      invalidFileStore ? invalidFileStoreString : missingFile,
+      invalidPosix ? invalidPosixString : missingFile,
+      invalidPosix ? invalidPosixString : missingFile,
+      invalidPosix ? invalidPosixString : missingFile,
+      invalidPosix ? invalidPosixString : missingFile,
+    };
+    return String.format(EXPECTED_RESULT, vars);
+  }
+
+  @Test
+  public void test() throws Throwable {
+    if (parameters.isCfRuntime()) {
+      // Reference runtime, we use Jdk 11 since this is Jdk 11 desugared library, not that Jdk 8
+      // behaves differently on this test.
+      Assume.assumeTrue(parameters.isCfRuntime(CfVm.JDK11) && !ToolHelper.isWindows());
+      testForJvm()
+          .addInnerClasses(getClass())
+          .run(parameters.getRuntime(), TestClass.class)
+          .assertSuccessWithOutput(getExpectedResult());
+      return;
+    }
+    testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+        .addInnerClasses(getClass())
+        .addKeepMainRule(TestClass.class)
+        .compile()
+        .withArt6Plus64BitsLib()
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(getExpectedResult());
+  }
+
+  public static class TestClass {
+
+    public static void main(String[] args) throws Throwable {
+      System.out.println("PRESENT FILE");
+      Path path = Files.createTempFile("example", ".txt");
+      testProperties(path);
+      System.out.println("PRESENT DIR");
+      Path dir = Files.createTempDirectory("dir");
+      testProperties(dir);
+      System.out.println("ABSENT FILE");
+      Path notExisting = Paths.get("notExisting.txt");
+      testProperties(notExisting);
+    }
+
+    private static void printError(Throwable t) {
+      String[] split =
+          t.getMessage() == null ? new String[] {"no-message"} : t.getMessage().split("/");
+      System.out.println(t.getClass() + " :: " + split[split.length - 1]);
+    }
+
+    private static void testProperties(Path path) {
+      try {
+        System.out.print("fileStore:");
+        System.out.println(Files.getFileStore(path) != null);
+      } catch (Throwable t) {
+        printError(t);
+      }
+      try {
+        System.out.print("lastModifiedTime:");
+        System.out.println(Files.getLastModifiedTime(path).toMillis() > 0);
+      } catch (Throwable t) {
+        printError(t);
+      }
+      try {
+        System.out.print("lastModifiedTime:");
+        System.out.println(
+            Files.getLastModifiedTime(path, LinkOption.NOFOLLOW_LINKS).toMillis() > 0);
+      } catch (Throwable t) {
+        printError(t);
+      }
+      try {
+        System.out.print("owner:");
+        System.out.println(Files.getOwner(path) != null);
+      } catch (Throwable t) {
+        printError(t);
+      }
+      try {
+        System.out.print("owner:");
+        System.out.println(Files.getOwner(path, LinkOption.NOFOLLOW_LINKS) != null);
+      } catch (Throwable t) {
+        printError(t);
+      }
+      try {
+        System.out.print("posix:");
+        Set<PosixFilePermission> perms = Files.getPosixFilePermissions(path);
+        System.out.println(perms == null ? "null" : perms.contains(PosixFilePermission.OWNER_READ));
+      } catch (Throwable t) {
+        printError(t);
+      }
+      try {
+        System.out.print("posix:");
+        Set<PosixFilePermission> perms =
+            Files.getPosixFilePermissions(path, LinkOption.NOFOLLOW_LINKS);
+        System.out.println(perms == null ? "null" : perms.contains(PosixFilePermission.OWNER_READ));
+      } catch (Throwable t) {
+        printError(t);
+      }
+      try {
+        System.out.print("dir:");
+        System.out.println(Files.isDirectory(path));
+      } catch (Throwable t) {
+        printError(t);
+      }
+      try {
+        System.out.print("dir:");
+        System.out.println(Files.isDirectory(path, LinkOption.NOFOLLOW_LINKS));
+      } catch (Throwable t) {
+        printError(t);
+      }
+      try {
+        System.out.print("hidden:");
+        System.out.println(Files.isHidden(path));
+      } catch (Throwable t) {
+        printError(t);
+      }
+      try {
+        System.out.print("readable:");
+        System.out.println(Files.isReadable(path));
+      } catch (Throwable t) {
+        printError(t);
+      }
+      try {
+        System.out.print("writable:");
+        System.out.println(Files.isWritable(path));
+      } catch (Throwable t) {
+        printError(t);
+      }
+      try {
+        System.out.print("executable:");
+        System.out.println(Files.isExecutable(path));
+      } catch (Throwable t) {
+        printError(t);
+      }
+      try {
+        System.out.print("regular:");
+        System.out.println(Files.isRegularFile(path));
+      } catch (Throwable t) {
+        printError(t);
+      }
+      try {
+        System.out.print("regular:");
+        System.out.println(Files.isRegularFile(path, LinkOption.NOFOLLOW_LINKS));
+      } catch (Throwable t) {
+        printError(t);
+      }
+      try {
+        System.out.print("same:");
+        System.out.println(Files.isSameFile(path, path));
+      } catch (Throwable t) {
+        printError(t);
+      }
+      try {
+        System.out.print("same:");
+        System.out.println(Files.isSameFile(path, Paths.get("/")));
+      } catch (Throwable t) {
+        printError(t);
+      }
+      try {
+        System.out.print("symlink:");
+        System.out.println(Files.isSymbolicLink(path));
+      } catch (Throwable t) {
+        printError(t);
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesAttributesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesAttributesTest.java
new file mode 100644
index 0000000..2ccaa08
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesAttributesTest.java
@@ -0,0 +1,302 @@
+// 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 static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.DEFAULT_SPECIFICATIONS;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_PATH;
+import static java.nio.file.attribute.PosixFilePermission.OWNER_READ;
+
+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.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
+import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.AclFileAttributeView;
+import java.nio.file.attribute.BasicFileAttributeView;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.DosFileAttributeView;
+import java.nio.file.attribute.DosFileAttributes;
+import java.nio.file.attribute.FileOwnerAttributeView;
+import java.nio.file.attribute.PosixFileAttributeView;
+import java.nio.file.attribute.PosixFileAttributes;
+import java.nio.file.attribute.UserDefinedFileAttributeView;
+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 FilesAttributesTest extends DesugaredLibraryTestBase {
+
+  private static final String EXPECTED_RESULT_JVM_LINUX =
+      StringUtils.lines(
+          "PRESENT FILE",
+          "basic:true",
+          "posix:true",
+          "dos:false",
+          "acl:null",
+          "fileOwner:true",
+          "userDefined:user",
+          "basic:true",
+          "posix:true",
+          "dos:false",
+          "ABSENT FILE",
+          "basic:class java.nio.file.NoSuchFileException :: notExisting.txt",
+          "posix:class java.nio.file.NoSuchFileException :: notExisting.txt",
+          "dos:class java.nio.file.NoSuchFileException :: notExisting.txt",
+          "acl:null",
+          "fileOwner:class java.nio.file.NoSuchFileException :: notExisting.txt",
+          "userDefined:user",
+          "basic:class java.nio.file.NoSuchFileException :: notExisting.txt",
+          "posix:class java.nio.file.NoSuchFileException :: notExisting.txt",
+          "dos:class java.nio.file.NoSuchFileException :: notExisting.txt");
+  private static final String EXPECTED_RESULT_ANDROID =
+      StringUtils.lines(
+          "PRESENT FILE",
+          "basic:true",
+          "posix:true",
+          "dos:null",
+          "acl:null",
+          "fileOwner:true",
+          "userDefined:null",
+          "basic:true",
+          "posix:true",
+          "dos:class java.lang.UnsupportedOperationException :: no-message",
+          "ABSENT FILE",
+          "basic:class java.nio.file.NoSuchFileException :: notExisting.txt",
+          "posix:class java.nio.file.NoSuchFileException :: notExisting.txt",
+          "dos:null",
+          "acl:null",
+          "fileOwner:class java.nio.file.NoSuchFileException :: notExisting.txt",
+          "userDefined:null",
+          "basic:class java.nio.file.NoSuchFileException :: notExisting.txt",
+          "posix:class java.nio.file.NoSuchFileException :: notExisting.txt",
+          "dos:class java.lang.UnsupportedOperationException :: no-message");
+  private static final String EXPECTED_RESULT_ANDROID_DESUGARING =
+      StringUtils.lines(
+          "PRESENT FILE",
+          "basic:true",
+          "posix:null",
+          "dos:null",
+          "acl:null",
+          "fileOwner:null",
+          "userDefined:null",
+          "basic:true",
+          "posix:class java.lang.UnsupportedOperationException :: no-message",
+          "dos:class java.lang.UnsupportedOperationException :: no-message",
+          "ABSENT FILE",
+          "basic:class java.nio.file.NoSuchFileException :: notExisting.txt",
+          "posix:null",
+          "dos:null",
+          "acl:null",
+          "fileOwner:null",
+          "userDefined:null",
+          "basic:class java.nio.file.NoSuchFileException :: notExisting.txt",
+          "posix:class java.lang.UnsupportedOperationException :: no-message",
+          "dos:class java.lang.UnsupportedOperationException :: no-message");
+
+  private final TestParameters parameters;
+  private final LibraryDesugaringSpecification libraryDesugaringSpecification;
+  private final CompilationSpecification compilationSpecification;
+
+  @Parameters(name = "{0}, spec: {1}, {2}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        // Skip Android 4.4.4 due to missing libjavacrypto.
+        getTestParameters()
+            .withCfRuntime(CfVm.JDK11)
+            .withDexRuntime(Version.V4_0_4)
+            .withDexRuntimesStartingFromIncluding(Version.V5_1_1)
+            .withAllApiLevels()
+            .build(),
+        ImmutableList.of(JDK11_PATH),
+        DEFAULT_SPECIFICATIONS);
+  }
+
+  public FilesAttributesTest(
+      TestParameters parameters,
+      LibraryDesugaringSpecification libraryDesugaringSpecification,
+      CompilationSpecification compilationSpecification) {
+    this.parameters = parameters;
+    this.libraryDesugaringSpecification = libraryDesugaringSpecification;
+    this.compilationSpecification = compilationSpecification;
+  }
+
+  private String getExpectedResult() {
+    if (parameters.isCfRuntime()) {
+      return EXPECTED_RESULT_JVM_LINUX;
+    }
+    if (libraryDesugaringSpecification.usesPlatformFileSystem(parameters)) {
+      return EXPECTED_RESULT_ANDROID;
+    }
+    return EXPECTED_RESULT_ANDROID_DESUGARING;
+  }
+
+  @Test
+  public void test() throws Throwable {
+    if (parameters.isCfRuntime()) {
+      // Reference runtime, we use Jdk 11 since this is Jdk 11 desugared library, not that Jdk 8
+      // behaves differently on this test.
+      Assume.assumeTrue(parameters.isCfRuntime(CfVm.JDK11) && !ToolHelper.isWindows());
+      testForJvm()
+          .addInnerClasses(getClass())
+          .run(parameters.getRuntime(), TestClass.class)
+          .assertSuccessWithOutput(getExpectedResult());
+      return;
+    }
+    testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+        .addInnerClasses(getClass())
+        .addKeepMainRule(TestClass.class)
+        .compile()
+        .withArt6Plus64BitsLib()
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(getExpectedResult());
+  }
+
+  public static class TestClass {
+
+    public static void main(String[] args) throws Throwable {
+      System.out.println("PRESENT FILE");
+      Path path = Files.createTempFile("example", ".txt");
+      attributeViewAccess(path);
+      attributeAccess(path);
+      System.out.println("ABSENT FILE");
+      Path notExisting = Paths.get("notExisting.txt");
+      Files.deleteIfExists(notExisting);
+      attributeViewAccess(notExisting);
+      attributeAccess(notExisting);
+    }
+
+    private static void printError(Throwable t) {
+      String[] split =
+          t.getMessage() == null ? new String[] {"no-message"} : t.getMessage().split("/");
+      System.out.println(t.getClass() + " :: " + split[split.length - 1]);
+    }
+
+    private static void attributeViewAccess(Path path) {
+      try {
+        System.out.print("basic:");
+        BasicFileAttributeView basicView =
+            Files.getFileAttributeView(path, BasicFileAttributeView.class);
+        if (basicView != null) {
+          System.out.println(basicView.readAttributes().isRegularFile());
+        } else {
+          System.out.println("null");
+        }
+      } catch (Throwable t) {
+        printError(t);
+      }
+
+      try {
+        System.out.print("posix:");
+        PosixFileAttributeView posixView =
+            Files.getFileAttributeView(path, PosixFileAttributeView.class);
+        if (posixView != null) {
+          System.out.println(posixView.readAttributes().permissions().contains(OWNER_READ));
+        } else {
+          System.out.println("null");
+        }
+      } catch (Throwable t) {
+        printError(t);
+      }
+
+      try {
+        System.out.print("dos:");
+        DosFileAttributeView dosView = Files.getFileAttributeView(path, DosFileAttributeView.class);
+        if (dosView != null) {
+          System.out.println(dosView.readAttributes().isReadOnly());
+        } else {
+          System.out.println("null");
+        }
+      } catch (Throwable t) {
+        printError(t);
+      }
+
+      try {
+        System.out.print("acl:");
+        AclFileAttributeView aclView = Files.getFileAttributeView(path, AclFileAttributeView.class);
+        if (aclView != null) {
+          System.out.println(aclView.getAcl().isEmpty());
+        } else {
+          System.out.println("null");
+        }
+      } catch (Throwable t) {
+        printError(t);
+      }
+      try {
+        System.out.print("fileOwner:");
+        FileOwnerAttributeView foView =
+            Files.getFileAttributeView(path, FileOwnerAttributeView.class);
+        if (foView != null) {
+          System.out.println(foView.getOwner() != null);
+        } else {
+          System.out.println("null");
+        }
+      } catch (Throwable t) {
+        printError(t);
+      }
+
+      try {
+        System.out.print("userDefined:");
+        UserDefinedFileAttributeView udView =
+            Files.getFileAttributeView(path, UserDefinedFileAttributeView.class);
+        if (udView != null) {
+          System.out.println(udView.name());
+        } else {
+          System.out.println("null");
+        }
+      } catch (Throwable t) {
+        printError(t);
+      }
+    }
+
+    private static void attributeAccess(Path path) {
+      try {
+        System.out.print("basic:");
+        BasicFileAttributes attributes = Files.readAttributes(path, BasicFileAttributes.class);
+        if (attributes != null) {
+          System.out.println(attributes.isRegularFile());
+        } else {
+          System.out.println("null");
+        }
+      } catch (Throwable t) {
+        printError(t);
+      }
+
+      try {
+        System.out.print("posix:");
+        PosixFileAttributes posixAttributes = Files.readAttributes(path, PosixFileAttributes.class);
+        if (posixAttributes != null) {
+          System.out.println(posixAttributes.permissions().contains(OWNER_READ));
+        } else {
+          System.out.println("null");
+        }
+      } catch (Throwable t) {
+        printError(t);
+      }
+
+      try {
+        System.out.print("dos:");
+        DosFileAttributes dosFileAttributes = Files.readAttributes(path, DosFileAttributes.class);
+        if (dosFileAttributes != null) {
+          System.out.println(dosFileAttributes.isReadOnly());
+        } else {
+          System.out.println("null");
+        }
+      } catch (Throwable t) {
+        printError(t);
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesCreateTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesCreateTest.java
index af5df8a..5f7fd4b 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesCreateTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesCreateTest.java
@@ -41,14 +41,7 @@
           "ind3f class java.nio.file.NoSuchFileException :: dir",
           "ind4f class java.nio.file.NoSuchFileException :: dir",
           "ind5f class java.nio.file.NoSuchFileException :: f.txt",
-          "ind6f class java.nio.file.NoSuchFileException :: f.txt",
-          "true",
-          "true",
-          "notExisting1 class java.nio.file.NoSuchFileException :: notExisting1.txt",
-          "false",
-          "false",
-          "readAttributes class java.nio.file.NoSuchFileException :: f1.txt",
-          "readAttributes class java.nio.file.NoSuchFileException :: f1.txt");
+          "ind6f class java.nio.file.NoSuchFileException :: f.txt");
   private static final String EXPECTED_RESULT_DESUGARING =
       StringUtils.lines(
           "ind3f class java.nio.file.NoSuchFileException :: dir",
@@ -56,17 +49,24 @@
           "ind5f class java.io.FileNotFoundException :: f.txt: open failed: ENOENT (No such file or"
               + " directory)",
           "ind6f class java.io.FileNotFoundException :: f.txt: open failed: ENOENT (No such file or"
-              + " directory)",
-          "true",
-          "true",
-          "notExisting1 class java.io.IOException :: notExisting1.txt before deletion.",
-          "false",
-          "true",
-          "1970-01-01T00:00:00Z",
-          "1970-01-01T00:00:00Z");
-  private static final String EXPECTED_FILES =
+              + " directory)");
+  private static final String COMMON_EXPECTATIONS =
       StringUtils.lines(
-          "ind2s/dir", "ind2s", "ind1s/dir", "ind1s", "f4.txt", "f3.txt", "dir2s", "dir1s");
+          "true",
+          "true",
+          "notExisting1 class java.nio.file.NoSuchFileException :: notExisting1.txt",
+          "false",
+          "false",
+          "readAttributes class java.nio.file.NoSuchFileException :: f1.txt",
+          "readAttributes class java.nio.file.NoSuchFileException :: f1.txt",
+          "ind2s/dir",
+          "ind2s",
+          "ind1s/dir",
+          "ind1s",
+          "f4.txt",
+          "f3.txt",
+          "dir2s",
+          "dir1s");
 
   private final TestParameters parameters;
   private final LibraryDesugaringSpecification libraryDesugaringSpecification;
@@ -122,12 +122,12 @@
 
   private String getExpectedResult() {
     if (parameters.isCfRuntime()) {
-      return EXPECTED_RESULT + EXPECTED_FILES;
+      return EXPECTED_RESULT + COMMON_EXPECTATIONS;
     }
     return (libraryDesugaringSpecification.usesPlatformFileSystem(parameters)
             ? EXPECTED_RESULT
             : EXPECTED_RESULT_DESUGARING)
-        + EXPECTED_FILES;
+        + COMMON_EXPECTATIONS;
   }
 
   public static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesInOutTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesInOutTest.java
new file mode 100644
index 0000000..fe483f8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesInOutTest.java
@@ -0,0 +1,295 @@
+// 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 static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.DEFAULT_SPECIFICATIONS;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_PATH;
+
+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.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
+import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.List;
+import java.util.function.Supplier;
+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 FilesInOutTest extends DesugaredLibraryTestBase {
+
+  private static final String EXPECTED_RESULT =
+      StringUtils.lines(
+          "PRESENT FILE",
+          "buffRead:cHello!",
+          "inStream[READ]:cHello",
+          "outStream[READ]:class java.lang.IllegalArgumentException :: READ not allowed",
+          "buffWrite[READ]:class java.lang.IllegalArgumentException :: READ not allowed",
+          "newByte[READ]:c6",
+          "inStream[WRITE]:class java.lang.UnsupportedOperationException :: 'WRITE' not allowed",
+          "outStream[WRITE]:cwGame over!",
+          "buffWrite[WRITE]:cwHello!",
+          "newByte[WRITE]:c6",
+          "inStream[APPEND]:class java.lang.UnsupportedOperationException :: 'APPEND' not allowed",
+          "outStream[APPEND]:cwHello!Game over!",
+          "buffWrite[APPEND]:cwHello!",
+          "newByte[APPEND]:c6",
+          "inStream[TRUNCATE_EXISTING]:cHello",
+          "outStream[TRUNCATE_EXISTING]:cwGame over!",
+          "buffWrite[TRUNCATE_EXISTING]:cwclass java.lang.IndexOutOfBoundsException :: index 0,"
+              + " size 0",
+          "newByte[TRUNCATE_EXISTING]:c6",
+          "inStream[CREATE]:cHello",
+          "outStream[CREATE]:cwGame over!",
+          "buffWrite[CREATE]:cwHello!",
+          "newByte[CREATE]:c6",
+          "inStream[CREATE_NEW]:cHello",
+          "outStream[CREATE_NEW]:class java.nio.file.FileAlreadyExistsException :: example",
+          "buffWrite[CREATE_NEW]:class java.nio.file.FileAlreadyExistsException :: example",
+          "newByte[CREATE_NEW]:c6",
+          "inStream[DELETE_ON_CLOSE]:cHello",
+          "outStream[DELETE_ON_CLOSE]:%s",
+          "buffWrite[DELETE_ON_CLOSE]:%s",
+          "newByte[DELETE_ON_CLOSE]:c6",
+          "inStream[SPARSE]:cHello",
+          "outStream[SPARSE]:cwGame over!",
+          "buffWrite[SPARSE]:cwHello!",
+          "newByte[SPARSE]:c6",
+          "inStream[SYNC]:cHello",
+          "outStream[SYNC]:cwGame over!",
+          "buffWrite[SYNC]:cwHello!",
+          "newByte[SYNC]:c6",
+          "inStream[DSYNC]:cHello",
+          "outStream[DSYNC]:cwGame over!",
+          "buffWrite[DSYNC]:cwHello!",
+          "newByte[DSYNC]:c6",
+          "ABSENT FILE",
+          "buffRead:class java.nio.file.NoSuchFileException :: notExisting",
+          "inStream[READ]:class java.nio.file.NoSuchFileException :: notExisting",
+          "outStream[READ]:class java.lang.IllegalArgumentException :: READ not allowed",
+          "buffWrite[READ]:class java.lang.IllegalArgumentException :: READ not allowed",
+          "newByte[READ]:class java.nio.file.NoSuchFileException :: notExisting",
+          "inStream[WRITE]:class java.lang.UnsupportedOperationException :: 'WRITE' not allowed",
+          "outStream[WRITE]:class java.nio.file.NoSuchFileException :: notExisting",
+          "buffWrite[WRITE]:class java.nio.file.NoSuchFileException :: notExisting",
+          "newByte[WRITE]:class java.nio.file.NoSuchFileException :: notExisting",
+          "inStream[APPEND]:class java.lang.UnsupportedOperationException :: 'APPEND' not allowed",
+          "outStream[APPEND]:class java.nio.file.NoSuchFileException :: notExisting",
+          "buffWrite[APPEND]:class java.nio.file.NoSuchFileException :: notExisting",
+          "newByte[APPEND]:class java.nio.file.NoSuchFileException :: notExisting",
+          "inStream[TRUNCATE_EXISTING]:class java.nio.file.NoSuchFileException :: notExisting",
+          "outStream[TRUNCATE_EXISTING]:class java.nio.file.NoSuchFileException :: notExisting",
+          "buffWrite[TRUNCATE_EXISTING]:class java.nio.file.NoSuchFileException :: notExisting",
+          "newByte[TRUNCATE_EXISTING]:class java.nio.file.NoSuchFileException :: notExisting",
+          "inStream[CREATE]:%s",
+          "outStream[CREATE]:cwGame over!",
+          "buffWrite[CREATE]:cwclass java.lang.IndexOutOfBoundsException :: index 0, size 0",
+          "newByte[CREATE]:%s",
+          "inStream[CREATE_NEW]:%s",
+          "outStream[CREATE_NEW]:cwGame over!",
+          "buffWrite[CREATE_NEW]:cwclass java.lang.IndexOutOfBoundsException :: index 0, size 0",
+          "newByte[CREATE_NEW]:%s",
+          "inStream[DELETE_ON_CLOSE]:class java.nio.file.NoSuchFileException :: notExisting",
+          "outStream[DELETE_ON_CLOSE]:class java.nio.file.NoSuchFileException :: notExisting",
+          "buffWrite[DELETE_ON_CLOSE]:class java.nio.file.NoSuchFileException :: notExisting",
+          "newByte[DELETE_ON_CLOSE]:class java.nio.file.NoSuchFileException :: notExisting",
+          "inStream[SPARSE]:class java.nio.file.NoSuchFileException :: notExisting",
+          "outStream[SPARSE]:class java.nio.file.NoSuchFileException :: notExisting",
+          "buffWrite[SPARSE]:class java.nio.file.NoSuchFileException :: notExisting",
+          "newByte[SPARSE]:class java.nio.file.NoSuchFileException :: notExisting",
+          "inStream[SYNC]:class java.nio.file.NoSuchFileException :: notExisting",
+          "outStream[SYNC]:class java.nio.file.NoSuchFileException :: notExisting",
+          "buffWrite[SYNC]:class java.nio.file.NoSuchFileException :: notExisting",
+          "newByte[SYNC]:class java.nio.file.NoSuchFileException :: notExisting",
+          "inStream[DSYNC]:class java.nio.file.NoSuchFileException :: notExisting",
+          "outStream[DSYNC]:class java.nio.file.NoSuchFileException :: notExisting",
+          "buffWrite[DSYNC]:class java.nio.file.NoSuchFileException :: notExisting",
+          "newByte[DSYNC]:class java.nio.file.NoSuchFileException :: notExisting");
+
+  private static final String[] EXPECTED_RESULT_NO_DESUGARING =
+      new String[] {
+        "cwclass java.nio.file.NoSuchFileException :: example",
+        "cwclass java.nio.file.NoSuchFileException :: example",
+        "class java.nio.file.NoSuchFileException :: notExisting",
+        "class java.nio.file.NoSuchFileException :: notExisting",
+        "class java.nio.file.NoSuchFileException :: notExisting",
+        "class java.nio.file.NoSuchFileException :: notExisting"
+      };
+
+  private static final String[] EXPECTED_RESULT_DESUGARING =
+      new String[] {
+        "cwGame over!",
+        "cwHello!",
+        // In some cases the desugaring version raises FileNotFoundException instead of
+        // NoSuchFileException on the same file.
+        "class java.io.FileNotFoundException :: notExisting",
+        "class java.io.FileNotFoundException :: notExisting",
+        "class java.io.FileNotFoundException :: notExisting",
+        "class java.io.FileNotFoundException :: notExisting"
+      };
+
+  private final TestParameters parameters;
+  private final LibraryDesugaringSpecification libraryDesugaringSpecification;
+  private final CompilationSpecification compilationSpecification;
+
+  @Parameters(name = "{0}, spec: {1}, {2}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        // Skip Android 4.4.4 due to missing libjavacrypto.
+        getTestParameters()
+            .withCfRuntime(CfVm.JDK11)
+            .withDexRuntime(Version.V4_0_4)
+            .withDexRuntimesStartingFromIncluding(Version.V5_1_1)
+            .withAllApiLevels()
+            .build(),
+        ImmutableList.of(JDK11_PATH),
+        DEFAULT_SPECIFICATIONS);
+  }
+
+  public FilesInOutTest(
+      TestParameters parameters,
+      LibraryDesugaringSpecification libraryDesugaringSpecification,
+      CompilationSpecification compilationSpecification) {
+    this.parameters = parameters;
+    this.libraryDesugaringSpecification = libraryDesugaringSpecification;
+    this.compilationSpecification = compilationSpecification;
+  }
+
+  private String getExpectedResult() {
+    if (parameters.isCfRuntime()
+        || libraryDesugaringSpecification.usesPlatformFileSystem(parameters)) {
+      return String.format(EXPECTED_RESULT, EXPECTED_RESULT_NO_DESUGARING);
+    }
+    return String.format(EXPECTED_RESULT, EXPECTED_RESULT_DESUGARING);
+  }
+
+  @Test
+  public void test() throws Throwable {
+    if (parameters.isCfRuntime()) {
+      // Reference runtime, we use Jdk 11 since this is Jdk 11 desugared library, not that Jdk 8
+      // behaves differently on this test.
+      Assume.assumeTrue(parameters.isCfRuntime(CfVm.JDK11) && !ToolHelper.isWindows());
+      testForJvm()
+          .addInnerClasses(getClass())
+          .run(parameters.getRuntime(), TestClass.class)
+          .assertSuccessWithOutput(getExpectedResult());
+      return;
+    }
+    testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+        .addInnerClasses(getClass())
+        .addKeepMainRule(TestClass.class)
+        .compile()
+        .withArt6Plus64BitsLib()
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(getExpectedResult());
+  }
+
+  public static class TestClass {
+
+    public static void main(String[] args) throws Throwable {
+      System.out.println("PRESENT FILE");
+      Supplier<Path> presentFileSupplier =
+          () -> {
+            try {
+              Path path = Files.createTempFile("example_", ".txt");
+              Files.write(path, "Hello!".getBytes(StandardCharsets.UTF_8));
+              return path;
+            } catch (IOException t) {
+              // For the test it should never happen so this is enough.
+              throw new RuntimeException(t);
+            }
+          };
+      testProperties(presentFileSupplier);
+      System.out.println("ABSENT FILE");
+      testProperties(() -> Paths.get("notExisting_XXX.txt"));
+    }
+
+    private static void printError(Throwable t) {
+      String[] split =
+          t.getMessage() == null ? new String[] {"no-message_XXX"} : t.getMessage().split("/");
+      split = split[split.length - 1].split("_");
+      if (t instanceof IndexOutOfBoundsException) {
+        // IndexOutOfBoundsException are printed slightly differently across platform and it's not
+        // really relevant for the test.
+        split = new String[] {"index 0, size 0"};
+      }
+      System.out.println(t.getClass() + " :: " + split[0]);
+    }
+
+    private static void testProperties(Supplier<Path> pathSupplier) throws IOException {
+      Path path = pathSupplier.get();
+      System.out.print("buffRead:");
+      try (BufferedReader bufferedReader = Files.newBufferedReader(path)) {
+        System.out.print("c");
+        System.out.println(bufferedReader.readLine());
+      } catch (Throwable t) {
+        printError(t);
+      }
+      Files.deleteIfExists(path);
+      for (StandardOpenOption value : StandardOpenOption.values()) {
+        path = pathSupplier.get();
+        System.out.print("inStream[" + value + "]:");
+        try (InputStream inputStream = Files.newInputStream(path, value)) {
+          System.out.print("c");
+          byte[] read = new byte[5];
+          inputStream.read(read);
+          System.out.println(new String(read));
+        } catch (Throwable t) {
+          printError(t);
+        }
+        Files.deleteIfExists(path);
+        path = pathSupplier.get();
+        System.out.print("outStream[" + value + "]:");
+        try (OutputStream outputStream = Files.newOutputStream(path, value)) {
+          System.out.print("c");
+          outputStream.write("Game over!".getBytes(StandardCharsets.UTF_8));
+          System.out.print("w");
+          System.out.println(Files.readAllLines(path).get(0));
+        } catch (Throwable t) {
+          printError(t);
+        }
+        Files.deleteIfExists(path);
+        path = pathSupplier.get();
+        System.out.print("buffWrite[" + value + "]:");
+        try (BufferedWriter bufferedWriter = Files.newBufferedWriter(path, value)) {
+          System.out.print("c");
+          bufferedWriter.write("Game over!");
+          System.out.print("w");
+          System.out.println(Files.readAllLines(path).get(0));
+        } catch (Throwable t) {
+          printError(t);
+        }
+        Files.deleteIfExists(path);
+        path = pathSupplier.get();
+        System.out.print("newByte[" + value + "]:");
+        try (SeekableByteChannel seekableByteChannel = Files.newByteChannel(path, value)) {
+          System.out.print("c");
+          System.out.println(seekableByteChannel.size());
+        } catch (Throwable t) {
+          printError(t);
+        }
+        Files.deleteIfExists(path);
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesMoveCopyTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesMoveCopyTest.java
index e6eeff2..59564e6 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesMoveCopyTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesMoveCopyTest.java
@@ -108,8 +108,7 @@
         // Skip Android 4.4.4 due to missing libjavacrypto.
         getTestParameters()
             .withCfRuntime(CfVm.JDK11)
-            .withDexRuntime(Version.V4_0_4)
-            .withDexRuntimesStartingFromIncluding(Version.V5_1_1)
+            .withDexRuntimesStartingFromIncluding(Version.V8_1_0)
             .withAllApiLevels()
             .build(),
         ImmutableList.of(JDK11_PATH),
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesSymLinkTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesSymLinkTest.java
new file mode 100644
index 0000000..9a4521c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesSymLinkTest.java
@@ -0,0 +1,127 @@
+// 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 static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.DEFAULT_SPECIFICATIONS;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_PATH;
+
+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.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
+import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Files;
+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 FilesSymLinkTest extends DesugaredLibraryTestBase {
+
+  private static final String EXPECTED_RESULT =
+      StringUtils.lines("link created.", "true", "Symlink:true", "true");
+  private static final String EXPECTED_RESULT_DESUGARING =
+      StringUtils.lines(
+          "class java.lang.UnsupportedOperationException :: null",
+          "true",
+          "class java.lang.UnsupportedOperationException :: null",
+          "true");
+
+  private final TestParameters parameters;
+  private final LibraryDesugaringSpecification libraryDesugaringSpecification;
+  private final CompilationSpecification compilationSpecification;
+
+  @Parameters(name = "{0}, spec: {1}, {2}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        // Skip Android 4.4.4 due to missing libjavacrypto.
+        getTestParameters()
+            .withCfRuntime(CfVm.JDK11)
+            .withDexRuntime(Version.V4_0_4)
+            .withDexRuntimesStartingFromIncluding(Version.V5_1_1)
+            .withAllApiLevels()
+            .build(),
+        ImmutableList.of(JDK11_PATH),
+        DEFAULT_SPECIFICATIONS);
+  }
+
+  public FilesSymLinkTest(
+      TestParameters parameters,
+      LibraryDesugaringSpecification libraryDesugaringSpecification,
+      CompilationSpecification compilationSpecification) {
+    this.parameters = parameters;
+    this.libraryDesugaringSpecification = libraryDesugaringSpecification;
+    this.compilationSpecification = compilationSpecification;
+  }
+
+  @Test
+  public void test() throws Throwable {
+    if (parameters.isCfRuntime()) {
+      // Reference runtime, we use Jdk 11 since this is Jdk 11 desugared library, not that Jdk 8
+      // behaves differently on this test.
+      Assume.assumeTrue(parameters.isCfRuntime(CfVm.JDK11) && !ToolHelper.isWindows());
+      testForJvm()
+          .addInnerClasses(getClass())
+          .run(parameters.getRuntime(), TestClass.class, "10000")
+          .assertSuccessWithOutput(EXPECTED_RESULT);
+      return;
+    }
+    testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+        .addInnerClasses(getClass())
+        .addKeepMainRule(TestClass.class)
+        .compile()
+        .withArt6Plus64BitsLib()
+        .run(
+            parameters.getRuntime(),
+            TestClass.class,
+            String.valueOf(parameters.getApiLevel().getLevel()))
+        .assertSuccessWithOutput(
+            libraryDesugaringSpecification.usesPlatformFileSystem(parameters)
+                ? EXPECTED_RESULT
+                : EXPECTED_RESULT_DESUGARING);
+  }
+
+  public static class TestClass {
+
+    public static void main(String[] args) throws Throwable {
+      Path target = Files.createTempFile("guinea_pig_", ".txt");
+
+      Path link = Paths.get("link");
+      try {
+        Files.createLink(link, target);
+        System.out.println("link created.");
+      } catch (Throwable t) {
+        printError(t);
+      }
+      Files.deleteIfExists(link);
+      System.out.println((Files.exists(target)));
+
+      Path symLink = Paths.get("symLink");
+      try {
+        Files.createSymbolicLink(symLink, target);
+        Path path = Files.readSymbolicLink(symLink);
+        System.out.println("Symlink:" + path.equals(target));
+      } catch (Throwable t) {
+        printError(t);
+      }
+      Files.deleteIfExists(symLink);
+      System.out.println((Files.exists(target)));
+      Files.delete(target);
+    }
+
+    private static void printError(Throwable t) {
+      System.out.println(t.getClass() + " :: " + t.getMessage());
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesTest.java
index 27f42f0..58af649 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesTest.java
@@ -27,17 +27,9 @@
 import java.nio.file.OpenOption;
 import java.nio.file.Path;
 import java.nio.file.StandardOpenOption;
-import java.nio.file.attribute.AclFileAttributeView;
-import java.nio.file.attribute.BasicFileAttributeView;
-import java.nio.file.attribute.BasicFileAttributes;
 import java.nio.file.attribute.DosFileAttributeView;
-import java.nio.file.attribute.DosFileAttributes;
-import java.nio.file.attribute.FileOwnerAttributeView;
 import java.nio.file.attribute.FileTime;
-import java.nio.file.attribute.PosixFileAttributeView;
-import java.nio.file.attribute.PosixFileAttributes;
 import java.nio.file.attribute.PosixFilePermission;
-import java.nio.file.attribute.UserDefinedFileAttributeView;
 import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -63,15 +55,6 @@
           "String read: Hello World",
           "bytes read: 11",
           "String read: Hello World",
-          "true",
-          "%s",
-          "null",
-          "null",
-          "%s",
-          "null",
-          "true",
-          "%s",
-          "unsupported",
           "tmp",
           "/",
           "true",
@@ -85,17 +68,11 @@
           "%s");
   private static final List<String> EXPECTED_RESULT_POSIX =
       ImmutableList.of(
-          "true",
-          "true",
-          "true",
           "Succeeded with POSIX RO:false",
           "Successfully set RO with POSIX",
           "Succeeded with POSIX RO:true");
   private static final List<String> EXPECTED_RESULT_DESUGARING_NON_POSIX =
       ImmutableList.of(
-          "null",
-          "null",
-          "unsupported",
           "Fail to understand if the file is read-only: class"
               + " java.lang.UnsupportedOperationException",
           "Fail to set file as read-only: class java.lang.UnsupportedOperationException",
@@ -161,8 +138,6 @@
       Path path = Files.createTempFile("example", ".txt");
       readWriteThroughFilesAPI(path);
       readThroughFileChannelAPI(path);
-      attributeViewAccess(path);
-      attributeAccess(path);
       Files.setAttribute(path, "basic:lastModifiedTime", FileTime.from(Instant.EPOCH));
       pathGeneric();
       lines(path);
@@ -256,101 +231,6 @@
       System.out.println(mapping.values().iterator().next().getClass().getPackage().getName());
     }
 
-    private static void attributeViewAccess(Path path) throws IOException {
-      BasicFileAttributeView basicView =
-          Files.getFileAttributeView(path, BasicFileAttributeView.class);
-      if (basicView != null) {
-        System.out.println(basicView.readAttributes().isRegularFile());
-      } else {
-        System.out.println("null");
-      }
-
-      PosixFileAttributeView posixView =
-          Files.getFileAttributeView(path, PosixFileAttributeView.class);
-      if (posixView != null) {
-        System.out.println(posixView.readAttributes().permissions().contains(OWNER_READ));
-      } else {
-        System.out.println("null");
-      }
-
-      try {
-        DosFileAttributeView dosView = Files.getFileAttributeView(path, DosFileAttributeView.class);
-        if (dosView != null) {
-          System.out.println(dosView.readAttributes().isReadOnly());
-        } else {
-          System.out.println("null");
-        }
-      } catch (UnsupportedOperationException e) {
-        System.out.println("unsupported");
-      }
-
-      try {
-        AclFileAttributeView aclView = Files.getFileAttributeView(path, AclFileAttributeView.class);
-        if (aclView != null) {
-          System.out.println(aclView.getAcl().isEmpty());
-        } else {
-          System.out.println("null");
-        }
-      } catch (UnsupportedOperationException e) {
-        System.out.println("unsupported");
-      }
-
-      try {
-        FileOwnerAttributeView foView =
-            Files.getFileAttributeView(path, FileOwnerAttributeView.class);
-        if (foView != null) {
-          System.out.println(foView.getOwner() != null);
-        } else {
-          System.out.println("null");
-        }
-      } catch (UnsupportedOperationException e) {
-        System.out.println("unsupported");
-      }
-
-      try {
-        UserDefinedFileAttributeView udView =
-            Files.getFileAttributeView(path, UserDefinedFileAttributeView.class);
-        if (udView != null) {
-          System.out.println(udView.name());
-        } else {
-          System.out.println("null");
-        }
-      } catch (UnsupportedOperationException e) {
-        System.out.println("unsupported");
-      }
-    }
-
-    private static void attributeAccess(Path path) throws IOException {
-      BasicFileAttributes attributes = Files.readAttributes(path, BasicFileAttributes.class);
-      if (attributes != null) {
-        System.out.println(attributes.isRegularFile());
-      } else {
-        System.out.println("null");
-      }
-
-      try {
-        PosixFileAttributes posixAttributes = Files.readAttributes(path, PosixFileAttributes.class);
-        if (posixAttributes != null) {
-          System.out.println(posixAttributes.permissions().contains(OWNER_READ));
-        } else {
-          System.out.println("null");
-        }
-      } catch (UnsupportedOperationException e) {
-        System.out.println("unsupported");
-      }
-
-      try {
-        DosFileAttributes dosFileAttributes = Files.readAttributes(path, DosFileAttributes.class);
-        if (dosFileAttributes != null) {
-          System.out.println(dosFileAttributes.isReadOnly());
-        } else {
-          System.out.println("null");
-        }
-      } catch (UnsupportedOperationException e) {
-        System.out.println("unsupported");
-      }
-    }
-
     private static void lines(Path path) throws IOException {
       Files.write(path, "This\nis\nfun!".getBytes(StandardCharsets.UTF_8));
       Stream<String> lines = Files.lines(path, StandardCharsets.UTF_8);
diff --git a/src/test/java/com/android/tools/r8/dex/container/DexContainerFormatEmptyTest.java b/src/test/java/com/android/tools/r8/dex/container/DexContainerFormatEmptyTest.java
new file mode 100644
index 0000000..b2e6aec
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/dex/container/DexContainerFormatEmptyTest.java
@@ -0,0 +1,68 @@
+// 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.dex.container;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.ZipUtils;
+import com.google.common.io.ByteStreams;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+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 DexContainerFormatEmptyTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  @Test
+  public void testNonContainerD8() throws Exception {
+    Path outputA = testForD8(Backend.DEX).setMinApi(AndroidApiLevel.L).compile().writeToZip();
+    assertEquals(0, unzipContent(outputA).size());
+
+    Path outputB = testForD8(Backend.DEX).setMinApi(AndroidApiLevel.L).compile().writeToZip();
+    assertEquals(0, unzipContent(outputB).size());
+
+    Path outputMerged =
+        testForD8(Backend.DEX)
+            .addProgramFiles(outputA, outputB)
+            .setMinApi(AndroidApiLevel.L)
+            .compile()
+            .writeToZip();
+    assertEquals(0, unzipContent(outputMerged).size());
+  }
+
+  @Test
+  public void testD8Experiment() throws Exception {
+    Path outputFromDexing =
+        testForD8(Backend.DEX)
+            .setMinApi(AndroidApiLevel.L)
+            .addOptionsModification(
+                options -> options.getTestingOptions().dexContainerExperiment = true)
+            .compile()
+            .writeToZip();
+    assertEquals(0, unzipContent(outputFromDexing).size());
+  }
+
+  private List<byte[]> unzipContent(Path zip) throws IOException {
+    List<byte[]> result = new ArrayList<>();
+    ZipUtils.iter(zip, (entry, inputStream) -> result.add(ByteStreams.toByteArray(inputStream)));
+    return result;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/varhandle/DesugarVarHandle.java b/src/test/java/com/android/tools/r8/ir/desugar/varhandle/DesugarVarHandle.java
index 57c13dd..af2bb5b 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/varhandle/DesugarVarHandle.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/varhandle/DesugarVarHandle.java
@@ -135,14 +135,15 @@
   }
 
   long getLong(Object ct1) {
-    // TODO(b/247076137): Implement.
-    return -1L;
+    return U.getLong(ct1, offset);
   }
 
   // set variants.
   void set(Object ct1, Object newValue) {
     if (type == int.class) {
       setInt(ct1, toIntIfPossible(newValue));
+    } else if (type == long.class) {
+      setLong(ct1, toLongIfPossible(newValue));
     } else {
       U.putObject(ct1, offset, newValue);
     }
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepEdgeAnnotationsTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepEdgeAnnotationsTest.java
index 01ec5ac..8743b48 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepEdgeAnnotationsTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepEdgeAnnotationsTest.java
@@ -193,7 +193,7 @@
         .assertSuccessWithOutput(getExpected());
   }
 
-  private List<String> getKeepRulesForClass(Class<?> clazz) throws IOException {
+  public static List<String> getKeepRulesForClass(Class<?> clazz) throws IOException {
     Set<KeepEdge> keepEdges = KeepEdgeReader.readKeepEdges(ToolHelper.getClassAsBytes(clazz));
     List<String> rules = new ArrayList<>();
     KeepRuleExtractor extractor = new KeepRuleExtractor(rules::add);
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationTest.java
new file mode 100644
index 0000000..9bee408
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationTest.java
@@ -0,0 +1,117 @@
+// 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.keepanno;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.keepanno.annotations.KeepTarget;
+import com.android.tools.r8.keepanno.annotations.UsesReflection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class KeepUsesReflectionAnnotationTest extends TestBase {
+
+  static final String EXPECTED = StringUtils.lines("Hello, world");
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build();
+  }
+
+  public KeepUsesReflectionAnnotationTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testReference() throws Exception {
+    testForRuntime(parameters)
+        .addProgramClasses(getInputClasses())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  @Test
+  public void testWithRuleExtraction() throws Exception {
+    List<String> rules = getExtractedKeepRules();
+    testForR8(parameters.getBackend())
+        .addProgramClassFileData(getInputClassesWithoutAnnotations())
+        .addKeepRules(rules)
+        .addKeepMainRule(TestClass.class)
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED)
+        .inspect(this::checkOutput);
+  }
+
+  public List<Class<?>> getInputClasses() {
+    return ImmutableList.of(TestClass.class, A.class, B.class);
+  }
+
+  public List<byte[]> getInputClassesWithoutAnnotations() throws Exception {
+    List<Class<?>> classes = getInputClasses();
+    List<byte[]> transformed = new ArrayList<>(classes.size());
+    for (Class<?> clazz : classes) {
+      transformed.add(transformer(clazz).removeAllAnnotations().transform());
+    }
+    return transformed;
+  }
+
+  public List<String> getExtractedKeepRules() throws Exception {
+    List<Class<?>> classes = getInputClasses();
+    List<String> rules = new ArrayList<>();
+    for (Class<?> clazz : classes) {
+      rules.addAll(KeepEdgeAnnotationsTest.getKeepRulesForClass(clazz));
+    }
+    return rules;
+  }
+
+  private void checkOutput(CodeInspector inspector) {
+    assertThat(inspector.clazz(A.class), isPresent());
+    assertThat(inspector.clazz(B.class), isPresent());
+    assertThat(inspector.clazz(B.class).uniqueMethodWithOriginalName("bar"), isPresent());
+  }
+
+  static class A {
+
+    @UsesReflection({
+      // Ensure that the class A remains as we are assuming the contents of its name.
+      @KeepTarget(classConstant = A.class),
+      // Ensure that the class B remains as we are looking it up by reflected name.
+      @KeepTarget(classConstant = B.class),
+      // Ensure the method 'bar' remains as we are invoking it by reflected name.
+      @KeepTarget(classConstant = B.class, methodName = "bar")
+    })
+    public void foo() throws Exception {
+      Class<?> clazz = Class.forName(A.class.getTypeName().replace("$A", "$B"));
+      clazz.getDeclaredMethod("bar").invoke(clazz);
+    }
+  }
+
+  static class B {
+    public static void bar() {
+      System.out.println("Hello, world");
+    }
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) throws Exception {
+      new A().foo();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationWithAdditionalPreconditionTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationWithAdditionalPreconditionTest.java
new file mode 100644
index 0000000..732f2d2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationWithAdditionalPreconditionTest.java
@@ -0,0 +1,128 @@
+// 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.keepanno;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.keepanno.annotations.KeepCondition;
+import com.android.tools.r8.keepanno.annotations.KeepTarget;
+import com.android.tools.r8.keepanno.annotations.UsesReflection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class KeepUsesReflectionAnnotationWithAdditionalPreconditionTest extends TestBase {
+
+  static final String EXPECTED = StringUtils.lines("Hello, world");
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build();
+  }
+
+  public KeepUsesReflectionAnnotationWithAdditionalPreconditionTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testReference() throws Exception {
+    testForRuntime(parameters)
+        .addProgramClasses(getInputClasses())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  @Test
+  public void testWithRuleExtraction() throws Exception {
+    List<String> rules = getExtractedKeepRules();
+    testForR8(parameters.getBackend())
+        .addProgramClassFileData(getInputClassesWithoutAnnotations())
+        .addKeepRules(rules)
+        .addKeepMainRule(TestClass.class)
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED)
+        .inspect(this::checkOutput);
+  }
+
+  public List<Class<?>> getInputClasses() {
+    return ImmutableList.of(TestClass.class, A.class, B.class);
+  }
+
+  public List<byte[]> getInputClassesWithoutAnnotations() throws Exception {
+    List<Class<?>> classes = getInputClasses();
+    List<byte[]> transformed = new ArrayList<>(classes.size());
+    for (Class<?> clazz : classes) {
+      transformed.add(transformer(clazz).removeAllAnnotations().transform());
+    }
+    return transformed;
+  }
+
+  public List<String> getExtractedKeepRules() throws Exception {
+    List<Class<?>> classes = getInputClasses();
+    List<String> rules = new ArrayList<>();
+    for (Class<?> clazz : classes) {
+      rules.addAll(KeepEdgeAnnotationsTest.getKeepRulesForClass(clazz));
+    }
+    return rules;
+  }
+
+  private void checkOutput(CodeInspector inspector) {
+    assertThat(inspector.clazz(A.class), isPresent());
+    assertThat(inspector.clazz(B.class), isPresent());
+    assertThat(inspector.clazz(B.class).uniqueMethodWithOriginalName("<init>"), isPresent());
+    assertThat(inspector.clazz(B.class).uniqueMethodWithOriginalName("bar"), isPresent());
+  }
+
+  static class A {
+
+    @UsesReflection(
+        value = {
+          // Ensure B's constructor and method 'bar' remain as they are invoked by reflection.
+          @KeepTarget(classConstant = B.class, methodName = "<init>"),
+          @KeepTarget(classConstant = B.class, methodName = "bar")
+        },
+        additionalPreconditions = {
+          // The reflection depends on the class constant being used in the program in addition to
+          // this method. In rule extraction, this will lead to an over-approximation as the rules
+          // will need to keep the above live if either of the two conditions are met. There is no
+          // way to express the conjunction with multiple distinct precondition classes in the
+          // rule language. With direct annotation interpretation this limitation is avoided and
+          // a more precise shrinking is possible.
+          // TODO(b/248408342): Check this once direct interpretation is supported.
+          @KeepCondition(classConstant = B.class)
+        })
+    public void foo(Class<B> clazz) throws Exception {
+      if (clazz != null) {
+        clazz.getDeclaredMethod("bar").invoke(clazz.getDeclaredConstructor().newInstance());
+      }
+    }
+  }
+
+  static class B {
+    public static void bar() {
+      System.out.println("Hello, world");
+    }
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) throws Exception {
+      new A().foo(System.nanoTime() > 0 ? B.class : null);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index afc975a..bcd5360 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -1463,6 +1463,10 @@
         });
   }
 
+  public ClassFileTransformer removeAllAnnotations() {
+    return removeClassAnnotations().removeMethodAnnotations().removeFieldAnnotations();
+  }
+
   public ClassFileTransformer removeClassAnnotations() {
     return addClassTransformer(
         new ClassTransformer() {