Merge commit 'c25e07eaeb7b581173c16a8a47cdb4c357581f85' into dev-release

Change-Id: I7859e63678e602bebc4e36e1f7b814b63fdcbbe7
diff --git a/.gitignore b/.gitignore
index 062fe33..8eab4b8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -91,8 +91,6 @@
 third_party/ddmlib.tar.gz
 third_party/dependencies/
 third_party/dependencies.tar.gz
-third_party/dependencies_new/
-third_party/dependencies_new.tar.gz
 third_party/dependencies_plugin/
 third_party/dependencies_plugin.tar.gz
 third_party/desugar/desugar_*.tar.gz
diff --git a/README.md b/README.md
index 0a085c1..a152891 100644
--- a/README.md
+++ b/README.md
@@ -35,6 +35,9 @@
 The prebuilt JARs have been processed by R8, and for each build the corresponding
 mapping file is located together with the prebuild under the name `r8lib.jar.map`.
 
+To get prebuilds which has not been processed by R8 replace `r8lib.jar` with `r8.jar`
+in the URLs above.
+
 The Google Cloud Storage bucket r8-releases can also be used as a simple
 Maven repository using the following in a Gradle build file:
 
@@ -150,6 +153,40 @@
 R8 is not command line compatible with ProGuard, for instance keep rules cannot be passed
 on the command line, but have to be passed in a file using the `--pg-conf` option.
 
+## <a name="replacing-r8-in-agp"></a>Replacing R8 in Android Gradle plugin
+
+Android Gradle plugin (AGP) ships with R8 embedded (as part of the `builder.jar` from
+`com.android.tools.build:builder:<agp version>` on https://maven.google.com).
+
+To override the embedded version with a prebuilt R8 with version `<version>`, merge
+the following into the top level `settings.gradle` or `settings.gradle.kts`:
+```
+pluginManagement {
+    buildscript {
+        repositories {
+            mavenCentral()
+            maven {
+                url = uri("https://storage.googleapis.com/r8-releases/raw")
+            }
+        }
+        dependencies {
+            classpath("com.android.tools:r8:<version>")
+        }
+    }
+}
+```
+To override the embedded version with a downloaded or freshly built `<path>/r8.jar` merge
+the following into the top level `settings.gradle` or `settings.gradle.kts`:
+```
+pluginManagement {
+    buildscript {
+        dependencies {
+            classpath(files("<path>/r8.jar"))
+        }
+    }
+}
+```
+
 ## Testing
 
 Typical steps to run tests:
diff --git a/d8_r8/commonBuildSrc/settings.gradle.kts b/d8_r8/commonBuildSrc/settings.gradle.kts
index f0a7aba..9180dc7 100644
--- a/d8_r8/commonBuildSrc/settings.gradle.kts
+++ b/d8_r8/commonBuildSrc/settings.gradle.kts
@@ -5,10 +5,10 @@
 pluginManagement {
   repositories {
     maven {
-      url = uri("file:../../third_party/dependencies")
+      url = uri("file:../../third_party/dependencies_plugin")
     }
     maven {
-      url = uri("file:../../third_party/dependencies_new")
+      url = uri("file:../../third_party/dependencies")
     }
   }
 }
@@ -18,9 +18,6 @@
     maven {
       url = uri("file:../../third_party/dependencies")
     }
-    maven {
-      url = uri("file:../../third_party/dependencies_new")
-    }
   }
 }
 
diff --git a/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt b/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
index 794e58e..32e3732 100644
--- a/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
+++ b/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
@@ -308,14 +308,14 @@
 }
 
 object Versions {
-  const val asmVersion = "9.5"
+  const val asmVersion = "9.6"
   const val errorproneVersion = "2.18.0"
   const val fastUtilVersion = "7.2.1"
   const val gsonVersion = "2.10.1"
   const val guavaVersion = "32.1.2-jre"
   const val javassist = "3.29.2-GA"
   const val junitVersion = "4.13-beta-2"
-  const val kotlinVersion = "1.8.10"
+  const val kotlinVersion = "1.9.0"
   const val kotlinMetadataVersion = "0.7.0"
   const val mockito = "2.10.0"
   const val smaliVersion = "3.0.3"
diff --git a/d8_r8/keepanno/settings.gradle.kts b/d8_r8/keepanno/settings.gradle.kts
index 8159946..4b9b240 100644
--- a/d8_r8/keepanno/settings.gradle.kts
+++ b/d8_r8/keepanno/settings.gradle.kts
@@ -5,10 +5,10 @@
 pluginManagement {
   repositories {
     maven {
-      url = uri("file:../../third_party/dependencies")
+      url = uri("file:../../third_party/dependencies_plugin")
     }
     maven {
-      url = uri("file:../../third_party/dependencies_new")
+      url = uri("file:../../third_party/dependencies")
     }
   }
 }
@@ -18,9 +18,6 @@
     maven {
       url = uri("file:../../third_party/dependencies")
     }
-    maven {
-      url = uri("file:../../third_party/dependencies_new")
-    }
   }
 }
 
diff --git a/d8_r8/library_desugar/settings.gradle.kts b/d8_r8/library_desugar/settings.gradle.kts
index 642d900..02ff676 100644
--- a/d8_r8/library_desugar/settings.gradle.kts
+++ b/d8_r8/library_desugar/settings.gradle.kts
@@ -5,10 +5,10 @@
 pluginManagement {
   repositories {
     maven {
-      url = uri("file:../../third_party/dependencies")
+      url = uri("file:../../third_party/dependencies_plugin")
     }
     maven {
-      url = uri("file:../../third_party/dependencies_new")
+      url = uri("file:../../third_party/dependencies")
     }
   }
 }
@@ -18,9 +18,6 @@
     maven {
       url = uri("file:../../third_party/dependencies")
     }
-    maven {
-      url = uri("file:../../third_party/dependencies_new")
-    }
   }
 }
 
diff --git a/d8_r8/main/build.gradle.kts b/d8_r8/main/build.gradle.kts
index 47b5da0..3aeb083 100644
--- a/d8_r8/main/build.gradle.kts
+++ b/d8_r8/main/build.gradle.kts
@@ -107,11 +107,7 @@
         fun getOriginJson() : java.nio.file.Path {
           var repositoryDir =
               moduleId.group.replace('.', '/') + "/" + moduleId.name + "/" + moduleId.version
-          val path : Path =
-              Paths.get("third_party", "dependencies", repositoryDir, "origin.json");
-          val path_new : Path =
-              Paths.get("third_party", "dependencies_new", repositoryDir, "origin.json");
-          return if (path.exists()) path else path_new
+          return Paths.get("third_party", "dependencies", repositoryDir, "origin.json");
         }
 
         // Simple data model of the content of origin.json generated by the tool to download
diff --git a/d8_r8/main/settings.gradle.kts b/d8_r8/main/settings.gradle.kts
index d4f7ab8..f279f0b 100644
--- a/d8_r8/main/settings.gradle.kts
+++ b/d8_r8/main/settings.gradle.kts
@@ -8,7 +8,7 @@
       url = uri("file:../../third_party/dependencies_plugin")
     }
     maven {
-      url = uri("file:../../third_party/dependencies_new")
+      url = uri("file:../../third_party/dependencies")
     }
   }
 }
@@ -18,9 +18,6 @@
     maven {
       url = uri("file:../../third_party/dependencies")
     }
-    maven {
-      url = uri("file:../../third_party/dependencies_new")
-    }
   }
 }
 
diff --git a/d8_r8/resourceshrinker/build.gradle.kts b/d8_r8/resourceshrinker/build.gradle.kts
index 750f076..c716378 100644
--- a/d8_r8/resourceshrinker/build.gradle.kts
+++ b/d8_r8/resourceshrinker/build.gradle.kts
@@ -36,9 +36,9 @@
   compileOnly(files(resolve(ThirdPartyDeps.r8, "r8lib_8.2.20-dev.jar")))
   implementation("com.android.tools.build:aapt2-proto:8.2.0-alpha10-10154469")
   implementation("com.google.protobuf:protobuf-java:3.19.3")
-  implementation("com.android.tools.layoutlib:layoutlib-api:31.2.0-alpha10")
-  implementation("com.android.tools:common:31.2.0-alpha10")
-  implementation("com.android.tools:sdk-common:31.2.0-alpha10")
+  implementation("com.android.tools.layoutlib:layoutlib-api:31.2.0-rc01")
+  implementation("com.android.tools:common:31.2.0-rc01")
+  implementation("com.android.tools:sdk-common:31.2.0-rc01")
 }
 
 tasks {
diff --git a/d8_r8/resourceshrinker/settings.gradle.kts b/d8_r8/resourceshrinker/settings.gradle.kts
index d879107..31a3ce7 100644
--- a/d8_r8/resourceshrinker/settings.gradle.kts
+++ b/d8_r8/resourceshrinker/settings.gradle.kts
@@ -5,10 +5,10 @@
 pluginManagement {
   repositories {
     maven {
-      url = uri("file:../../third_party/dependencies")
+      url = uri("file:../../third_party/dependencies_plugin")
     }
     maven {
-      url = uri("file:../../third_party/dependencies_new")
+      url = uri("file:../../third_party/dependencies")
     }
   }
 }
@@ -18,9 +18,6 @@
     maven {
       url = uri("file:../../third_party/dependencies")
     }
-    maven {
-      url = uri("file:../../third_party/dependencies_new")
-    }
   }
 }
 
diff --git a/d8_r8/settings.gradle.kts b/d8_r8/settings.gradle.kts
index ad7577f..03039c0 100644
--- a/d8_r8/settings.gradle.kts
+++ b/d8_r8/settings.gradle.kts
@@ -67,16 +67,15 @@
 
 val thirdParty = getRepoRoot().resolve("third_party")
 downloadFromGoogleStorage(thirdParty.resolve("dependencies"))
-downloadFromGoogleStorage(thirdParty.resolve("dependencies_new"))
 downloadFromGoogleStorage(thirdParty.resolve("dependencies_plugin"))
 
 pluginManagement {
   repositories {
     maven {
-      url = uri("file:../third_party/dependencies")
+      url = uri("file:../third_party/dependencies_plugin")
     }
     maven {
-      url = uri("file:../third_party/dependencies_new")
+      url = uri("file:../third_party/dependencies")
     }
   }
   includeBuild(rootProject.projectDir.resolve("commonBuildSrc"))
@@ -87,9 +86,6 @@
     maven {
       url = uri("file:../third_party/dependencies")
     }
-    maven {
-      url = uri("file:../third_party/dependencies_new")
-    }
   }
 }
 
diff --git a/d8_r8/shared/settings.gradle.kts b/d8_r8/shared/settings.gradle.kts
index 0de348f..35918f8 100644
--- a/d8_r8/shared/settings.gradle.kts
+++ b/d8_r8/shared/settings.gradle.kts
@@ -5,10 +5,10 @@
 pluginManagement {
   repositories {
     maven {
-      url = uri("file:../../third_party/dependencies")
+      url = uri("file:../../third_party/dependencies_plugin")
     }
     maven {
-      url = uri("file:../../third_party/dependencies_new")
+      url = uri("file:../../third_party/dependencies")
     }
   }
 }
@@ -18,9 +18,6 @@
     maven {
       url = uri("file:../../third_party/dependencies")
     }
-    maven {
-      url = uri("file:../../third_party/dependencies_new")
-    }
   }
 }
 
diff --git a/d8_r8/test/settings.gradle.kts b/d8_r8/test/settings.gradle.kts
index 375809a..38ba359 100644
--- a/d8_r8/test/settings.gradle.kts
+++ b/d8_r8/test/settings.gradle.kts
@@ -5,10 +5,10 @@
 pluginManagement {
   repositories {
     maven {
-      url = uri("file:../../third_party/dependencies")
+      url = uri("file:../../third_party/dependencies_plugin")
     }
     maven {
-      url = uri("file:../../third_party/dependencies_new")
+      url = uri("file:../../third_party/dependencies")
     }
   }
 }
@@ -18,9 +18,6 @@
     maven {
       url = uri("file:../../third_party/dependencies")
     }
-    maven {
-      url = uri("file:../../third_party/dependencies_new")
-    }
   }
 }
 
diff --git a/d8_r8/test_modules/tests_bootstrap/settings.gradle.kts b/d8_r8/test_modules/tests_bootstrap/settings.gradle.kts
index cb05e4f..b4fb012 100644
--- a/d8_r8/test_modules/tests_bootstrap/settings.gradle.kts
+++ b/d8_r8/test_modules/tests_bootstrap/settings.gradle.kts
@@ -5,10 +5,10 @@
 pluginManagement {
   repositories {
     maven {
-      url = uri("file:../../../third_party/dependencies")
+      url = uri("file:../../../third_party/dependencies_plugin")
     }
     maven {
-      url = uri("file:../../../third_party/dependencies_new")
+      url = uri("file:../../../third_party/dependencies")
     }
   }
 }
@@ -18,9 +18,6 @@
     maven {
       url = uri("file:../../../third_party/dependencies")
     }
-    maven {
-      url = uri("file:../../../third_party/dependencies_new")
-    }
   }
 }
 
diff --git a/d8_r8/test_modules/tests_java_10/settings.gradle.kts b/d8_r8/test_modules/tests_java_10/settings.gradle.kts
index d7b2f9a..c1ca87a 100644
--- a/d8_r8/test_modules/tests_java_10/settings.gradle.kts
+++ b/d8_r8/test_modules/tests_java_10/settings.gradle.kts
@@ -5,10 +5,10 @@
 pluginManagement {
   repositories {
     maven {
-      url = uri("file:../../../third_party/dependencies")
+      url = uri("file:../../../third_party/dependencies_plugin")
     }
     maven {
-      url = uri("file:../../../third_party/dependencies_new")
+      url = uri("file:../../../third_party/dependencies")
     }
   }
 }
@@ -18,9 +18,6 @@
     maven {
       url = uri("file:../../../third_party/dependencies")
     }
-    maven {
-      url = uri("file:../../../third_party/dependencies_new")
-    }
   }
 }
 
diff --git a/d8_r8/test_modules/tests_java_11/settings.gradle.kts b/d8_r8/test_modules/tests_java_11/settings.gradle.kts
index 90fa823..98af16e 100644
--- a/d8_r8/test_modules/tests_java_11/settings.gradle.kts
+++ b/d8_r8/test_modules/tests_java_11/settings.gradle.kts
@@ -5,10 +5,10 @@
 pluginManagement {
   repositories {
     maven {
-      url = uri("file:../../../third_party/dependencies")
+      url = uri("file:../../../third_party/dependencies_plugin")
     }
     maven {
-      url = uri("file:../../../third_party/dependencies_new")
+      url = uri("file:../../../third_party/dependencies")
     }
   }
 }
@@ -18,9 +18,6 @@
     maven {
       url = uri("file:../../../third_party/dependencies")
     }
-    maven {
-      url = uri("file:../../../third_party/dependencies_new")
-    }
   }
 }
 
diff --git a/d8_r8/test_modules/tests_java_17/settings.gradle.kts b/d8_r8/test_modules/tests_java_17/settings.gradle.kts
index fc53d30..c01f377 100644
--- a/d8_r8/test_modules/tests_java_17/settings.gradle.kts
+++ b/d8_r8/test_modules/tests_java_17/settings.gradle.kts
@@ -5,10 +5,10 @@
 pluginManagement {
   repositories {
     maven {
-      url = uri("file:../../../third_party/dependencies")
+      url = uri("file:../../../third_party/dependencies_plugin")
     }
     maven {
-      url = uri("file:../../../third_party/dependencies_new")
+      url = uri("file:../../../third_party/dependencies")
     }
   }
 }
@@ -18,9 +18,6 @@
     maven {
       url = uri("file:../../../third_party/dependencies")
     }
-    maven {
-      url = uri("file:../../../third_party/dependencies_new")
-    }
   }
 }
 
diff --git a/d8_r8/test_modules/tests_java_20/settings.gradle.kts b/d8_r8/test_modules/tests_java_20/settings.gradle.kts
index 85d94d0..390fdb7 100644
--- a/d8_r8/test_modules/tests_java_20/settings.gradle.kts
+++ b/d8_r8/test_modules/tests_java_20/settings.gradle.kts
@@ -5,10 +5,10 @@
 pluginManagement {
   repositories {
     maven {
-      url = uri("file:../../../third_party/dependencies")
+      url = uri("file:../../../third_party/dependencies_plugin")
     }
     maven {
-      url = uri("file:../../../third_party/dependencies_new")
+      url = uri("file:../../../third_party/dependencies")
     }
   }
 }
@@ -18,9 +18,6 @@
     maven {
       url = uri("file:../../../third_party/dependencies")
     }
-    maven {
-      url = uri("file:../../../third_party/dependencies_new")
-    }
   }
 }
 
diff --git a/d8_r8/test_modules/tests_java_8/build.gradle.kts b/d8_r8/test_modules/tests_java_8/build.gradle.kts
index 3397217..cb1f81e 100644
--- a/d8_r8/test_modules/tests_java_8/build.gradle.kts
+++ b/d8_r8/test_modules/tests_java_8/build.gradle.kts
@@ -34,6 +34,7 @@
 val keepAnnoCompileTask = projectTask("keepanno", "compileJava")
 val mainCompileTask = projectTask("main", "compileJava")
 val mainDepsJarTask = projectTask("main", "depsJar")
+val resourceShrinkerDepsJarTask = projectTask("resourceshrinker", "depsJar")
 
 dependencies {
   implementation(keepAnnoJarTask.outputs.files)
@@ -41,7 +42,7 @@
   implementation(projectTask("main", "processResources").outputs.files)
   implementation(projectTask("resourceshrinker", "compileJava").outputs.files)
   implementation(projectTask("resourceshrinker", "compileKotlin").outputs.files)
-  implementation(projectTask("resourceshrinker", "depsJar").outputs.files)
+  implementation(resourceShrinkerDepsJarTask.outputs.files)
   implementation(Deps.asm)
   implementation(Deps.asmCommons)
   implementation(Deps.asmUtil)
@@ -158,6 +159,7 @@
     dependsOn(gradle.includedBuild("keepanno").task(":jar"))
     dependsOn(gradle.includedBuild("resourceshrinker").task(":jar"))
     from(testDependencies().map(::zipTree))
+    from(resourceShrinkerDepsJarTask.outputs.getFiles().map(::zipTree))
     duplicatesStrategy = DuplicatesStrategy.EXCLUDE
     archiveFileName.set("deps.jar")
   }
diff --git a/d8_r8/test_modules/tests_java_8/settings.gradle.kts b/d8_r8/test_modules/tests_java_8/settings.gradle.kts
index a0cc31c..6600f09 100644
--- a/d8_r8/test_modules/tests_java_8/settings.gradle.kts
+++ b/d8_r8/test_modules/tests_java_8/settings.gradle.kts
@@ -5,10 +5,10 @@
 pluginManagement {
   repositories {
     maven {
-      url = uri("file:../../../third_party/dependencies")
+      url = uri("file:../../../third_party/dependencies_plugin")
     }
     maven {
-      url = uri("file:../../../third_party/dependencies_new")
+      url = uri("file:../../../third_party/dependencies")
     }
   }
 }
@@ -18,9 +18,6 @@
     maven {
       url = uri("file:../../../third_party/dependencies")
     }
-    maven {
-      url = uri("file:../../../third_party/dependencies_new")
-    }
   }
 }
 
diff --git a/d8_r8/test_modules/tests_java_9/settings.gradle.kts b/d8_r8/test_modules/tests_java_9/settings.gradle.kts
index 46bc3a8..ad1c8ab 100644
--- a/d8_r8/test_modules/tests_java_9/settings.gradle.kts
+++ b/d8_r8/test_modules/tests_java_9/settings.gradle.kts
@@ -5,10 +5,10 @@
 pluginManagement {
   repositories {
     maven {
-      url = uri("file:../../../third_party/dependencies")
+      url = uri("file:../../../third_party/dependencies_plugin")
     }
     maven {
-      url = uri("file:../../../third_party/dependencies_new")
+      url = uri("file:../../../third_party/dependencies")
     }
   }
 }
@@ -18,9 +18,6 @@
     maven {
       url = uri("file:../../../third_party/dependencies")
     }
-    maven {
-      url = uri("file:../../../third_party/dependencies_new")
-    }
   }
 }
 
diff --git a/src/main/java/com/android/tools/r8/ArchiveProtoAndroidResourceConsumer.java b/src/main/java/com/android/tools/r8/ArchiveProtoAndroidResourceConsumer.java
index c601e80..f2c4b5d 100644
--- a/src/main/java/com/android/tools/r8/ArchiveProtoAndroidResourceConsumer.java
+++ b/src/main/java/com/android/tools/r8/ArchiveProtoAndroidResourceConsumer.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.utils.OutputBuilder;
 import java.nio.file.Path;
 
+@Keep
 public class ArchiveProtoAndroidResourceConsumer implements AndroidResourceConsumer {
   private final OutputBuilder outputBuilder;
 
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 9964d6e..8be855e 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -7,6 +7,9 @@
 import static com.android.tools.r8.utils.AssertionUtils.forTesting;
 import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
 
+import com.android.build.shrinker.r8integration.LegacyResourceShrinker;
+import com.android.build.shrinker.r8integration.LegacyResourceShrinker.ShrinkerResult;
+import com.android.tools.r8.DexIndexedConsumer.ForwardingConsumer;
 import com.android.tools.r8.androidapi.ApiReferenceStubber;
 import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryKeepRuleGenerator;
 import com.android.tools.r8.dex.ApplicationReader;
@@ -25,6 +28,7 @@
 import com.android.tools.r8.graph.DirectMappedDexApplication;
 import com.android.tools.r8.graph.GenericSignatureContextBuilder;
 import com.android.tools.r8.graph.GenericSignatureCorrectnessHelper;
+import com.android.tools.r8.graph.LazyLoadedDexApplication;
 import com.android.tools.r8.graph.ProgramDefinition;
 import com.android.tools.r8.graph.PrunedItems;
 import com.android.tools.r8.graph.SubtypingInfo;
@@ -75,6 +79,7 @@
 import com.android.tools.r8.optimize.proto.ProtoNormalizer;
 import com.android.tools.r8.optimize.redundantbridgeremoval.RedundantBridgeRemover;
 import com.android.tools.r8.origin.CommandLineOrigin;
+import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.profile.art.ArtProfileCompletenessChecker;
 import com.android.tools.r8.profile.rewriting.ProfileCollectionAdditions;
 import com.android.tools.r8.repackaging.Repackaging;
@@ -105,9 +110,11 @@
 import com.android.tools.r8.synthesis.SyntheticFinalization;
 import com.android.tools.r8.synthesis.SyntheticItems;
 import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
 import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.ResourceTracing;
+import com.android.tools.r8.utils.Pair;
+import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.SelfRetraceTest;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.StringUtils;
@@ -118,6 +125,8 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.PrintStream;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -125,6 +134,8 @@
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.function.Supplier;
+import javax.xml.parsers.ParserConfigurationException;
+import org.xml.sax.SAXException;
 
 /**
  * The R8 compiler.
@@ -246,6 +257,7 @@
 
   @SuppressWarnings("DefaultCharset")
   private void run(AndroidApp inputApp, ExecutorService executorService) throws IOException {
+    timing.begin("Run prelude");
     assert options.programConsumer != null;
     if (options.quiet) {
       System.setOut(new PrintStream(ByteStreams.nullOutputStream()));
@@ -267,21 +279,32 @@
       System.out.println("R8 is running with max memory:" + runtime.maxMemory());
     }
     options.prepareForReportingLibraryAndProgramDuplicates();
+    timing.end();
     try {
       AppView<AppInfoWithClassHierarchy> appView;
       {
+        timing.begin("Read app");
         ApplicationReader applicationReader = new ApplicationReader(inputApp, options, timing);
-        DirectMappedDexApplication application = applicationReader.read(executorService).toDirect();
+        LazyLoadedDexApplication lazyLoaded = applicationReader.read(executorService);
+        timing.begin("To direct app");
+        DirectMappedDexApplication application = lazyLoaded.toDirect();
+        timing.end();
+        timing.end();
         options.loadMachineDesugaredLibrarySpecification(timing, application);
+        timing.begin("Read main dex classes");
         MainDexInfo mainDexInfo = applicationReader.readMainDexClassesForR8(application);
+        timing.end();
         // Now that the dex-application is fully loaded, close any internal archive providers.
-        inputApp.closeInternalArchiveProviders();
-
+        timing.time("Close providers", () -> inputApp.closeInternalArchiveProviders());
+        timing.begin("Create AppView");
         appView = AppView.createForR8(application, mainDexInfo);
-        appView.setAppServices(AppServices.builder(appView).build());
-        SyntheticItems.collectSyntheticInputs(appView);
+        timing.end();
+        timing.time(
+            "Set app services", () -> appView.setAppServices(AppServices.builder(appView).build()));
+        timing.time(
+            "Collect synthetic inputs", () -> SyntheticItems.collectSyntheticInputs(appView));
       }
-
+      timing.begin("Register references and more setup");
       assert ArtProfileCompletenessChecker.verify(appView);
 
       // Check for potentially having pass-through of Cf-code for kotlin libraries.
@@ -320,7 +343,7 @@
                 .rebuildWithClassHierarchy(
                     appView.getSyntheticItems().commit(appView.appInfo().app())));
       }
-
+      timing.end();
       timing.begin("Strip unused code");
       timing.begin("Before enqueuer");
       RuntimeTypeCheckInfo.Builder classMergingEnqueuerExtensionBuilder =
@@ -435,7 +458,7 @@
       } finally {
         timing.end();
       }
-
+      timing.begin("Run center tasks");
       assert appView.appInfo().hasLiveness();
       AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
 
@@ -550,7 +573,7 @@
       timing.begin("AppliedGraphLens construction");
       appView.setGraphLens(new AppliedGraphLens(appView));
       timing.end();
-
+      timing.end();
       if (options.shouldRerunEnqueuer()) {
         timing.begin("Post optimization code stripping");
         try {
@@ -678,7 +701,7 @@
                       converter, executorService, timing));
         }
       }
-
+      timing.begin("Run postlude");
       performFinalMainDexTracing(appView, executorService);
 
       if (appView.appInfo().hasLiveness()) {
@@ -830,17 +853,31 @@
 
       new DesugaredLibraryKeepRuleGenerator(appView).runIfNecessary(timing);
 
+      List<Pair<Integer, byte[]>> dexFileContent = new ArrayList<>();
       if (options.androidResourceProvider != null && options.androidResourceConsumer != null) {
-        // Currently this is simply a pass through of all resources.
-        writeAndroidResources(
-            options.androidResourceProvider, options.androidResourceConsumer, appView.reporter());
+        options.programConsumer =
+            new ForwardingConsumer((DexIndexedConsumer) options.programConsumer) {
+              @Override
+              public void accept(
+                  int fileIndex,
+                  ByteDataView data,
+                  Set<String> descriptors,
+                  DiagnosticsHandler handler) {
+                dexFileContent.add(new Pair<>(fileIndex, data.copyByteData()));
+                super.accept(fileIndex, data, descriptors, handler);
+              }
+            };
       }
 
       assert appView.verifyMovedMethodsHaveOriginalMethodPosition();
+      timing.end();
 
       // Generate the resulting application resources.
       writeApplication(appView, inputApp, executorService);
 
+      if (options.androidResourceProvider != null && options.androidResourceConsumer != null) {
+        shrinkResources(dexFileContent);
+      }
       assert appView.getDontWarnConfiguration().validate(options);
 
       options.printWarnings();
@@ -856,14 +893,68 @@
     }
   }
 
-  private void writeAndroidResources(
-      AndroidResourceProvider androidResourceProvider,
-      AndroidResourceConsumer androidResourceConsumer,
-      DiagnosticsHandler diagnosticsHandler) {
-    ResourceTracing resourceTracing = ResourceTracing.getImpl();
-    resourceTracing.setConsumer(androidResourceConsumer);
-    resourceTracing.setProvider(androidResourceProvider);
-    resourceTracing.done(diagnosticsHandler);
+  private void shrinkResources(List<Pair<Integer, byte[]>> dexFileContent) {
+    LegacyResourceShrinker.Builder resourceShrinkerBuilder = LegacyResourceShrinker.builder();
+    Reporter reporter = options.reporter;
+    dexFileContent.forEach(p -> resourceShrinkerBuilder.addDexInput(p.getFirst(), p.getSecond()));
+    try {
+      Collection<AndroidResourceInput> androidResources =
+          options.androidResourceProvider.getAndroidResources();
+      for (AndroidResourceInput androidResource : androidResources) {
+        try {
+          byte[] bytes = androidResource.getByteStream().readAllBytes();
+          Path path = Paths.get(androidResource.getPath().location());
+          switch (androidResource.getKind()) {
+            case MANIFEST:
+              resourceShrinkerBuilder.setManifest(path, bytes);
+              break;
+            case RES_FOLDER_FILE:
+              resourceShrinkerBuilder.addResFolderInput(path, bytes);
+              break;
+            case RESOURCE_TABLE:
+              resourceShrinkerBuilder.setResourceTable(path, bytes);
+              break;
+            case XML_FILE:
+              resourceShrinkerBuilder.addXmlInput(path, bytes);
+              break;
+            case UNKNOWN:
+              break;
+          }
+        } catch (IOException e) {
+          reporter.error(new ExceptionDiagnostic(e, androidResource.getOrigin()));
+        }
+      }
+
+      LegacyResourceShrinker shrinker = resourceShrinkerBuilder.build();
+      ShrinkerResult shrinkerResult = shrinker.run();
+      AndroidResourceConsumer androidResourceConsumer = options.androidResourceConsumer;
+      Set<String> toKeep = shrinkerResult.getResFolderEntriesToKeep();
+      for (AndroidResourceInput androidResource : androidResources) {
+        switch (androidResource.getKind()) {
+          case MANIFEST:
+          case UNKNOWN:
+            androidResourceConsumer.accept(
+                new R8PassThroughAndroidResource(androidResource, reporter), reporter);
+            break;
+          case RESOURCE_TABLE:
+            androidResourceConsumer.accept(
+                new R8AndroidResourceWithData(
+                    androidResource, reporter, shrinkerResult.getResourceTableInProtoFormat()),
+                reporter);
+            break;
+          case RES_FOLDER_FILE:
+          case XML_FILE:
+            if (toKeep.contains(androidResource.getPath().location())) {
+              androidResourceConsumer.accept(
+                  new R8PassThroughAndroidResource(androidResource, reporter), reporter);
+            }
+            break;
+        }
+      }
+      androidResourceConsumer.finished(reporter);
+    } catch (ParserConfigurationException | SAXException | ResourceException | IOException e) {
+      reporter.error(new ExceptionDiagnostic(e));
+    }
   }
 
   private static boolean allReferencesAssignedApiLevel(
@@ -1093,4 +1184,58 @@
     }
     ExceptionUtils.withMainProgramHandler(() -> run(args));
   }
+
+  private abstract static class R8AndroidResourceBase implements AndroidResourceOutput {
+
+    protected final AndroidResourceInput androidResource;
+    protected final Reporter reporter;
+
+    public R8AndroidResourceBase(AndroidResourceInput androidResource, Reporter reporter) {
+      this.androidResource = androidResource;
+      this.reporter = reporter;
+    }
+
+    @Override
+    public ResourcePath getPath() {
+      return androidResource.getPath();
+    }
+
+    @Override
+    public Origin getOrigin() {
+      return androidResource.getOrigin();
+    }
+  }
+
+  private static class R8PassThroughAndroidResource extends R8AndroidResourceBase {
+
+    public R8PassThroughAndroidResource(AndroidResourceInput androidResource, Reporter reporter) {
+      super(androidResource, reporter);
+    }
+
+    @Override
+    public ByteDataView getByteDataView() {
+      try {
+        return ByteDataView.of(ByteStreams.toByteArray(androidResource.getByteStream()));
+      } catch (IOException | ResourceException e) {
+        reporter.error(new ExceptionDiagnostic(e, androidResource.getOrigin()));
+      }
+      return null;
+    }
+  }
+
+  private static class R8AndroidResourceWithData extends R8AndroidResourceBase {
+
+    private final byte[] data;
+
+    public R8AndroidResourceWithData(
+        AndroidResourceInput androidResource, Reporter reporter, byte[] data) {
+      super(androidResource, reporter);
+      this.data = data;
+    }
+
+    @Override
+    public ByteDataView getByteDataView() {
+      return ByteDataView.of(data);
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/CfVersion.java b/src/main/java/com/android/tools/r8/cf/CfVersion.java
index c85f50f..0c1d72b 100644
--- a/src/main/java/com/android/tools/r8/cf/CfVersion.java
+++ b/src/main/java/com/android/tools/r8/cf/CfVersion.java
@@ -46,8 +46,8 @@
   public static final CfVersion V20_PREVIEW = new CfVersion(Opcodes.V20 | Opcodes.V_PREVIEW);
   public static final CfVersion V21 = new CfVersion(Opcodes.V21);
   public static final CfVersion V21_PREVIEW = new CfVersion(Opcodes.V21 | Opcodes.V_PREVIEW);
-  public static final CfVersion V22 = new CfVersion(66);
-  public static final CfVersion V22_PREVIEW = new CfVersion(66 | Opcodes.V_PREVIEW);
+  public static final CfVersion V22 = new CfVersion(Opcodes.V22);
+  public static final CfVersion V22_PREVIEW = new CfVersion(Opcodes.V22 | Opcodes.V_PREVIEW);
 
   private final int version;
 
@@ -72,7 +72,8 @@
     CfVersion.V18,
     CfVersion.V19,
     CfVersion.V20,
-    CfVersion.V21
+    CfVersion.V21,
+    CfVersion.V22
   };
 
   // Private constructor in case we want to canonicalize versions.
diff --git a/src/resourceshrinker/java/com/android/build/shrinker/ResourceShrinkerImpl.kt b/src/resourceshrinker/java/com/android/build/shrinker/ResourceShrinkerImpl.kt
index 1136085..153f1ce 100644
--- a/src/resourceshrinker/java/com/android/build/shrinker/ResourceShrinkerImpl.kt
+++ b/src/resourceshrinker/java/com/android/build/shrinker/ResourceShrinkerImpl.kt
@@ -301,6 +301,11 @@
         .any { it.isReachable }
 }
 
+fun ResourceStore.isJarPathReachable(path: String) : Boolean {
+    val (_, folder, name) = path.split('/', limit = 3)
+    return isJarPathReachable(folder, name);
+}
+
 private fun ResourceStore.getResourceId(
     folder: String,
     name: String
diff --git a/src/resourceshrinker/java/com/android/build/shrinker/graph/ProtoResourcesGraphBuilder.kt b/src/resourceshrinker/java/com/android/build/shrinker/graph/ProtoResourcesGraphBuilder.kt
index 130e585..a0a1f76 100644
--- a/src/resourceshrinker/java/com/android/build/shrinker/graph/ProtoResourcesGraphBuilder.kt
+++ b/src/resourceshrinker/java/com/android/build/shrinker/graph/ProtoResourcesGraphBuilder.kt
@@ -21,6 +21,7 @@
 import com.android.aapt.Resources.FileReference
 import com.android.aapt.Resources.FileReference.Type.PROTO_XML
 import com.android.aapt.Resources.Reference
+import com.android.aapt.Resources.ResourceTable
 import com.android.aapt.Resources.XmlAttribute
 import com.android.aapt.Resources.XmlElement
 import com.android.aapt.Resources.XmlNode
@@ -56,12 +57,23 @@
  * @param resourceTable path to resource table in proto format.
  */
 class ProtoResourcesGraphBuilder(
-    private val resourceRoot: Path,
-    private val resourceTable: Path
+    private val resourceRoot: ResFolderFileTree,
+    private val resourceTableProducer: (ResourceShrinkerModel) -> ResourceTable
 ) : ResourcesGraphBuilder {
 
+    constructor(resourceRootPath: Path, resourceTablePath: Path) : this(
+        object : ResFolderFileTree {
+            override fun getEntryByName(pathInRes: String): ByteArray {
+                val lazyVal : ByteArray by lazy {
+                    Files.readAllBytes(resourceRootPath.resolve(pathInRes))
+                }
+                return lazyVal
+            }
+        },
+        { model -> model.readResourceTable(resourceTablePath) }
+    )
     override fun buildGraph(model: ResourceShrinkerModel) {
-        model.readResourceTable(resourceTable).entriesSequence()
+        resourceTableProducer(model).entriesSequence()
             .map { (id, _, _, entry) ->
                 model.resourceStore.getResource(id)?.let {
                     ReferencesForResourceFinder(resourceRoot, model, entry, it)
@@ -71,9 +83,12 @@
             .forEach { it.findReferences() }
     }
 }
+interface ResFolderFileTree {
+    fun getEntryByName(pathInRes: String) : ByteArray
+}
 
 private class ReferencesForResourceFinder(
-    private val resourcesRoot: Path,
+    private val resourcesRoot: ResFolderFileTree,
     private val model: ResourceShrinkerModel,
     private val entry: Entry,
     private val current: Resource
@@ -196,10 +211,9 @@
     }
 
     private fun findFromFile(file: FileReference) {
-        val path = resourcesRoot.resolve(file.path.substringAfter("res/"))
-        val bytes: ByteArray by lazy { Files.readAllBytes(path) }
+        val bytes = resourcesRoot.getEntryByName(file.path.substringAfter("res/"))
         val content: String by lazy { String(bytes, StandardCharsets.UTF_8) }
-        val extension = Ascii.toLowerCase(path.fileName.toString()).substringAfter('.')
+        val extension = Ascii.toLowerCase(file.path.substringAfterLast('.'))
         when {
             file.type == PROTO_XML -> fillFromXmlNode(XmlNode.parseFrom(bytes))
             extension in listOf("html", "htm") -> webTokenizers.tokenizeHtml(content)
diff --git a/src/resourceshrinker/java/com/android/build/shrinker/r8integration/LegacyResourceShrinker.java b/src/resourceshrinker/java/com/android/build/shrinker/r8integration/LegacyResourceShrinker.java
new file mode 100644
index 0000000..87158c9
--- /dev/null
+++ b/src/resourceshrinker/java/com/android/build/shrinker/r8integration/LegacyResourceShrinker.java
@@ -0,0 +1,277 @@
+// Copyright (c) 2023, 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.build.shrinker.r8integration;
+
+import static java.nio.charset.StandardCharsets.UTF_16BE;
+import static java.nio.charset.StandardCharsets.UTF_16LE;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.android.aapt.Resources.ResourceTable;
+import com.android.aapt.Resources.XmlNode;
+import com.android.build.shrinker.NoDebugReporter;
+import com.android.build.shrinker.ResourceShrinkerImplKt;
+import com.android.build.shrinker.ResourceTableUtilKt;
+import com.android.build.shrinker.graph.ProtoResourcesGraphBuilder;
+import com.android.build.shrinker.graph.ResFolderFileTree;
+import com.android.build.shrinker.r8integration.R8ResourceShrinkerState.R8ResourceShrinkerModel;
+import com.android.build.shrinker.usages.DexFileAnalysisCallback;
+import com.android.build.shrinker.usages.ProtoAndroidManifestUsageRecorderKt;
+import com.android.build.shrinker.usages.R8ResourceShrinker;
+import com.android.build.shrinker.usages.ToolsAttributeUsageRecorderKt;
+import com.android.ide.common.resources.ResourcesUtil;
+import com.android.ide.common.resources.usage.ResourceStore;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.xml.parsers.ParserConfigurationException;
+import org.jetbrains.annotations.NotNull;
+import org.xml.sax.SAXException;
+
+public class LegacyResourceShrinker {
+  private final Map<Integer, byte[]> dexInputs;
+  private final List<PathAndBytes> resFolderInputs;
+  private final List<PathAndBytes> xmlInputs;
+  private final PathAndBytes manifest;
+  private final PathAndBytes resourceTable;
+
+  public static class Builder {
+
+    private final Map<Integer, byte[]> dexInputs = new HashMap<>();
+    private final List<PathAndBytes> resFolderInputs = new ArrayList<>();
+    private final List<PathAndBytes> xmlInputs = new ArrayList<>();
+
+    private PathAndBytes manifest;
+    private PathAndBytes resourceTable;
+
+    private Builder() {}
+
+    public Builder setManifest(Path path, byte[] bytes) {
+      this.manifest = new PathAndBytes(bytes, path);
+      return this;
+    }
+
+    public Builder setResourceTable(Path path, byte[] bytes) {
+      this.resourceTable = new PathAndBytes(bytes, path);
+      return this;
+    }
+
+    public Builder addDexInput(int index, byte[] bytes) {
+      dexInputs.put(index, bytes);
+      return this;
+    }
+
+    public Builder addResFolderInput(Path path, byte[] bytes) {
+      resFolderInputs.add(new PathAndBytes(bytes, path));
+      return this;
+    }
+
+    public Builder addXmlInput(Path path, byte[] bytes) {
+      xmlInputs.add(new PathAndBytes(bytes, path));
+      return this;
+    }
+
+    public LegacyResourceShrinker build() {
+      assert manifest != null && resourceTable != null;
+      return new LegacyResourceShrinker(
+          dexInputs, resFolderInputs, manifest, resourceTable, xmlInputs);
+    }
+  }
+
+  private LegacyResourceShrinker(
+      Map<Integer, byte[]> dexInputs,
+      List<PathAndBytes> resFolderInputs,
+      PathAndBytes manifest,
+      PathAndBytes resourceTable,
+      List<PathAndBytes> xmlInputs) {
+    this.dexInputs = dexInputs;
+    this.resFolderInputs = resFolderInputs;
+    this.manifest = manifest;
+    this.resourceTable = resourceTable;
+    this.xmlInputs = xmlInputs;
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public ShrinkerResult run() throws IOException, ParserConfigurationException, SAXException {
+    R8ResourceShrinkerModel model = new R8ResourceShrinkerModel(NoDebugReporter.INSTANCE, false);
+    ResourceTable loadedResourceTable = ResourceTable.parseFrom(resourceTable.bytes);
+    model.instantiateFromResourceTable(loadedResourceTable);
+    for (Entry<Integer, byte[]> entry : dexInputs.entrySet()) {
+      // The analysis needs an origin for the dex files, synthesize an easy recognizable one.
+      Path inMemoryR8 = Paths.get("in_memory_r8_classes" + entry.getKey() + ".dex");
+      R8ResourceShrinker.runResourceShrinkerAnalysis(
+          entry.getValue(), inMemoryR8, new DexFileAnalysisCallback(inMemoryR8, model));
+    }
+    ProtoAndroidManifestUsageRecorderKt.recordUsagesFromNode(
+        XmlNode.parseFrom(manifest.bytes), model);
+    for (PathAndBytes xmlInput : xmlInputs) {
+      if (xmlInput.path.startsWith("res/raw")) {
+        ToolsAttributeUsageRecorderKt.processRawXml(getUtfReader(xmlInput.getBytes()), model);
+      }
+    }
+    new ProtoResourcesGraphBuilder(
+            new ResFolderFileTree() {
+              Map<String, PathAndBytes> pathToBytes =
+                  new ImmutableMap.Builder<String, PathAndBytes>()
+                      .putAll(
+                          xmlInputs.stream()
+                              .collect(Collectors.toMap(PathAndBytes::getPathWithoutRes, a -> a)))
+                      .putAll(
+                          resFolderInputs.stream()
+                              .collect(Collectors.toMap(PathAndBytes::getPathWithoutRes, a -> a)))
+                      .build();
+
+              @Override
+              public byte[] getEntryByName(@NotNull String pathInRes) {
+                return pathToBytes.get(pathInRes).getBytes();
+              }
+            },
+            unused -> loadedResourceTable)
+        .buildGraph(model);
+    ResourceStore resourceStore = model.getResourceStore();
+    resourceStore.processToolsAttributes();
+    model.keepPossiblyReferencedResources();
+    // Transitively mark the reachable resources in the model.
+    // Finds unused resources in provided resources collection.
+    // Marks all used resources as 'reachable' in original collection.
+    ResourcesUtil.findUnusedResources(model.getResourceStore().getResources(), x -> {});
+    ImmutableSet.Builder<String> resEntriesToKeep = new ImmutableSet.Builder<>();
+    for (PathAndBytes xmlInput : Iterables.concat(xmlInputs, resFolderInputs)) {
+      if (ResourceShrinkerImplKt.isJarPathReachable(resourceStore, xmlInput.path.toString())) {
+        resEntriesToKeep.add(xmlInput.path.toString());
+      }
+    }
+    List<Integer> resourceIdsToRemove =
+        model.getResourceStore().getResources().stream()
+            .filter(r -> !r.isReachable())
+            .map(r -> r.value)
+            .collect(Collectors.toList());
+    ResourceTable shrunkenResourceTable =
+        ResourceTableUtilKt.nullOutEntriesWithIds(loadedResourceTable, resourceIdsToRemove);
+    return new ShrinkerResult(resEntriesToKeep.build(), shrunkenResourceTable.toByteArray());
+  }
+
+  // Lifted from com/android/utils/XmlUtils.java which we can't easily update internal dependency
+  // for.
+  /**
+   * Returns a character reader for the given bytes, which must be a UTF encoded file.
+   *
+   * <p>The reader does not need to be closed by the caller (because the file is read in full in one
+   * shot and the resulting array is then wrapped in a byte array input stream, which does not need
+   * to be closed.)
+   */
+  public static Reader getUtfReader(byte[] bytes) throws IOException {
+    int length = bytes.length;
+    if (length == 0) {
+      return new StringReader("");
+    }
+
+    switch (bytes[0]) {
+      case (byte) 0xEF:
+        {
+          if (length >= 3 && bytes[1] == (byte) 0xBB && bytes[2] == (byte) 0xBF) {
+            // UTF-8 BOM: EF BB BF: Skip it
+            return new InputStreamReader(new ByteArrayInputStream(bytes, 3, length - 3), UTF_8);
+          }
+          break;
+        }
+      case (byte) 0xFE:
+        {
+          if (length >= 2 && bytes[1] == (byte) 0xFF) {
+            // UTF-16 Big Endian BOM: FE FF
+            return new InputStreamReader(new ByteArrayInputStream(bytes, 2, length - 2), UTF_16BE);
+          }
+          break;
+        }
+      case (byte) 0xFF:
+        {
+          if (length >= 2 && bytes[1] == (byte) 0xFE) {
+            if (length >= 4 && bytes[2] == (byte) 0x00 && bytes[3] == (byte) 0x00) {
+              // UTF-32 Little Endian BOM: FF FE 00 00
+              return new InputStreamReader(
+                  new ByteArrayInputStream(bytes, 4, length - 4), "UTF-32LE");
+            }
+
+            // UTF-16 Little Endian BOM: FF FE
+            return new InputStreamReader(new ByteArrayInputStream(bytes, 2, length - 2), UTF_16LE);
+          }
+          break;
+        }
+      case (byte) 0x00:
+        {
+          if (length >= 4
+              && bytes[0] == (byte) 0x00
+              && bytes[1] == (byte) 0x00
+              && bytes[2] == (byte) 0xFE
+              && bytes[3] == (byte) 0xFF) {
+            // UTF-32 Big Endian BOM: 00 00 FE FF
+            return new InputStreamReader(
+                new ByteArrayInputStream(bytes, 4, length - 4), "UTF-32BE");
+          }
+          break;
+        }
+    }
+
+    // No byte order mark: Assume UTF-8 (where the BOM is optional).
+    return new InputStreamReader(new ByteArrayInputStream(bytes), UTF_8);
+  }
+
+  public static class ShrinkerResult {
+    private final Set<String> resFolderEntriesToKeep;
+    private final byte[] resourceTableInProtoFormat;
+
+    public ShrinkerResult(Set<String> resFolderEntriesToKeep, byte[] resourceTableInProtoFormat) {
+      this.resFolderEntriesToKeep = resFolderEntriesToKeep;
+      this.resourceTableInProtoFormat = resourceTableInProtoFormat;
+    }
+
+    public byte[] getResourceTableInProtoFormat() {
+      return resourceTableInProtoFormat;
+    }
+
+    public Set<String> getResFolderEntriesToKeep() {
+      return resFolderEntriesToKeep;
+    }
+  }
+
+  private static class PathAndBytes {
+    private final byte[] bytes;
+    private final Path path;
+
+    private PathAndBytes(byte[] bytes, Path path) {
+      this.bytes = bytes;
+      this.path = path;
+    }
+
+    public Path getPath() {
+      return path;
+    }
+
+    public String getPathWithoutRes() {
+      assert path.toString().startsWith("res/");
+      return path.toString().substring(4);
+    }
+
+    public byte[] getBytes() {
+      return bytes;
+    }
+  }
+}
diff --git a/src/resourceshrinker/java/com/android/build/shrinker/r8integration/R8ResourceShrinkerState.java b/src/resourceshrinker/java/com/android/build/shrinker/r8integration/R8ResourceShrinkerState.java
index 5f9bec6..d8ac6a9 100644
--- a/src/resourceshrinker/java/com/android/build/shrinker/r8integration/R8ResourceShrinkerState.java
+++ b/src/resourceshrinker/java/com/android/build/shrinker/r8integration/R8ResourceShrinkerState.java
@@ -32,7 +32,7 @@
     r8ResourceShrinkerModel.instantiateFromResourceTable(inputStream);
   }
 
-  private static class R8ResourceShrinkerModel extends ResourceShrinkerModel {
+  public static class R8ResourceShrinkerModel extends ResourceShrinkerModel {
 
     public R8ResourceShrinkerModel(
         ShrinkerDebugReporter debugReporter, boolean supportMultipackages) {
@@ -40,25 +40,29 @@
     }
 
     // Similar to instantiation in ProtoResourceTableGatherer, but using an inputstream.
-    public void instantiateFromResourceTable(InputStream inputStream) {
+    void instantiateFromResourceTable(InputStream inputStream) {
       try {
         ResourceTable resourceTable = ResourceTable.parseFrom(inputStream);
-        ResourceTableUtilKt.entriesSequence(resourceTable)
-            .iterator()
-            .forEachRemaining(
-                entryWrapper -> {
-                  ResourceType resourceType = ResourceType.fromClassName(entryWrapper.getType());
-                  if (resourceType != ResourceType.STYLEABLE) {
-                    this.addResource(
-                        resourceType,
-                        entryWrapper.getPackageName(),
-                        ResourcesUtil.resourceNameToFieldName(entryWrapper.getEntry().getName()),
-                        entryWrapper.getId());
-                  }
-                });
+        instantiateFromResourceTable(resourceTable);
       } catch (IOException ex) {
         throw new RuntimeException(ex);
       }
     }
+
+    void instantiateFromResourceTable(ResourceTable resourceTable) {
+      ResourceTableUtilKt.entriesSequence(resourceTable)
+          .iterator()
+          .forEachRemaining(
+              entryWrapper -> {
+                ResourceType resourceType = ResourceType.fromClassName(entryWrapper.getType());
+                if (resourceType != ResourceType.STYLEABLE) {
+                  this.addResource(
+                      resourceType,
+                      entryWrapper.getPackageName(),
+                      ResourcesUtil.resourceNameToFieldName(entryWrapper.getEntry().getName()),
+                      entryWrapper.getId());
+                }
+              });
+    }
   }
 }
diff --git a/src/resourceshrinker/java/com/android/build/shrinker/usages/DexUsageRecorder.kt b/src/resourceshrinker/java/com/android/build/shrinker/usages/DexUsageRecorder.kt
index 3f1a676..5d6a0df 100644
--- a/src/resourceshrinker/java/com/android/build/shrinker/usages/DexUsageRecorder.kt
+++ b/src/resourceshrinker/java/com/android/build/shrinker/usages/DexUsageRecorder.kt
@@ -56,7 +56,7 @@
     }
 }
 
-private class DexFileAnalysisCallback(
+class DexFileAnalysisCallback(
         private val path: Path,
         private val model: ResourceShrinkerModel
 ) : AnalysisCallback {
diff --git a/src/resourceshrinker/java/com/android/build/shrinker/usages/ProtoAndroidManifestUsageRecorder.kt b/src/resourceshrinker/java/com/android/build/shrinker/usages/ProtoAndroidManifestUsageRecorder.kt
index 4e6aa3e..77bb8f6 100644
--- a/src/resourceshrinker/java/com/android/build/shrinker/usages/ProtoAndroidManifestUsageRecorder.kt
+++ b/src/resourceshrinker/java/com/android/build/shrinker/usages/ProtoAndroidManifestUsageRecorder.kt
@@ -34,25 +34,25 @@
         recordUsagesFromNode(root, model)
     }
 
-    private fun recordUsagesFromNode(node: XmlNode, model: ResourceShrinkerModel) {
-        // Records only resources from element attributes that have reference items with resolved
-        // ids or names.
-        if (!node.hasElement()) {
-            return
-        }
-        node.element.attributeList.asSequence()
-            .filter { it.hasCompiledItem() }
-            .map { it.compiledItem }
-            .filter { it.hasRef() }
-            .map { it.ref }
-            .flatMap {
-                // If resource id is available prefer this id to name.
-                when {
-                    it.id != 0 -> listOfNotNull(model.resourceStore.getResource(it.id))
-                    else -> model.resourceStore.getResourcesFromUrl("@${it.name}")
-                }.asSequence()
-            }
-            .forEach { ResourceUsageModel.markReachable(it) }
-        node.element.childList.forEach { recordUsagesFromNode(it, model) }
+}
+fun recordUsagesFromNode(node: XmlNode, model: ResourceShrinkerModel) {
+    // Records only resources from element attributes that have reference items with resolved
+    // ids or names.
+    if (!node.hasElement()) {
+        return
     }
+    node.element.attributeList.asSequence()
+        .filter { it.hasCompiledItem() }
+        .map { it.compiledItem }
+        .filter { it.hasRef() }
+        .map { it.ref }
+        .flatMap {
+            // If resource id is available prefer this id to name.
+            when {
+                it.id != 0 -> listOfNotNull(model.resourceStore.getResource(it.id))
+                else -> model.resourceStore.getResourcesFromUrl("@${it.name}")
+            }.asSequence()
+        }
+        .forEach { ResourceUsageModel.markReachable(it) }
+    node.element.childList.forEach { recordUsagesFromNode(it, model) }
 }
diff --git a/src/resourceshrinker/java/com/android/build/shrinker/usages/ToolsAttributeUsageRecorder.kt b/src/resourceshrinker/java/com/android/build/shrinker/usages/ToolsAttributeUsageRecorder.kt
index 0dae39a..8e43cd8 100644
--- a/src/resourceshrinker/java/com/android/build/shrinker/usages/ToolsAttributeUsageRecorder.kt
+++ b/src/resourceshrinker/java/com/android/build/shrinker/usages/ToolsAttributeUsageRecorder.kt
@@ -16,9 +16,11 @@
 
 package com.android.build.shrinker.usages
 
+import com.android.SdkConstants.TOOLS_NS_NAME
 import com.android.SdkConstants.VALUE_STRICT
 import com.android.build.shrinker.ResourceShrinkerModel
 import com.android.utils.XmlUtils
+import com.google.common.collect.ImmutableMap
 import com.google.common.collect.ImmutableMap.copyOf
 import java.io.Reader
 import java.nio.file.Files
@@ -37,9 +39,6 @@
  * @param rawResourcesPath path to folder with resources in raw format.
  */
 class ToolsAttributeUsageRecorder(val rawResourcesPath: Path) : ResourceUsageRecorder {
-    companion object {
-        private val TOOLS_NAMESPACE = "http://schemas.android.com/tools"
-    }
 
     override fun recordUsages(model: ResourceShrinkerModel) {
         Files.walk(rawResourcesPath)
@@ -48,42 +47,48 @@
     }
 
     private fun processRawXml(path: Path, model: ResourceShrinkerModel) {
-        processResourceToolsAttributes(path).forEach { key, value ->
-            when (key) {
-                "keep" -> model.resourceStore.recordKeepToolAttribute(value)
-                "discard" -> model.resourceStore.recordDiscardToolAttribute(value)
-                "shrinkMode" ->
-                    if (value == VALUE_STRICT) {
-                        model.resourceStore.safeMode = false
-                    }
-            }
-        }
-    }
-
-    private fun processResourceToolsAttributes(path: Path): Map<String, String> {
-        val toolsAttributes = mutableMapOf<String, String>()
-        XmlUtils.getUtfReader(path.toFile()).use { reader: Reader ->
-            val factory = XMLInputFactory.newInstance()
-            val xmlStreamReader = factory.createXMLStreamReader(reader)
-
-            var rootElementProcessed = false
-            while (!rootElementProcessed && xmlStreamReader.hasNext()) {
-                xmlStreamReader.next()
-                if (xmlStreamReader.isStartElement) {
-                    if (xmlStreamReader.localName == "resources") {
-                        for (i in 0 until xmlStreamReader.attributeCount) {
-                            if (xmlStreamReader.getAttributeNamespace(i) == TOOLS_NAMESPACE) {
-                                toolsAttributes.put(
-                                    xmlStreamReader.getAttributeLocalName(i),
-                                    xmlStreamReader.getAttributeValue(i)
-                                )
-                            }
-                        }
-                    }
-                    rootElementProcessed = true
-                }
-            }
-        }
-        return copyOf(toolsAttributes)
+        processRawXml(XmlUtils.getUtfReader(path.toFile()), model)
     }
 }
+
+fun processRawXml(reader: Reader, model: ResourceShrinkerModel) {
+    processResourceToolsAttributes(reader).forEach { key, value ->
+        when (key) {
+            "keep" -> model.resourceStore.recordKeepToolAttribute(value)
+            "discard" -> model.resourceStore.recordDiscardToolAttribute(value)
+            "shrinkMode" ->
+                if (value == VALUE_STRICT) {
+                    model.resourceStore.safeMode = false
+                }
+        }
+    }
+}
+
+fun processResourceToolsAttributes(utfReader: Reader?): ImmutableMap<String, String> {
+    val toolsAttributes = mutableMapOf<String, String>()
+    utfReader.use { reader: Reader? ->
+        val factory = XMLInputFactory.newInstance()
+        val xmlStreamReader = factory.createXMLStreamReader(reader)
+
+        var rootElementProcessed = false
+        while (!rootElementProcessed && xmlStreamReader.hasNext()) {
+            xmlStreamReader.next()
+            if (xmlStreamReader.isStartElement) {
+                if (xmlStreamReader.localName == "resources") {
+                    for (i in 0 until xmlStreamReader.attributeCount) {
+                        val namespace = "http://schemas.android.com/tools"
+                        if (xmlStreamReader.getAttributeNamespace(i) == namespace) {
+                            toolsAttributes.put(
+                                xmlStreamReader.getAttributeLocalName(i),
+                                xmlStreamReader.getAttributeValue(i)
+                            )
+                        }
+                    }
+                }
+                rootElementProcessed = true
+            }
+        }
+    }
+    return copyOf(toolsAttributes)
+}
+
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 238594e..d55e5c5 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -9,6 +9,7 @@
 
 import com.android.tools.r8.R8Command.Builder;
 import com.android.tools.r8.TestBase.Backend;
+import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.AndroidTestResource;
 import com.android.tools.r8.benchmarks.BenchmarkResults;
 import com.android.tools.r8.dexsplitter.SplitterTestBase.RunInterface;
 import com.android.tools.r8.dexsplitter.SplitterTestBase.SplitRunner;
@@ -16,6 +17,7 @@
 import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
 import com.android.tools.r8.keepanno.KeepEdgeAnnotationsTest;
 import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.origin.PathOrigin;
 import com.android.tools.r8.profile.art.ArtProfileConsumer;
 import com.android.tools.r8.profile.art.ArtProfileProvider;
 import com.android.tools.r8.profile.art.model.ExternalArtProfile;
@@ -81,6 +83,7 @@
   private final List<Path> mainDexRulesFiles = new ArrayList<>();
   private final List<String> applyMappingMaps = new ArrayList<>();
   private final List<Path> features = new ArrayList<>();
+  private Path resourceShrinkerOutput = null;
   private PartitionMapConsumer partitionMapConsumer = null;
 
   @Override
@@ -141,6 +144,7 @@
     }
 
     class Box {
+
       private List<ProguardConfigurationRule> syntheticProguardRules;
       private ProguardConfiguration proguardConfiguration;
     }
@@ -166,7 +170,8 @@
             graphConsumer,
             getMinApiLevel(),
             features,
-            residualArtProfiles);
+            residualArtProfiles,
+            resourceShrinkerOutput);
     switch (allowedDiagnosticMessages) {
       case ALL:
         compileResult.getDiagnosticMessages().assertAllDiagnosticsMatch(new IsAnything<>());
@@ -868,4 +873,25 @@
     this.partitionMapConsumer = partitionMapConsumer;
     return self();
   }
+
+  public T addAndroidResources(AndroidTestResource testResource) throws IOException {
+    return addAndroidResources(
+        testResource, getState().getNewTempFile("resourceshrinkeroutput.zip"));
+  }
+
+  public T addAndroidResources(AndroidTestResource testResource, Path output) throws IOException {
+    addResourceShrinkerProviderAndConsumer(testResource.getResourceZip(), output);
+    return addProgramClassFileData(testResource.getRClass().getClassFileData());
+  }
+
+  private T addResourceShrinkerProviderAndConsumer(Path resources, Path output) throws IOException {
+    resourceShrinkerOutput = output;
+    getBuilder()
+        .setAndroidResourceProvider(
+            new ArchiveProtoAndroidResourceProvider(resources, new PathOrigin(resources)));
+    getBuilder()
+        .setAndroidResourceConsumer(
+            new ArchiveProtoAndroidResourceConsumer(resourceShrinkerOutput));
+    return self();
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/R8TestCompileResult.java b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
index 1ca26f0..82997e9 100644
--- a/src/test/java/com/android/tools/r8/R8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
@@ -6,8 +6,10 @@
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 
 import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.ResourceTableInspector;
 import com.android.tools.r8.dexsplitter.SplitterTestBase.SplitRunner;
 import com.android.tools.r8.profile.art.model.ExternalArtProfile;
 import com.android.tools.r8.profile.art.utils.ArtProfileInspector;
@@ -19,6 +21,7 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ThrowingBiConsumer;
 import com.android.tools.r8.utils.ThrowingConsumer;
+import com.android.tools.r8.utils.ZipUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.graphinspector.GraphInspector;
@@ -36,6 +39,7 @@
   private final CollectingGraphConsumer graphConsumer;
   private final List<Path> features;
   private final List<ExternalArtProfile> residualArtProfiles;
+  private final Path resourceShrinkerOutput;
 
   R8TestCompileResult(
       TestState state,
@@ -48,7 +52,8 @@
       CollectingGraphConsumer graphConsumer,
       int minApiLevel,
       List<Path> features,
-      List<ExternalArtProfile> residualArtProfiles) {
+      List<ExternalArtProfile> residualArtProfiles,
+      Path resourceShrinkerOutput) {
     super(state, app, minApiLevel, outputMode, libraryDesugaringTestConfiguration);
     this.proguardConfiguration = proguardConfiguration;
     this.syntheticProguardRules = syntheticProguardRules;
@@ -56,6 +61,7 @@
     this.graphConsumer = graphConsumer;
     this.features = features;
     this.residualArtProfiles = residualArtProfiles;
+    this.resourceShrinkerOutput = resourceShrinkerOutput;
   }
 
   @Override
@@ -149,6 +155,15 @@
     return self();
   }
 
+  public <E extends Throwable> R8TestCompileResult inspectShrunkenResources(
+      Consumer<ResourceTableInspector> consumer) throws IOException {
+    assertNotNull(resourceShrinkerOutput);
+    consumer.accept(
+        new ResourceTableInspector(
+            ZipUtils.readSingleEntry(resourceShrinkerOutput, "resources.pb")));
+    return self();
+  }
+
   public GraphInspector graphInspector() throws IOException {
     assert graphConsumer != null;
     return new GraphInspector(graphConsumer, inspector());
diff --git a/src/test/java/com/android/tools/r8/TestBuilder.java b/src/test/java/com/android/tools/r8/TestBuilder.java
index 4e98e36..84ccfd9 100644
--- a/src/test/java/com/android/tools/r8/TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestBuilder.java
@@ -136,10 +136,6 @@
     return addProgramClasses(classes).addInnerClasses(classes);
   }
 
-  public T addAndroidResources(AndroidTestResource testResource) throws IOException {
-    return addProgramClassFileData(testResource.getRClass().getClassFileData());
-  }
-
   public T addInnerClasses(Class<?>... classes) throws IOException {
     return addInnerClasses(Arrays.asList(classes));
   }
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index eceaf77..f1f836b 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -212,8 +212,8 @@
 
   public static final String R8_TEST_BUCKET = "r8-test-results";
 
-  public static final String ASM_JAR = BUILD_DIR + "deps/asm-9.5.jar";
-  public static final String ASM_UTIL_JAR = BUILD_DIR + "deps/asm-util-9.5.jar";
+  public static final String ASM_JAR = BUILD_DIR + "deps/asm-9.6.jar";
+  public static final String ASM_UTIL_JAR = BUILD_DIR + "deps/asm-util-9.6.jar";
 
   public static final Path API_SAMPLE_JAR =
       Paths.get(getProjectRoot(), "tests", "r8_api_usage_sample.jar");
diff --git a/src/test/java/com/android/tools/r8/androidresources/AndroidResourceTestingUtils.java b/src/test/java/com/android/tools/r8/androidresources/AndroidResourceTestingUtils.java
index c1dde31..63de716 100644
--- a/src/test/java/com/android/tools/r8/androidresources/AndroidResourceTestingUtils.java
+++ b/src/test/java/com/android/tools/r8/androidresources/AndroidResourceTestingUtils.java
@@ -6,6 +6,10 @@
 import static com.android.tools.r8.TestBase.javac;
 import static com.android.tools.r8.TestBase.transformer;
 
+import com.android.aapt.Resources;
+import com.android.aapt.Resources.ConfigValue;
+import com.android.aapt.Resources.Item;
+import com.android.aapt.Resources.ResourceTable;
 import com.android.tools.r8.TestRuntime.CfRuntime;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.ProcessResult;
@@ -16,23 +20,28 @@
 import com.android.tools.r8.utils.StreamUtils;
 import com.android.tools.r8.utils.ZipUtils;
 import com.google.common.collect.MoreCollectors;
+import com.google.protobuf.InvalidProtocolBufferException;
+import java.io.File;
 import java.io.IOException;
 import java.lang.reflect.Field;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.TreeMap;
 import java.util.stream.Collectors;
+import org.junit.Assert;
 import org.junit.rules.TemporaryFolder;
 
 public class AndroidResourceTestingUtils {
 
   enum RClassType {
     STRING,
-    DRAWABLE;
+    DRAWABLE,
+    XML;
 
     public static RClassType fromClass(Class clazz) {
       String type = rClassWithoutNamespaceAndOuter(clazz).substring(2);
@@ -65,6 +74,14 @@
           + "</manifest>\n"
           + "\n";
 
+  public static String XML_FILE_WITH_STRING_REFERENCE =
+      "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+          + "<paths>\n"
+          + "    <files-path\n"
+          + "        name=\"@string/%s\"\n"
+          + "        path=\"let/it/be\" />\n"
+          + "</paths>";
+
   public static class AndroidTestRClass {
     // The original aapt2 generated R.java class
     private final Path javaFilePath;
@@ -103,10 +120,97 @@
     }
   }
 
+  // Easy traversable resource table.
+  public static class TestResourceTable {
+    private Map<String, ResourceNameToValueMapping> mapping = new HashMap<>();
+
+    private TestResourceTable(ResourceTable resourceTable) {
+      // For now, we don't have any test that use multiple packages.
+      assert resourceTable.getPackageCount() == 1;
+      for (Resources.Type type : resourceTable.getPackage(0).getTypeList()) {
+        String typeName = type.getName();
+        mapping.put(typeName, new ResourceNameToValueMapping(type));
+      }
+    }
+
+    public static TestResourceTable parseFrom(byte[] bytes) throws InvalidProtocolBufferException {
+      return new TestResourceTable(ResourceTable.parseFrom(bytes));
+    }
+
+    public boolean containsValueFor(String type, String name) {
+      return mapping.containsKey(type) && mapping.get(type).containsValueFor(name);
+    }
+
+    public static class ResourceNameToValueMapping {
+      private final Map<String, List<ResourceValue>> mapping = new HashMap<>();
+
+      public ResourceNameToValueMapping(Resources.Type type) {
+        for (Resources.Entry entry : type.getEntryList()) {
+          String name = entry.getName();
+          List<ResourceValue> entries = new ArrayList<>();
+          for (ConfigValue configValue : entry.getConfigValueList()) {
+            Item item = configValue.getValue().getItem();
+            // Currently supporting files and strings, we just flatten this to strings for easy
+            // testing.
+            if (item.hasFile()) {
+              entries.add(
+                  new ResourceValue(item.getFile().getPath(), configValue.getConfig().toString()));
+            } else if (item.hasStr()) {
+              entries.add(
+                  new ResourceValue(item.getStr().getValue(), configValue.getConfig().toString()));
+            }
+            mapping.put(name, entries);
+          }
+        }
+      }
+
+      public boolean containsValueFor(String name) {
+        return mapping.containsKey(name);
+      }
+
+      public static class ResourceValue {
+
+        private final String value;
+        private final String config;
+
+        public ResourceValue(String value, String config) {
+          this.value = value;
+          this.config = config;
+        }
+
+        public String getValue() {
+          return value;
+        }
+
+        public String getConfig() {
+          return config;
+        }
+      }
+    }
+  }
+
+  public static class ResourceTableInspector {
+
+    private final TestResourceTable testResourceTable;
+
+    public ResourceTableInspector(byte[] bytes) throws InvalidProtocolBufferException {
+      testResourceTable = TestResourceTable.parseFrom(bytes);
+    }
+
+    public void assertContainsResourceWithName(String type, String name) {
+      Assert.assertTrue(testResourceTable.containsValueFor(type, name));
+    }
+
+    public void assertDoesNotContainResourceWithName(String type, String name) {
+      Assert.assertFalse(testResourceTable.containsValueFor(type, name));
+    }
+  }
+
   public static class AndroidTestResourceBuilder {
     private String manifest;
     private final Map<String, String> stringValues = new TreeMap<>();
     private final Map<String, byte[]> drawables = new TreeMap<>();
+    private final Map<String, String> xmlFiles = new TreeMap<>();
     private final List<Class<?>> classesToRemap = new ArrayList<>();
 
     // Create the android resources from the passed in R classes
@@ -136,6 +240,12 @@
       return this;
     }
 
+    AndroidTestResourceBuilder addXmlWithStringReference(
+        String xmlName, String nameOfReferencedString) {
+      xmlFiles.put(xmlName, String.format(XML_FILE_WITH_STRING_REFERENCE, nameOfReferencedString));
+      return this;
+    }
+
     AndroidTestResourceBuilder withSimpleManifestAndAppNameString() {
       this.manifest = SIMPLE_MANIFEST_WITH_APP_NAME;
       addStringValue("app_name", "Most important app ever.");
@@ -160,13 +270,19 @@
         FileUtils.writeTextFile(
             temp.newFolder("res", "values").toPath().resolve("strings.xml"),
             createStringResourceXml());
+
       }
       if (drawables.size() > 0) {
+        File drawableFolder = temp.newFolder("res", "drawable");
         for (Entry<String, byte[]> entry : drawables.entrySet()) {
           FileUtils.writeToFile(
-              temp.newFolder("res", "drawable").toPath().resolve(entry.getKey()),
-              null,
-              entry.getValue());
+              drawableFolder.toPath().resolve(entry.getKey()), null, entry.getValue());
+        }
+      }
+      if (xmlFiles.size() > 0) {
+        File xmlFolder = temp.newFolder("res", "xml");
+        for (Entry<String, String> entry : xmlFiles.entrySet()) {
+          FileUtils.writeTextFile(xmlFolder.toPath().resolve(entry.getKey()), entry.getValue());
         }
       }
 
@@ -189,7 +305,7 @@
       ZipUtils.iter(
           rClassClassFileOutput,
           (entry, input) -> {
-            if (ZipUtils.isClassFile(entry.getName())) {
+            if (ZipUtils.isClassFile(entry.getName()) && !entry.getName().endsWith("R.class")) {
               rewrittenRClassFiles.add(
                   transformer(StreamUtils.streamToByteArrayClose(input), null)
                       .addClassTransformer(
@@ -215,6 +331,23 @@
                                   superName,
                                   interfaces);
                             }
+
+                            @Override
+                            public void visitNestHost(String nestHost) {
+                              // Don't make nest host relationsships
+                            }
+
+                            @Override
+                            public void visitOuterClass(
+                                String owner, String name, String descriptor) {
+                              // Don't make the inner<>outer class connection
+                            }
+
+                            @Override
+                            public void visitInnerClass(
+                                String name, String outerName, String innerName, int access) {
+                              // Don't make the inner<>outer class connection
+                            }
                           })
                       .transform());
             }
diff --git a/src/test/java/com/android/tools/r8/androidresources/AndroidResourcesPassthroughTest.java b/src/test/java/com/android/tools/r8/androidresources/AndroidResourcesPassthroughTest.java
deleted file mode 100644
index 41754aa..0000000
--- a/src/test/java/com/android/tools/r8/androidresources/AndroidResourcesPassthroughTest.java
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright (c) 2023, 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.androidresources;
-
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertThat;
-
-import com.android.tools.r8.ArchiveProtoAndroidResourceConsumer;
-import com.android.tools.r8.ArchiveProtoAndroidResourceProvider;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.AndroidTestResource;
-import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.AndroidTestResourceBuilder;
-import com.android.tools.r8.origin.PathOrigin;
-import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.ZipUtils;
-import java.nio.charset.Charset;
-import java.nio.file.Path;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameter;
-import org.junit.runners.Parameterized.Parameters;
-
-@RunWith(Parameterized.class)
-public class AndroidResourcesPassthroughTest extends TestBase {
-
-  @Parameter(0)
-  public TestParameters parameters;
-
-  @Parameters(name = "{0}")
-  public static TestParametersCollection parameters() {
-    return getTestParameters().withDexRuntimes().withAllApiLevels().build();
-  }
-
-  @Test
-  public void testR8() throws Exception {
-    String manifestPath = "AndroidManifest.xml";
-    String resourcePath = "resources.pb";
-    String pngPath = "res/drawable/foo.png";
-
-    AndroidTestResource testResource =
-        new AndroidTestResourceBuilder()
-            .withSimpleManifestAndAppNameString()
-            .addDrawable("foo.png", AndroidResourceTestingUtils.TINY_PNG)
-            .build(temp);
-    Path resources = testResource.getResourceZip();
-    Path output = temp.newFile("resources_out.zip").toPath();
-    testForR8(parameters.getBackend())
-        .addInnerClasses(getClass())
-        .setMinApi(parameters)
-        .addOptionsModification(
-            o -> {
-              o.androidResourceProvider =
-                  new ArchiveProtoAndroidResourceProvider(resources, new PathOrigin(resources));
-              o.androidResourceConsumer = new ArchiveProtoAndroidResourceConsumer(output);
-            })
-        .addKeepMainRule(FooBar.class)
-        .run(parameters.getRuntime(), FooBar.class)
-        .assertSuccessWithOutputLines("Hello World");
-    assertArrayEquals(
-        ZipUtils.readSingleEntry(output, manifestPath),
-        ZipUtils.readSingleEntry(resources, manifestPath));
-    assertArrayEquals(
-        ZipUtils.readSingleEntry(output, resourcePath),
-        ZipUtils.readSingleEntry(resources, resourcePath));
-    assertArrayEquals(
-        ZipUtils.readSingleEntry(output, pngPath), ZipUtils.readSingleEntry(resources, pngPath));
-    String rClassContent =
-        FileUtils.readTextFile(
-            testResource.getRClass().getJavaFilePath(), Charset.defaultCharset());
-    assertThat(rClassContent, containsString("app_name"));
-    assertThat(rClassContent, containsString("foo"));
-  }
-
-  public static class FooBar {
-
-    public static void main(String[] args) {
-      System.out.println("Hello World");
-    }
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/androidresources/SimpleNoCodeReferenceAndroidResourceTest.java b/src/test/java/com/android/tools/r8/androidresources/SimpleNoCodeReferenceAndroidResourceTest.java
new file mode 100644
index 0000000..34b2b9a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/androidresources/SimpleNoCodeReferenceAndroidResourceTest.java
@@ -0,0 +1,106 @@
+// Copyright (c) 2023, 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.androidresources;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.AndroidTestResource;
+import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.AndroidTestResourceBuilder;
+import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.ResourceTableInspector;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.ZipUtils;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.file.Path;
+import java.util.Arrays;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class SimpleNoCodeReferenceAndroidResourceTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection parameters() {
+    return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    String manifestPath = "AndroidManifest.xml";
+    String resourcePath = "resources.pb";
+    String pngPath = "res/drawable/foo.png";
+
+    AndroidTestResource testResource =
+        new AndroidTestResourceBuilder()
+            .withSimpleManifestAndAppNameString()
+            .addDrawable("foo.png", AndroidResourceTestingUtils.TINY_PNG)
+            .addDrawable("bar.png", AndroidResourceTestingUtils.TINY_PNG)
+            .build(temp);
+    Path resources = testResource.getResourceZip();
+    Path output = temp.newFile("resources_out.zip").toPath();
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .setMinApi(parameters)
+        .addAndroidResources(testResource, output)
+        .addKeepMainRule(FooBar.class)
+        .compile()
+        .inspectShrunkenResources(
+            shrunkenInspector -> {
+              // Reachable from the manifest
+              shrunkenInspector.assertContainsResourceWithName("string", "app_name");
+              // Not reachable from anything
+              shrunkenInspector.assertDoesNotContainResourceWithName("drawable", "foo");
+              shrunkenInspector.assertDoesNotContainResourceWithName("drawable", "bar");
+              try {
+                assertFalse(ZipUtils.containsEntry(output, pngPath));
+              } catch (IOException e) {
+                throw new RuntimeException(e);
+              }
+            })
+        .run(parameters.getRuntime(), FooBar.class)
+        .assertSuccessWithOutputLines("Hello World");
+    // We don't touch the manifest
+    assertArrayEquals(
+        ZipUtils.readSingleEntry(output, manifestPath),
+        ZipUtils.readSingleEntry(resources, manifestPath));
+
+    String rClassContent =
+        FileUtils.readTextFile(
+            testResource.getRClass().getJavaFilePath(), Charset.defaultCharset());
+    assertFalse(
+        Arrays.equals(
+            ZipUtils.readSingleEntry(output, resourcePath),
+            ZipUtils.readSingleEntry(resources, resourcePath)));
+    assertThat(rClassContent, containsString("app_name"));
+    assertThat(rClassContent, containsString("foo"));
+    assertThat(rClassContent, containsString("bar"));
+    assertTrue(ZipUtils.containsEntry(resources, pngPath));
+    ResourceTableInspector resourceTableInspector =
+        new ResourceTableInspector(
+            ZipUtils.readSingleEntry(testResource.getResourceZip(), resourcePath));
+    resourceTableInspector.assertContainsResourceWithName("string", "app_name");
+    resourceTableInspector.assertContainsResourceWithName("drawable", "foo");
+    resourceTableInspector.assertContainsResourceWithName("drawable", "bar");
+  }
+
+  public static class FooBar {
+
+    public static void main(String[] args) {
+      System.out.println("Hello World");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/androidresources/TestShrinkingWithCodeReferences.java b/src/test/java/com/android/tools/r8/androidresources/TestShrinkingWithCodeReferences.java
new file mode 100644
index 0000000..00b000f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/androidresources/TestShrinkingWithCodeReferences.java
@@ -0,0 +1,84 @@
+// Copyright (c) 2023, 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.androidresources;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.AndroidTestResource;
+import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.AndroidTestResourceBuilder;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TestShrinkingWithCodeReferences extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection parameters() {
+    return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+  }
+
+  public static AndroidTestResource getTestResources(TemporaryFolder temp) throws Exception {
+    return new AndroidTestResourceBuilder()
+        .withSimpleManifestAndAppNameString()
+        .addRClassInitializeWithDefaultValues(R.string.class, R.drawable.class)
+        .build(temp);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .setMinApi(parameters)
+        .addProgramClasses(FooBar.class)
+        .addAndroidResources(getTestResources(temp))
+        .addKeepMainRule(FooBar.class)
+        .compile()
+        .inspectShrunkenResources(
+            resourceTableInspector -> {
+              resourceTableInspector.assertContainsResourceWithName("string", "bar");
+              resourceTableInspector.assertContainsResourceWithName("string", "foo");
+              resourceTableInspector.assertContainsResourceWithName("drawable", "foobar");
+              resourceTableInspector.assertDoesNotContainResourceWithName(
+                  "string", "unused_string");
+              resourceTableInspector.assertDoesNotContainResourceWithName(
+                  "drawable", "unused_drawable");
+            })
+        .run(parameters.getRuntime(), FooBar.class)
+        .assertSuccess();
+  }
+
+  public static class FooBar {
+
+    public static void main(String[] args) {
+      if (System.currentTimeMillis() == 0) {
+        System.out.println(R.drawable.foobar);
+        System.out.println(R.string.bar);
+        System.out.println(R.string.foo);
+      }
+    }
+  }
+
+  public static class R {
+
+    public static class string {
+
+      public static int bar;
+      public static int foo;
+      public static int unused_string;
+    }
+
+    public static class drawable {
+
+      public static int foobar;
+      public static int unused_drawable;
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/androidresources/XmlFilesWithReferences.java b/src/test/java/com/android/tools/r8/androidresources/XmlFilesWithReferences.java
new file mode 100644
index 0000000..fb43ba7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/androidresources/XmlFilesWithReferences.java
@@ -0,0 +1,80 @@
+// Copyright (c) 2023, 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.androidresources;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.AndroidTestResource;
+import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.AndroidTestResourceBuilder;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class XmlFilesWithReferences extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection parameters() {
+    return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+  }
+
+  public static AndroidTestResource getTestResources(TemporaryFolder temp) throws Exception {
+    return new AndroidTestResourceBuilder()
+        .withSimpleManifestAndAppNameString()
+        .addRClassInitializeWithDefaultValues(R.string.class, R.xml.class)
+        .addXmlWithStringReference("foo_with_reference", "referenced_from_xml")
+        .build(temp);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .setMinApi(parameters)
+        .addProgramClasses(FooBar.class)
+        .addAndroidResources(getTestResources(temp))
+        .addKeepMainRule(FooBar.class)
+        .compile()
+        .inspectShrunkenResources(
+            resourceTableInspector -> {
+              resourceTableInspector.assertContainsResourceWithName(
+                  "string", "referenced_from_xml");
+              resourceTableInspector.assertContainsResourceWithName("xml", "foo_with_reference");
+
+              resourceTableInspector.assertDoesNotContainResourceWithName(
+                  "string", "unused_string");
+            })
+        .run(parameters.getRuntime(), FooBar.class)
+        .assertSuccess();
+  }
+
+  public static class FooBar {
+
+    public static void main(String[] args) {
+      if (System.currentTimeMillis() == 0) {
+        // Reference only the xml
+        System.out.println(R.xml.foo_with_reference);
+      }
+    }
+  }
+
+  public static class R {
+
+    public static class string {
+
+      public static int unused_string;
+      public static int referenced_from_xml;
+    }
+
+    public static class xml {
+      public static int foo_with_reference;
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/ClassFileVersion66Test.java b/src/test/java/com/android/tools/r8/cf/ClassFileVersion66Test.java
index d97f14c..aabbf04 100644
--- a/src/test/java/com/android/tools/r8/cf/ClassFileVersion66Test.java
+++ b/src/test/java/com/android/tools/r8/cf/ClassFileVersion66Test.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.cf;
 
-import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
@@ -34,8 +33,7 @@
     this.parameters = parameters;
   }
 
-  // TODO(b/301189814): Update ASM once it has a release with v22 support.
-  @Test(expected = CompilationFailedException.class)
+  @Test
   public void test() throws Exception {
     testForD8(parameters.getBackend())
         .addProgramClassFileData(transformer(TestClass.class).setVersion(CfVersion.V22).transform())
diff --git a/src/test/java/com/android/tools/r8/cf/frames/InitBeforeNewInInstructionStreamTest.java b/src/test/java/com/android/tools/r8/cf/frames/InitBeforeNewInInstructionStreamTest.java
index 1d93a7a..c0808d2 100644
--- a/src/test/java/com/android/tools/r8/cf/frames/InitBeforeNewInInstructionStreamTest.java
+++ b/src/test/java/com/android/tools/r8/cf/frames/InitBeforeNewInInstructionStreamTest.java
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.cf.frames;
 
-import static org.junit.Assert.fail;
+import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -41,7 +41,7 @@
   public void testJvm() throws Exception {
     parameters.assumeJvmTestParameters();
     testForJvm(parameters)
-        .addProgramClassFileData(patchedDump())
+        .addProgramClassFileData(dump())
         .run(parameters.getRuntime(), MAIN_CLASS)
         .assertSuccessWithOutput(EXPECTED_OUTPUT);
   }
@@ -50,7 +50,7 @@
   public void testD8() throws Exception {
     parameters.assumeDexRuntime();
     testForD8(parameters.getBackend())
-        .addProgramClassFileData(patchedDump())
+        .addProgramClassFileData(dump())
         .setMinApi(parameters)
         .run(parameters.getRuntime(), MAIN_CLASS)
         .assertSuccessWithOutput(EXPECTED_OUTPUT);
@@ -60,21 +60,16 @@
   public void testR8() throws Exception {
     parameters.assumeR8TestParameters();
     testForR8(parameters.getBackend())
-        .addProgramClassFileData(patchedDump())
+        .addProgramClassFileData(dump())
         .addKeepMainRule(MAIN_CLASS)
         .setMinApi(parameters)
         .run(parameters.getRuntime(), MAIN_CLASS)
         .assertSuccessWithOutput(EXPECTED_OUTPUT);
   }
 
-  // This is reproducing b/b274337639, where a new instruction is before the corresponding
-  // invokespecial of <init> in the instruction stream. The code is correct as control flow ensures
-  // new is called before init, and the stack map encodes this.
-  //
-  // Writing this code with ASM did not generate the correct stack map, so using javassist to patch
-  // it up.
-
-  public static byte[] patchedDump() throws Exception {
+  @Test
+  public void checkDump() throws Exception {
+    parameters.assumeJvmTestParameters();
     ClassPool classPool = new ClassPool();
     classPool.insertClassPath(new ByteArrayClassPath(MAIN_CLASS, dump()));
     CtClass clazz = classPool.get(MAIN_CLASS);
@@ -85,24 +80,19 @@
     byte[] stackMapTable = stackMapTableAttribute.get();
     // Uninitialized has type 8 in the stack map. See
     // https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-4.html#jvms-4.7.4.
-    // The instruction index 0 generated by ASM is not correct, patch to index 12.
-    if (stackMapTable[15] == 8
-        && stackMapTable[16] == 0
-        && stackMapTable[17] == 0
-        && stackMapTable[18] == 8
-        && stackMapTable[19] == 0
-        && stackMapTable[20] == 0) {
-      stackMapTable[17] = 12;
-      stackMapTable[20] = 12;
-    } else {
-      // If an ASM update fails here maybe the stack map is generated correctly and the javassist
-      // patching can be removed.
-      fail("Unexpected class file*");
-    }
-    stackMapTableAttribute.set(stackMapTable);
-    return clazz.toBytecode();
+    // Keep to validate https://gitlab.ow2.org/asm/asm/-/issues/317995 is fixed.
+    assertTrue(
+        stackMapTable[15] == 8
+            && stackMapTable[16] == 0
+            && stackMapTable[17] == 12
+            && stackMapTable[18] == 8
+            && stackMapTable[19] == 0
+            && stackMapTable[20] == 12);
   }
 
+  // This is reproducing b/274337639, where a new instruction is before the corresponding
+  // invokespecial of <init> in the instruction stream. The code is correct as control flow ensures
+  // new is called before init, and the stack map encodes this.
   public static byte[] dump() throws Exception {
 
     ClassWriter classWriter = new ClassWriter(0);
diff --git a/third_party/dependencies.tar.gz.sha1 b/third_party/dependencies.tar.gz.sha1
index 8d3ba4e..c41f552 100644
--- a/third_party/dependencies.tar.gz.sha1
+++ b/third_party/dependencies.tar.gz.sha1
@@ -1 +1 @@
-a8ce32b32965771099a4e91fedf13abfda7ffbed
+0276892a18f142c7399e09f5dada5e5f98857114
\ No newline at end of file
diff --git a/third_party/dependencies_new.tar.gz.sha1 b/third_party/dependencies_new.tar.gz.sha1
deleted file mode 100644
index 01f988b..0000000
--- a/third_party/dependencies_new.tar.gz.sha1
+++ /dev/null
@@ -1 +0,0 @@
-d5238791ca8a92b1ea3dcd91b013ef41e1b5ca38
diff --git a/third_party/dependencies_plugin.tar.gz.sha1 b/third_party/dependencies_plugin.tar.gz.sha1
index 08fb793..73510da 100644
--- a/third_party/dependencies_plugin.tar.gz.sha1
+++ b/third_party/dependencies_plugin.tar.gz.sha1
@@ -1 +1 @@
-77f5f4042c4340df908bb39cdb40a67009195cac
\ No newline at end of file
+577ea397b7e8db5975176c059ce1c5e82e2ea085
\ No newline at end of file
diff --git a/tools/asmifier.py b/tools/asmifier.py
index 9af1499..f599876 100755
--- a/tools/asmifier.py
+++ b/tools/asmifier.py
@@ -10,7 +10,7 @@
 import sys
 import utils
 
-ASM_VERSION = '9.5'
+ASM_VERSION = '9.6'
 ASM_JAR = os.path.join(utils.DEPENDENCIES_DIR, 'org', 'ow2', 'asm', 'asm',
                        ASM_VERSION, 'asm-' + ASM_VERSION + '.jar')
 ASM_UTIL_JAR = os.path.join(utils.DEPENDENCIES_DIR, 'org', 'ow2', 'asm',
diff --git a/tools/create_local_maven_with_dependencies.py b/tools/create_local_maven_with_dependencies.py
index e5d8885..fd06fa4 100755
--- a/tools/create_local_maven_with_dependencies.py
+++ b/tools/create_local_maven_with_dependencies.py
@@ -21,13 +21,12 @@
 ]
 
 ANDRDID_SUPPORT_VERSION = '25.4.0'
-ASM_VERSION = '9.5'
+ASM_VERSION = '9.6' # When updating update tools/asmifier.py and Toolhelper as well.
 ESPRESSO_VERSION = '3.0.0'
 FASTUTIL_VERSION = '7.2.1'
 KOTLIN_METADATA_VERSION = '0.7.0'
-KOTLIN_VERSION = '1.8.0'
-GUAVA_VERSION = '31.1-jre'
-GUAVA_VERSION_NEW = '32.1.2-jre'
+KOTLIN_VERSION = '1.9.0'
+GUAVA_VERSION = '32.1.2-jre'
 GSON_VERSION = '2.10.1'
 JAVASSIST_VERSION = '3.29.2-GA'
 JUNIT_VERSION = '4.13-beta-2'
@@ -39,17 +38,51 @@
 # Resource shrinker dependency versions
 AAPT2_PROTO_VERSION = '8.2.0-alpha10-10154469'
 PROTOBUF_VERSION = '3.19.3'
-STUDIO_SDK_VERSION = '31.2.0-alpha10'
+STUDIO_SDK_VERSION = '31.2.0-rc01'
 
 BUILD_DEPENDENCIES = [
     'com.google.code.gson:gson:{version}'.format(version=GSON_VERSION),
     'com.google.guava:guava:{version}'.format(version=GUAVA_VERSION),
     'it.unimi.dsi:fastutil:{version}'.format(version=FASTUTIL_VERSION),
-    'org.jetbrains.kotlinx:kotlinx-metadata-jvm:{version}'.format(
-        version=KOTLIN_METADATA_VERSION),
+
     'org.ow2.asm:asm:{version}'.format(version=ASM_VERSION),
     'org.ow2.asm:asm-util:{version}'.format(version=ASM_VERSION),
     'org.ow2.asm:asm-commons:{version}'.format(version=ASM_VERSION),
+
+    'com.google.errorprone:javac:9+181-r4173-1',
+
+    'com.android.tools.build:aapt2-proto:{version}'.format(
+        version=AAPT2_PROTO_VERSION),
+    'com.android.tools.layoutlib:layoutlib-api:{version}'.format(
+        version=STUDIO_SDK_VERSION),
+    'com.android.tools:common:{version}'.format(version=STUDIO_SDK_VERSION),
+    'com.android.tools:sdk-common:{version}'.format(version=STUDIO_SDK_VERSION),
+    'com.google.protobuf:protobuf-java:{version}'.format(
+        version=PROTOBUF_VERSION),
+
+    'org.jetbrains.kotlin:kotlin-assignment-compiler-plugin-embeddable:{version}'.format(
+        version=KOTLIN_VERSION),
+    'org.jetbrains.kotlin:kotlin-compiler-embeddable:{version}'.format(
+        version=KOTLIN_VERSION),
+    'org.jetbrains.kotlin:kotlin-gradle-plugin-api:{version}'.format(
+        version=KOTLIN_VERSION),
+    'org.jetbrains.kotlin:kotlin-gradle-plugin-idea:{version}'.format(
+        version=KOTLIN_VERSION),
+    'org.jetbrains.kotlin:kotlin-reflect:{version}'.format(
+        version=KOTLIN_VERSION),
+    'org.jetbrains.kotlin:kotlin-sam-with-receiver-compiler-plugin-embeddable:{version}'.format(
+        version=KOTLIN_VERSION),
+    'org.jetbrains.kotlin:kotlin-script-runtime:{version}'.format(
+        version=KOTLIN_VERSION),
+    'org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:{version}'.format(
+        version=KOTLIN_VERSION),
+    'org.jetbrains.kotlin:kotlin-stdlib-jdk8:{version}'.format(
+        version=KOTLIN_VERSION),
+    'org.jetbrains.kotlin:kotlin-tooling-core:{version}'.format(
+        version=KOTLIN_VERSION),
+    'org.jetbrains.kotlinx:kotlinx-metadata-jvm:{version}'.format(
+        version=KOTLIN_METADATA_VERSION),
+    'org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:4.1.3',
 ]
 
 TEST_DEPENDENCIES = [
@@ -60,44 +93,17 @@
     'com.google.errorprone:error_prone_core:{version}'.format(
         version=ERROR_PRONE_VERSION),
     'org.javassist:javassist:{version}'.format(version=JAVASSIST_VERSION),
-    'org.jetbrains.kotlin:kotlin-stdlib:{version}'.format(
-        version=KOTLIN_VERSION),
-    'org.jetbrains.kotlin:kotlin-reflect:{version}'.format(
-        version=KOTLIN_VERSION),
     'org.mockito:mockito-core:{version}'.format(version=MOCKITO_VERSION),
     'org.testng:testng:{version}'.format(version=TESTNG_VERSION),
 ]
 
-NEW_DEPENDENCIES = [
-    'com.google.guava:guava:{version}'.format(version=GUAVA_VERSION_NEW),
-    'org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:4.0.6',
-    'org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.8.10',
-    'org.jetbrains.kotlin:kotlin-gradle-plugin-idea:1.8.10',
-    'org.jetbrains.kotlin:kotlin-reflect:1.6.10',
-    'org.jetbrains.kotlin:kotlin-reflect:1.8.10',
-    'org.jetbrains.kotlin:kotlin-script-runtime:1.8.10',
-    'org.jetbrains.kotlin:kotlin-tooling-core:1.8.10',
-    'net.ltgt.errorprone:net.ltgt.errorprone.gradle.plugin:3.0.1',
-    'com.google.errorprone:javac:9+181-r4173-1',
-    # Gradle 8.3
-    'org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:4.1.0',
-    'org.jetbrains.kotlin:kotlin-assignment-compiler-plugin-embeddable:1.9.0',
-    'org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.9.0',
-    'org.jetbrains.kotlin:kotlin-reflect:1.9.0',
-    'org.jetbrains.kotlin:kotlin-script-runtime:1.9.0',
-    'org.jetbrains.kotlin:kotlin-sam-with-receiver-compiler-plugin-embeddable:1.9.0',
-    # Resource shrinker
-    'com.android.tools.build:aapt2-proto:{version}'.format(
-        version=AAPT2_PROTO_VERSION),
-    'com.android.tools.layoutlib:layoutlib-api:{version}'.format(
-        version=STUDIO_SDK_VERSION),
-    'com.android.tools:common:{version}'.format(version=STUDIO_SDK_VERSION),
-    'com.android.tools:sdk-common:{version}'.format(version=STUDIO_SDK_VERSION),
-    'com.google.protobuf:protobuf-java:{version}'.format(
-        version=PROTOBUF_VERSION),
-]
-
 PLUGIN_DEPENDENCIES = [
+  'org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:4.1.0',
+  'org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.9.10',
+  'net.ltgt.errorprone:net.ltgt.errorprone.gradle.plugin:3.0.1',
+
+  # Patched version of org.spdx.sbom:org.spdx.sbom.gradle.plugin:0.2.0.
+  # See commit message for a13217f333cc65fb602502ac446698dd74d10b7f.
   'org.spdx.sbom:org.spdx.sbom.gradle.plugin:0.2.0-r8-patch01',
   # See https://github.com/FasterXML/jackson-core/issues/999.
   'ch.randelshofer:fastdoubleparser:0.8.0',
@@ -204,13 +210,6 @@
             args, dependencies_path, repositories, BUILD_DEPENDENCIES + TEST_DEPENDENCIES)
         set_utime(dependencies_path)
         dependencies.append('dependencies')
-        dependencies_new_path = os.path.join(utils.THIRD_PARTY, 'dependencies_new')
-        remove_local_maven_repository(dependencies_new_path)
-        print("Downloading to " + dependencies_new_path)
-        create_local_maven_repository(
-           args, dependencies_new_path, repositories, NEW_DEPENDENCIES)
-        set_utime(dependencies_new_path)
-        dependencies.append('dependencies_new')
 
     upload_cmds = []
     for dependency in dependencies:
diff --git a/tools/update_prebuilds_in_android.py b/tools/update_prebuilds_in_android.py
index 31ee23e..623bacd 100755
--- a/tools/update_prebuilds_in_android.py
+++ b/tools/update_prebuilds_in_android.py
@@ -16,7 +16,8 @@
     'lib': [(utils.R8LIB, 'r8'),],
 }
 
-OTHER_TARGETS = ["LICENSE"]
+OTHER_TARGETS = ['LICENSE']
+SBOM_TARGETS = ['r8.spdx.json']
 KEEPANNO_JAR = 'keepanno-annotations.jar'
 
 
@@ -65,7 +66,7 @@
                 print('Copying: ' + src + '.map -> ' + dest + '.map')
                 copyfile(src + '.map', dest + '.map')
         else:
-            print('WARNING: Not copying ' + src + ' -> ' + dest + ', as' +
+            print('WARNING: Not copying ' + src + ' -> ' + dest + ', as ' +
                   dest + ' does not exist already')
 
 
@@ -75,8 +76,8 @@
     copy_targets(root, target_root, srcs, dests, maps=maps)
 
 
-def copy_other_targets(root, target_root):
-    copy_targets(root, target_root, OTHER_TARGETS, OTHER_TARGETS)
+def copy_other_targets(root, target_root, other_targets):
+    copy_targets(root, target_root, other_targets, other_targets)
 
 
 def download_hash(root, commit_hash, target, quiet=False):
@@ -96,9 +97,16 @@
 
 
 def main_download(hash, maps, targets, target_root, version, keepanno=False):
+    sbom_targets = []
+    if version:
+        semver = utils.check_basic_semver_version(version, allowPrerelease=True)
+        # Generation of SBOM started from version 8.3.13-dev.
+        if semver.larger_than(utils.SemanticVersion(8, 3, 12, 'dev')):
+            sbom_targets = SBOM_TARGETS
+
     jar_targets = JAR_TARGETS_MAP[targets]
     final_targets = list(map(
-        (lambda t: t[0] + '.jar'), jar_targets)) + OTHER_TARGETS
+        (lambda t: t[0] + '.jar'), jar_targets)) + OTHER_TARGETS + sbom_targets
     with utils.TempDir() as root:
         for target in final_targets:
             if hash:
@@ -110,12 +118,12 @@
             else:
                 assert version
                 download_version(root, version, target)
-                if maps and target not in OTHER_TARGETS:
+                if maps and target not in (OTHER_TARGETS + sbom_targets):
                     download_version(root, version, target + '.map')
                 if keepanno:
                     download_version(root, version, KEEPANNO_JAR)
         copy_jar_targets(root, target_root, jar_targets, maps)
-        copy_other_targets(root, target_root)
+        copy_other_targets(root, target_root, OTHER_TARGETS + sbom_targets)
         if keepanno:
             copy_targets(root, target_root, [KEEPANNO_JAR], [KEEPANNO_JAR])
 
@@ -129,7 +137,7 @@
         gradle_args.append('-Dorg.gradle.jvmargs=-Xmx' + max_memory_size)
     gradle.RunGradle(gradle_args)
     copy_jar_targets(utils.LIBS, target_root, jar_targets, maps)
-    copy_other_targets(utils.GENERATED_LICENSE_DIR, target_root)
+    copy_other_targets(utils.GENERATED_LICENSE_DIR, target_root, OTHER_TARGETS)
 
 
 def main(args):
diff --git a/tools/utils.py b/tools/utils.py
index db9844e..06fc3a7 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -720,8 +720,6 @@
         # Build metadata currently not suppported
 
     def larger_than(self, other):
-        if self.prerelease or other.prerelease:
-            raise Exception("Comparison with prerelease not implemented")
         if self.major > other.major:
             return True
         if self.major == other.major and self.minor > other.minor:
@@ -729,6 +727,12 @@
         if self.patch:
             return (self.major == other.major and self.minor == other.minor and
                     self.patch > other.patch)
+        if self.prerelease:
+            if other.prerelease:
+                return self.prerelease > other.prerelease
+            else:
+                return False
+
         else:
             return False