Merge remote-tracking branch 'aosp/upstream-master'

* aosp/upstream-master:
  Add ServiceProcessor to MainProcessor

Bug: 318750023
Test: m javac-check
Change-Id: Ie073f84085384b0cbfece22ad857bfe8eaa4e96a
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index d38d89d..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-build
-target
-dist
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..3608320
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,80 @@
+// Copyright (C) 2014 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["external_jarjar_license"],
+}
+
+// Added automatically by a large-scale-change
+//
+// large-scale-change included anything that looked like it might be a license
+// text as a license_text. e.g. LICENSE, NOTICE, COPYING etc.
+//
+// Please consider removing redundant or irrelevant files from 'license_text:'.
+// See: http://go/android-license-faq
+license {
+    name: "external_jarjar_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+    ],
+    license_text: [
+        "COPYING",
+        "LICENSE.txt",
+        "NOTICE",
+    ],
+}
+
+java_library_host {
+    name: "jarjar",
+    manifest: "manifest.txt",
+
+    srcs: [
+        "src/android/**/*.java",
+        "src/main/**/*.java",
+    ],
+    java_resource_dirs: ["res"],
+
+    static_libs: [
+        "ow2-asm",
+        "ow2-asm-commons",
+    ],
+
+    libs: [
+        "jarjar-maven-plugin-api",
+        "jarjar-apache-ant",
+    ],
+}
+
+java_test_host {
+    name: "jarjar-tests",
+    srcs: ["src/test/**/*.java"],
+    static_libs: [
+        "jarjar",
+        "junit",
+    ],
+    java_resource_dirs: ["src/test"],
+}
+
+//#################################################
+
+java_import_host {
+    name: "jarjar-maven-plugin-api",
+    jars: ["lib/maven-plugin-api.jar"],
+}
+
+java_import_host {
+    name: "jarjar-apache-ant",
+    jars: ["lib/apache-ant-1.9.4.jar"],
+}
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index c980350..0000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,23 +0,0 @@
-# How to Contribute
-
-We'd love to accept your patches and contributions to this project. There are
-just a few small guidelines you need to follow.
-
-## Contributor License Agreement
-
-Contributions to this project must be accompanied by a Contributor License
-Agreement. You (or your employer) retain the copyright to your contribution;
-this simply gives us permission to use and redistribute your contributions as
-part of the project. Head over to <https://cla.developers.google.com/> to see
-your current agreements on file or to sign a new one.
-
-You generally only need to submit a CLA once, so if you've already submitted one
-(even if it was for a different project), you probably don't need to do it
-again.
-
-## Code reviews
-
-All submissions, including submissions by project members, require review. We
-use GitHub pull requests for this purpose. Consult
-[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
-information on using pull requests.
diff --git a/LICENSE b/COPYING
similarity index 100%
copy from LICENSE
copy to COPYING
diff --git a/LICENSE b/LICENSE
new file mode 120000
index 0000000..85de3d4
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1 @@
+LICENSE.txt
\ No newline at end of file
diff --git a/LICENSE b/LICENSE.txt
similarity index 100%
copy from LICENSE
copy to LICENSE.txt
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..7f221e5
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,14 @@
+name: "jarjar"
+description:
+    "Utility that makes it easy to repackage Java libraries and embed them into "
+    "your own distribution"
+
+third_party {
+  url {
+    type: GIT
+    value: "https://github.com/google/jarjar.git"
+  }
+  version: "8ce1d0abd195c24a75d9ddcb0ea31b1d1b62aaa6"
+  last_upgrade_date { year: 2024 month: 1 day: 12 }
+  license_type: NOTICE
+}
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/LICENSE b/NOTICE
similarity index 100%
rename from LICENSE
rename to NOTICE
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..87a5dbe
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1 @@
+include platform/libcore:/OWNERS
diff --git a/README.md b/README.md
deleted file mode 100644
index bd59829..0000000
--- a/README.md
+++ /dev/null
@@ -1,20 +0,0 @@
-This is not an officially supported Google product.
-
-*Embedding Java libraries since 2004*
-
-**Jar Jar Links** is a utility that makes it easy to repackage Java libraries
-and embed them into your own distribution. This is useful for two reasons:
-
-*   You can easily ship a single jar file with no external dependencies.
-
-*   You can avoid problems where your library depends on a specific version of a
-    library, which may conflict with the dependencies of another library.
-
-## How does it work?
-
-Jar Jar Links includes an Ant task that extends the built-in jar task. The
-normal `zipfileset` element is used to embed jar files. A new rule element is
-added which uses wildcards patterns to rename the embedded class files. Bytecode
-transformation (via ASM) is used to change references to the renamed classes,
-and special handling is provided for moving resource files and transforming
-string literals.
diff --git a/README.version b/README.version
new file mode 100644
index 0000000..9a1d0c4
--- /dev/null
+++ b/README.version
@@ -0,0 +1,3 @@
+URL: https://code.google.com/p/jarjar/downloads/detail?name=jarjar-src-1.4.zip&can=2&q=
+Version: 1.4
+BugComponent: 33621
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..1a061f2
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+apply plugin: 'java'
+
+sourceSets {
+	main {
+		java.srcDirs = ['src/main']
+		resources.srcDirs = ['res']
+	}
+}
+jar.manifest.from file('manifest.txt')
+
+dependencies {
+	compile files('lib/apache-ant-1.9.4.jar')
+	compile files('lib/asm-4.0.jar')
+	compile files('lib/asm-commons-4.0.jar')
+	compile files('lib/maven-plugin-api.jar')
+}
diff --git a/lib/apache-ant-1.9.4.jar b/lib/apache-ant-1.9.4.jar
new file mode 100644
index 0000000..24641e7
--- /dev/null
+++ b/lib/apache-ant-1.9.4.jar
Binary files differ
diff --git a/lib/junit-4.8.1.jar b/lib/junit-4.8.1.jar
deleted file mode 100644
index f28b4ef..0000000
--- a/lib/junit-4.8.1.jar
+++ /dev/null
Binary files differ
diff --git a/manifest.txt b/manifest.txt
new file mode 100644
index 0000000..63c68e0
--- /dev/null
+++ b/manifest.txt
@@ -0,0 +1 @@
+Main-Class: com.tonicsystems.jarjar.Main
diff --git a/pom.xml b/pom.xml
deleted file mode 100644
index 72bf3f1..0000000
--- a/pom.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-
-  <parent>
-    <groupId>org.sonatype.oss</groupId>
-    <artifactId>oss-parent</artifactId>
-    <version>7</version>
-  </parent>
-
-  <groupId>com.googlecode.jarjar</groupId>
-  <artifactId>jarjar</artifactId>
-  <packaging>jar</packaging>
-  <version>1.3</version>
-  <name>Jar Jar Links</name>
-  <description>Jar Jar Links is a utility that makes it easy to repackage Java libraries and embed them into your own distribution.</description>
-  <url>http://jarjar.googlecode.com/</url>
-  <licenses>
-    <license>
-      <name>Apache License version 2.0</name>
-      <url>http://www.apache.org/licenses/LICENSE-2.0</url>
-      <distribution>repo</distribution>
-    </license>
-  </licenses>
-  <scm>
-    <connection>scm:svn:http://jarjar.googlecode.com/svn/trunk/</connection>
-    <url>http://code.google.com/p/jarjar/source/checkout</url>
-  </scm>
-  <issueManagement>
-    <system>Google Code</system>
-    <url>http://code.google.com/p/jarjar/issues</url>
-  </issueManagement>
-  <dependencies></dependencies>
-  <developers>
-    <developer>
-      <id>cnokleberg</id>
-      <name>Chris Nokleberg</name>
-    </developer>
-    <developer>
-      <id>aglenning</id>
-      <name>A. Glenning</name>
-    </developer>
-  </developers>
-</project>
diff --git a/src/main/com/tonicsystems/jarjar/help.txt b/res/com/tonicsystems/jarjar/help.txt
similarity index 92%
rename from src/main/com/tonicsystems/jarjar/help.txt
rename to res/com/tonicsystems/jarjar/help.txt
index 8410909..6f14db6 100644
--- a/src/main/com/tonicsystems/jarjar/help.txt
+++ b/res/com/tonicsystems/jarjar/help.txt
@@ -49,6 +49,7 @@
     rule <pattern> <result>
     zap <pattern>
     keep <pattern>
+    strip-annotations <annotation>
 
   The standard rule ("rule") is used to rename classes. All references
   to the renamed classes will also be updated. If a class name is
@@ -73,3 +74,6 @@
   via dependency analysis are discarded when writing the output
   jar. This is the last step in the process, after renaming and zapping.
 
+  The "strip-annotations" rule will remove all the references to a certain
+  annotation from a jar file. As no pattern matching is performed, the
+  annotations have to be provided as one per line.
diff --git a/src/Jarjar.iml b/src/Jarjar.iml
new file mode 100644
index 0000000..70f1faf
--- /dev/null
+++ b/src/Jarjar.iml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/main" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="lib" level="project" />
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MAVEN_REPOSITORY$/org/apache/ant/ant/1.7.1/ant-1.7.1.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+  </component>
+</module>
+
diff --git a/src/android/com/android/jarjar/RemoveAndroidCompatAnnotationsJarTransformer.java b/src/android/com/android/jarjar/RemoveAndroidCompatAnnotationsJarTransformer.java
new file mode 100644
index 0000000..04942b1
--- /dev/null
+++ b/src/android/com/android/jarjar/RemoveAndroidCompatAnnotationsJarTransformer.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jarjar;
+
+import com.tonicsystems.jarjar.util.JarTransformer;
+import java.util.Set;
+import java.util.function.Supplier;
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.commons.Remapper;
+
+/** A transformer that removes annotations from repackaged classes. */
+public final class RemoveAndroidCompatAnnotationsJarTransformer extends JarTransformer {
+
+  private static int ASM_VERSION = Opcodes.ASM9;
+
+  private static final Set<String> REMOVE_ANNOTATIONS =
+      Set.of("Landroid/compat/annotation/UnsupportedAppUsage;");
+
+  private final Remapper remapper;
+
+  public RemoveAndroidCompatAnnotationsJarTransformer(Remapper remapper) {
+    this.remapper = remapper;
+  }
+
+  protected ClassVisitor transform(ClassVisitor classVisitor) {
+    return new AnnotationRemover(classVisitor);
+  }
+
+  private class AnnotationRemover extends ClassVisitor {
+
+    private boolean isClassRemapped;
+
+    AnnotationRemover(ClassVisitor cv) {
+      super(ASM_VERSION, cv);
+    }
+
+    @Override
+    public void visit(
+        int version,
+        int access,
+        String name,
+        String signature,
+        String superName,
+        String[] interfaces) {
+      String newName = remapper.map(name);
+      // On every new class header visit, remember whether the class is repackaged.
+      isClassRemapped = newName != null && !newName.equals(name);
+      super.visit(version, access, name, signature, superName, interfaces);
+    }
+
+    @Override
+    public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
+      return visitAnnotationCommon(descriptor, () -> super.visitAnnotation(descriptor, visible));
+    }
+
+    @Override
+    public FieldVisitor visitField(
+        int access, String name, String descriptor, String signature, Object value) {
+      FieldVisitor superVisitor = super.visitField(access, name, descriptor, signature, value);
+      return new FieldVisitor(ASM_VERSION, superVisitor) {
+        @Override
+        public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
+          return visitAnnotationCommon(
+              descriptor, () -> super.visitAnnotation(descriptor, visible));
+        }
+      };
+    }
+
+    @Override
+    public MethodVisitor visitMethod(
+        int access, String name, String descriptor, String signature, String[] exceptions) {
+      MethodVisitor superVisitor =
+          super.visitMethod(access, name, descriptor, signature, exceptions);
+      return new MethodVisitor(ASM_VERSION, superVisitor) {
+        @Override
+        public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
+          return visitAnnotationCommon(
+              descriptor, () -> super.visitAnnotation(descriptor, visible));
+        }
+      };
+    }
+
+    /**
+     * Create an {@link AnnotationVisitor} that removes any annotations from {@link
+     * #REMOVE_ANNOTATIONS} if the class is being repackaged.
+     *
+     * <p>For the annotations to be dropped correctly, do not visit the annotation beforehand,
+     * provide a supplier instead.
+     */
+    private AnnotationVisitor visitAnnotationCommon(
+        String annotation, Supplier<AnnotationVisitor> defaultVisitorSupplier) {
+      if (isClassRemapped && REMOVE_ANNOTATIONS.contains(annotation)) {
+        return null;
+      }
+      // Only get() the default AnnotationVisitor if the annotation is to be included.
+      // Invoking super.visitAnnotation(descriptor, visible) causes the annotation to be
+      // included in the output even if the resulting AnnotationVisitor is not returned or
+      // used.
+      return defaultVisitorSupplier.get();
+    }
+  }
+}
diff --git a/src/android/com/android/jarjar/StripAnnotation.java b/src/android/com/android/jarjar/StripAnnotation.java
new file mode 100644
index 0000000..60fc965
--- /dev/null
+++ b/src/android/com/android/jarjar/StripAnnotation.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jarjar;
+
+import com.tonicsystems.jarjar.PatternElement;
+
+/** Configuration element for stripping annotations in a jar file. */
+public class StripAnnotation extends PatternElement {}
diff --git a/src/android/com/android/jarjar/StripAnnotationsJarTransformer.java b/src/android/com/android/jarjar/StripAnnotationsJarTransformer.java
new file mode 100644
index 0000000..aa71715
--- /dev/null
+++ b/src/android/com/android/jarjar/StripAnnotationsJarTransformer.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jarjar;
+
+import com.tonicsystems.jarjar.util.JarTransformer;
+import java.util.List;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/** A transformer that strips annotations from all classes based on custom rules. */
+public final class StripAnnotationsJarTransformer extends JarTransformer {
+
+  private static int ASM_VERSION = Opcodes.ASM9;
+
+  private final List<String> stripAnnotationList;
+
+  public StripAnnotationsJarTransformer(List<StripAnnotation> stripAnnotationList) {
+    this.stripAnnotationList = getAnnotationList(stripAnnotationList);
+  }
+
+  private static List<String> getAnnotationList(List<StripAnnotation> stripAnnotationList) {
+    return stripAnnotationList.stream().map(el -> getClassName(el)).collect(Collectors.toList());
+  }
+
+  private static String getClassName(StripAnnotation element) {
+    return "L" + element.getPattern().replace('.', '/') + ";";
+  }
+
+  @Override
+  protected ClassVisitor transform(ClassVisitor classVisitor) {
+    return new AnnotationRemover(classVisitor);
+  }
+
+  private class AnnotationRemover extends ClassVisitor {
+
+    AnnotationRemover(ClassVisitor cv) {
+      super(ASM_VERSION, cv);
+    }
+
+    @Override
+    public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
+      return visitAnnotationCommon(descriptor, () -> super.visitAnnotation(descriptor, visible));
+    }
+
+    @Override
+    public FieldVisitor visitField(
+        int access, String name, String descriptor, String signature, Object value) {
+      FieldVisitor superVisitor = super.visitField(access, name, descriptor, signature, value);
+      return new FieldVisitor(ASM_VERSION, superVisitor) {
+        @Override
+        public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
+          return visitAnnotationCommon(
+              descriptor, () -> super.visitAnnotation(descriptor, visible));
+        }
+      };
+    }
+
+    @Override
+    public MethodVisitor visitMethod(
+        int access, String name, String descriptor, String signature, String[] exceptions) {
+      MethodVisitor superVisitor =
+          super.visitMethod(access, name, descriptor, signature, exceptions);
+      return new MethodVisitor(ASM_VERSION, superVisitor) {
+        @Override
+        public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
+          return visitAnnotationCommon(
+              descriptor, () -> super.visitAnnotation(descriptor, visible));
+        }
+
+        @Override
+        public AnnotationVisitor visitParameterAnnotation(
+            int parameter, String descriptor, boolean visible) {
+          return visitAnnotationCommon(
+              descriptor, () -> super.visitParameterAnnotation(parameter, descriptor, visible));
+        }
+      };
+    }
+
+    /**
+     * Create an {@link AnnotationVisitor} that removes any annotations from {@link
+     * #stripAnnotationList}.
+     */
+    private AnnotationVisitor visitAnnotationCommon(
+        String annotation, Supplier<AnnotationVisitor> defaultVisitorSupplier) {
+      if (stripAnnotationList.contains(annotation)) {
+        return null;
+      }
+      // Only get() the default AnnotationVisitor if the annotation is to be included.
+      // Invoking super.visitAnnotation(descriptor, visible) causes the annotation to be
+      // included in the output even if the resulting AnnotationVisitor is not returned or
+      // used.
+      return defaultVisitorSupplier.get();
+    }
+  }
+}
diff --git a/src/main/com/tonicsystems/jarjar/Main.java b/src/main/com/tonicsystems/jarjar/Main.java
index afa9a1e..aa78836 100644
--- a/src/main/com/tonicsystems/jarjar/Main.java
+++ b/src/main/com/tonicsystems/jarjar/Main.java
@@ -53,7 +53,6 @@
     return sb.toString();
   }
 
-
   public static void main(String[] args) throws Exception {
     MainUtil.runMain(new Main(), args, "help");
   }
@@ -99,7 +98,11 @@
     List<PatternElement> rules = RulesFileParser.parse(rulesFile);
     boolean verbose = Boolean.getBoolean("verbose");
     boolean skipManifest = Boolean.getBoolean("skipManifest");
-    MainProcessor proc = new MainProcessor(rules, verbose, skipManifest);
+    // ANDROID-BEGIN: b/146418363 Add an Android-specific transformer to strip compat annotation
+    boolean removeAndroidCompatAnnotations = Boolean.getBoolean("removeAndroidCompatAnnotations");
+    MainProcessor proc =
+        new MainProcessor(rules, verbose, skipManifest, removeAndroidCompatAnnotations);
+    // ANDROID-END: b/146418363 Add an Android-specific transformer to strip compat annotation
     StandaloneJarProcessor.run(inJar, outJar, proc);
     proc.strip(outJar);
   }
diff --git a/src/main/com/tonicsystems/jarjar/MainProcessor.java b/src/main/com/tonicsystems/jarjar/MainProcessor.java
index 2e2a7be..5839719 100644
--- a/src/main/com/tonicsystems/jarjar/MainProcessor.java
+++ b/src/main/com/tonicsystems/jarjar/MainProcessor.java
@@ -16,6 +16,9 @@
 
 package com.tonicsystems.jarjar;
 
+import com.android.jarjar.RemoveAndroidCompatAnnotationsJarTransformer;
+import com.android.jarjar.StripAnnotation;
+import com.android.jarjar.StripAnnotationsJarTransformer;
 import com.tonicsystems.jarjar.util.EntryStruct;
 import com.tonicsystems.jarjar.util.JarProcessor;
 import com.tonicsystems.jarjar.util.JarProcessorChain;
@@ -37,11 +40,24 @@
   private final KeepProcessor kp;
   private final Map<String, String> renames = new HashMap<>();
 
+  // ANDROID-BEGIN: b/146418363 Add an Android-specific transformer to strip compat annotation
   public MainProcessor(List<PatternElement> patterns, boolean verbose, boolean skipManifest) {
+    this(patterns, verbose, skipManifest, false /* removeAndroidCompatAnnotations */);
+  }
+
+  public MainProcessor(
+      List<PatternElement> patterns,
+      boolean verbose,
+      boolean skipManifest,
+      boolean removeAndroidCompatAnnotations) {
+    // ANDROID-END: b/146418363 Add an Android-specific transformer to strip compat annotation
     this.verbose = verbose;
     List<Zap> zapList = new ArrayList<>();
     List<Rule> ruleList = new ArrayList<>();
     List<Keep> keepList = new ArrayList<>();
+    // ANDROID-BEGIN: b/222743634 Strip annotations from system module stubs
+    List<StripAnnotation> stripAnnotationList = new ArrayList<StripAnnotation>();
+    // ANDROID-END: b/222743634 Strip annotations from system module stubs
     for (PatternElement pattern : patterns) {
       if (pattern instanceof Zap) {
         zapList.add((Zap) pattern);
@@ -49,7 +65,11 @@
         ruleList.add((Rule) pattern);
       } else if (pattern instanceof Keep) {
         keepList.add((Keep) pattern);
+      // ANDROID-BEGIN: b/222743634 Strip annotations from system module stubs
+      } else if (pattern instanceof StripAnnotation) {
+        stripAnnotationList.add((StripAnnotation) pattern);
       }
+      // ANDROID-END: b/222743634 Strip annotations from system module stubs
     }
 
     PackageRemapper pr = new PackageRemapper(ruleList, verbose);
@@ -63,6 +83,15 @@
       processors.add(kp);
     }
     processors.add(new ZapProcessor(zapList));
+    // ANDROID-BEGIN: b/146418363 Add an Android-specific transformer to strip compat annotation
+    if (removeAndroidCompatAnnotations)
+      processors.add(new RemoveAndroidCompatAnnotationsJarTransformer(pr));
+    // ANDROID-END: b/146418363 Add an Android-specific transformer to strip compat annotation
+    // ANDROID-BEGIN: b/222743634 Strip annotations from system module stubs
+    if (!stripAnnotationList.isEmpty()) {
+      processors.add(new StripAnnotationsJarTransformer(stripAnnotationList));
+    }
+    // ANDROID-END: b/222743634 Strip annotations from system module stubs
     processors.add(
         new JarTransformerChain(
             new RemappingClassTransformer[] {new RemappingClassTransformer(pr)}));
diff --git a/src/main/com/tonicsystems/jarjar/PackageRemapper.java b/src/main/com/tonicsystems/jarjar/PackageRemapper.java
index 22f4dce..7533082 100644
--- a/src/main/com/tonicsystems/jarjar/PackageRemapper.java
+++ b/src/main/com/tonicsystems/jarjar/PackageRemapper.java
@@ -28,7 +28,7 @@
   private static final Pattern ARRAY_FOR_NAME_PATTERN =
       Pattern.compile("\\[L[\\p{javaJavaIdentifierPart}\\.]+?;");
 
-  private final List<Wildcard> wildcards;
+  private final WildcardTrie wildcards;
   private final Map<String, String> typeCache = new HashMap<>();
   private final Map<String, String> pathCache = new HashMap<>();
   private final Map<Object, String> valueCache = new HashMap<>();
@@ -36,7 +36,7 @@
 
   public PackageRemapper(List<Rule> ruleList, boolean verbose) {
     this.verbose = verbose;
-    wildcards = PatternElement.createWildcards(ruleList);
+    wildcards = new WildcardTrie(PatternElement.createWildcards(ruleList));
   }
 
   // also used by KeepProcessor
@@ -128,7 +128,7 @@
   }
 
   private String replaceHelper(String value) {
-    for (Wildcard wildcard : wildcards) {
+    for (Wildcard wildcard : wildcards.getPossibleMatches(value)) {
       String test = wildcard.replace(value);
       if (test != null) {
         return test;
diff --git a/src/main/com/tonicsystems/jarjar/PatternElement.java b/src/main/com/tonicsystems/jarjar/PatternElement.java
index eeae88f..399928d 100644
--- a/src/main/com/tonicsystems/jarjar/PatternElement.java
+++ b/src/main/com/tonicsystems/jarjar/PatternElement.java
@@ -32,13 +32,15 @@
 
   static List<Wildcard> createWildcards(List<? extends PatternElement> patterns) {
     List<Wildcard> wildcards = new ArrayList<>();
+    int ruleIndex = 0;
     for (PatternElement pattern : patterns) {
       String result = (pattern instanceof Rule) ? ((Rule) pattern).getResult() : "";
       String expr = pattern.getPattern();
       if (expr.indexOf('/') >= 0) {
         throw new IllegalArgumentException("Patterns cannot contain slashes");
       }
-      wildcards.add(new Wildcard(expr.replace('.', '/'), result));
+      wildcards.add(new Wildcard(expr.replace('.', '/'), result, ruleIndex));
+      ruleIndex++;
     }
     return wildcards;
   }
diff --git a/src/main/com/tonicsystems/jarjar/RulesFileParser.java b/src/main/com/tonicsystems/jarjar/RulesFileParser.java
index 8364aaf..4297ee7 100644
--- a/src/main/com/tonicsystems/jarjar/RulesFileParser.java
+++ b/src/main/com/tonicsystems/jarjar/RulesFileParser.java
@@ -18,6 +18,7 @@
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 
+import com.android.jarjar.StripAnnotation;
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.IOException;
@@ -70,6 +71,11 @@
           case "keep":
             element = new Keep();
             break;
+          // ANDROID-BEGIN: b/222743634 Strip annotations from system module stubs
+          case "strip-annotation":
+            element = new StripAnnotation();
+            break;
+          // ANDROID-END: b/222743634 Strip annotations from system module stubs
           default:
             error(c, parts);
         }
diff --git a/src/main/com/tonicsystems/jarjar/Wildcard.java b/src/main/com/tonicsystems/jarjar/Wildcard.java
index d32e2a4..c5d0c20 100644
--- a/src/main/com/tonicsystems/jarjar/Wildcard.java
+++ b/src/main/com/tonicsystems/jarjar/Wildcard.java
@@ -25,14 +25,19 @@
   private static final Pattern DSTAR = Pattern.compile("\\*\\*");
   private static final Pattern STAR = Pattern.compile("\\*");
   private static final Pattern ESTAR = Pattern.compile("\\+\\??\\)\\Z");
+  private static final Pattern DOLLAR = Pattern.compile("\\$");
+  // Apart from stars and dollar signs, wildcards are plain-text full matches
+  private static final Pattern PLAIN_TEXT_PREFIX_PATTERN = Pattern.compile("^[^*$]*");
 
   private final Pattern pattern;
+  private final String plainTextPrefix;
+  private final int ruleIndex;
   private final int count;
   private final ArrayList<Object> parts = new ArrayList<>(16); // kept for debugging
   private final String[] strings;
   private final int[] refs;
 
-  public Wildcard(String pattern, String result) {
+  public Wildcard(String pattern, String result, int ruleIndex) {
     if (pattern.equals("**")) {
       throw new IllegalArgumentException("'**' is not a valid pattern");
     }
@@ -47,6 +52,14 @@
     regex = replaceAllLiteral(DSTAR, regex, "(.+?)");
     regex = replaceAllLiteral(STAR, regex, "([^/]+)");
     regex = replaceAllLiteral(ESTAR, regex, "*)");
+    regex = replaceAllLiteral(DOLLAR, regex, "\\$");
+    Matcher prefixMatcher = PLAIN_TEXT_PREFIX_PATTERN.matcher(pattern);
+    // prefixMatcher will always match, but may match an empty string
+    if (!prefixMatcher.find()) {
+      throw new IllegalArgumentException(PLAIN_TEXT_PREFIX_PATTERN + " not found in " + pattern);
+    }
+    this.plainTextPrefix = prefixMatcher.group();
+    this.ruleIndex = ruleIndex;
     this.pattern = Pattern.compile("\\A" + regex + "\\Z");
     this.count = this.pattern.matcher("foo").groupCount();
 
@@ -107,6 +120,14 @@
     // System.err.println(this);
   }
 
+  public String getPlainTextPrefix() {
+    return plainTextPrefix;
+  }
+
+  public int getRuleIndex() {
+    return ruleIndex;
+  }
+
   public boolean matches(String value) {
     return getMatcher(value) != null;
   }
@@ -138,6 +159,10 @@
     if (expr.endsWith("package-info")) {
       expr = expr.substring(0, expr.length() - "package-info".length());
     }
+    // Android-changed: also include module-info
+    if (expr.endsWith("module-info")) {
+      expr = expr.substring(0, expr.length() - "module-info".length());
+    }
     for (int i = 0, len = expr.length(); i < len; i++) {
       char c = expr.charAt(i);
       if (extra.indexOf(c) >= 0) {
diff --git a/src/main/com/tonicsystems/jarjar/WildcardTrie.java b/src/main/com/tonicsystems/jarjar/WildcardTrie.java
new file mode 100644
index 0000000..c1e9b2e
--- /dev/null
+++ b/src/main/com/tonicsystems/jarjar/WildcardTrie.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.tonicsystems.jarjar;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.TreeMap;
+
+/**
+ * A prefix trie of {@link Wildcard}, where the prefix is obtained from {@link
+ * Wildcard#getPlainTextPrefix()}.
+ *
+ * <p>This allows quick lookup of applicable wildcards in the common case where wildcards have a
+ * non-empty plain-text prefix.
+ */
+public class WildcardTrie {
+  private final TreeMap<String, WildcardTrie> subTries = new TreeMap<>();
+  private final List<Wildcard> wildcards = new ArrayList<>();
+  private final String prefix;
+
+  public WildcardTrie(List<Wildcard> wildcards) {
+    this("");
+    final ArrayList<Wildcard> lst = new ArrayList<>(wildcards);
+    // Sort values to ensure that wildcards that prefix others are added first
+    lst.sort(Comparator.comparing(Wildcard::getPlainTextPrefix));
+    for (Wildcard w : lst) {
+      final String prefix = w.getPlainTextPrefix();
+      final WildcardTrie prefixTrie = findSubTrieWhichPrefixes(prefix, this);
+      if (prefixTrie.prefix.equals(prefix)) {
+        prefixTrie.wildcards.add(w);
+      } else {
+        final WildcardTrie newTrie = new WildcardTrie(prefix);
+        newTrie.wildcards.add(w);
+        prefixTrie.subTries.put(prefix, newTrie);
+      }
+    }
+  }
+
+  private WildcardTrie(String prefix) {
+    this.prefix = prefix;
+  }
+
+  private static WildcardTrie findSubTrieWhichPrefixes(String value, WildcardTrie baseTrie) {
+    final String possiblePrefix = baseTrie.subTries.floorKey(value);
+    // Because each level of the trie does not contain keys that are prefixes of each other,
+    // there can be at most one prefix of the value at that level, and that prefix will be the
+    // highest key ordered before the value (any non-prefix key would have a character
+    // difference with the prefix and so be ordered before the prefix or after the value).
+    if (possiblePrefix != null && value.startsWith(possiblePrefix)) {
+      return findSubTrieWhichPrefixes(value, baseTrie.subTries.get(possiblePrefix));
+    }
+    return baseTrie;
+  }
+
+  public List<Wildcard> getPossibleMatches(String value) {
+    WildcardTrie baseTrie = this;
+    List<Wildcard> prefixMatches =
+        wildcards.isEmpty()
+            // If there's no match, don't even allocate a list and use the singleton emptyList
+            ? Collections.emptyList()
+            : new ArrayList<>(wildcards);
+    while (true) {
+      final String possiblePrefix = baseTrie.subTries.floorKey(value);
+      if (possiblePrefix != null && value.startsWith(possiblePrefix)) {
+        baseTrie = baseTrie.subTries.get(possiblePrefix);
+        if (prefixMatches.isEmpty()) {
+          prefixMatches = new ArrayList<>(baseTrie.wildcards);
+        } else {
+          prefixMatches.addAll(baseTrie.wildcards);
+        }
+      } else {
+        prefixMatches.sort(Comparator.comparing(Wildcard::getRuleIndex));
+        return prefixMatches;
+      }
+    }
+  }
+}
diff --git a/src/main/com/tonicsystems/jarjar/util/JarTransformer.java b/src/main/com/tonicsystems/jarjar/util/JarTransformer.java
index d109083..aced2ad 100644
--- a/src/main/com/tonicsystems/jarjar/util/JarTransformer.java
+++ b/src/main/com/tonicsystems/jarjar/util/JarTransformer.java
@@ -24,12 +24,14 @@
 public abstract class JarTransformer implements JarProcessor {
   @Override
   public boolean process(EntryStruct struct) throws IOException {
-    if (struct.isClass()) {
+    if (struct.isClass() && !struct.name.startsWith("META-INF")) {
       ClassReader reader;
       try {
         reader = new ClassReader(struct.data);
       } catch (RuntimeException e) {
-        return true; // TODO?
+        // Android-changed: Made this failure fatal to highlight class version issues.
+        // http://b/27637680
+        throw new RuntimeException("Failed to load " + struct.name, e);
       }
       GetNameClassWriter w = new GetNameClassWriter(ClassWriter.COMPUTE_MAXS);
       ClassVisitor visitor = transform(w);
diff --git a/src/test/com/tonicsystems/jarjar/WildcardTest.java b/src/test/com/tonicsystems/jarjar/WildcardTest.java
index a37ff95..d08d75c 100644
--- a/src/test/com/tonicsystems/jarjar/WildcardTest.java
+++ b/src/test/com/tonicsystems/jarjar/WildcardTest.java
@@ -33,10 +33,17 @@
     wildcard("net/sf/cglib/**", "foo/@1", "net/sf/cglib/!", null);
     wildcard("net/sf/cglib/*", "foo/@1", "net/sf/cglib/Bar", "foo/Bar");
     wildcard("net/sf/cglib/*/*", "foo/@2/@1", "net/sf/cglib/Bar/Baz", "foo/Baz/Bar");
+    wildcard(
+        "**/package-info",
+        "bar/baz/@1/package-info",
+        "foo/package-info",
+        "bar/baz/foo/package-info");
+    wildcard(
+        "**/module-info", "bar/baz/@1/module-info", "foo/module-info", "bar/baz/foo/module-info");
   }
 
   private void wildcard(String pattern, String result, String value, String expect) {
-    Wildcard wc = new Wildcard(pattern, result);
+    Wildcard wc = new Wildcard(pattern, result, 0);
     // System.err.println(wc);
     assertEquals(expect, wc.replace(value));
   }