Resolve remaining tracing TODOs in repackaging

Bug: 165783399
Change-Id: Idc06584aa8143019f4e40d3bd2cdcd3715f47336
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index e3bf2d8..cde4638 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -562,7 +562,11 @@
   }
 
   public DexType getType() {
-    return this.type;
+    return type;
+  }
+
+  public DexType getSuperType() {
+    return superType;
   }
 
   public boolean hasClassInitializer() {
@@ -817,6 +821,11 @@
     return null;
   }
 
+  public void forEachNestMember(Consumer<DexType> consumer) {
+    assert isNestHost();
+    getNestMembersClassAttributes().forEach(member -> consumer.accept(member.getNestMember()));
+  }
+
   public NestHostClassAttribute getNestHostClassAttribute() {
     return nestHost;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndField.java b/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
index 7b11467..288b336 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
@@ -4,21 +4,11 @@
 
 package com.android.tools.r8.graph;
 
-import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.origin.Origin;
-
-public class DexClassAndField {
-
-  private final DexClass holder;
-  private final DexEncodedField field;
+public class DexClassAndField extends DexClassAndMember<DexEncodedField, DexField> {
 
   DexClassAndField(DexClass holder, DexEncodedField field) {
-    assert holder != null;
-    assert field != null;
-    assert holder.type == field.holder();
+    super(holder, field);
     assert holder.isProgramClass() == (this instanceof ProgramField);
-    this.holder = holder;
-    this.field = field;
   }
 
   public static DexClassAndField create(DexClass holder, DexEncodedField field) {
@@ -29,26 +19,6 @@
     }
   }
 
-  public DexClass getHolder() {
-    return holder;
-  }
-
-  public DexType getHolderType() {
-    return holder.type;
-  }
-
-  public DexEncodedField getDefinition() {
-    return field;
-  }
-
-  public DexField getReference() {
-    return field.field;
-  }
-
-  public Origin getOrigin() {
-    return holder.origin;
-  }
-
   public boolean isProgramField() {
     return false;
   }
@@ -56,23 +26,4 @@
   public ProgramField asProgramField() {
     return null;
   }
-
-  public String toSourceString() {
-    return getReference().toSourceString();
-  }
-
-  @Override
-  public String toString() {
-    return toSourceString();
-  }
-
-  @Override
-  public boolean equals(Object object) {
-    throw new Unreachable("Unsupported attempt at comparing Class and DexClassAndField");
-  }
-
-  @Override
-  public int hashCode() {
-    throw new Unreachable("Unsupported attempt at computing the hashcode of DexClassAndField");
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java b/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java
new file mode 100644
index 0000000..0bc5348
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java
@@ -0,0 +1,66 @@
+// Copyright (c) 2020, 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.graph;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.origin.Origin;
+
+public abstract class DexClassAndMember<
+    D extends DexEncodedMember<D, R>, R extends DexMember<D, R>> {
+
+  private final DexClass holder;
+  private final D definition;
+
+  public DexClassAndMember(DexClass holder, D definition) {
+    assert holder != null;
+    assert definition != null;
+    assert holder.type == definition.holder();
+    this.holder = holder;
+    this.definition = definition;
+  }
+
+  public DexType getContextType() {
+    return getHolderType();
+  }
+
+  public DexClass getHolder() {
+    return holder;
+  }
+
+  public DexType getHolderType() {
+    return holder.type;
+  }
+
+  public D getDefinition() {
+    return definition;
+  }
+
+  public R getReference() {
+    return definition.toReference();
+  }
+
+  public Origin getOrigin() {
+    return holder.origin;
+  }
+
+  public String toSourceString() {
+    return getReference().toSourceString();
+  }
+
+  @Override
+  public String toString() {
+    return toSourceString();
+  }
+
+  @Override
+  public boolean equals(Object object) {
+    throw new Unreachable("Unsupported attempt at comparing Class and DexClassAndMember");
+  }
+
+  @Override
+  public int hashCode() {
+    throw new Unreachable("Unsupported attempt at computing the hash code of DexClassAndMember");
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
index c6c2f07..87d1bb9 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
@@ -4,21 +4,12 @@
 
 package com.android.tools.r8.graph;
 
-import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.origin.Origin;
-
-public class DexClassAndMethod implements LookupTarget {
-
-  private final DexClass holder;
-  private final DexEncodedMethod method;
+public class DexClassAndMethod extends DexClassAndMember<DexEncodedMethod, DexMethod>
+    implements LookupTarget {
 
   DexClassAndMethod(DexClass holder, DexEncodedMethod method) {
-    assert holder != null;
-    assert method != null;
-    assert holder.type == method.holder();
+    super(holder, method);
     assert holder.isProgramClass() == (this instanceof ProgramMethod);
-    this.holder = holder;
-    this.method = method;
   }
 
   public static DexClassAndMethod create(DexClass holder, DexEncodedMethod method) {
@@ -33,16 +24,6 @@
   }
 
   @Override
-  public boolean equals(Object object) {
-    throw new Unreachable("Unsupported attempt at comparing Class and DexClassAndMethod");
-  }
-
-  @Override
-  public int hashCode() {
-    throw new Unreachable("Unsupported attempt at computing the hashcode of DexClassAndMethod");
-  }
-
-  @Override
   public boolean isMethodTarget() {
     return true;
   }
@@ -52,26 +33,6 @@
     return this;
   }
 
-  public DexClass getHolder() {
-    return holder;
-  }
-
-  public DexType getHolderType() {
-    return holder.type;
-  }
-
-  public DexEncodedMethod getDefinition() {
-    return method;
-  }
-
-  public DexMethod getReference() {
-    return method.method;
-  }
-
-  public Origin getOrigin() {
-    return holder.origin;
-  }
-
   public boolean isClasspathMethod() {
     return false;
   }
@@ -87,13 +48,4 @@
   public ProgramMethod asProgramMethod() {
     return null;
   }
-
-  public String toSourceString() {
-    return method.method.toSourceString();
-  }
-
-  @Override
-  public String toString() {
-    return toSourceString();
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java
index e480799..cb4eb4e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.utils.ArrayUtils;
 import java.util.Arrays;
+import java.util.function.Consumer;
 import java.util.function.Function;
 
 public class DexEncodedAnnotation extends DexItem {
@@ -30,6 +31,12 @@
     }
   }
 
+  public void forEachElement(Consumer<DexAnnotationElement> consumer) {
+    for (DexAnnotationElement element : elements) {
+      consumer.accept(element);
+    }
+  }
+
   public DexAnnotationElement getElement(int i) {
     return elements[i];
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 31903d7..4936ceb 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -775,6 +775,10 @@
     return builder.toString();
   }
 
+  public ParameterAnnotationsList getParameterAnnotations() {
+    return parameterAnnotationsList;
+  }
+
   public void clearParameterAnnotations() {
     parameterAnnotationsList = ParameterAnnotationsList.empty();
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexField.java b/src/main/java/com/android/tools/r8/graph/DexField.java
index a58ec39..0997619 100644
--- a/src/main/java/com/android/tools/r8/graph/DexField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexField.java
@@ -23,6 +23,10 @@
     }
   }
 
+  public DexType getType() {
+    return type;
+  }
+
   @Override
   public DexEncodedField lookupOnClass(DexClass clazz) {
     return clazz != null ? clazz.lookupField(this) : null;
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java
index c2caa61..15e75bf 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -29,6 +29,10 @@
     }
   }
 
+  public DexType getHolderType() {
+    return holder;
+  }
+
   public DexString getName() {
     return name;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index f0efe20..ca7a264 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -29,7 +29,8 @@
 import java.util.function.Predicate;
 import java.util.function.Supplier;
 
-public class DexProgramClass extends DexClass implements Supplier<DexProgramClass> {
+public class DexProgramClass extends DexClass
+    implements ProgramDefinition, Supplier<DexProgramClass> {
 
   @FunctionalInterface
   public interface ChecksumSupplier {
@@ -491,6 +492,16 @@
     return this;
   }
 
+  @Override
+  public DexType getContextType() {
+    return getType();
+  }
+
+  @Override
+  public DexProgramClass getDefinition() {
+    return this;
+  }
+
   public void setInitialClassFileVersion(int initialClassFileVersion) {
     assert this.initialClassFileVersion == -1 && initialClassFileVersion > 0;
     this.initialClassFileVersion = initialClassFileVersion;
diff --git a/src/main/java/com/android/tools/r8/graph/DexProto.java b/src/main/java/com/android/tools/r8/graph/DexProto.java
index 09985c3..a0cacd9 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProto.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProto.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.naming.NamingLens;
 import com.google.common.hash.Hasher;
+import java.util.function.Consumer;
 
 public class DexProto extends IndexedDexItem implements PresortedComparable<DexProto> {
 
@@ -21,6 +22,11 @@
     this.parameters = parameters;
   }
 
+  public void forEachType(Consumer<DexType> consumer) {
+    consumer.accept(returnType);
+    parameters.forEach(consumer);
+  }
+
   public DexType getParameter(int index) {
     return parameters.values[index];
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexTypeList.java b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
index c61de91..ae25597 100644
--- a/src/main/java/com/android/tools/r8/graph/DexTypeList.java
+++ b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.utils.ArrayUtils;
 import java.util.Arrays;
+import java.util.function.Consumer;
 import java.util.stream.Stream;
 
 public class DexTypeList extends DexItem {
@@ -34,6 +35,12 @@
     return ArrayUtils.contains(values, type);
   }
 
+  public void forEach(Consumer<DexType> consumer) {
+    for (DexType value : values) {
+      consumer.accept(value);
+    }
+  }
+
   @Override
   public int hashCode() {
     return Arrays.hashCode(values);
diff --git a/src/main/java/com/android/tools/r8/graph/DexValue.java b/src/main/java/com/android/tools/r8/graph/DexValue.java
index 2fe8c3f..273c69a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexValue.java
+++ b/src/main/java/com/android/tools/r8/graph/DexValue.java
@@ -24,6 +24,7 @@
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.EncodedValueUtils;
 import java.util.Arrays;
+import java.util.function.Consumer;
 import org.objectweb.asm.Handle;
 import org.objectweb.asm.Type;
 
@@ -1381,6 +1382,12 @@
       this.values = values;
     }
 
+    public void forEachElement(Consumer<DexValue> consumer) {
+      for (DexValue value : values) {
+        consumer.accept(value);
+      }
+    }
+
     public DexValue[] getValues() {
       return values;
     }
@@ -1474,6 +1481,10 @@
       this.value = value;
     }
 
+    public DexEncodedAnnotation getValue() {
+      return value;
+    }
+
     @Override
     public DexValueKind getValueKind() {
       return DexValueKind.ANNOTATION;
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java b/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java
new file mode 100644
index 0000000..91edb06
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2020, 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.graph;
+
+public interface ProgramDefinition {
+
+  DexType getContextType();
+
+  DexDefinition getDefinition();
+}
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMember.java b/src/main/java/com/android/tools/r8/graph/ProgramMember.java
index 7dc209c..328c71f 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramMember.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramMember.java
@@ -4,8 +4,10 @@
 
 package com.android.tools.r8.graph;
 
-public interface ProgramMember<D extends DexEncodedMember<D, R>, R extends DexMember<D, R>> {
+public interface ProgramMember<D extends DexEncodedMember<D, R>, R extends DexMember<D, R>>
+    extends ProgramDefinition {
 
+  @Override
   D getDefinition();
 
   DexType getHolderType();
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingAnnotationTracer.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingAnnotationTracer.java
new file mode 100644
index 0000000..f52b8b7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingAnnotationTracer.java
@@ -0,0 +1,108 @@
+// Copyright (c) 2020, 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.repackaging;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexAnnotationElement;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexEncodedAnnotation;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.ParameterAnnotationsList;
+
+public class RepackagingAnnotationTracer {
+
+  private final AppInfoWithClassHierarchy appInfo;
+  private final RepackagingUseRegistry registry;
+
+  public RepackagingAnnotationTracer(
+      AppView<? extends AppInfoWithClassHierarchy> appView, RepackagingUseRegistry registry) {
+    this.appInfo = appView.appInfo();
+    this.registry = registry;
+  }
+
+  public void trace(DexAnnotationSet annotations) {
+    annotations.forEach(this::traceAnnotation);
+  }
+
+  public void trace(ParameterAnnotationsList annotations) {
+    annotations.forEachAnnotation(this::traceAnnotation);
+  }
+
+  private void traceAnnotation(DexAnnotation annotation) {
+    traceEncodedAnnotation(annotation.annotation);
+  }
+
+  private void traceEncodedAnnotation(DexEncodedAnnotation annotation) {
+    registry.registerTypeReference(annotation.type);
+    annotation.forEachElement(this::traceAnnotationElement);
+  }
+
+  private void traceAnnotationElement(DexAnnotationElement element) {
+    traceDexValue(element.value);
+  }
+
+  private void traceDexValue(DexValue value) {
+    switch (value.getValueKind()) {
+      case BOOLEAN:
+      case BYTE:
+      case CHAR:
+      case DOUBLE:
+      case FLOAT:
+      case INT:
+      case LONG:
+      case NULL:
+      case SHORT:
+      case STRING:
+        break;
+
+      case ANNOTATION:
+        traceEncodedAnnotation(value.asDexValueAnnotation().getValue());
+        break;
+
+      case ARRAY:
+        value.asDexValueArray().forEachElement(this::traceDexValue);
+        break;
+
+      case ENUM:
+        registry.registerFieldAccess(value.asDexValueEnum().getValue());
+        break;
+
+      case FIELD:
+        registry.registerFieldAccess(value.asDexValueField().getValue());
+        break;
+
+      case METHOD:
+        registry.registerMethodReference(value.asDexValueMethod().getValue());
+        break;
+
+      case METHOD_HANDLE:
+        {
+          DexMethodHandle handle = value.asDexValueMethodHandle().getValue();
+          if (handle.isFieldHandle()) {
+            registry.registerFieldAccess(handle.asField());
+          } else {
+            assert handle.isMethodHandle();
+            registry.registerMethodReference(handle.asMethod());
+          }
+        }
+        break;
+
+      case METHOD_TYPE:
+        value.asDexValueMethodType().getValue().forEachType(registry::registerTypeReference);
+        break;
+
+      case TYPE:
+        registry.registerTypeReference(value.asDexValueType().getValue());
+        break;
+
+      default:
+        throw new Unreachable();
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
index 33bbc11..1d97079 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.graph.DexEncodedMember;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.EnclosingMethodAttribute;
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.ProgramPackage;
@@ -68,7 +69,7 @@
   }
 
   private Node createNode(DexDefinition definition) {
-    Node node = new Node(definition);
+    Node node = new Node();
     nodes.put(definition, node);
     return node;
   }
@@ -89,23 +90,65 @@
   }
 
   private void registerReferencesFromClass(DexProgramClass clazz) {
-    // TODO(b/165783399): Trace the references to the immediate super types.
-    // TODO(b/165783399): Maybe trace the references in the nest host and/or members.
-    // TODO(b/165783399): Maybe trace the references to the inner classes.
-    // TODO(b/165783399): Maybe trace the references in @kotlin.Metadata.
+    RepackagingUseRegistry registry = new RepackagingUseRegistry(appView, this, clazz);
+
+    // Trace the references to the immediate super types.
+    registry.registerTypeReference(clazz.getSuperType());
+    clazz.interfaces.forEach(registry::registerTypeReference);
+
+    // Trace the references from the class annotations.
+    new RepackagingAnnotationTracer(appView, registry).trace(clazz.annotations());
+
+    // Trace the references in the nest host and/or members.
+    if (clazz.isInANest()) {
+      if (clazz.isNestHost()) {
+        clazz.forEachNestMember(registry::registerTypeReference);
+      } else {
+        assert clazz.isNestMember();
+        registry.registerTypeReference(clazz.getNestHost());
+      }
+    }
+
+    // Trace the references to the inner and outer classes.
+    clazz
+        .getInnerClasses()
+        .forEach(
+            innerClassAttribute -> {
+              registry.registerNullableTypeReference(innerClassAttribute.getInner());
+              registry.registerNullableTypeReference(innerClassAttribute.getOuter());
+            });
+
+    // Trace the references from the enclosing method attribute.
+    EnclosingMethodAttribute attr = clazz.getEnclosingMethodAttribute();
+    registry.registerNullableTypeReference(attr.getEnclosingClass());
+    registry.registerNullableMethodReference(attr.getEnclosingMethod());
   }
 
   private void registerReferencesFromField(ProgramField field) {
-    // TODO(b/165783399): Trace the type of the field.
-    // TODO(b/165783399): Trace the references in the field annotations.
+    RepackagingUseRegistry registry = new RepackagingUseRegistry(appView, this, field);
+
+    // Trace the type of the field.
+    registry.registerTypeReference(field.getReference().getType());
+
+    // Trace the references in the field annotations.
+    new RepackagingAnnotationTracer(appView, registry).trace(field.getDefinition().annotations());
   }
 
   private void registerReferencesFromMethod(ProgramMethod method) {
-    // TODO(b/165783399): Trace the type references in the method signature.
-    // TODO(b/165783399): Trace the references in the method and method parameter annotations.
     DexEncodedMethod definition = method.getDefinition();
+    RepackagingUseRegistry registry = new RepackagingUseRegistry(appView, this, method);
+
+    // Trace the type references in the method signature.
+    definition.getProto().forEachType(registry::registerTypeReference);
+
+    // Trace the references in the method and method parameter annotations.
+    RepackagingAnnotationTracer annotationTracer =
+        new RepackagingAnnotationTracer(appView, registry);
+    annotationTracer.trace(definition.annotations());
+    annotationTracer.trace(definition.getParameterAnnotations());
+
+    // Trace the references from the code.
     if (definition.hasCode()) {
-      RepackagingUseRegistry registry = new RepackagingUseRegistry(appView, this, method);
       definition.getCode().registerCodeReferences(method, registry);
     }
   }
@@ -127,14 +170,8 @@
 
   static class Node {
 
-    private final DexDefinition definition;
-
     private final Set<Node> neighbors = Sets.newConcurrentHashSet();
 
-    private Node(DexDefinition definition) {
-      this.definition = definition;
-    }
-
     public void addNeighbor(Node neighbor) {
       neighbors.add(neighbor);
       neighbor.neighbors.add(this);
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
index 531e6c8..72810b0 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
@@ -13,8 +13,8 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.MemberResolutionResult;
+import com.android.tools.r8.graph.ProgramDefinition;
 import com.android.tools.r8.graph.ProgramMember;
-import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.SuccessfulMemberResolutionResult;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -23,13 +23,13 @@
 
   private final AppInfoWithLiveness appInfo;
   private final RepackagingConstraintGraph constraintGraph;
-  private final ProgramMethod context;
+  private final ProgramDefinition context;
   private final RepackagingConstraintGraph.Node node;
 
   public RepackagingUseRegistry(
       AppView<AppInfoWithLiveness> appView,
       RepackagingConstraintGraph constraintGraph,
-      ProgramMethod context) {
+      ProgramDefinition context) {
     super(appView.dexItemFactory());
     this.appInfo = appView.appInfo();
     this.constraintGraph = constraintGraph;
@@ -43,7 +43,7 @@
       return true;
     }
     if (accessFlags.isProtected()
-        && !appInfo.isSubtype(context.getHolderType(), referencedClass.getType())) {
+        && !appInfo.isSubtype(context.getContextType(), referencedClass.getType())) {
       return true;
     }
     return false;
@@ -55,13 +55,27 @@
       return true;
     }
     if (accessFlags.isProtected()
-        && !appInfo.isSubtype(context.getHolderType(), member.getHolderType())) {
+        && !appInfo.isSubtype(context.getContextType(), member.getHolderType())) {
       return true;
     }
     return false;
   }
 
-  private void registerMemberAccess(MemberResolutionResult<?, ?> resolutionResult) {
+  public void registerFieldAccess(DexField field) {
+    registerMemberAccess(appInfo.resolveField(field));
+  }
+
+  public void registerMethodReference(DexMethod method) {
+    registerMemberAccess(appInfo.unsafeResolveMethodDueToDexFormat(method));
+  }
+
+  public void registerNullableMethodReference(DexMethod method) {
+    if (method != null) {
+      registerMethodReference(method);
+    }
+  }
+
+  public void registerMemberAccess(MemberResolutionResult<?, ?> resolutionResult) {
     SuccessfulMemberResolutionResult<?, ?> successfulResolutionResult =
         resolutionResult.asSuccessfulMemberResolutionResult();
     if (successfulResolutionResult == null) {
@@ -92,7 +106,7 @@
       registerTypeAccess(type.toBaseType(appInfo.dexItemFactory()));
       return;
     }
-    if (type.isPrimitiveType()) {
+    if (type.isPrimitiveType() || type.isVoidType()) {
       return;
     }
     assert type.isClassType();
@@ -147,12 +161,12 @@
 
   @Override
   public void registerInstanceFieldRead(DexField field) {
-    registerMemberAccess(appInfo.resolveField(field));
+    registerFieldAccess(field);
   }
 
   @Override
   public void registerInstanceFieldWrite(DexField field) {
-    registerMemberAccess(appInfo.resolveField(field));
+    registerFieldAccess(field);
   }
 
   @Override
@@ -162,12 +176,12 @@
 
   @Override
   public void registerStaticFieldRead(DexField field) {
-    registerMemberAccess(appInfo.resolveField(field));
+    registerFieldAccess(field);
   }
 
   @Override
   public void registerStaticFieldWrite(DexField field) {
-    registerMemberAccess(appInfo.resolveField(field));
+    registerFieldAccess(field);
   }
 
   @Override
@@ -175,6 +189,12 @@
     registerTypeAccess(type);
   }
 
+  public void registerNullableTypeReference(DexType type) {
+    if (type != null) {
+      registerTypeReference(type);
+    }
+  }
+
   @Override
   public void registerInstanceOf(DexType type) {
     registerTypeAccess(type);
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 98881e9..69a42a4 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -261,6 +261,11 @@
     return addKeepRules("-keepattributes " + String.join(",", attributes));
   }
 
+  public T addKeepAttributeInnerClassesAndEnclosingMethod() {
+    return addKeepAttributes(
+        ProguardKeepAttributes.INNER_CLASSES, ProguardKeepAttributes.ENCLOSING_METHOD);
+  }
+
   public T addKeepAttributeLineNumberTable() {
     return addKeepAttributes(ProguardKeepAttributes.LINE_NUMBER_TABLE);
   }
@@ -273,6 +278,10 @@
     return addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS);
   }
 
+  public T addKeepRuntimeVisibleParameterAnnotations() {
+    return addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS);
+  }
+
   public T addKeepAllAttributes() {
     return addKeepAttributes("*");
   }
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateClassAnnotationTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateClassAnnotationTest.java
new file mode 100644
index 0000000..811d2ac
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateClassAnnotationTest.java
@@ -0,0 +1,101 @@
+// Copyright (c) 2020, 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.repackage;
+
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.FLATTEN_PACKAGE_HIERARCHY;
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.REPACKAGE_CLASSES;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.List;
+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 RepackageWithPackagePrivateClassAnnotationTest extends TestBase {
+
+  private static final String REPACKAGE_PACKAGE = "foo";
+
+  private final String flattenPackageHierarchyOrRepackageClasses;
+  private final TestParameters parameters;
+
+  @Parameters(name = "{1}, kind: {0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        ImmutableList.of(FLATTEN_PACKAGE_HIERARCHY, REPACKAGE_CLASSES),
+        getTestParameters().withAllRuntimesAndApiLevels().build());
+  }
+
+  public RepackageWithPackagePrivateClassAnnotationTest(
+      String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+    this.flattenPackageHierarchyOrRepackageClasses = flattenPackageHierarchyOrRepackageClasses;
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(TestClass.class)
+        .addKeepClassRules(NonPublicKeptAnnotation.class)
+        .addKeepRules(
+            "-" + flattenPackageHierarchyOrRepackageClasses + " \"" + REPACKAGE_PACKAGE + "\"")
+        .addKeepRuntimeVisibleAnnotations()
+        .addOptionsModification(
+            options -> {
+              assert !options.testing.enableExperimentalRepackaging;
+              options.testing.enableExperimentalRepackaging = true;
+            })
+        .enableInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(this::inspect)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("Hello world!");
+  }
+
+  private void inspect(CodeInspector inspector) {
+    ClassSubject classSubject = inspector.clazz(IneligibleForRepackaging.class);
+    assertThat(classSubject, isPresent());
+
+    // Verify that the class was not repackaged.
+    assertEquals(
+        IneligibleForRepackaging.class.getPackage().getName(),
+        classSubject.getDexProgramClass().getType().getPackageName());
+  }
+
+  public static class TestClass {
+
+    public static void main(String[] args) {
+      IneligibleForRepackaging.greet();
+    }
+  }
+
+  @Retention(RetentionPolicy.RUNTIME)
+  @Target({ElementType.TYPE})
+  @interface NonPublicKeptAnnotation {}
+
+  @NonPublicKeptAnnotation
+  public static class IneligibleForRepackaging {
+
+    @NeverInline
+    public static void greet() {
+      System.out.println("Hello world!");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateFieldAnnotationTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateFieldAnnotationTest.java
new file mode 100644
index 0000000..cd3eb8c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateFieldAnnotationTest.java
@@ -0,0 +1,104 @@
+// Copyright (c) 2020, 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.repackage;
+
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.FLATTEN_PACKAGE_HIERARCHY;
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.REPACKAGE_CLASSES;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverPropagateValue;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.List;
+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 RepackageWithPackagePrivateFieldAnnotationTest extends TestBase {
+
+  private static final String REPACKAGE_PACKAGE = "foo";
+
+  private final String flattenPackageHierarchyOrRepackageClasses;
+  private final TestParameters parameters;
+
+  @Parameters(name = "{1}, kind: {0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        ImmutableList.of(FLATTEN_PACKAGE_HIERARCHY, REPACKAGE_CLASSES),
+        getTestParameters().withAllRuntimesAndApiLevels().build());
+  }
+
+  public RepackageWithPackagePrivateFieldAnnotationTest(
+      String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+    this.flattenPackageHierarchyOrRepackageClasses = flattenPackageHierarchyOrRepackageClasses;
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(TestClass.class)
+        .addKeepClassRules(NonPublicKeptAnnotation.class)
+        .addKeepRules(
+            "-" + flattenPackageHierarchyOrRepackageClasses + " \"" + REPACKAGE_PACKAGE + "\"")
+        .addKeepRuntimeVisibleAnnotations()
+        .addOptionsModification(
+            options -> {
+              assert !options.testing.enableExperimentalRepackaging;
+              options.testing.enableExperimentalRepackaging = true;
+            })
+        .enableInliningAnnotations()
+        .enableMemberValuePropagationAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(this::inspect)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("Hello world!");
+  }
+
+  private void inspect(CodeInspector inspector) {
+    ClassSubject classSubject = inspector.clazz(IneligibleForRepackaging.class);
+    assertThat(classSubject, isPresent());
+
+    // Verify that the class was not repackaged.
+    assertEquals(
+        IneligibleForRepackaging.class.getPackage().getName(),
+        classSubject.getDexProgramClass().getType().getPackageName());
+  }
+
+  public static class TestClass {
+
+    public static void main(String[] args) {
+      IneligibleForRepackaging.greet();
+    }
+  }
+
+  @Retention(RetentionPolicy.RUNTIME)
+  @Target({ElementType.FIELD})
+  @interface NonPublicKeptAnnotation {}
+
+  public static class IneligibleForRepackaging {
+
+    @NeverPropagateValue @NonPublicKeptAnnotation private static String GREETING = "Hello world!";
+
+    @NeverInline
+    public static void greet() {
+      System.out.println(GREETING);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateFieldTypeTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateFieldTypeTest.java
new file mode 100644
index 0000000..7a83908
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateFieldTypeTest.java
@@ -0,0 +1,102 @@
+// Copyright (c) 2020, 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.repackage;
+
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.FLATTEN_PACKAGE_HIERARCHY;
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.REPACKAGE_CLASSES;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverPropagateValue;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+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 RepackageWithPackagePrivateFieldTypeTest extends TestBase {
+
+  private static final String REPACKAGE_PACKAGE = "foo";
+
+  private final String flattenPackageHierarchyOrRepackageClasses;
+  private final TestParameters parameters;
+
+  @Parameters(name = "{1}, kind: {0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        ImmutableList.of(FLATTEN_PACKAGE_HIERARCHY, REPACKAGE_CLASSES),
+        getTestParameters().withAllRuntimesAndApiLevels().build());
+  }
+
+  public RepackageWithPackagePrivateFieldTypeTest(
+      String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+    this.flattenPackageHierarchyOrRepackageClasses = flattenPackageHierarchyOrRepackageClasses;
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(TestClass.class)
+        .addKeepClassRules(NonPublicKeptClass.class)
+        .addKeepRules(
+            "-" + flattenPackageHierarchyOrRepackageClasses + " \"" + REPACKAGE_PACKAGE + "\"")
+        .addOptionsModification(
+            options -> {
+              assert !options.testing.enableExperimentalRepackaging;
+              options.testing.enableExperimentalRepackaging = true;
+            })
+        .enableInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(this::inspect)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("Hello world!");
+  }
+
+  private void inspect(CodeInspector inspector) {
+    ClassSubject classSubject = inspector.clazz(IneligibleForRepackaging.class);
+    assertThat(classSubject, isPresent());
+
+    // Verify that the class was not repackaged.
+    assertEquals(
+        IneligibleForRepackaging.class.getPackage().getName(),
+        classSubject.getDexProgramClass().getType().getPackageName());
+  }
+
+  public static class TestClass {
+
+    public static void main(String[] args) {
+      IneligibleForRepackaging.greet();
+    }
+  }
+
+  static class NonPublicKeptClass {}
+
+  public static class PublicSubClass extends NonPublicKeptClass {}
+
+  public static class IneligibleForRepackaging {
+
+    @NeverPropagateValue
+    private static NonPublicKeptClass FIELD =
+        System.currentTimeMillis() > 0 ? new PublicSubClass() : null;
+
+    @NeverInline
+    public static void greet() {
+      if (FIELD != null) {
+        System.out.println("Hello world!");
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateInnerClassTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateInnerClassTest.java
new file mode 100644
index 0000000..51f37b6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateInnerClassTest.java
@@ -0,0 +1,95 @@
+// Copyright (c) 2020, 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.repackage;
+
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.FLATTEN_PACKAGE_HIERARCHY;
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.REPACKAGE_CLASSES;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.repackage.RepackageWithPackagePrivateInnerClassTest.IneligibleForRepackaging.NonPublicKeptClass;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+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 RepackageWithPackagePrivateInnerClassTest extends TestBase {
+
+  private static final String REPACKAGE_PACKAGE = "foo";
+
+  private final String flattenPackageHierarchyOrRepackageClasses;
+  private final TestParameters parameters;
+
+  @Parameters(name = "{1}, kind: {0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        ImmutableList.of(FLATTEN_PACKAGE_HIERARCHY, REPACKAGE_CLASSES),
+        getTestParameters().withAllRuntimesAndApiLevels().build());
+  }
+
+  public RepackageWithPackagePrivateInnerClassTest(
+      String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+    this.flattenPackageHierarchyOrRepackageClasses = flattenPackageHierarchyOrRepackageClasses;
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(TestClass.class)
+        .addKeepClassRules(NonPublicKeptClass.class)
+        .addKeepRules(
+            "-" + flattenPackageHierarchyOrRepackageClasses + " \"" + REPACKAGE_PACKAGE + "\"")
+        .addKeepAttributeInnerClassesAndEnclosingMethod()
+        .addOptionsModification(
+            options -> {
+              assert !options.testing.enableExperimentalRepackaging;
+              options.testing.enableExperimentalRepackaging = true;
+            })
+        .enableInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(this::inspect)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("Hello world!");
+  }
+
+  private void inspect(CodeInspector inspector) {
+    ClassSubject classSubject = inspector.clazz(IneligibleForRepackaging.class);
+    assertThat(classSubject, isPresent());
+
+    // Verify that the class was not repackaged.
+    assertEquals(
+        IneligibleForRepackaging.class.getPackage().getName(),
+        classSubject.getDexProgramClass().getType().getPackageName());
+  }
+
+  public static class TestClass {
+
+    public static void main(String[] args) {
+      IneligibleForRepackaging.greet();
+    }
+  }
+
+  public static class IneligibleForRepackaging {
+
+    @NeverInline
+    public static void greet() {
+      System.out.println("Hello world!");
+    }
+
+    static class NonPublicKeptClass {}
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateInterfaceTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateInterfaceTest.java
new file mode 100644
index 0000000..01d3358
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateInterfaceTest.java
@@ -0,0 +1,93 @@
+// Copyright (c) 2020, 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.repackage;
+
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.FLATTEN_PACKAGE_HIERARCHY;
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.REPACKAGE_CLASSES;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+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 RepackageWithPackagePrivateInterfaceTest extends TestBase {
+
+  private static final String REPACKAGE_PACKAGE = "foo";
+
+  private final String flattenPackageHierarchyOrRepackageClasses;
+  private final TestParameters parameters;
+
+  @Parameters(name = "{1}, kind: {0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        ImmutableList.of(FLATTEN_PACKAGE_HIERARCHY, REPACKAGE_CLASSES),
+        getTestParameters().withAllRuntimesAndApiLevels().build());
+  }
+
+  public RepackageWithPackagePrivateInterfaceTest(
+      String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+    this.flattenPackageHierarchyOrRepackageClasses = flattenPackageHierarchyOrRepackageClasses;
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(TestClass.class)
+        .addKeepClassRules(NonPublicKeptInterface.class)
+        .addKeepRules(
+            "-" + flattenPackageHierarchyOrRepackageClasses + " \"" + REPACKAGE_PACKAGE + "\"")
+        .addOptionsModification(
+            options -> {
+              assert !options.testing.enableExperimentalRepackaging;
+              options.testing.enableExperimentalRepackaging = true;
+            })
+        .enableInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(this::inspect)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("Hello world!");
+  }
+
+  private void inspect(CodeInspector inspector) {
+    ClassSubject classSubject = inspector.clazz(IneligibleForRepackaging.class);
+    assertThat(classSubject, isPresent());
+
+    // Verify that the class was not repackaged.
+    assertEquals(
+        IneligibleForRepackaging.class.getPackage().getName(),
+        classSubject.getDexProgramClass().getType().getPackageName());
+  }
+
+  public static class TestClass {
+
+    public static void main(String[] args) {
+      IneligibleForRepackaging.greet();
+    }
+  }
+
+  interface NonPublicKeptInterface {}
+
+  public static class IneligibleForRepackaging implements NonPublicKeptInterface {
+
+    @NeverInline
+    public static void greet() {
+      System.out.println("Hello world!");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateMethodAnnotationTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateMethodAnnotationTest.java
new file mode 100644
index 0000000..535eb6b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateMethodAnnotationTest.java
@@ -0,0 +1,101 @@
+// Copyright (c) 2020, 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.repackage;
+
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.FLATTEN_PACKAGE_HIERARCHY;
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.REPACKAGE_CLASSES;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.List;
+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 RepackageWithPackagePrivateMethodAnnotationTest extends TestBase {
+
+  private static final String REPACKAGE_PACKAGE = "foo";
+
+  private final String flattenPackageHierarchyOrRepackageClasses;
+  private final TestParameters parameters;
+
+  @Parameters(name = "{1}, kind: {0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        ImmutableList.of(FLATTEN_PACKAGE_HIERARCHY, REPACKAGE_CLASSES),
+        getTestParameters().withAllRuntimesAndApiLevels().build());
+  }
+
+  public RepackageWithPackagePrivateMethodAnnotationTest(
+      String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+    this.flattenPackageHierarchyOrRepackageClasses = flattenPackageHierarchyOrRepackageClasses;
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(TestClass.class)
+        .addKeepClassRules(NonPublicKeptAnnotation.class)
+        .addKeepRules(
+            "-" + flattenPackageHierarchyOrRepackageClasses + " \"" + REPACKAGE_PACKAGE + "\"")
+        .addKeepRuntimeVisibleAnnotations()
+        .addOptionsModification(
+            options -> {
+              assert !options.testing.enableExperimentalRepackaging;
+              options.testing.enableExperimentalRepackaging = true;
+            })
+        .enableInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(this::inspect)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("Hello world!");
+  }
+
+  private void inspect(CodeInspector inspector) {
+    ClassSubject classSubject = inspector.clazz(IneligibleForRepackaging.class);
+    assertThat(classSubject, isPresent());
+
+    // Verify that the class was not repackaged.
+    assertEquals(
+        IneligibleForRepackaging.class.getPackage().getName(),
+        classSubject.getDexProgramClass().getType().getPackageName());
+  }
+
+  public static class TestClass {
+
+    public static void main(String[] args) {
+      IneligibleForRepackaging.greet();
+    }
+  }
+
+  @Retention(RetentionPolicy.RUNTIME)
+  @Target({ElementType.METHOD})
+  @interface NonPublicKeptAnnotation {}
+
+  public static class IneligibleForRepackaging {
+
+    @NonPublicKeptAnnotation
+    @NeverInline
+    public static void greet() {
+      System.out.println("Hello world!");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateMethodParameterAnnotationTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateMethodParameterAnnotationTest.java
new file mode 100644
index 0000000..ece0498
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateMethodParameterAnnotationTest.java
@@ -0,0 +1,100 @@
+// Copyright (c) 2020, 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.repackage;
+
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.FLATTEN_PACKAGE_HIERARCHY;
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.REPACKAGE_CLASSES;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.List;
+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 RepackageWithPackagePrivateMethodParameterAnnotationTest extends TestBase {
+
+  private static final String REPACKAGE_PACKAGE = "foo";
+
+  private final String flattenPackageHierarchyOrRepackageClasses;
+  private final TestParameters parameters;
+
+  @Parameters(name = "{1}, kind: {0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        ImmutableList.of(FLATTEN_PACKAGE_HIERARCHY, REPACKAGE_CLASSES),
+        getTestParameters().withAllRuntimesAndApiLevels().build());
+  }
+
+  public RepackageWithPackagePrivateMethodParameterAnnotationTest(
+      String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+    this.flattenPackageHierarchyOrRepackageClasses = flattenPackageHierarchyOrRepackageClasses;
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(TestClass.class)
+        .addKeepClassRules(NonPublicKeptAnnotation.class)
+        .addKeepRules(
+            "-" + flattenPackageHierarchyOrRepackageClasses + " \"" + REPACKAGE_PACKAGE + "\"")
+        .addKeepRuntimeVisibleParameterAnnotations()
+        .addOptionsModification(
+            options -> {
+              assert !options.testing.enableExperimentalRepackaging;
+              options.testing.enableExperimentalRepackaging = true;
+            })
+        .enableInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(this::inspect)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("Hello world!");
+  }
+
+  private void inspect(CodeInspector inspector) {
+    ClassSubject classSubject = inspector.clazz(IneligibleForRepackaging.class);
+    assertThat(classSubject, isPresent());
+
+    // Verify that the class was not repackaged.
+    assertEquals(
+        IneligibleForRepackaging.class.getPackage().getName(),
+        classSubject.getDexProgramClass().getType().getPackageName());
+  }
+
+  public static class TestClass {
+
+    public static void main(String[] args) {
+      IneligibleForRepackaging.greet("Hello world!");
+    }
+  }
+
+  @Retention(RetentionPolicy.RUNTIME)
+  @Target({ElementType.PARAMETER})
+  @interface NonPublicKeptAnnotation {}
+
+  public static class IneligibleForRepackaging {
+
+    @NeverInline
+    public static void greet(@NonPublicKeptAnnotation String greeting) {
+      System.out.println(greeting);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateSuperClassTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateSuperClassTest.java
new file mode 100644
index 0000000..43c3361
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateSuperClassTest.java
@@ -0,0 +1,93 @@
+// Copyright (c) 2020, 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.repackage;
+
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.FLATTEN_PACKAGE_HIERARCHY;
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.REPACKAGE_CLASSES;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+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 RepackageWithPackagePrivateSuperClassTest extends TestBase {
+
+  private static final String REPACKAGE_PACKAGE = "foo";
+
+  private final String flattenPackageHierarchyOrRepackageClasses;
+  private final TestParameters parameters;
+
+  @Parameters(name = "{1}, kind: {0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        ImmutableList.of(FLATTEN_PACKAGE_HIERARCHY, REPACKAGE_CLASSES),
+        getTestParameters().withAllRuntimesAndApiLevels().build());
+  }
+
+  public RepackageWithPackagePrivateSuperClassTest(
+      String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+    this.flattenPackageHierarchyOrRepackageClasses = flattenPackageHierarchyOrRepackageClasses;
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(TestClass.class)
+        .addKeepClassRules(NonPublicKeptClass.class)
+        .addKeepRules(
+            "-" + flattenPackageHierarchyOrRepackageClasses + " \"" + REPACKAGE_PACKAGE + "\"")
+        .addOptionsModification(
+            options -> {
+              assert !options.testing.enableExperimentalRepackaging;
+              options.testing.enableExperimentalRepackaging = true;
+            })
+        .enableInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(this::inspect)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("Hello world!");
+  }
+
+  private void inspect(CodeInspector inspector) {
+    ClassSubject classSubject = inspector.clazz(IneligibleForRepackaging.class);
+    assertThat(classSubject, isPresent());
+
+    // Verify that the class was not repackaged.
+    assertEquals(
+        IneligibleForRepackaging.class.getPackage().getName(),
+        classSubject.getDexProgramClass().getType().getPackageName());
+  }
+
+  public static class TestClass {
+
+    public static void main(String[] args) {
+      IneligibleForRepackaging.greet();
+    }
+  }
+
+  static class NonPublicKeptClass {}
+
+  public static class IneligibleForRepackaging extends NonPublicKeptClass {
+
+    @NeverInline
+    public static void greet() {
+      System.out.println("Hello world!");
+    }
+  }
+}