Merge "Revert^5 "Reland linking shared_libs to their modules"" into main
diff --git a/backported_fixes/Android.bp b/backported_fixes/Android.bp
index a20f3fc..0caea56 100644
--- a/backported_fixes/Android.bp
+++ b/backported_fixes/Android.bp
@@ -19,20 +19,26 @@
 
 genrule {
     name: "applied_backported_fixes",
-    tools: ["applied_backported_fixes_main"],
+    tools: ["applied_backported_fixes_property_writer"],
     srcs: [":applied_backported_fix_binpbs"],
     out: ["applied_backported_fixes.prop"],
-    cmd: "$(location applied_backported_fixes_main)" +
+    cmd: "$(location applied_backported_fixes_property_writer)" +
         " -p $(location applied_backported_fixes.prop)" +
         " $(in)",
 }
 
-java_library {
-    name: "backported_fixes_proto",
+filegroup {
+    name: "backported_fixes_proto_file",
     srcs: [
         "backported_fixes.proto",
     ],
+}
+
+java_library {
+    name: "backported_fixes_proto",
+    srcs: ["backported_fixes.proto"],
     host_supported: true,
+    sdk_version: "current",
 }
 
 java_library {
@@ -63,7 +69,7 @@
 }
 
 java_library {
-    name: "applied_backported_fixes_lib",
+    name: "backported_fixes_main_lib",
     srcs: ["src/java/com/android/build/backportedfixes/*.java"],
     static_libs: [
         "backported_fixes_common",
@@ -75,18 +81,35 @@
 }
 
 java_binary_host {
-    name: "applied_backported_fixes_main",
-    main_class: "com.android.build.backportedfixes.Main",
+    name: "applied_backported_fixes_property_writer",
+    main_class: "com.android.build.backportedfixes.WriteBackportedFixesPropFile",
     static_libs: [
-        "applied_backported_fixes_lib",
+        "backported_fixes_main_lib",
     ],
 }
 
+java_binary_host {
+    name: "backported_fixes_combiner",
+    main_class: "com.android.build.backportedfixes.CombineBackportedFixes",
+    static_libs: [
+        "backported_fixes_main_lib",
+    ],
+}
+
+// Combines BackportedFix binary proto files into a single BackportedFixes binary proto file.
+genrule_defaults {
+    name: "default_backported_fixes_combiner",
+    tools: ["backported_fixes_combiner"],
+    cmd: "$(location backported_fixes_combiner)" +
+        " -o $(out)" +
+        " $(in)",
+}
+
 java_test_host {
-    name: "applied_backported_fixes_test",
+    name: "backported_fixes_main_lib_test",
     srcs: ["tests/java/com/android/build/backportedfixes/*.java"],
     static_libs: [
-        "applied_backported_fixes_lib",
+        "backported_fixes_main_lib",
         "backported_fixes_proto",
         "junit",
         "truth",
@@ -97,19 +120,25 @@
     test_suites: ["general-tests"],
 }
 
+// Converts BackprotedFix text protos to binary protos
+genrule_defaults {
+    name: "default_backported_fix_binpbs",
+    tools: ["aprotoc"],
+    tool_files: [
+        ":backported_fixes_proto_file",
+    ],
+    cmd: "$(location aprotoc)  " +
+        " --encode=com.android.build.backportedfixes.BackportedFix" +
+        "  $(location :backported_fixes_proto_file)" +
+        " < $(in)" +
+        " > $(out); echo $(out)",
+}
+
 gensrcs {
     name: "applied_backported_fix_binpbs",
-    tools: ["aprotoc"],
+    defaults: ["default_backported_fix_binpbs"],
+    output_extension: "binpb",
     srcs: [
         "applied_fixes/*.txtpb",
     ],
-    tool_files: [
-        "backported_fixes.proto",
-    ],
-    output_extension: "binpb",
-    cmd: "$(location aprotoc)  " +
-        " --encode=com.android.build.backportedfixes.BackportedFix" +
-        "  $(location backported_fixes.proto)" +
-        " < $(in)" +
-        " > $(out); echo $(out)",
 }
diff --git a/backported_fixes/src/java/com/android/build/backportedfixes/CombineBackportedFixes.java b/backported_fixes/src/java/com/android/build/backportedfixes/CombineBackportedFixes.java
new file mode 100644
index 0000000..0592cc1
--- /dev/null
+++ b/backported_fixes/src/java/com/android/build/backportedfixes/CombineBackportedFixes.java
@@ -0,0 +1,65 @@
+
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.build.backportedfixes;
+
+import com.android.build.backportedfixes.common.Parser;
+
+import com.beust.jcommander.JCommander;
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.converters.FileConverter;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+
+
+/** Creates a BackportedFixes binary proto file from a list of BackportedFix proto binary files. */
+public final class CombineBackportedFixes {
+
+    @Parameter(description = "BackportedFix proto binary files",
+            converter = FileConverter.class,
+            required = true)
+    List<File> fixFiles;
+    @Parameter(description = "Write the BackportedFixes proto binary to this file",
+            names = {"--out","-o"},
+            converter = FileConverter.class,
+            required = true)
+    File outFile;
+
+    public static void main(String... argv) throws Exception {
+        CombineBackportedFixes main = new CombineBackportedFixes();
+        JCommander.newBuilder().addObject(main).build().parse(argv);
+        main.run();
+    }
+
+    CombineBackportedFixes() {
+    }
+
+    private void run() throws Exception {
+        try (var out = new FileOutputStream(outFile)) {
+            var fixes = Parser.parseBackportedFixFiles(fixFiles);
+            writeBackportedFixes(fixes, out);
+        }
+    }
+
+    static void writeBackportedFixes(BackportedFixes fixes, OutputStream out)
+            throws IOException {
+        fixes.writeTo(out);
+    }
+}
diff --git a/backported_fixes/src/java/com/android/build/backportedfixes/Main.java b/backported_fixes/src/java/com/android/build/backportedfixes/WriteBackportedFixesPropFile.java
similarity index 74%
rename from backported_fixes/src/java/com/android/build/backportedfixes/Main.java
rename to backported_fixes/src/java/com/android/build/backportedfixes/WriteBackportedFixesPropFile.java
index 79148cc..0ffb4ac 100644
--- a/backported_fixes/src/java/com/android/build/backportedfixes/Main.java
+++ b/backported_fixes/src/java/com/android/build/backportedfixes/WriteBackportedFixesPropFile.java
@@ -18,7 +18,6 @@
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 
-import com.android.build.backportedfixes.common.ClosableCollection;
 import com.android.build.backportedfixes.common.Parser;
 
 import com.beust.jcommander.JCommander;
@@ -33,27 +32,38 @@
 import java.util.List;
 import java.util.stream.Collectors;
 
-public final class Main {
-    @Parameter(description = "BackportedFix proto binary files", converter = FileConverter.class,
+
+/**
+ * Creates backported fix properties file.
+ *
+ * <p>Writes BitSet of backported fix aliases from a list of BackportedFix proto binary files and
+ * writes the property {@value PROPERTY_NAME} to a file.
+ */
+public final class WriteBackportedFixesPropFile {
+
+    private static final String PROPERTY_NAME = "ro.build.backported_fixes.alias_bitset.long_list";
+    @Parameter(description = "BackportedFix proto binary files",
+            converter = FileConverter.class,
             required = true)
     List<File> fixFiles;
     @Parameter(description = "The file to write the property value to.",
-            names = {"--property_file", "-p"}, converter = FileConverter.class, required = true)
+            names = {"--property_file", "-p"},
+            converter = FileConverter.class,
+            required = true)
     File propertyFile;
 
     public static void main(String... argv) throws Exception {
-        Main main = new Main();
+        WriteBackportedFixesPropFile main = new WriteBackportedFixesPropFile();
         JCommander.newBuilder().addObject(main).build().parse(argv);
         main.run();
     }
 
-    Main() {
+    WriteBackportedFixesPropFile() {
     }
 
     private void run() throws Exception {
-        try (var fixStreams = ClosableCollection.wrap(Parser.getFileInputStreams(fixFiles));
-             var out = Files.newWriter(propertyFile, UTF_8)) {
-            var fixes = Parser.parseBackportedFixes(fixStreams.getCollection());
+        try (var out = Files.newWriter(propertyFile, UTF_8)) {
+            var fixes = Parser.parseBackportedFixFiles(fixFiles);
             writeFixesAsAliasBitSet(fixes, out);
         }
     }
@@ -70,7 +80,7 @@
                 fixes.getFixesList().stream().mapToInt(BackportedFix::getAlias).toArray());
         String bsString = Arrays.stream(bsArray).mapToObj(Long::toString).collect(
                 Collectors.joining(","));
-        printWriter.printf("ro.build.backported_fixes.alias_bitset.long_list=%s", bsString);
+        printWriter.printf("%s=%s", PROPERTY_NAME, bsString);
         printWriter.println();
         if (printWriter.checkError()) {
             throw new RuntimeException("There was an error writing to " + out.toString());
diff --git a/backported_fixes/src/java/com/android/build/backportedfixes/common/ClosableCollection.java b/backported_fixes/src/java/com/android/build/backportedfixes/common/ClosableCollection.java
deleted file mode 100644
index 75b6730..0000000
--- a/backported_fixes/src/java/com/android/build/backportedfixes/common/ClosableCollection.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.build.backportedfixes.common;
-
-import com.google.common.collect.ImmutableList;
-
-import java.util.ArrayList;
-import java.util.Collection;
-
-/** An AutoCloseable holder for a collection of AutoCloseables. */
-public final class ClosableCollection<T extends AutoCloseable, C extends Collection<T>> implements
-        AutoCloseable {
-    C source;
-
-    /** Makes the collection AutoCloseable. */
-    public static <T extends AutoCloseable, C extends Collection<T>> ClosableCollection<T, C> wrap(
-            C source) {
-        return new ClosableCollection<>(source);
-    }
-
-    private ClosableCollection(C source) {
-        this.source = source;
-    }
-
-    /** Get the source collection. */
-    public C getCollection() {
-        return source;
-    }
-
-    /**
-     * Closes each item in the collection.
-     *
-     * @throws Exception if any close throws an an exception, a new exception is thrown with
-     *                   all the exceptions thrown closing the streams added as a suppressed
-     *                   exceptions.
-     */
-    @Override
-    public void close() throws Exception {
-        var failures = new ArrayList<Exception>();
-        for (T t : source) {
-            try {
-                t.close();
-            } catch (Exception e) {
-                failures.add(e);
-            }
-        }
-        if (!failures.isEmpty()) {
-            Exception e = new Exception(
-                    "%d of %d failed while closing".formatted(failures.size(), source.size()));
-            failures.forEach(e::addSuppressed);
-            throw e;
-        }
-    }
-}
diff --git a/backported_fixes/src/java/com/android/build/backportedfixes/common/Parser.java b/backported_fixes/src/java/com/android/build/backportedfixes/common/Parser.java
index 6b08b8f..6180fdc 100644
--- a/backported_fixes/src/java/com/android/build/backportedfixes/common/Parser.java
+++ b/backported_fixes/src/java/com/android/build/backportedfixes/common/Parser.java
@@ -15,9 +15,12 @@
  */
 package com.android.build.backportedfixes.common;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 import com.android.build.backportedfixes.BackportedFix;
 import com.android.build.backportedfixes.BackportedFixes;
 
+import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
 
 import java.io.File;
@@ -26,7 +29,10 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.BitSet;
+import java.util.Comparator;
 import java.util.List;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
 
 
 /** Static utilities for working with {@link BackportedFixes}. */
@@ -54,16 +60,79 @@
     /**
      * Creates a {@link BackportedFixes} from a list of {@link BackportedFix} binary proto streams.
      */
-    public static BackportedFixes parseBackportedFixes(List<? extends InputStream> fixStreams)
-            throws
-            IOException {
-        var fixes = BackportedFixes.newBuilder();
-        for (var s : fixStreams) {
-            BackportedFix fix = BackportedFix.parseFrom(s);
-            fixes.addFixes(fix);
-            s.close();
+    public static BackportedFixes parseBackportedFixFiles(List<File> fixFiles)
+            throws IOException {
+        try {
+            return fixFiles.stream().map(Parser::tunelFileInputStream)
+                    .map(Parser::tunnelParse)
+                    .sorted(Comparator.comparing(BackportedFix::getKnownIssue))
+                    .collect(fixCollector());
+
+        } catch (TunnelException e) {
+            throw e.rethrow(FileNotFoundException.class, IOException.class);
         }
-        return fixes.build();
+    }
+
+
+    private static Collector<BackportedFix, ?, BackportedFixes> fixCollector() {
+        return Collectors.collectingAndThen(Collectors.toList(), fixList -> {
+            var result = BackportedFixes.newBuilder();
+            result.addAllFixes(fixList);
+            return result.build();
+        });
+    }
+
+    private static FileInputStream tunelFileInputStream(File file) throws TunnelException {
+        try {
+            return new FileInputStream(file);
+        } catch (FileNotFoundException e) {
+            throw new TunnelException(e);
+        }
+    }
+
+    private static BackportedFix tunnelParse(InputStream s) throws TunnelException {
+        try {
+            var fix = BackportedFix.parseFrom(s);
+            s.close();
+            return fix;
+        } catch (IOException e) {
+            throw new TunnelException(e);
+        }
+    }
+
+    private static class TunnelException extends RuntimeException {
+        TunnelException(Exception cause) {
+            super("If you see this TunnelException something went wrong.  It should always be rethrown as the cause.", cause);
+        }
+
+        <X extends Exception> RuntimeException rethrow(Class<X> exceptionClazz) throws X {
+            checkNotNull(exceptionClazz);
+            Throwables.throwIfInstanceOf(getCause(), exceptionClazz);
+            throw exception(
+                    getCause(),
+                    "rethrow(%s) doesn't match underlying exception", exceptionClazz);
+        }
+
+        public <X1 extends Exception, X2 extends Exception> RuntimeException rethrow(
+                Class<X1> exceptionClazz1, Class<X2> exceptionClazz2) throws X1, X2 {
+            checkNotNull(exceptionClazz1);
+            checkNotNull(exceptionClazz2);
+            Throwables.throwIfInstanceOf(getCause(), exceptionClazz1);
+            Throwables.throwIfInstanceOf(getCause(), exceptionClazz2);
+            throw exception(
+                    getCause(),
+                    "rethrow(%s, %s) doesn't match underlying exception",
+                    exceptionClazz1,
+                    exceptionClazz2);
+        }
+
+        private static ClassCastException exception(
+                Throwable cause, String message, Object... formatArgs) {
+            ClassCastException result = new ClassCastException(String.format(message, formatArgs));
+            result.initCause(cause);
+            return result;
+        }
+
     }
 
     private Parser() {
diff --git a/backported_fixes/tests/java/com/android/build/backportedfixes/CombineBackportedFixesTest.java b/backported_fixes/tests/java/com/android/build/backportedfixes/CombineBackportedFixesTest.java
new file mode 100644
index 0000000..21d5f1e
--- /dev/null
+++ b/backported_fixes/tests/java/com/android/build/backportedfixes/CombineBackportedFixesTest.java
@@ -0,0 +1,41 @@
+
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.build.backportedfixes;
+
+import com.google.common.truth.Truth;
+
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/** Tests for {@link CombineBackportedFixes}. */
+public class CombineBackportedFixesTest {
+
+
+    @Test
+    public void writeBackportedFixes_default() throws IOException {
+        // Not much of a test, but there is not much to test.
+        BackportedFixes fixes = BackportedFixes.newBuilder()
+                .addFixes(BackportedFix.newBuilder().setKnownIssue(123).build())
+                .build();
+        var result = new ByteArrayOutputStream();
+        CombineBackportedFixes.writeBackportedFixes(fixes, result);
+        Truth.assertThat(BackportedFixes.parseFrom(result.toByteArray()))
+                .isEqualTo(fixes);
+    }
+}
diff --git a/backported_fixes/tests/java/com/android/build/backportedfixes/MainTest.java b/backported_fixes/tests/java/com/android/build/backportedfixes/WriteBackportedFixesPropFileTest.java
similarity index 87%
rename from backported_fixes/tests/java/com/android/build/backportedfixes/MainTest.java
rename to backported_fixes/tests/java/com/android/build/backportedfixes/WriteBackportedFixesPropFileTest.java
index 84061e1..3209c15 100644
--- a/backported_fixes/tests/java/com/android/build/backportedfixes/MainTest.java
+++ b/backported_fixes/tests/java/com/android/build/backportedfixes/WriteBackportedFixesPropFileTest.java
@@ -23,8 +23,8 @@
 import java.io.PrintWriter;
 import java.io.StringWriter;
 
-/** Tests for {@link Main}. */
-public class MainTest {
+/** Tests for {@link WriteBackportedFixesPropFile}. */
+public class WriteBackportedFixesPropFileTest {
 
 
     @Test
@@ -32,7 +32,7 @@
         BackportedFixes fixes = BackportedFixes.newBuilder().build();
         var result = new StringWriter();
 
-        Main.writeFixesAsAliasBitSet(fixes, new PrintWriter(result));
+        WriteBackportedFixesPropFile.writeFixesAsAliasBitSet(fixes, new PrintWriter(result));
 
         Truth.assertThat(result.toString())
                 .isEqualTo("""
@@ -50,7 +50,7 @@
                 .build();
         var result = new StringWriter();
 
-        Main.writeFixesAsAliasBitSet(fixes, new PrintWriter(result));
+        WriteBackportedFixesPropFile.writeFixesAsAliasBitSet(fixes, new PrintWriter(result));
 
         Truth.assertThat(result.toString())
                 .isEqualTo("""
diff --git a/backported_fixes/tests/java/com/android/build/backportedfixes/common/CloseableCollectionTest.java b/backported_fixes/tests/java/com/android/build/backportedfixes/common/CloseableCollectionTest.java
deleted file mode 100644
index d3d84a8..0000000
--- a/backported_fixes/tests/java/com/android/build/backportedfixes/common/CloseableCollectionTest.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.build.backportedfixes.common;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.common.truth.Correspondence;
-import com.google.common.truth.Truth;
-
-import org.junit.Test;
-
-/** Tests for {@link ClosableCollection}. */
-public class CloseableCollectionTest {
-
-    private static class FakeCloseable implements AutoCloseable {
-        private final boolean throwOnClose;
-        private final String name;
-
-
-        private boolean isClosed = false;
-
-        private FakeCloseable(String name, boolean throwOnClose) {
-            this.name = name;
-            this.throwOnClose = throwOnClose;
-
-        }
-
-        private static FakeCloseable named(String name) {
-            return new FakeCloseable(name, false);
-        }
-
-        private static FakeCloseable failing(String name) {
-            return new FakeCloseable(name, true);
-        }
-
-        public boolean isClosed() {
-            return isClosed;
-        }
-
-        @Override
-        public void close() throws Exception {
-            if (throwOnClose) {
-                throw new Exception(name + " close failed");
-            }
-            isClosed = true;
-        }
-    }
-
-
-    @Test
-    public void bothClosed() throws Exception {
-        var c = ImmutableSet.of(FakeCloseable.named("foo"), FakeCloseable.named("bar"));
-        try (var cc = ClosableCollection.wrap(c);) {
-            Truth.assertThat(cc.getCollection()).isSameInstanceAs(c);
-        }
-        Truth.assertThat(c)
-                .comparingElementsUsing(
-                        Correspondence.transforming(FakeCloseable::isClosed, "is closed"))
-                .containsExactly(true, true);
-    }
-
-    @Test
-    public void bothFailed() {
-        var c = ImmutableSet.of(FakeCloseable.failing("foo"), FakeCloseable.failing("bar"));
-
-        try {
-            try (var cc = ClosableCollection.wrap(c);) {
-                Truth.assertThat(cc.getCollection()).isSameInstanceAs(c);
-            }
-        } catch (Exception e) {
-            Truth.assertThat(e).hasMessageThat().isEqualTo("2 of 2 failed while closing");
-            Truth.assertThat(e.getSuppressed())
-                    .asList()
-                    .comparingElementsUsing(
-                            Correspondence.transforming(Exception::getMessage, "has a message of "))
-                    .containsExactly("foo close failed", "bar close failed");
-        }
-    }
-}
diff --git a/backported_fixes/tests/java/com/android/build/backportedfixes/common/ParserTest.java b/backported_fixes/tests/java/com/android/build/backportedfixes/common/ParserTest.java
index 444e694..57a0a40 100644
--- a/backported_fixes/tests/java/com/android/build/backportedfixes/common/ParserTest.java
+++ b/backported_fixes/tests/java/com/android/build/backportedfixes/common/ParserTest.java
@@ -23,15 +23,21 @@
 
 import com.google.common.collect.ImmutableList;
 
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
 
-import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.nio.file.Files;
 
 /** Tests for {@link Parser}.*/
 public class ParserTest {
 
+    @Rule
+    public TemporaryFolder mTempFolder = new TemporaryFolder();
+
     @Test
     public void getFileInputStreams() throws IOException {
         var results = Parser.getFileInputStreams(
@@ -53,15 +59,15 @@
     }
 
     @Test
-    public void parseBackportedFixes_empty() throws IOException {
-        var result = Parser.parseBackportedFixes(ImmutableList.of());
+    public void parseBackportedFixFiles_empty() throws IOException {
+        var result = Parser.parseBackportedFixFiles(ImmutableList.of());
         assertThat(result).isEqualTo(BackportedFixes.getDefaultInstance());
     }
 
+
     @Test
-    public void parseBackportedFixes_oneBlank() throws IOException {
-        var result = Parser.parseBackportedFixes(
-                ImmutableList.of(inputStream(BackportedFix.getDefaultInstance())));
+    public void parseBackportedFixFiles_oneBlank() throws IOException {
+        var result = Parser.parseBackportedFixFiles(ImmutableList.of(mTempFolder.newFile()));
 
         assertThat(result).isEqualTo(
                 BackportedFixes.newBuilder()
@@ -70,7 +76,7 @@
     }
 
     @Test
-    public void parseBackportedFixes_two() throws IOException {
+    public void parseBackportedFixFiles_two() throws IOException {
         BackportedFix ki123 = BackportedFix.newBuilder()
                 .setKnownIssue(123)
                 .setAlias(1)
@@ -79,8 +85,8 @@
                 .setKnownIssue(456)
                 .setAlias(2)
                 .build();
-        var result = Parser.parseBackportedFixes(
-                ImmutableList.of(inputStream(ki123), inputStream(ki456)));
+        var result = Parser.parseBackportedFixFiles(
+                ImmutableList.of(tempFile(ki456), tempFile(ki123)));
         assertThat(result).isEqualTo(
                 BackportedFixes.newBuilder()
                         .addFixes(ki123)
@@ -88,7 +94,11 @@
                         .build());
     }
 
-    private static ByteArrayInputStream inputStream(BackportedFix f) {
-        return new ByteArrayInputStream(f.toByteArray());
+    private File tempFile(BackportedFix fix) throws IOException {
+        File f = mTempFolder.newFile();
+        try (FileOutputStream out = new FileOutputStream(f)) {
+            fix.writeTo(out);
+            return f;
+        }
     }
 }
diff --git a/ci/build_device_and_tests b/ci/build_device_and_tests
new file mode 100755
index 0000000..63d3ce3
--- /dev/null
+++ b/ci/build_device_and_tests
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+#
+# Copyright 2024, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+set -euo pipefail
+
+build/soong/soong_ui.bash --make-mode build_test_suites
+$(build/soong/soong_ui.bash --dumpvar-mode HOST_OUT)/bin/build_test_suites --device-build $@
diff --git a/ci/build_metadata b/ci/build_metadata
index cd011c8..3e9218f 100755
--- a/ci/build_metadata
+++ b/ci/build_metadata
@@ -14,15 +14,31 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-set -ex
+set -x
+
+source build/make/shell_utils.sh
 
 export TARGET_PRODUCT=aosp_arm64
 export TARGET_RELEASE=trunk_staging
 export TARGET_BUILD_VARIANT=eng
 
+import_build_vars \
+        OUT_DIR \
+        DIST_DIR \
+        HOST_OUT_EXECUTABLES \
+    || exit $?
+
 TARGETS=(
     all_teams
+    source_tree_size
     release_config_metadata
 )
 
-build/soong/bin/m dist ${TARGETS[@]}
+# Build modules
+build/soong/bin/m dist ${TARGETS[@]} || exit $?
+
+# List all source files in the tree
+( \
+    $HOST_OUT_EXECUTABLES/source_tree_size -o $DIST_DIR/all_source_tree_files.pb \
+        && gzip -fn $DIST_DIR/all_source_tree_files.pb \
+) || exit $?
diff --git a/ci/build_test_suites b/ci/build_test_suites
index 9d11268..74470a8 100755
--- a/ci/build_test_suites
+++ b/ci/build_test_suites
@@ -13,6 +13,7 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
+set -euo pipefail
 
-build/soong/soong_ui.bash --make-mode build_test_suites || exit $?
-$(build/soong/soong_ui.bash --dumpvar-mode HOST_OUT)/bin/build_test_suites $@ || exit $?
+build/soong/soong_ui.bash --make-mode build_test_suites
+$(build/soong/soong_ui.bash --dumpvar-mode HOST_OUT)/bin/build_test_suites $@
diff --git a/ci/build_test_suites.py b/ci/build_test_suites.py
index cd9d76d..cdcba5a 100644
--- a/ci/build_test_suites.py
+++ b/ci/build_test_suites.py
@@ -30,9 +30,12 @@
 import test_discovery_agent
 
 
-REQUIRED_ENV_VARS = frozenset(['TARGET_PRODUCT', 'TARGET_RELEASE', 'TOP'])
+REQUIRED_ENV_VARS = frozenset(['TARGET_PRODUCT', 'TARGET_RELEASE', 'TOP', 'DIST_DIR'])
 SOONG_UI_EXE_REL_PATH = 'build/soong/soong_ui.bash'
 LOG_PATH = 'logs/build_test_suites.log'
+# Currently, this prevents the removal of those tags when they exist. In the future we likely
+# want the script to supply 'dist directly
+REQUIRED_BUILD_TARGETS = frozenset(['dist', 'droid', 'checkbuild'])
 
 
 class Error(Exception):
@@ -71,31 +74,28 @@
     if 'optimized_build' not in self.build_context.enabled_build_features:
       return BuildPlan(set(self.args.extra_targets), set())
 
+    if not self.build_context.test_infos:
+      logging.warning('Build context has no test infos, skipping optimizations.')
+      for target in self.args.extra_targets:
+        get_metrics_agent().report_unoptimized_target(target, 'BUILD_CONTEXT has no test infos.')
+      return BuildPlan(set(self.args.extra_targets), set())
+
     build_targets = set()
     packaging_commands_getters = []
-    test_discovery_zip_regexes = set()
-    optimization_rationale = ''
-    try:
-      # Do not use these regexes for now, only run this to collect data on what
-      # would be optimized.
-      test_discovery_zip_regexes = self._get_test_discovery_zip_regexes()
-      logging.info(f'Discovered test discovery regexes: {test_discovery_zip_regexes}')
-    except test_discovery_agent.TestDiscoveryError as e:
-      optimization_rationale = e.message
-      logging.warning(f'Unable to perform test discovery: {optimization_rationale}')
-    for target in self.args.extra_targets:
-      if optimization_rationale:
-        get_metrics_agent().report_unoptimized_target(target, optimization_rationale)
-      else:
-        regex = r'\b(%s)\b' % re.escape(target)
-        if any(re.search(regex, opt) for opt in test_discovery_zip_regexes):
-          get_metrics_agent().report_optimized_target(target)
+    # In order to roll optimizations out differently between test suites and
+    # device builds, we have separate flags.
+    enable_discovery = ('test_suites_zip_test_discovery'
+        in self.build_context.enabled_build_features
+        and not self.args.device_build
+    ) or (
+        'device_zip_test_discovery'
+        in self.build_context.enabled_build_features
+        and self.args.device_build
+    )
+    logging.info(f'Discovery mode is enabled= {enable_discovery}')
+    preliminary_build_targets = self._collect_preliminary_build_targets(enable_discovery)
 
-      if self._unused_target_exclusion_enabled(
-          target
-      ) and not self.build_context.build_target_used(target):
-        continue
-
+    for target in preliminary_build_targets:
       target_optimizer_getter = self.target_optimizations.get(target, None)
       if not target_optimizer_getter:
         build_targets.add(target)
@@ -111,6 +111,61 @@
 
     return BuildPlan(build_targets, packaging_commands_getters)
 
+  def _collect_preliminary_build_targets(self, enable_discovery: bool):
+    build_targets = set()
+    try:
+      test_discovery_zip_regexes = self._get_test_discovery_zip_regexes()
+      logging.info(f'Discovered test discovery regexes: {test_discovery_zip_regexes}')
+    except test_discovery_agent.TestDiscoveryError as e:
+      optimization_rationale = e.message
+      logging.warning(f'Unable to perform test discovery: {optimization_rationale}')
+
+      for target in self.args.extra_targets:
+        get_metrics_agent().report_unoptimized_target(target, optimization_rationale)
+      return self._legacy_collect_preliminary_build_targets()
+
+    for target in self.args.extra_targets:
+      if target in REQUIRED_BUILD_TARGETS:
+        build_targets.add(target)
+        get_metrics_agent().report_unoptimized_target(target, 'Required build target.')
+        continue
+      # If nothing is discovered without error, that means nothing is needed.
+      if not test_discovery_zip_regexes:
+        get_metrics_agent().report_optimized_target(target)
+        continue
+
+      regex = r'\b(%s.*)\b' % re.escape(target)
+      for opt in test_discovery_zip_regexes:
+        try:
+          if re.search(regex, opt):
+            get_metrics_agent().report_unoptimized_target(target, 'Test artifact used.')
+            build_targets.add(target)
+            # proceed to next target evaluation
+            break
+          get_metrics_agent().report_optimized_target(target)
+        except Exception as e:
+          # In case of exception report as unoptimized
+          build_targets.add(target)
+          get_metrics_agent().report_unoptimized_target(target, f'Error in parsing test discovery output for {target}: {repr(e)}')
+          logging.error(f'unable to parse test discovery output: {repr(e)}')
+          break
+    # If discovery is not enabled, return the original list
+    if not enable_discovery:
+      return self._legacy_collect_preliminary_build_targets()
+
+    return build_targets
+
+  def _legacy_collect_preliminary_build_targets(self):
+    build_targets = set()
+    for target in self.args.extra_targets:
+      if self._unused_target_exclusion_enabled(
+          target
+      ) and not self.build_context.build_target_used(target):
+        continue
+
+      build_targets.add(target)
+    return build_targets
+
   def _unused_target_exclusion_enabled(self, target: str) -> bool:
     return (
         f'{target}_unused_exclusion'
@@ -192,6 +247,11 @@
   argparser.add_argument(
       'extra_targets', nargs='*', help='Extra test suites to build.'
   )
+  argparser.add_argument(
+      '--device-build',
+      action='store_true',
+      help='Flag to indicate running a device build.',
+  )
 
   return argparser.parse_args(argv)
 
diff --git a/ci/build_test_suites_test.py b/ci/build_test_suites_test.py
index 26f4316..190740f 100644
--- a/ci/build_test_suites_test.py
+++ b/ci/build_test_suites_test.py
@@ -78,6 +78,12 @@
     with self.assert_raises_word(build_test_suites.Error, 'TOP'):
       build_test_suites.main([])
 
+  def test_missing_dist_dir_env_var_raises(self):
+    del os.environ['DIST_DIR']
+
+    with self.assert_raises_word(build_test_suites.Error, 'DIST_DIR'):
+      build_test_suites.main([])
+
   def test_invalid_arg_raises(self):
     invalid_args = ['--invalid_arg']
 
@@ -114,6 +120,9 @@
     self.soong_ui_dir = self.fake_top.joinpath('build/soong')
     self.soong_ui_dir.mkdir(parents=True, exist_ok=True)
 
+    self.logs_dir = self.fake_top.joinpath('dist/logs')
+    self.logs_dir.mkdir(parents=True, exist_ok=True)
+
     self.soong_ui = self.soong_ui_dir.joinpath('soong_ui.bash')
     self.soong_ui.touch()
 
@@ -121,6 +130,7 @@
         'TARGET_RELEASE': 'release',
         'TARGET_PRODUCT': 'product',
         'TOP': str(self.fake_top),
+        'DIST_DIR': str(self.fake_top.joinpath('dist')),
     })
 
     self.mock_subprocess_run.return_value = 0
@@ -296,7 +306,8 @@
     build_planner = self.create_build_planner(
         build_targets=build_targets,
         build_context=self.create_build_context(
-            enabled_build_features=[{'name': self.get_target_flag('target_1')}]
+            enabled_build_features=[{'name': self.get_target_flag('target_1')}],
+            test_context=self.get_test_context('target_1'),
         ),
     )
 
@@ -312,7 +323,8 @@
     build_planner = self.create_build_planner(
         build_targets=build_targets,
         build_context=self.create_build_context(
-            enabled_build_features=[{'name': self.get_target_flag('target_1')}]
+            enabled_build_features=[{'name': self.get_target_flag('target_1')}],
+            test_context=self.get_test_context('target_1'),
         ),
         packaging_commands=packaging_commands,
     )
diff --git a/core/BUILD.bazel b/core/BUILD.bazel
deleted file mode 100644
index f4869d4..0000000
--- a/core/BUILD.bazel
+++ /dev/null
@@ -1,28 +0,0 @@
-# Copyright (C) 2023 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License
-
-# Export tradefed templates for tests.
-exports_files(
-    glob(["*.xml"]),
-)
-
-# Export proguard flag files for r8.
-filegroup(
-    name = "global_proguard_flags",
-    srcs = [
-        "proguard.flags",
-        "proguard_basic_keeps.flags",
-    ],
-    visibility = ["//visibility:public"],
-)
diff --git a/core/Makefile b/core/Makefile
index 5e0d4df..5a7ddd2 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -169,7 +169,7 @@
             $(eval $(call copy-xml-file-checked,$(_src),$(_fulldest))),\
             $(if $(and $(filter %.jar,$(_dest)),$(filter $(basename $(notdir $(_dest))),$(PRODUCT_LOADED_BY_PRIVILEGED_MODULES))),\
                 $(eval $(call copy-and-uncompress-dexs,$(_src),$(_fulldest))), \
-                $(if $(filter init%rc,$(notdir $(_dest)))$(filter %/etc/init,$(dir $(_dest))),\
+                $(if $(filter init%rc,$(notdir $(_dest)))$(filter %/etc/init/,$(dir $(_dest))),\
                     $(eval $(call copy-init-script-file-checked,$(_src),$(_fulldest))),\
                     $(if $(and $(filter true,$(check_elf_prebuilt_product_copy_files)), \
                                $(filter bin lib lib64,$(subst /,$(space),$(_dest)))), \
@@ -872,6 +872,7 @@
 SOONG_CONV_DATA := $(call intermediates-dir-for,PACKAGING,soong_conversion)/soong_conv_data
 $(SOONG_CONV_DATA):
 	@rm -f $@
+	@touch $@ # This file must be present even if SOONG_CONV is empty.
 	@$(foreach s,$(SOONG_CONV),echo "$(s),$(SOONG_CONV.$(s).TYPE),$(sort $(SOONG_CONV.$(s).PROBLEMS)),$(sort $(filter-out $(SOONG_ALREADY_CONV),$(SOONG_CONV.$(s).DEPS))),$(sort $(SOONG_CONV.$(s).MAKEFILES)),$(sort $(SOONG_CONV.$(s).INSTALLED))" >>$@;)
 
 $(call declare-1p-target,$(SOONG_CONV_DATA),build)
@@ -884,11 +885,6 @@
 $(call declare-1p-target,$(SOONG_TO_CONVERT),build)
 $(call dist-for-goals,droidcore-unbundled,$(SOONG_TO_CONVERT))
 
-$(PRODUCT_OUT)/product_packages.txt:
-	@rm -f $@
-	echo "" > $@
-	$(foreach x,$(PRODUCT_PACKAGES),echo $(x) >> $@$(newline))
-
 MK2BP_CATALOG_SCRIPT := build/make/tools/mk2bp_catalog.py
 PRODUCT_PACKAGES_TXT := $(PRODUCT_OUT)/product_packages.txt
 MK2BP_REMAINING_HTML := $(PRODUCT_OUT)/mk2bp_remaining.html
@@ -969,27 +965,12 @@
 
 # -----------------------------------------------------------------
 
-.PHONY: event-log-tags
-
-# Produce an event logs tag file for everything we know about, in order
-# to properly allocate numbers.  Then produce a file that's filtered
-# for what's going to be installed.
-
-all_event_log_tags_file := $(TARGET_OUT_COMMON_INTERMEDIATES)/all-event-log-tags.txt
-
 event_log_tags_file := $(TARGET_OUT)/etc/event-log-tags
 
 # Include tags from all packages that we know about
 all_event_log_tags_src := \
     $(sort $(foreach m, $(ALL_MODULES), $(ALL_MODULES.$(m).EVENT_LOG_TAGS)))
 
-$(all_event_log_tags_file): PRIVATE_SRC_FILES := $(all_event_log_tags_src)
-$(all_event_log_tags_file): $(all_event_log_tags_src) $(MERGETAGS) build/make/tools/event_log_tags.py
-	$(hide) mkdir -p $(dir $@)
-	$(hide) $(MERGETAGS) -o $@ $(PRIVATE_SRC_FILES)
-
-$(call declare-0p-target,$(all_event_log_tags_file))
-
 # Include tags from all packages included in this product, plus all
 # tags that are part of the system (ie, not in a vendor/ or device/
 # directory).
@@ -1001,13 +982,13 @@
       $(filter-out vendor/% device/% out/%,$(all_event_log_tags_src)))
 
 $(event_log_tags_file): PRIVATE_SRC_FILES := $(event_log_tags_src)
-$(event_log_tags_file): PRIVATE_MERGED_FILE := $(all_event_log_tags_file)
-$(event_log_tags_file): $(event_log_tags_src) $(all_event_log_tags_file) $(MERGETAGS) build/make/tools/event_log_tags.py
+$(event_log_tags_file): $(event_log_tags_src) $(MERGETAGS)
 	$(hide) mkdir -p $(dir $@)
-	$(hide) $(MERGETAGS) -o $@ -m $(PRIVATE_MERGED_FILE) $(PRIVATE_SRC_FILES)
+	$(hide) $(MERGETAGS) -o $@ $(PRIVATE_SRC_FILES)
 
 $(eval $(call declare-0p-target,$(event_log_tags_file)))
 
+.PHONY: event-log-tags
 event-log-tags: $(event_log_tags_file)
 
 ALL_DEFAULT_INSTALLED_MODULES += $(event_log_tags_file)
@@ -1596,6 +1577,7 @@
 	$(AVBTOOL) add_hash_footer \
            --image $@ \
 	   $(call get-partition-size-argument,$(BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE)) \
+	   --salt $$(sha256sum $(BUILD_NUMBER_FILE) $(BUILD_DATETIME_FILE) | cut -d " " -f 1 | tr -d '\n') \
 	   --partition_name init_boot $(INTERNAL_AVB_INIT_BOOT_SIGNING_ARGS) \
 	   $(BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS)
 
@@ -1654,6 +1636,8 @@
     $(ALL_DEFAULT_INSTALLED_MODULES))
 
 INTERNAL_VENDOR_RAMDISK_TARGET := $(call intermediates-dir-for,PACKAGING,vendor_boot)/vendor_ramdisk.cpio$(RAMDISK_EXT)
+vendor_ramdisk_intermediates :=$= $(call intermediates-dir-for,PACKAGING,vendor_ramdisk)
+$(eval $(call write-partition-file-list,$(vendor_ramdisk_intermediates)/file_list.txt,$(TARGET_VENDOR_RAMDISK_OUT),$(INTERNAL_VENDOR_RAMDISK_FILES)))
 
 # Exclude recovery files in the default vendor ramdisk if including a standalone
 # recovery ramdisk in vendor_boot.
@@ -1769,6 +1753,7 @@
 	$(AVBTOOL) add_hash_footer \
            --image $@ \
 	   $(call get-partition-size-argument,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE)) \
+	   --salt $$(sha256sum $(BUILD_NUMBER_FILE) $(BUILD_DATETIME_FILE) | cut -d " " -f 1 | tr -d '\n') \
 	   --partition_name vendor_boot $(INTERNAL_AVB_VENDOR_BOOT_SIGNING_ARGS) \
 	   $(BOARD_AVB_VENDOR_BOOT_ADD_HASH_FOOTER_ARGS)
 else
@@ -2413,7 +2398,7 @@
 $(hide) echo "root_dir=$(TARGET_ROOT_OUT)" >> $(1)
 $(if $(filter true,$(PRODUCT_USE_DYNAMIC_PARTITION_SIZE)),\
     $(hide) echo "use_dynamic_partition_size=true" >> $(1))
-$(if $(COPY_IMAGES_FOR_TARGET_FILES_ZIP),\
+$(if $(USE_FIXED_TIMESTAMP_IMG_FILES)$(COPY_IMAGES_FOR_TARGET_FILES_ZIP),\
     $(hide) echo "use_fixed_timestamp=true" >> $(1))
 $(if $(3),$(hide) $(foreach kv,$(3),echo "$(kv)" >> $(1);))
 $(hide) sort -o $(1) $(1)
@@ -2659,7 +2644,7 @@
 TARGET_PRIVATE_RES_DIRS := $(wildcard $(TARGET_DEVICE_DIR)/recovery/res)
 endif
 recovery_resource_deps := $(shell find $(recovery_resources_common) \
-  $(TARGET_PRIVATE_RES_DIRS) -type f)
+  $(TARGET_PRIVATE_RES_DIRS) -type f -not -name "*.bp")
 recovery_resource_deps += $(generated_recovery_text_files)
 
 
@@ -2932,6 +2917,9 @@
 $(error MTD device is no longer supported and thus BOARD_NAND_SPARE_SIZE is deprecated.)
 endif
 
+recovery_intermediates := $(call intermediates-dir-for,PACKAGING,recovery)
+$(eval $(call write-partition-file-list,$(recovery_intermediates)/file_list.txt,$(TARGET_RECOVERY_OUT),$(INTERNAL_RECOVERYIMAGE_FILES)))
+
 
 # -----------------------------------------------------------------
 # Build debug ramdisk and debug boot image.
@@ -3511,18 +3499,20 @@
 # Collect all available stub libraries installed in system and install with predefined linker configuration
 # Also append LLNDK libraries in the APEX as required libs
 SYSTEM_LINKER_CONFIG := $(TARGET_OUT)/etc/linker.config.pb
-SYSTEM_LINKER_CONFIG_SOURCE := $(call intermediates-dir-for,ETC,system_linker_config)/system_linker_config
+SYSTEM_LINKER_CONFIG_SOURCE := system/core/rootdir/etc/linker.config.json
 $(SYSTEM_LINKER_CONFIG): PRIVATE_SYSTEM_LINKER_CONFIG_SOURCE := $(SYSTEM_LINKER_CONFIG_SOURCE)
 $(SYSTEM_LINKER_CONFIG): $(INTERNAL_SYSTEMIMAGE_FILES) $(SYSTEM_LINKER_CONFIG_SOURCE) | conv_linker_config
 	@echo Creating linker config: $@
 	@mkdir -p $(dir $@)
-	@rm -f $@
-	$(HOST_OUT_EXECUTABLES)/conv_linker_config systemprovide --source $(PRIVATE_SYSTEM_LINKER_CONFIG_SOURCE) \
+	@rm -f $@ $@.step1
+	$(HOST_OUT_EXECUTABLES)/conv_linker_config proto --force -s $(PRIVATE_SYSTEM_LINKER_CONFIG_SOURCE) -o $@.step1
+	$(HOST_OUT_EXECUTABLES)/conv_linker_config systemprovide --source $@.step1 \
 		--output $@ --value "$(STUB_LIBRARIES)" --system "$(TARGET_OUT)"
 	$(HOST_OUT_EXECUTABLES)/conv_linker_config append --source $@ --output $@ --key requireLibs \
 		--value "$(foreach lib,$(LLNDK_MOVED_TO_APEX_LIBRARIES), $(lib).so)"
 	$(HOST_OUT_EXECUTABLES)/conv_linker_config append --source $@ --output $@ --key provideLibs \
 		--value "$(foreach lib,$(PRODUCT_EXTRA_STUB_LIBRARIES), $(lib).so)"
+	rm -f $@.step1
 
 $(call declare-1p-target,$(SYSTEM_LINKER_CONFIG),)
 $(call declare-license-deps,$(SYSTEM_LINKER_CONFIG),$(INTERNAL_SYSTEMIMAGE_FILES) $(SYSTEM_LINKER_CONFIG_SOURCE))
@@ -5169,6 +5159,8 @@
 # Run apex_sepolicy_tests for all installed APEXes
 
 ifeq (,$(TARGET_BUILD_UNBUNDLED))
+# TODO(b/353896817) apex_sepolicy_tests supports only ext4
+ifeq (ext4,$(PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE))
 intermediate := $(call intermediates-dir-for,PACKAGING,apex_sepolicy_tests)
 apex_dirs := \
   $(TARGET_OUT)/apex/% \
@@ -5208,6 +5200,7 @@
 
 apex_files :=
 intermediate :=
+endif # PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE
 endif # TARGET_BUILD_UNBUNDLED
 
 # -----------------------------------------------------------------
@@ -5676,6 +5669,7 @@
   brotli \
   bsdiff \
   build_image \
+  build_mixed_kernels_ramdisk_host \
   build_super_image \
   build_verity_metadata \
   build_verity_tree \
@@ -6666,7 +6660,7 @@
 	@# Contents of the system image
 ifneq ($(SOONG_DEFINED_SYSTEM_IMAGE_PATH),)
 	$(hide) $(call package_files-copy-root, \
-	    $(SOONG_DEFINED_SYSTEM_IMAGE_BASE)/root/system,$(zip_root)/SYSTEM)
+	    $(SOONG_DEFINED_SYSTEM_IMAGE_BASE)/system/system,$(zip_root)/SYSTEM)
 else
 	$(hide) $(call package_files-copy-root, \
 	    $(SYSTEMIMAGE_SOURCE_DIR),$(zip_root)/SYSTEM)
diff --git a/core/android_soong_config_vars.mk b/core/android_soong_config_vars.mk
index 769a6f7..862fcb2 100644
--- a/core/android_soong_config_vars.mk
+++ b/core/android_soong_config_vars.mk
@@ -48,6 +48,11 @@
 RECOVERY_FSTAB_VERSION := 2
 $(call soong_config_set, recovery, recovery_api_version, $(RECOVERY_API_VERSION))
 $(call soong_config_set, recovery, recovery_fstab_version, $(RECOVERY_FSTAB_VERSION))
+$(call soong_config_set_bool, recovery ,target_userimages_use_f2fs ,$(if $(TARGET_USERIMAGES_USE_F2FS),true,false))
+$(call soong_config_set_bool, recovery ,has_board_cacheimage_partition_size ,$(if $(BOARD_CACHEIMAGE_PARTITION_SIZE),true,false))
+ifdef TARGET_RECOVERY_UI_LIB
+  $(call soong_config_set_string_list, recovery, target_recovery_ui_lib, $(TARGET_RECOVERY_UI_LIB))
+endif
 
 # For Sanitizers
 $(call soong_config_set_bool,ANDROID,ASAN_ENABLED,$(if $(filter address,$(SANITIZE_TARGET)),true,false))
@@ -73,9 +78,11 @@
 $(call soong_config_set_bool,art_module,art_build_host_debug,$(if $(filter false,$(ART_BUILD_HOST_DEBUG)),false,true))
 
 # For chre
-$(call soong_config_set_bool,chre,chre_daemon_lama_enabled,$(if $(filter true,$(CHRE_DAEMON_LPMA_ENABLED)),true,false))
+$(call soong_config_set_bool,chre,chre_daemon_lpma_enabled,$(if $(filter true,$(CHRE_DAEMON_LPMA_ENABLED)),true,false))
 $(call soong_config_set_bool,chre,chre_dedicated_transport_channel_enabled,$(if $(filter true,$(CHRE_DEDICATED_TRANSPORT_CHANNEL_ENABLED)),true,false))
 $(call soong_config_set_bool,chre,chre_log_atom_extension_enabled,$(if $(filter true,$(CHRE_LOG_ATOM_EXTENSION_ENABLED)),true,false))
+$(call soong_config_set_bool,chre,building_vendor_image,$(if $(filter true,$(BUILDING_VENDOR_IMAGE)),true,false))
+$(call soong_config_set_bool,chre,chre_usf_daemon_enabled,$(if $(filter true,$(CHRE_USF_DAEMON_ENABLED)),true,false))
 
 ifdef TARGET_BOARD_AUTO
   $(call add_soong_config_var_value, ANDROID, target_board_auto, $(TARGET_BOARD_AUTO))
@@ -94,6 +101,9 @@
 SYSTEMUI_OPTIMIZE_JAVA ?= true
 $(call add_soong_config_var,ANDROID,SYSTEMUI_OPTIMIZE_JAVA)
 
+# Flag for enabling compose for Launcher.
+$(call soong_config_set,ANDROID,release_enable_compose_in_launcher,$(RELEASE_ENABLE_COMPOSE_IN_LAUNCHER))
+
 ifdef PRODUCT_AVF_ENABLED
 $(call add_soong_config_var_value,ANDROID,avf_enabled,$(PRODUCT_AVF_ENABLED))
 endif
@@ -118,6 +128,8 @@
 
 ifdef PRODUCT_CGROUP_V2_SYS_APP_ISOLATION_ENABLED
 $(call add_soong_config_var_value,ANDROID,cgroup_v2_sys_app_isolation,$(PRODUCT_CGROUP_V2_SYS_APP_ISOLATION_ENABLED))
+else
+$(call add_soong_config_var_value,ANDROID,cgroup_v2_sys_app_isolation,true)
 endif
 
 $(call add_soong_config_var_value,ANDROID,release_avf_allow_preinstalled_apps,$(RELEASE_AVF_ALLOW_PREINSTALLED_APPS))
@@ -193,10 +205,24 @@
 # Required as platform_bootclasspath is using this namespace
 $(call soong_config_set,bootclasspath,release_crashrecovery_module,$(RELEASE_CRASHRECOVERY_MODULE))
 
+# Add uprobestats build flag to soong
+$(call soong_config_set,ANDROID,release_uprobestats_module,$(RELEASE_UPROBESTATS_MODULE))
+# Add uprobestats file move flags to soong, for both platform and module
+ifeq (true,$(RELEASE_UPROBESTATS_FILE_MOVE))
+  $(call soong_config_set,ANDROID,uprobestats_files_in_module,true)
+  $(call soong_config_set,ANDROID,uprobestats_files_in_platform,false)
+else
+  $(call soong_config_set,ANDROID,uprobestats_files_in_module,false)
+  $(call soong_config_set,ANDROID,uprobestats_files_in_platform,true)
+endif
+
 # Enable Profiling module. Also used by platform_bootclasspath.
 $(call soong_config_set,ANDROID,release_package_profiling_module,$(RELEASE_PACKAGE_PROFILING_MODULE))
 $(call soong_config_set,bootclasspath,release_package_profiling_module,$(RELEASE_PACKAGE_PROFILING_MODULE))
 
+# Move VCN from platform to the Tethering module; used by both platform and module
+$(call soong_config_set,ANDROID,is_vcn_in_mainline,$(RELEASE_MOVE_VCN_TO_MAINLINE))
+
 # Add perf-setup build flag to soong
 # Note: BOARD_PERFSETUP_SCRIPT location must be under platform_testing/scripts/perf-setup/.
 ifdef BOARD_PERFSETUP_SCRIPT
@@ -262,3 +288,18 @@
 $(call soong_config_set_bool,fs_config,vendor_dlkm,$(if $(BOARD_USES_VENDOR_DLKMIMAGE)$(BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE),true,false))
 $(call soong_config_set_bool,fs_config,odm_dlkm,$(if $(BOARD_USES_ODM_DLKMIMAGE)$(BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE),true,false))
 $(call soong_config_set_bool,fs_config,system_dlkm,$(if $(BOARD_USES_SYSTEM_DLKMIMAGE)$(BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE),true,false))
+
+# Variables for telephony
+$(call soong_config_set_bool,telephony,sec_cp_secure_boot,$(if $(filter true,$(SEC_CP_SECURE_BOOT)),true,false))
+$(call soong_config_set_bool,telephony,cbd_protocol_sit,$(if $(filter true,$(CBD_PROTOCOL_SIT)),true,false))
+$(call soong_config_set_bool,telephony,use_radioexternal_hal_aidl,$(if $(filter true,$(USE_RADIOEXTERNAL_HAL_AIDL)),true,false))
+
+# Variables for hwcomposer.$(TARGET_BOARD_PLATFORM)
+$(call soong_config_set_bool,google_graphics,board_uses_hwc_services,$(if $(filter true,$(BOARD_USES_HWC_SERVICES)),true,false))
+
+# Variables for controlling android.hardware.composer.hwc3-service.pixel
+$(call soong_config_set,google_graphics,board_hwc_version,$(BOARD_HWC_VERSION))
+
+# Variables for extra branches
+# TODO(b/383238397): Use bootstrap_go_package to enable extra flags.
+-include vendor/google/build/extra_soong_config_vars.mk
diff --git a/core/board_config.mk b/core/board_config.mk
index ea0d022..ad89c03 100644
--- a/core/board_config.mk
+++ b/core/board_config.mk
@@ -291,6 +291,7 @@
 include $(BUILD_SYSTEM)/board_config_wifi.mk
 
 # Set up soong config for "soong_config_value_variable".
+-include hardware/interfaces/configstore/1.1/default/surfaceflinger.mk
 -include vendor/google/build/soong/soong_config_namespace/camera.mk
 
 # Default *_CPU_VARIANT_RUNTIME to CPU_VARIANT if unspecified.
diff --git a/core/config.mk b/core/config.mk
index b546ea1..454c0e5 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -330,6 +330,18 @@
 $(eval SOONG_CONFIG_TYPE_$(strip $1)_$(strip $2):=bool)
 endef
 
+# soong_config_set_string_list is the same as soong_config_set, but it will
+# also type the variable as a list of strings, so that when using select() expressions
+# in blueprint files they can use list values instead of strings.
+# The values of the list must be space-separated.
+# $1 is the namespace. $2 is the variable name. $3 is the variable value.
+# Ex: $(call soong_config_set_string_list,acme,COOL_LIBS,a b)
+define soong_config_set_string_list
+$(call soong_config_define_internal,$1,$2) \
+$(eval SOONG_CONFIG_$(strip $1)_$(strip $2):=$(strip $3))
+$(eval SOONG_CONFIG_TYPE_$(strip $1)_$(strip $2):=string_list)
+endef
+
 # soong_config_append appends to the value of the variable in the given Soong
 # config namespace. If the variable does not exist, it will be defined. If the
 # namespace does not  exist, it will be defined.
@@ -718,8 +730,8 @@
 PROGUARD_HOME := external/proguard
 PROGUARD := $(PROGUARD_HOME)/bin/proguard.sh
 PROGUARD_DEPS := $(PROGUARD) $(PROGUARD_HOME)/lib/proguard.jar
-JAVATAGS := build/make/tools/java-event-log-tags.py
-MERGETAGS := build/make/tools/merge-event-log-tags.py
+JAVATAGS := $(HOST_OUT_EXECUTABLES)/java-event-log-tags
+MERGETAGS := $(HOST_OUT_EXECUTABLES)/merge-event-log-tags
 APPEND2SIMG := $(HOST_OUT_EXECUTABLES)/append2simg
 VERITY_SIGNER := $(HOST_OUT_EXECUTABLES)/verity_signer
 BUILD_VERITY_METADATA := $(HOST_OUT_EXECUTABLES)/build_verity_metadata
@@ -1321,3 +1333,58 @@
 $(error SYSTEM_OPTIMIZE_JAVA must be enabled when FULL_SYSTEM_OPTIMIZE_JAVA is enabled)
 endif
 endif
+
+# -----------------------------------------------------------------
+# Define fingerprint, thumbprint, and version tags for the current build
+#
+# BUILD_VERSION_TAGS is a comma-separated list of tags chosen by the device
+# implementer that further distinguishes the build. It's basically defined
+# by the device implementer. Here, we are adding a mandatory tag that
+# identifies the signing config of the build.
+BUILD_VERSION_TAGS := $(BUILD_VERSION_TAGS)
+ifeq ($(TARGET_BUILD_TYPE),debug)
+  BUILD_VERSION_TAGS += debug
+endif
+# The "test-keys" tag marks builds signed with the old test keys,
+# which are available in the SDK.  "dev-keys" marks builds signed with
+# non-default dev keys (usually private keys from a vendor directory).
+# Both of these tags will be removed and replaced with "release-keys"
+# when the target-files is signed in a post-build step.
+ifeq ($(DEFAULT_SYSTEM_DEV_CERTIFICATE),build/make/target/product/security/testkey)
+BUILD_KEYS := test-keys
+else
+BUILD_KEYS := dev-keys
+endif
+BUILD_VERSION_TAGS += $(BUILD_KEYS)
+BUILD_VERSION_TAGS := $(subst $(space),$(comma),$(sort $(BUILD_VERSION_TAGS)))
+
+# BUILD_FINGERPRINT is used used to uniquely identify the combined build and
+# product; used by the OTA server.
+ifeq (,$(strip $(BUILD_FINGERPRINT)))
+  BUILD_FINGERPRINT := $(PRODUCT_BRAND)/$(TARGET_PRODUCT)/$(TARGET_DEVICE):$(PLATFORM_VERSION)/$(BUILD_ID)/$(BUILD_NUMBER_FROM_FILE):$(TARGET_BUILD_VARIANT)/$(BUILD_VERSION_TAGS)
+endif
+
+BUILD_FINGERPRINT_FILE := $(PRODUCT_OUT)/build_fingerprint.txt
+ifneq (,$(shell mkdir -p $(PRODUCT_OUT) && echo $(BUILD_FINGERPRINT) >$(BUILD_FINGERPRINT_FILE).tmp && (if ! cmp -s $(BUILD_FINGERPRINT_FILE).tmp $(BUILD_FINGERPRINT_FILE); then mv $(BUILD_FINGERPRINT_FILE).tmp $(BUILD_FINGERPRINT_FILE); else rm $(BUILD_FINGERPRINT_FILE).tmp; fi) && grep " " $(BUILD_FINGERPRINT_FILE)))
+  $(error BUILD_FINGERPRINT cannot contain spaces: "$(file <$(BUILD_FINGERPRINT_FILE))")
+endif
+BUILD_FINGERPRINT_FROM_FILE := $$(cat $(BUILD_FINGERPRINT_FILE))
+# unset it for safety.
+BUILD_FINGERPRINT :=
+
+# BUILD_THUMBPRINT is used to uniquely identify the system build; used by the
+# OTA server. This purposefully excludes any product-specific variables.
+ifeq (,$(strip $(BUILD_THUMBPRINT)))
+  BUILD_THUMBPRINT := $(PLATFORM_VERSION)/$(BUILD_ID)/$(BUILD_NUMBER_FROM_FILE):$(TARGET_BUILD_VARIANT)/$(BUILD_VERSION_TAGS)
+endif
+
+BUILD_THUMBPRINT_FILE := $(PRODUCT_OUT)/build_thumbprint.txt
+ifeq ($(strip $(HAS_BUILD_NUMBER)),true)
+$(BUILD_THUMBPRINT_FILE): $(BUILD_NUMBER_FILE)
+endif
+ifneq (,$(shell mkdir -p $(PRODUCT_OUT) && echo $(BUILD_THUMBPRINT) >$(BUILD_THUMBPRINT_FILE) && grep " " $(BUILD_THUMBPRINT_FILE)))
+  $(error BUILD_THUMBPRINT cannot contain spaces: "$(file <$(BUILD_THUMBPRINT_FILE))")
+endif
+# unset it for safety.
+BUILD_THUMBPRINT_FILE :=
+BUILD_THUMBPRINT :=
diff --git a/core/definitions.mk b/core/definitions.mk
index cd1b36e..1ab6388 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -1555,7 +1555,7 @@
 define transform-logtags-to-java
 @mkdir -p $(dir $@)
 @echo "logtags: $@ <= $<"
-$(hide) $(JAVATAGS) -o $@ $< $(PRIVATE_MERGED_TAG)
+$(hide) $(JAVATAGS) -o $@ $<
 endef
 
 
@@ -2605,7 +2605,87 @@
         @$(call emit-line,$(wordlist 108501,109000,$(1)),$(2))
         @$(call emit-line,$(wordlist 109001,109500,$(1)),$(2))
         @$(call emit-line,$(wordlist 109501,110000,$(1)),$(2))
-        @$(if $(wordlist 110001,110002,$(1)),$(error dump-words-to-file: Too many words ($(words $(1)))))
+        @$(call emit-line,$(wordlist 110001,110500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 110501,111000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 111001,111500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 111501,112000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 112001,112500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 112501,113000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 113001,113500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 113501,114000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 114001,114500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 114501,115000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 115001,115500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 115501,116000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 116001,116500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 116501,117000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 117001,117500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 117501,118000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 118001,118500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 118501,119000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 119001,119500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 119501,120000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 120001,120500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 120501,121000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 121001,121500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 121501,122000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 122001,122500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 122501,123000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 123001,123500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 123501,124000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 124001,124500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 124501,125000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 125001,125500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 125501,126000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 126001,126500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 126501,127000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 127001,127500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 127501,128000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 128001,128500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 128501,129000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 129001,129500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 129501,130000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 130001,130500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 130501,131000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 131001,131500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 131501,132000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 132001,132500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 132501,133000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 133001,133500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 133501,134000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 134001,134500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 134501,135000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 135001,135500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 135501,136000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 136001,136500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 136501,137000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 137001,137500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 137501,138000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 138001,138500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 138501,139000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 139001,139500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 139501,140000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 140001,140500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 140501,141000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 141001,141500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 141501,142000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 142001,142500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 142501,143000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 143001,143500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 143501,144000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 144001,144500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 144501,145000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 145001,145500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 145501,146000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 146001,146500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 146501,147000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 147001,147500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 147501,148000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 148001,148500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 148501,149000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 149001,149500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 149501,150000,$(1)),$(2))
+        @$(if $(wordlist 150001,150002,$(1)),$(error dump-words-to-file: Too many words ($(words $(1)))))
 endef
 # Return jar arguments to compress files in a given directory
 # $(1): directory
diff --git a/core/java.mk b/core/java.mk
index 5fbc916..41a1b1b 100644
--- a/core/java.mk
+++ b/core/java.mk
@@ -140,8 +140,7 @@
 logtags_java_sources := $(patsubst %.logtags,%.java,$(addprefix $(intermediates.COMMON)/logtags/, $(logtags_sources)))
 logtags_sources := $(addprefix $(LOCAL_PATH)/, $(logtags_sources))
 
-$(logtags_java_sources): PRIVATE_MERGED_TAG := $(TARGET_OUT_COMMON_INTERMEDIATES)/all-event-log-tags.txt
-$(logtags_java_sources): $(intermediates.COMMON)/logtags/%.java: $(LOCAL_PATH)/%.logtags $(TARGET_OUT_COMMON_INTERMEDIATES)/all-event-log-tags.txt $(JAVATAGS) build/make/tools/event_log_tags.py
+$(logtags_java_sources): $(intermediates.COMMON)/logtags/%.java: $(LOCAL_PATH)/%.logtags $(JAVATAGS)
 	$(transform-logtags-to-java)
 
 else
diff --git a/core/java_common.mk b/core/java_common.mk
index a21f062..f574b76 100644
--- a/core/java_common.mk
+++ b/core/java_common.mk
@@ -32,7 +32,7 @@
     else ifneq (,$(LOCAL_SDK_VERSION)$(TARGET_BUILD_USE_PREBUILT_SDKS))
       # TODO(ccross): allow 1.9 for current and unbundled once we have SDK system modules
       LOCAL_JAVA_LANGUAGE_VERSION := 1.8
-    else ifeq ($(EXPERIMENTAL_TARGET_JAVA_VERSION_21),true)
+    else ifeq ($(RELEASE_TARGET_JAVA_21),true)
       LOCAL_JAVA_LANGUAGE_VERSION := 21
     else
       LOCAL_JAVA_LANGUAGE_VERSION := 17
diff --git a/core/main.mk b/core/main.mk
index 624df49..25580e2 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -1759,10 +1759,6 @@
 	@echo $(sort $(patsubst $(PRODUCT_OUT)/%,%,$(filter $(PRODUCT_OUT)/%,$(modules_to_install)))) | tr -s ' ' '\n'
 	@echo Successfully dumped product target file list.
 
-.PHONY: nothing
-nothing:
-	@echo Successfully read the makefiles.
-
 .PHONY: tidy_only
 tidy_only:
 	@echo Successfully make tidy_only.
diff --git a/core/misc_prebuilt_internal.mk b/core/misc_prebuilt_internal.mk
index a562207..b14b9ce 100644
--- a/core/misc_prebuilt_internal.mk
+++ b/core/misc_prebuilt_internal.mk
@@ -25,7 +25,7 @@
 
 include $(BUILD_SYSTEM)/base_rules.mk
 
-ifneq ($(filter init%rc,$(notdir $(LOCAL_INSTALLED_MODULE)))$(filter %/etc/init,$(dir $(LOCAL_INSTALLED_MODULE))),)
+ifneq ($(filter init%rc,$(notdir $(LOCAL_INSTALLED_MODULE)))$(filter %/etc/init/,$(dir $(LOCAL_INSTALLED_MODULE))),)
   $(eval $(call copy-init-script-file-checked,$(my_prebuilt_src_file),$(LOCAL_BUILT_MODULE)))
 else
 $(LOCAL_BUILT_MODULE) : $(my_prebuilt_src_file)
diff --git a/core/packaging/flags.mk b/core/packaging/flags.mk
index ccb502c..a371a00 100644
--- a/core/packaging/flags.mk
+++ b/core/packaging/flags.mk
@@ -24,10 +24,11 @@
 # -----------------------------------------------------------------
 # Aconfig Flags
 
-# Create a summary file of build flags for each partition
+# Create a summary file of build flags for a single partition
 # $(1): built aconfig flags file (out)
 # $(2): installed aconfig flags file (out)
 # $(3): the partition (in)
+# $(4): input aconfig files for the partition (in)
 define generate-partition-aconfig-flag-file
 $(eval $(strip $(1)): PRIVATE_OUT := $(strip $(1)))
 $(eval $(strip $(1)): PRIVATE_IN := $(strip $(4)))
@@ -35,7 +36,8 @@
 	mkdir -p $$(dir $$(PRIVATE_OUT))
 	$$(if $$(PRIVATE_IN), \
 		$$(ACONFIG) dump --dedup --format protobuf --out $$(PRIVATE_OUT) \
-			--filter container:$(strip $(3)) \
+			--filter container:$(strip $(3))+state:ENABLED \
+			--filter container:$(strip $(3))+permission:READ_WRITE \
 			$$(addprefix --cache ,$$(PRIVATE_IN)), \
 		echo -n > $$(PRIVATE_OUT) \
 	)
@@ -107,10 +109,17 @@
 define generate-partition-aconfig-storage-file
 $(eval $(strip $(1)): PRIVATE_OUT := $(strip $(1)))
 $(eval $(strip $(1)): PRIVATE_IN := $(strip $(9)))
+
+ifneq (,$(RELEASE_FINGERPRINT_ACONFIG_PACKAGES))
+STORAGE_FILE_VERSION := 2
+else
+STORAGE_FILE_VERSION := 1
+endif
+
 $(strip $(1)): $(ACONFIG) $(strip $(9))
 	mkdir -p $$(dir $$(PRIVATE_OUT))
 	$$(if $$(PRIVATE_IN), \
-		$$(ACONFIG) create-storage --container $(10) --file package_map --out $$(PRIVATE_OUT) \
+		$$(ACONFIG) create-storage --container $(10) --file package_map --out $$(PRIVATE_OUT) --version $$(STORAGE_FILE_VERSION) \
 			$$(addprefix --cache ,$$(PRIVATE_IN)), \
 	)
 	touch $$(PRIVATE_OUT)
@@ -119,7 +128,7 @@
 $(strip $(2)): $(ACONFIG) $(strip $(9))
 	mkdir -p $$(dir $$(PRIVATE_OUT))
 	$$(if $$(PRIVATE_IN), \
-		$$(ACONFIG) create-storage --container $(10) --file flag_map --out $$(PRIVATE_OUT) \
+		$$(ACONFIG) create-storage --container $(10) --file flag_map --out $$(PRIVATE_OUT) --version $$(STORAGE_FILE_VERSION) \
 			$$(addprefix --cache ,$$(PRIVATE_IN)), \
 	)
 	touch $$(PRIVATE_OUT)
@@ -128,7 +137,7 @@
 $(strip $(3)): $(ACONFIG) $(strip $(9))
 	mkdir -p $$(dir $$(PRIVATE_OUT))
 	$$(if $$(PRIVATE_IN), \
-		$$(ACONFIG) create-storage --container $(10) --file flag_val --out $$(PRIVATE_OUT) \
+		$$(ACONFIG) create-storage --container $(10) --file flag_val --out $$(PRIVATE_OUT) --version $$(STORAGE_FILE_VERSION) \
 		$$(addprefix --cache ,$$(PRIVATE_IN)), \
 	)
 	touch $$(PRIVATE_OUT)
@@ -137,7 +146,7 @@
 $(strip $(4)): $(ACONFIG) $(strip $(9))
 	mkdir -p $$(dir $$(PRIVATE_OUT))
 	$$(if $$(PRIVATE_IN), \
-		$$(ACONFIG) create-storage --container $(10) --file flag_info --out $$(PRIVATE_OUT) \
+		$$(ACONFIG) create-storage --container $(10) --file flag_info --out $$(PRIVATE_OUT) --version $$(STORAGE_FILE_VERSION) \
 		$$(addprefix --cache ,$$(PRIVATE_IN)), \
 	)
 	touch $$(PRIVATE_OUT)
diff --git a/core/product.mk b/core/product.mk
index 9c567c3..1b336b0 100644
--- a/core/product.mk
+++ b/core/product.mk
@@ -284,6 +284,9 @@
 # Whether APEX should be compressed or not
 _product_single_value_vars += PRODUCT_COMPRESSED_APEX
 
+# Default fs type for APEX payload image (apex_payload.img)
+_product_single_value_vars += PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE
+
 # VNDK version of product partition. It can be 'current' if the product
 # partitions uses PLATFORM_VNDK_VERSION.
 _product_single_value_vars += PRODUCT_PRODUCT_VNDK_VERSION
diff --git a/core/product_config.mk b/core/product_config.mk
index abe6e38..f93b63c 100644
--- a/core/product_config.mk
+++ b/core/product_config.mk
@@ -468,17 +468,26 @@
     $(eval SANITIZER.$(TARGET_PRODUCT).$(m).CONFIG := $(cf))))
 _psmc_modules :=
 
-# Reset ADB keys for non-debuggable builds
-ifeq (,$(filter eng userdebug,$(TARGET_BUILD_VARIANT)))
+# Reset ADB keys. If RELEASE_BUILD_USE_VARIANT_FLAGS is set look for
+# the value of a dedicated flag. Otherwise check if build variant is
+# non-debuggable.
+ifneq (,$(RELEASE_BUILD_USE_VARIANT_FLAGS))
+ifneq (,$(RELEASE_BUILD_PURGE_PRODUCT_ADB_KEYS))
   PRODUCT_ADB_KEYS :=
 endif
+else ifeq (,$(filter eng userdebug,$(TARGET_BUILD_VARIANT)))
+  PRODUCT_ADB_KEYS :=
+endif
+
 ifneq ($(filter-out 0 1,$(words $(PRODUCT_ADB_KEYS))),)
   $(error Only one file may be in PRODUCT_ADB_KEYS: $(PRODUCT_ADB_KEYS))
 endif
 
 # Show a warning wall of text if non-compliance-GSI products set this option.
 ifdef PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT
-  ifeq (,$(filter gsi_arm gsi_arm64 gsi_x86 gsi_x86_64 gsi_car_arm64 gsi_car_x86_64 gsi_tv_arm gsi_tv_arm64,$(PRODUCT_NAME)))
+  ifeq (,$(filter gsi_arm gsi_arm64 gsi_arm64_soong_system gsi_x86 gsi_x86_64 \
+                  gsi_x86_64_soong_system gsi_car_arm64 gsi_car_x86_64 \
+                  gsi_tv_arm gsi_tv_arm64,$(PRODUCT_NAME)))
     $(warning PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT is set but \
       PRODUCT_NAME ($(PRODUCT_NAME)) doesn't look like a GSI for compliance \
       testing. This is a special configuration for compliance GSI, so do make \
@@ -534,6 +543,17 @@
   PRODUCT_COMPRESSED_APEX := $(OVERRIDE_PRODUCT_COMPRESSED_APEX)
 endif
 
+ifdef OVERRIDE_PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE
+  PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE := $(OVERRIDE_PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE)
+else ifeq ($(PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE),)
+  # Use ext4 as a default payload fs type
+  PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE := ext4
+endif
+ifeq ($(filter ext4 erofs,$(PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE)),)
+  $(error PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE should be either erofs or ext4,\
+    not $(PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE).)
+endif
+
 $(KATI_obsolete_var OVERRIDE_PRODUCT_EXTRA_VNDK_VERSIONS \
     ,Use PRODUCT_EXTRA_VNDK_VERSIONS instead)
 
diff --git a/core/release_config.mk b/core/release_config.mk
index fe2170e..68e115f 100644
--- a/core/release_config.mk
+++ b/core/release_config.mk
@@ -146,6 +146,9 @@
         # This will also set ALL_RELEASE_CONFIGS_FOR_PRODUCT and _used_files for us.
         $(eval include $(_flags_file))
         $(KATI_extra_file_deps $(OUT_DIR)/release-config $(protobuf_map_files) $(_flags_file))
+        ifneq (,$(_disallow_lunch_use))
+            $(error Release config ${TARGET_RELEASE} is disallowed for build.  Please use one of: $(ALL_RELEASE_CONFIGS_FOR_PRODUCT))
+        endif
     else
         # This is the first pass of product config.
         $(eval include $(_flags_varmk))
diff --git a/core/soong_app_prebuilt.mk b/core/soong_app_prebuilt.mk
index df1cf2d..ab9227f 100644
--- a/core/soong_app_prebuilt.mk
+++ b/core/soong_app_prebuilt.mk
@@ -224,30 +224,6 @@
 include $(BUILD_SYSTEM)/link_type.mk
 endif # !LOCAL_IS_HOST_MODULE
 
-ifeq (,$(filter tests,$(LOCAL_MODULE_TAGS)))
-  ifdef LOCAL_SOONG_DEVICE_RRO_DIRS
-    $(call append_enforce_rro_sources, \
-        $(my_register_name), \
-        false, \
-        $(LOCAL_FULL_MANIFEST_FILE), \
-        $(if $(LOCAL_EXPORT_PACKAGE_RESOURCES),true,false), \
-        $(LOCAL_SOONG_DEVICE_RRO_DIRS), \
-        vendor \
-    )
-  endif
-
-  ifdef LOCAL_SOONG_PRODUCT_RRO_DIRS
-    $(call append_enforce_rro_sources, \
-        $(my_register_name), \
-        false, \
-        $(LOCAL_FULL_MANIFEST_FILE), \
-        $(if $(LOCAL_EXPORT_PACKAGE_RESOURCES),true,false), \
-        $(LOCAL_SOONG_PRODUCT_RRO_DIRS), \
-        product \
-    )
-  endif
-endif
-
 ifdef LOCAL_PREBUILT_COVERAGE_ARCHIVE
   my_coverage_dir := $(TARGET_OUT_COVERAGE)/$(patsubst $(PRODUCT_OUT)/%,%,$(my_module_path))
   my_coverage_copy_pairs := $(foreach f,$(LOCAL_PREBUILT_COVERAGE_ARCHIVE),$(f):$(my_coverage_dir)/$(notdir  $(f)))
diff --git a/core/soong_config.mk b/core/soong_config.mk
index c5dd320..ba59f5e 100644
--- a/core/soong_config.mk
+++ b/core/soong_config.mk
@@ -194,6 +194,8 @@
 $(call add_json_str,  SystemDlkmPath,                    $(TARGET_COPY_OUT_SYSTEM_DLKM))
 $(call add_json_str,  OemPath,                           $(TARGET_COPY_OUT_OEM))
 $(call add_json_bool, MinimizeJavaDebugInfo,             $(filter true,$(PRODUCT_MINIMIZE_JAVA_DEBUG_INFO)))
+$(call add_json_str,  RecoveryPath,                      $(TARGET_COPY_OUT_RECOVERY))
+$(call add_json_bool, BuildingRecoveryImage,             $(BUILDING_RECOVERY_IMAGE))
 
 $(call add_json_bool, UseGoma,                           $(filter-out false,$(USE_GOMA)))
 $(call add_json_bool, UseRBE,                            $(filter-out false,$(USE_RBE)))
@@ -274,6 +276,7 @@
 $(call add_json_str,  DeviceCurrentApiLevelForVendorModules,  $(BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES))
 
 $(call add_json_bool, CompressedApex, $(filter true,$(PRODUCT_COMPRESSED_APEX)))
+$(call add_json_str, DefaultApexPayloadType, $(PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE))
 
 $(call add_json_bool, BoardUsesRecoveryAsBoot, $(filter true,$(BOARD_USES_RECOVERY_AS_BOOT)))
 
@@ -367,6 +370,7 @@
   $(firstword $(OVERRIDE_ENABLE_UFFD_GC) $(PRODUCT_ENABLE_UFFD_GC) default)
 $(call add_json_str, EnableUffdGc, $(_config_enable_uffd_gc))
 _config_enable_uffd_gc :=
+$(call add_json_str, BoardKernelVersion, $(BOARD_KERNEL_VERSION))
 
 $(call add_json_list, DeviceFrameworkCompatibilityMatrixFile, $(DEVICE_FRAMEWORK_COMPATIBILITY_MATRIX_FILE))
 $(call add_json_list, DeviceProductCompatibilityMatrixFile, $(DEVICE_PRODUCT_COMPATIBILITY_MATRIX_FILE))
@@ -379,7 +383,7 @@
   $(call add_json_str,  ProductDirectory,    $(dir $(INTERNAL_PRODUCT)))
 
   $(call add_json_map,PartitionQualifiedVariables)
-  $(foreach image_type,SYSTEM VENDOR CACHE USERDATA PRODUCT SYSTEM_EXT OEM ODM VENDOR_DLKM ODM_DLKM SYSTEM_DLKM, \
+  $(foreach image_type,INIT_BOOT BOOT VENDOR_BOOT SYSTEM VENDOR CACHE USERDATA PRODUCT SYSTEM_EXT OEM ODM VENDOR_DLKM ODM_DLKM SYSTEM_DLKM, \
     $(call add_json_map,$(call to-lower,$(image_type))) \
     $(call add_json_bool, BuildingImage, $(filter true,$(BUILDING_$(image_type)_IMAGE))) \
     $(call add_json_str, BoardErofsCompressor, $(BOARD_$(image_type)IMAGE_EROFS_COMPRESSOR)) \
@@ -401,6 +405,7 @@
     $(call add_json_str, BoardAvbAlgorithm, $(BOARD_AVB_$(image_type)_ALGORITHM)) \
     $(call add_json_str, BoardAvbRollbackIndex, $(BOARD_AVB_$(image_type)_ROLLBACK_INDEX)) \
     $(call add_json_str, BoardAvbRollbackIndexLocation, $(BOARD_AVB_$(image_type)_ROLLBACK_INDEX_LOCATION)) \
+    $(call add_json_str, BoardAvbAddHashtreeFooterArgs, $(BOARD_AVB_$(image_type)_ADD_HASHTREE_FOOTER_ARGS)) \
     $(call add_json_str, ProductBaseFsPath, $(PRODUCT_$(image_type)_BASE_FS_PATH)) \
     $(call add_json_str, ProductHeadroom, $(PRODUCT_$(image_type)_HEADROOM)) \
     $(call add_json_str, ProductVerityPartition, $(PRODUCT_$(image_type)_VERITY_PARTITION)) \
@@ -430,14 +435,47 @@
   # boot image stuff
   $(call add_json_bool, BuildingRamdiskImage, $(filter true,$(BUILDING_RAMDISK_IMAGE)))
   $(call add_json_bool, ProductBuildBootImage, $(filter true,$(PRODUCT_BUILD_BOOT_IMAGE)))
+  $(call add_json_str, ProductBuildVendorBootImage, $(PRODUCT_BUILD_VENDOR_BOOT_IMAGE))
   $(call add_json_bool, ProductBuildInitBootImage, $(filter true,$(PRODUCT_BUILD_INIT_BOOT_IMAGE)))
   $(call add_json_bool, BoardUsesRecoveryAsBoot, $(filter true,$(BOARD_USES_RECOVERY_AS_BOOT)))
   $(call add_json_str, BoardPrebuiltBootimage, $(BOARD_PREBUILT_BOOT_IMAGE))
   $(call add_json_str, BoardPrebuiltInitBootimage, $(BOARD_PREBUILT_INIT_BOOT_IMAGE))
   $(call add_json_str, BoardBootimagePartitionSize, $(BOARD_BOOTIMAGE_PARTITION_SIZE))
-  $(call add_json_str, BoardInitBootimagePartitionSize, $(BOARD_INIT_BOOTIMAGE_PARTITION_SIZE))
+  $(call add_json_str, BoardVendorBootimagePartitionSize, $(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE))
+  $(call add_json_str, BoardInitBootimagePartitionSize, $(BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE))
   $(call add_json_str, BoardBootHeaderVersion, $(BOARD_BOOT_HEADER_VERSION))
   $(call add_json_str, TargetKernelPath, $(TARGET_KERNEL_PATH))
+  $(call add_json_bool, BoardUsesGenericKernelImage, $(BOARD_USES_GENERIC_KERNEL_IMAGE))
+  $(call add_json_str, BootSecurityPatch, $(BOOT_SECURITY_PATCH))
+  $(call add_json_str, InitBootSecurityPatch, $(INIT_BOOT_SECURITY_PATCH))
+  $(call add_json_str, VendorSecurityPatch, $(VENDOR_SECURITY_PATCH))
+  $(call add_json_str, OdmSecurityPatch, $(ODM_SECURITY_PATCH))
+  $(call add_json_str, SystemDlkmSecurityPatch, $(SYSTEM_DLKM_SECURITY_PATCH))
+  $(call add_json_str, VendorDlkmSecurityPatch, $(VENDOR_DLKM_SECURITY_PATCH))
+  $(call add_json_str, OdmDlkmSecurityPatch, $(ODM_DLKM_SECURITY_PATCH))
+  $(call add_json_bool, BoardIncludeDtbInBootimg, $(BOARD_INCLUDE_DTB_IN_BOOTIMG))
+  $(call add_json_list, InternalKernelCmdline, $(INTERNAL_KERNEL_CMDLINE))
+  $(call add_json_list, InternalBootconfig, $(INTERNAL_BOOTCONFIG))
+  $(call add_json_str, InternalBootconfigFile, $(INTERNAL_BOOTCONFIG_FILE))
+
+  # super image stuff
+  $(call add_json_bool, ProductUseDynamicPartitions, $(filter true,$(PRODUCT_USE_DYNAMIC_PARTITIONS)))
+  $(call add_json_bool, ProductRetrofitDynamicPartitions, $(filter true,$(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS)))
+  $(call add_json_bool, ProductBuildSuperPartition, $(filter true,$(PRODUCT_BUILD_SUPER_PARTITION)))
+  $(call add_json_str, BoardSuperPartitionSize, $(BOARD_SUPER_PARTITION_SIZE))
+  $(call add_json_str, BoardSuperPartitionMetadataDevice, $(BOARD_SUPER_PARTITION_METADATA_DEVICE))
+  $(call add_json_list, BoardSuperPartitionBlockDevices, $(BOARD_SUPER_PARTITION_BLOCK_DEVICES))
+  $(call add_json_map, BoardSuperPartitionGroups)
+    $(foreach group, $(BOARD_SUPER_PARTITION_GROUPS), \
+      $(call add_json_map, $(group)) \
+        $(call add_json_str, GroupSize, $(BOARD_$(call to-upper,$(group))_SIZE)) \
+        $(if $(BOARD_$(call to-upper,$(group))_PARTITION_LIST), \
+          $(call add_json_list, PartitionList, $(BOARD_$(call to-upper,$(group))_PARTITION_LIST))) \
+      $(call end_json_map))
+    $(call end_json_map)
+  $(call add_json_bool, ProductVirtualAbOta, $(filter true,$(PRODUCT_VIRTUAL_AB_OTA)))
+  $(call add_json_bool, ProductVirtualAbOtaRetrofit, $(filter true,$(PRODUCT_VIRTUAL_AB_OTA_RETROFIT)))
+  $(call add_json_bool, AbOtaUpdater, $(filter true,$(AB_OTA_UPDATER)))
 
   # Avb (android verified boot) stuff
   $(call add_json_bool, BoardAvbEnable, $(filter true,$(BOARD_AVB_ENABLE)))
@@ -476,6 +514,10 @@
   $(call add_json_bool, BuildingOdmDlkmImage,               $(BUILDING_ODM_DLKM_IMAGE))
   $(call add_json_list, OdmKernelModules, $(BOARD_ODM_KERNEL_MODULES))
   $(call add_json_str, OdmKernelBlocklistFile, $(BOARD_ODM_KERNEL_MODULES_BLOCKLIST_FILE))
+  $(call add_json_list, VendorRamdiskKernelModules, $(BOARD_VENDOR_RAMDISK_KERNEL_MODULES))
+  $(call add_json_str, VendorRamdiskKernelBlocklistFile, $(BOARD_VENDOR_RAMDISK_KERNEL_MODULES_BLOCKLIST_FILE))
+  $(call add_json_list, VendorRamdiskKernelLoadModules, $(BOARD_VENDOR_RAMDISK_KERNEL_MODULES_LOAD))
+  $(call add_json_str, VendorRamdiskKernelOptionsFile, $(BOARD_VENDOR_RAMDISK_KERNEL_MODULES_OPTIONS_FILE))
 
   # Used to generate /vendor/build.prop
   $(call add_json_list, BoardInfoFiles, $(if $(TARGET_BOARD_INFO_FILES),$(TARGET_BOARD_INFO_FILES),$(firstword $(TARGET_BOARD_INFO_FILE) $(wildcard $(TARGET_DEVICE_DIR)/board-info.txt))))
@@ -483,6 +525,28 @@
 
   $(call add_json_list, ProductCopyFiles, $(PRODUCT_COPY_FILES))
 
+  # Used to generate fsv meta
+  $(call add_json_bool, ProductFsverityGenerateMetadata,               $(PRODUCT_FSVERITY_GENERATE_METADATA))
+
+  # Used to generate recovery partition
+  $(call add_json_str, TargetScreenDensity, $(TARGET_SCREEN_DENSITY))
+
+  # Used to generate /recovery/root/build.prop
+  $(call add_json_map, PrivateRecoveryUiProperties)
+    $(call add_json_str, animation_fps, $(TARGET_RECOVERY_UI_ANIMATION_FPS))
+    $(call add_json_str, margin_height, $(TARGET_RECOVERY_UI_MARGIN_HEIGHT))
+    $(call add_json_str, margin_width, $(TARGET_RECOVERY_UI_MARGIN_WIDTH))
+    $(call add_json_str, menu_unusable_rows, $(TARGET_RECOVERY_UI_MENU_UNUSABLE_ROWS))
+    $(call add_json_str, progress_bar_baseline, $(TARGET_RECOVERY_UI_PROGRESS_BAR_BASELINE))
+    $(call add_json_str, touch_low_threshold, $(TARGET_RECOVERY_UI_TOUCH_LOW_THRESHOLD))
+    $(call add_json_str, touch_high_threshold, $(TARGET_RECOVERY_UI_TOUCH_HIGH_THRESHOLD))
+    $(call add_json_str, vr_stereo_offset, $(TARGET_RECOVERY_UI_VR_STEREO_OFFSET))
+    $(call add_json_str, brightness_file, $(TARGET_RECOVERY_UI_BRIGHTNESS_FILE))
+    $(call add_json_str, max_brightness_file, $(TARGET_RECOVERY_UI_MAX_BRIGHTNESS_FILE))
+    $(call add_json_str, brightness_normal_percent, $(TARGET_RECOVERY_UI_BRIGHTNESS_NORMAL))
+    $(call add_json_str, brightness_dimmed_percent, $(TARGET_RECOVERY_UI_BRIGHTNESS_DIMMED))
+  $(call end_json_map)
+
 $(call end_json_map)
 
 # For converting vintf_data
diff --git a/core/soong_extra_config.mk b/core/soong_extra_config.mk
index 2ff83a1..8eee50a 100644
--- a/core/soong_extra_config.mk
+++ b/core/soong_extra_config.mk
@@ -80,7 +80,7 @@
 
 $(call add_json_str, ScreenDensity, $(TARGET_SCREEN_DENSITY))
 
-$(call add_json_bool, UsesVulkan, $(filter true,$(TARGET_USES_VULKAN)))
+$(call add_json_str, UsesVulkan, $(TARGET_USES_VULKAN))
 
 $(call add_json_bool, ZygoteForce64, $(filter true,$(ZYGOTE_FORCE_64)))
 
diff --git a/core/sysprop.mk b/core/sysprop.mk
index 255b699..e4f994f 100644
--- a/core/sysprop.mk
+++ b/core/sysprop.mk
@@ -122,11 +122,19 @@
 ifneq ($(strip $(7)), true)
 	$(hide) $$(call generate-common-build-props,$(call to-lower,$(strip $(1))),$$@)
 endif
+        # Make and Soong use different intermediate files to build vendor/build.prop.
+        # Although the sysprop contents are same, the absolute paths of android_info.prop are different.
+        # Print the filename for the intermediate files (files in OUT_DIR).
+        # This helps with validating mk->soong migration of android partitions.
 	$(hide) $(foreach file,$(strip $(3)),\
 	    if [ -f "$(file)" ]; then\
 	        echo "" >> $$@;\
 	        echo "####################################" >> $$@;\
-	        echo "# from $(file)" >> $$@;\
+	        $(if $(filter $(OUT_DIR)/%,$(file)), \
+		echo "# from $(notdir $(file))" >> $$@;\
+		,\
+		echo "# from $(file)" >> $$@;\
+		)\
 	        echo "####################################" >> $$@;\
 	        cat $(file) >> $$@;\
 	    fi;)
@@ -152,61 +160,6 @@
 $(call declare-1p-target,$(2))
 endef
 
-# -----------------------------------------------------------------
-# Define fingerprint, thumbprint, and version tags for the current build
-#
-# BUILD_VERSION_TAGS is a comma-separated list of tags chosen by the device
-# implementer that further distinguishes the build. It's basically defined
-# by the device implementer. Here, we are adding a mandatory tag that
-# identifies the signing config of the build.
-BUILD_VERSION_TAGS := $(BUILD_VERSION_TAGS)
-ifeq ($(TARGET_BUILD_TYPE),debug)
-  BUILD_VERSION_TAGS += debug
-endif
-# The "test-keys" tag marks builds signed with the old test keys,
-# which are available in the SDK.  "dev-keys" marks builds signed with
-# non-default dev keys (usually private keys from a vendor directory).
-# Both of these tags will be removed and replaced with "release-keys"
-# when the target-files is signed in a post-build step.
-ifeq ($(DEFAULT_SYSTEM_DEV_CERTIFICATE),build/make/target/product/security/testkey)
-BUILD_KEYS := test-keys
-else
-BUILD_KEYS := dev-keys
-endif
-BUILD_VERSION_TAGS += $(BUILD_KEYS)
-BUILD_VERSION_TAGS := $(subst $(space),$(comma),$(sort $(BUILD_VERSION_TAGS)))
-
-# BUILD_FINGERPRINT is used used to uniquely identify the combined build and
-# product; used by the OTA server.
-ifeq (,$(strip $(BUILD_FINGERPRINT)))
-  BUILD_FINGERPRINT := $(PRODUCT_BRAND)/$(TARGET_PRODUCT)/$(TARGET_DEVICE):$(PLATFORM_VERSION)/$(BUILD_ID)/$(BUILD_NUMBER_FROM_FILE):$(TARGET_BUILD_VARIANT)/$(BUILD_VERSION_TAGS)
-endif
-
-BUILD_FINGERPRINT_FILE := $(PRODUCT_OUT)/build_fingerprint.txt
-ifneq (,$(shell mkdir -p $(PRODUCT_OUT) && echo $(BUILD_FINGERPRINT) >$(BUILD_FINGERPRINT_FILE) && grep " " $(BUILD_FINGERPRINT_FILE)))
-  $(error BUILD_FINGERPRINT cannot contain spaces: "$(file <$(BUILD_FINGERPRINT_FILE))")
-endif
-BUILD_FINGERPRINT_FROM_FILE := $$(cat $(BUILD_FINGERPRINT_FILE))
-# unset it for safety.
-BUILD_FINGERPRINT :=
-
-# BUILD_THUMBPRINT is used to uniquely identify the system build; used by the
-# OTA server. This purposefully excludes any product-specific variables.
-ifeq (,$(strip $(BUILD_THUMBPRINT)))
-  BUILD_THUMBPRINT := $(PLATFORM_VERSION)/$(BUILD_ID)/$(BUILD_NUMBER_FROM_FILE):$(TARGET_BUILD_VARIANT)/$(BUILD_VERSION_TAGS)
-endif
-
-BUILD_THUMBPRINT_FILE := $(PRODUCT_OUT)/build_thumbprint.txt
-ifeq ($(strip $(HAS_BUILD_NUMBER)),true)
-$(BUILD_THUMBPRINT_FILE): $(BUILD_NUMBER_FILE)
-endif
-ifneq (,$(shell mkdir -p $(PRODUCT_OUT) && echo $(BUILD_THUMBPRINT) >$(BUILD_THUMBPRINT_FILE) && grep " " $(BUILD_THUMBPRINT_FILE)))
-  $(error BUILD_THUMBPRINT cannot contain spaces: "$(file <$(BUILD_THUMBPRINT_FILE))")
-endif
-# unset it for safety.
-BUILD_THUMBPRINT_FILE :=
-BUILD_THUMBPRINT :=
-
 KNOWN_OEM_THUMBPRINT_PROPERTIES := \
     ro.product.brand \
     ro.product.name \
diff --git a/core/tasks/cts.mk b/core/tasks/cts.mk
index 294cb57..d9f6150 100644
--- a/core/tasks/cts.mk
+++ b/core/tasks/cts.mk
@@ -97,12 +97,28 @@
 coverage_out := $(HOST_OUT)/cts-api-coverage
 api_map_out := $(HOST_OUT)/cts-api-map
 
-cts_jar_files := $(api_map_out)/api_map_files.txt
+cts_jar_files := $(api_map_out)/cts_jar_files.txt
+cts_interactive_jar_files := $(api_map_out)/cts_interactive_jar_files.txt
+cts_all_jar_files := $(api_map_out)/cts_all_jar_files.txt
+
 $(cts_jar_files): PRIVATE_API_MAP_FILES := $(sort $(COMPATIBILITY.cts.API_MAP_FILES))
 $(cts_jar_files):
 	mkdir -p $(dir $@)
 	echo $(PRIVATE_API_MAP_FILES) > $@
 
+$(cts_interactive_jar_files): PRIVATE_API_MAP_FILES := $(sort $(COMPATIBILITY.cts-interactive.API_MAP_FILES))
+$(cts_interactive_jar_files): $(SOONG_ANDROID_CTS_VERIFIER_APP_LIST)
+	mkdir -p $(dir $@)
+	cp $< $@
+	echo $(PRIVATE_API_MAP_FILES) >> $@
+
+$(cts_all_jar_files): PRIVATE_API_MAP_FILES := $(sort $(COMPATIBILITY.cts.API_MAP_FILES) \
+                                                      $(COMPATIBILITY.cts-interactive.API_MAP_FILES))
+$(cts_all_jar_files): $(SOONG_ANDROID_CTS_VERIFIER_APP_LIST)
+	mkdir -p $(dir $@)
+	cp $< $@
+	echo $(PRIVATE_API_MAP_FILES) >> $@
+
 api_xml_description := $(TARGET_OUT_COMMON_INTERMEDIATES)/api.xml
 
 napi_text_description := cts/tools/cts-api-coverage/etc/ndk-api.xml
@@ -124,13 +140,14 @@
 cts_api_coverage_dependencies := $(cts_api_coverage_exe) $(dexdeps_exe) $(api_xml_description) $(napi_xml_description)
 cts_system_api_coverage_dependencies := $(cts_api_coverage_exe) $(dexdeps_exe) $(system_api_xml_description)
 
-cts-api-xml-api-map-report := $(api_map_out)/api-map.xml
-cts-api-html-api-map-report := $(api_map_out)/api-map.html
-cts-system-api-xml-api-map-report := $(api_map_out)/system-api-map.xml
-cts-system-api-html-api-map-report := $(api_map_out)/system-api-map.html
+cts-system-api-map-xml-report := $(api_map_out)/cts-system-api-map.xml
+cts-interactive-system-api-map-xml-report := $(api_map_out)/cts-interactive-system-api-map.xml
+cts-combined-system-api-map-xml-report := $(api_map_out)/cts-combined-system-api-map.xml
+cts-combined-system-api-map-html-report := $(api_map_out)/cts-combined-system-api-map.html
 
 cts_system_api_map_dependencies := $(cts_api_map_exe) $(system_api_xml_description) $(cts_jar_files)
-cts_api_map_dependencies := $(cts_api_map_exe) $(api_xml_description) $(cts_jar_files)
+cts_interactive_system_api_map_dependencies := $(cts_api_map_exe) $(system_api_xml_description) $(cts_interactive_jar_files)
+cts_combined_system_api_map_dependencies := $(cts_api_map_exe) $(system_api_xml_description) $(cts_all_jar_files)
 
 android_cts_zip := $(HOST_OUT)/cts/android-cts.zip
 cts_verifier_apk := $(call intermediates-dir-for,APPS,CtsVerifier)/package.apk
@@ -210,45 +227,42 @@
 .PHONY: cts-coverage-report-all cts-api-coverage
 cts-coverage-report-all: cts-test-coverage cts-verifier-coverage cts-combined-coverage cts-combined-xml-coverage
 
-$(cts-system-api-xml-api-map-report): PRIVATE_CTS_API_MAP_EXE := $(cts_api_map_exe)
-$(cts-system-api-xml-api-map-report): PRIVATE_API_XML_DESC := $(system_api_xml_description)
-$(cts-system-api-xml-api-map-report): PRIVATE_JAR_FILES := $(cts_jar_files)
-$(cts-system-api-xml-api-map-report) : $(android_cts_zip) $(cts_system_api_map_dependencies) | $(ACP)
+$(cts-system-api-map-xml-report): PRIVATE_CTS_API_MAP_EXE := $(cts_api_map_exe)
+$(cts-system-api-map-xml-report): PRIVATE_API_XML_DESC := $(system_api_xml_description)
+$(cts-system-api-map-xml-report): PRIVATE_JAR_FILES := $(cts_jar_files)
+$(cts-system-api-map-xml-report) : $(android_cts_zip) $(cts_system_api_map_dependencies) | $(ACP)
 	$(call generate-api-map-report-cts,"CTS System API MAP Report - XML",\
 			$(PRIVATE_JAR_FILES),xml)
 
-$(cts-system-api-html-api-map-report): PRIVATE_CTS_API_MAP_EXE := $(cts_api_map_exe)
-$(cts-system-api-html-api-map-report): PRIVATE_API_XML_DESC := $(system_api_xml_description)
-$(cts-system-api-html-api-map-report): PRIVATE_JAR_FILES := $(cts_jar_files)
-$(cts-system-api-html-api-map-report) : $(android_cts_zip) $(cts_system_api_map_dependencies) | $(ACP)
-	$(call generate-api-map-report-cts,"CTS System API MAP Report - HTML",\
-			$(PRIVATE_JAR_FILES),html)
-
-$(cts-api-xml-api-map-report): PRIVATE_CTS_API_MAP_EXE := $(cts_api_map_exe)
-$(cts-api-xml-api-map-report): PRIVATE_API_XML_DESC := $(api_xml_description)
-$(cts-api-xml-api-map-report): PRIVATE_JAR_FILES := $(cts_jar_files)
-$(cts-api-xml-api-map-report) : $(android_cts_zip) $(cts_api_map_dependencies) | $(ACP)
-	$(call generate-api-map-report-cts,"CTS API MAP Report - XML",\
+$(cts-interactive-system-api-map-xml-report): PRIVATE_CTS_API_MAP_EXE := $(cts_api_map_exe)
+$(cts-interactive-system-api-map-xml-report): PRIVATE_API_XML_DESC := $(system_api_xml_description)
+$(cts-interactive-system-api-map-xml-report): PRIVATE_JAR_FILES := $(cts_interactive_jar_files)
+$(cts-interactive-system-api-map-xml-report) : $(verifier_zip) $(cts_interactive_system_api_map_dependencies) | $(ACP)
+	$(call generate-api-map-report-cts,"CTS Interactive System API MAP Report - XML",\
 			$(PRIVATE_JAR_FILES),xml)
 
-$(cts-api-html-api-map-report): PRIVATE_CTS_API_MAP_EXE := $(cts_api_map_exe)
-$(cts-api-html-api-map-report): PRIVATE_API_XML_DESC := $(api_xml_description)
-$(cts-api-html-api-map-report): PRIVATE_JAR_FILES := $(cts_jar_files)
-$(cts-api-html-api-map-report) : $(android_cts_zip) $(cts_api_map_dependencies) | $(ACP)
-	$(call generate-api-map-report-cts,"CTS API MAP Report - HTML",\
+$(cts-combined-system-api-map-xml-report): PRIVATE_CTS_API_MAP_EXE := $(cts_api_map_exe)
+$(cts-combined-system-api-map-xml-report): PRIVATE_API_XML_DESC := $(system_api_xml_description)
+$(cts-combined-system-api-map-xml-report): PRIVATE_JAR_FILES := $(cts_all_jar_files)
+$(cts-combined-system-api-map-xml-report) : $(verifier_zip) $(android_cts_zip) $(cts_combined_system_api_map_dependencies) | $(ACP)
+	$(call generate-api-map-report-cts,"CTS Combined System API MAP Report - XML",\
+			$(PRIVATE_JAR_FILES),xml)
+
+$(cts-combined-system-api-map-html-report): PRIVATE_CTS_API_MAP_EXE := $(cts_api_map_exe)
+$(cts-combined-system-api-map-html-report): PRIVATE_API_XML_DESC := $(system_api_xml_description)
+$(cts-combined-system-api-map-html-report): PRIVATE_JAR_FILES := $(cts_all_jar_files)
+$(cts-combined-system-api-map-html-report) : $(verifier_zip) $(android_cts_zip) $(cts_combined_system_api_map_dependencies) | $(ACP)
+	$(call generate-api-map-report-cts,"CTS Combined System API MAP Report - HTML",\
 			$(PRIVATE_JAR_FILES),html)
 
-.PHONY: cts-system-api-xml-api-map
-cts-system-api-xml-api-map : $(cts-system-api-xml-api-map-report)
+.PHONY: cts-system-api-map-xml
+cts-system-api-map-xml : $(cts-system-api-map-xml-report)
 
-.PHONY: cts-system-api-html-api-map
-cts-system-api-html-api-map : $(cts-system-api-html-api-map-report)
+.PHONY: cts-interactive-system-api-map-xml
+cts-interactive-system-api-map-xml: $(cts-interactive-system-api-map-xml-report)
 
-.PHONY: cts-api-xml-api-map
-cts-api-xml-api-map : $(cts-api-xml-api-map-report)
-
-.PHONY: cts-api-html-api-map
-cts-api-html-api-map : $(cts-api-html-api-map-report)
+.PHONY: cts-combined-system-api-map-xml
+cts-combined-system-api-map-xml : $(cts-combined-system-api-map-xml-report)
 
 .PHONY: cts-api-map-all
 
@@ -268,15 +282,13 @@
 ALL_TARGETS.$(cts-combined-xml-coverage-report).META_LIC:=$(module_license_metadata)
 
 # Put the test api map report in the dist dir if "cts-api-map-all" is among the build goals.
-$(call dist-for-goals, cts-api-map-all, $(cts-system-api-xml-api-map-report):cts-system-api-xml-api-map-report.xml)
-$(call dist-for-goals, cts-api-map-all, $(cts-system-api-html-api-map-report):cts-system-api-html-api-map-report.html)
-$(call dist-for-goals, cts-api-map-all, $(cts-api-xml-api-map-report):cts-api-xml-api-map-report.xml)
-$(call dist-for-goals, cts-api-map-all, $(cts-api-html-api-map-report):cts-api-html-api-map-report.html)
+$(call dist-for-goals, cts-api-map-all, $(cts-combined-system-api-map-xml-report):cts-api-map-report.xml)
+$(call dist-for-goals, cts-api-map-all, $(cts-combined-system-api-map-html-report):cts-api-map-report.html)
 
-ALL_TARGETS.$(cts-system-api-xml-api-map-report).META_LIC:=$(module_license_metadata)
-ALL_TARGETS.$(cts-system-api-html-api-map-report).META_LIC:=$(module_license_metadata)
-ALL_TARGETS.$(cts-api-xml-api-map-report).META_LIC:=$(module_license_metadata)
-ALL_TARGETS.$(cts-api-html-api-map-report).META_LIC:=$(module_license_metadata)
+ALL_TARGETS.$(cts-system-api-map-xml-report).META_LIC:=$(module_license_metadata)
+ALL_TARGETS.$(cts-interactive-system-api-map-xml-report).META_LIC:=$(module_license_metadata)
+ALL_TARGETS.$(cts-combined-system-api-map-xml-report).META_LIC:=$(module_license_metadata)
+ALL_TARGETS.$(cts-combined-system-api-map-html-report).META_LIC:=$(module_license_metadata)
 
 # Arguments;
 #  1 - Name of the report printed out on the screen
@@ -301,18 +313,19 @@
 # Reset temp vars
 cts_api_coverage_dependencies :=
 cts_system_api_coverage_dependencies :=
-cts_api_map_dependencies :=
 cts_system_api_map_dependencies :=
+cts_interactive_system_api_map_dependencies :=
+cts_combined_system_api_map_dependencies :=
 cts-combined-coverage-report :=
 cts-combined-xml-coverage-report :=
 cts-verifier-coverage-report :=
 cts-test-coverage-report :=
 cts-system-api-coverage-report :=
 cts-system-api-xml-coverage-report :=
-cts-api-xml-api-map-report :=
-cts-api-html-api-map-report :=
-cts-system-api-xml-api-map-report :=
-cts-system-api-html-api-map-report :=
+cts-system-api-map-xml-report :=
+cts-interactive-system-api-map-xml-report :=
+cts-combined-system-api-map-xml-report :=
+cts-combined-system-api-map-html-report :=
 api_xml_description :=
 api_text_description :=
 system_api_xml_description :=
diff --git a/core/tasks/mcts.mk b/core/tasks/mcts.mk
index 09a4191..02e916a 100644
--- a/core/tasks/mcts.mk
+++ b/core/tasks/mcts.mk
@@ -15,7 +15,8 @@
 ifneq ($(wildcard test/mts/README.md),)
 
 mcts_test_suites :=
-mcts_test_suites += mcts
+mcts_all_test_suites :=
+mcts_all_test_suites += mcts
 
 $(foreach module, $(mts_modules), $(eval mcts_test_suites += mcts-$(module)))
 
@@ -29,4 +30,14 @@
 	$(eval $(call dist-for-goals, $(suite), $(compatibility_zip))) \
 )
 
+$(foreach suite, $(mcts_all_test_suites), \
+	$(eval test_suite_name := $(suite)) \
+	$(eval test_suite_tradefed := mcts-tradefed) \
+	$(eval test_suite_readme := test/mts/README.md) \
+	$(eval include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk) \
+	$(eval .PHONY: $(suite)) \
+	$(eval $(suite): $(compatibility_zip)) \
+	$(eval $(call dist-for-goals, $(suite), $(compatibility_zip))) \
+)
+
 endif
diff --git a/core/tasks/module-info.mk b/core/tasks/module-info.mk
index 0ca27d8..dd01f96 100644
--- a/core/tasks/module-info.mk
+++ b/core/tasks/module-info.mk
@@ -50,6 +50,8 @@
 			$(call write-optional-json-list, "host_dependencies", $(sort $(ALL_MODULES.$(m).HOST_REQUIRED_FROM_TARGET))) \
 			$(call write-optional-json-list, "target_dependencies", $(sort $(ALL_MODULES.$(m).TARGET_REQUIRED_FROM_HOST))) \
 			$(call write-optional-json-bool, "test_module_config_base", $(ALL_MODULES.$(m).TEST_MODULE_CONFIG_BASE)) \
+			$(call write-optional-json-bool, "make", $(if $(ALL_MODULES.$(m).IS_SOONG_MODULE),,true)) \
+			$(call write-optional-json-bool, "make_generated_module_info", true) \
 		'}')'\n}\n' >> $@.tmp
 	$(PRIVATE_MERGE_JSON_OBJECTS) -o $@ $(PRIVATE_SOONG_MODULE_INFO) $@.tmp
 	rm $@.tmp
diff --git a/core/tasks/tools/compatibility.mk b/core/tasks/tools/compatibility.mk
index 9189c2d..f205cea 100644
--- a/core/tasks/tools/compatibility.mk
+++ b/core/tasks/tools/compatibility.mk
@@ -27,7 +27,6 @@
 #   compatibility_zip: the path to the output zip file.
 
 special_mts_test_suites :=
-special_mts_test_suites += mcts
 special_mts_test_suites += $(mts_modules)
 ifneq ($(filter $(special_mts_test_suites),$(patsubst mcts-%,%,$(test_suite_name))),)
 	test_suite_subdir := android-mts
diff --git a/core/version_util.mk b/core/version_util.mk
index 0e34634..2bf41ec 100644
--- a/core/version_util.mk
+++ b/core/version_util.mk
@@ -23,6 +23,7 @@
 #     PLATFORM_DISPLAY_VERSION
 #     PLATFORM_SDK_VERSION
 #     PLATFORM_SDK_EXTENSION_VERSION
+#     PLATFORM_BASE_SDK_EXTENSION_VERSION
 #     PLATFORM_VERSION_CODENAME
 #     DEFAULT_APP_TARGET_SDK
 #     BUILD_ID
@@ -67,8 +68,16 @@
 PLATFORM_SDK_EXTENSION_VERSION := $(RELEASE_PLATFORM_SDK_EXTENSION_VERSION)
 .KATI_READONLY := PLATFORM_SDK_EXTENSION_VERSION
 
-# This is the sdk extension version that PLATFORM_SDK_VERSION ships with.
-PLATFORM_BASE_SDK_EXTENSION_VERSION := $(PLATFORM_SDK_EXTENSION_VERSION)
+ifdef PLATFORM_BASE_SDK_EXTENSION_VERSION
+  $(error Do not set PLATFORM_BASE_SDK_EXTENSION_VERSION directly. Use RELEASE_PLATFORM_BASE_SDK_EXTENSION_VERSION. value: $(PLATFORM_BASE_SDK_EXTENSION_VERSION))
+endif
+ifdef RELEASE_PLATFORM_BASE_SDK_EXTENSION_VERSION
+  # This is the sdk extension version that PLATFORM_SDK_VERSION ships with.
+  PLATFORM_BASE_SDK_EXTENSION_VERSION := $(RELEASE_PLATFORM_BASE_SDK_EXTENSION_VERSION)
+else
+  # Fallback to PLATFORM_SDK_EXTENSION_VERSION if RELEASE_PLATFORM_BASE_SDK_EXTENSION_VERSION is undefined.
+  PLATFORM_BASE_SDK_EXTENSION_VERSION := $(PLATFORM_SDK_EXTENSION_VERSION)
+endif
 .KATI_READONLY := PLATFORM_BASE_SDK_EXTENSION_VERSION
 
 ifdef PLATFORM_VERSION_CODENAME
diff --git a/shell_utils.sh b/shell_utils.sh
index 9053c42..3124db5 100644
--- a/shell_utils.sh
+++ b/shell_utils.sh
@@ -214,3 +214,19 @@
     ' SIGINT SIGTERM SIGQUIT EXIT
 }
 
+# Import the build variables supplied as arguments into this shell's environment.
+# For absolute variables, prefix the variable name with a '/'. For example:
+#    import_build_vars OUT_DIR DIST_DIR /HOST_OUT_EXECUTABLES
+# Returns nonzero if the build command failed. Stderr is passed through.
+function import_build_vars()
+{
+    require_top
+    local script
+    script=$(cd $TOP && build/soong/bin/get_build_vars "$@")
+    local ret=$?
+    if [ $ret -ne 0 ] ; then
+        return $ret
+    fi
+    eval "$script"
+    return $?
+}
diff --git a/target/board/BoardConfigGsiCommon.mk b/target/board/BoardConfigGsiCommon.mk
index 67e31df..8a62796 100644
--- a/target/board/BoardConfigGsiCommon.mk
+++ b/target/board/BoardConfigGsiCommon.mk
@@ -69,6 +69,11 @@
 BOARD_SUPER_PARTITION_GROUPS := gsi_dynamic_partitions
 BOARD_GSI_DYNAMIC_PARTITIONS_PARTITION_LIST := system
 BOARD_GSI_DYNAMIC_PARTITIONS_SIZE := 3221225472
+
+# Build pvmfw with GSI: b/376363989
+ifeq (true,$(PRODUCT_BUILD_PVMFW_IMAGE))
+BOARD_PVMFWIMAGE_PARTITION_SIZE := 0x00100000
+endif
 endif
 
 # TODO(b/123695868, b/146149698):
diff --git a/target/board/generic_64bitonly_x86_64/device.mk b/target/board/generic_64bitonly_x86_64/device.mk
index bb49057..5edf5e0 100644
--- a/target/board/generic_64bitonly_x86_64/device.mk
+++ b/target/board/generic_64bitonly_x86_64/device.mk
@@ -17,8 +17,3 @@
 ifdef NET_ETH0_STARTONBOOT
   PRODUCT_PROPERTY_OVERRIDES += net.eth0.startonboot=1
 endif
-
-# Ensure we package the BIOS files too.
-PRODUCT_HOST_PACKAGES += \
-    bios.bin \
-    vgabios-cirrus.bin \
diff --git a/target/board/generic_x86/device.mk b/target/board/generic_x86/device.mk
index 60f0cc3..27fb310 100644
--- a/target/board/generic_x86/device.mk
+++ b/target/board/generic_x86/device.mk
@@ -17,8 +17,3 @@
 ifdef NET_ETH0_STARTONBOOT
   PRODUCT_VENDOR_PROPERTIES += net.eth0.startonboot=1
 endif
-
-# Ensure we package the BIOS files too.
-PRODUCT_HOST_PACKAGES += \
-	bios.bin \
-	vgabios-cirrus.bin \
diff --git a/target/product/app_function_extensions.mk b/target/product/app_function_extensions.mk
new file mode 100644
index 0000000..a61afdc
--- /dev/null
+++ b/target/product/app_function_extensions.mk
@@ -0,0 +1,22 @@
+#
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# The app function sidecar extensions
+
+# /system_ext packages
+PRODUCT_PACKAGES += \
+    com.google.android.appfunctions.sidecar \
+    appfunctions.sidecar.xml
diff --git a/target/product/base_system.mk b/target/product/base_system.mk
index 3a7256e..b3b58d3 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -17,7 +17,6 @@
 # Base modules and settings for the system partition.
 PRODUCT_PACKAGES += \
     abx \
-    aconfigd \
     aconfigd-system \
     adbd_system_api \
     aflags \
@@ -97,7 +96,6 @@
     enhanced-confirmation.xml \
     ExtShared \
     flags_health_check \
-    framework-connectivity-b \
     framework-graphics \
     framework-location \
     framework-minus-apex \
@@ -180,6 +178,7 @@
     libmedia \
     libmedia_jni \
     libmediandk \
+    libmonkey_jni \
     libmtp \
     libnetd_client \
     libnetlink \
@@ -206,6 +205,7 @@
     libstdc++ \
     libsysutils \
     libui \
+    libuprobestats_client \
     libusbhost \
     libutils \
     libvintf_jni \
@@ -249,6 +249,7 @@
     pintool \
     platform.xml \
     pm \
+    prefetch \
     preinstalled-packages-asl-files.xml \
     preinstalled-packages-platform.xml \
     preinstalled-packages-strict-signature.xml \
@@ -294,7 +295,6 @@
     uiautomator \
     uinput \
     uncrypt \
-    uprobestats \
     usbd \
     vdc \
     vintf \
@@ -312,6 +312,17 @@
 
 endif
 
+# When we release uprobestats module
+ifeq ($(RELEASE_UPROBESTATS_MODULE),true)
+    PRODUCT_PACKAGES += \
+        com.android.uprobestats \
+
+else
+    PRODUCT_PACKAGES += \
+        uprobestats \
+
+endif
+
 # These packages are not used on Android TV
 ifneq ($(PRODUCT_IS_ATV),true)
   PRODUCT_PACKAGES += \
@@ -348,6 +359,13 @@
         com.android.webview.bootstrap
 endif
 
+# Only add the jar when it is not in the Tethering module. Otherwise,
+# it will be added via com.android.tethering
+ifneq ($(RELEASE_MOVE_VCN_TO_MAINLINE),true)
+    PRODUCT_PACKAGES += \
+        framework-connectivity-b
+endif
+
 ifneq (,$(RELEASE_RANGING_STACK))
     PRODUCT_PACKAGES += \
         com.android.ranging
@@ -471,6 +489,7 @@
 
 PRODUCT_SYSTEM_PROPERTIES += debug.atrace.tags.enableflags=0
 PRODUCT_SYSTEM_PROPERTIES += persist.traced.enable=1
+PRODUCT_SYSTEM_PROPERTIES += ro.surface_flinger.game_default_frame_rate_override=60
 
 # Include kernel configs.
 PRODUCT_PACKAGES += \
diff --git a/target/product/build_variables.mk b/target/product/build_variables.mk
index 7661e06..e99ab06 100644
--- a/target/product/build_variables.mk
+++ b/target/product/build_variables.mk
@@ -20,6 +20,12 @@
 # Control libbinder client caching
 $(call soong_config_set, libbinder, release_libbinder_client_cache, $(RELEASE_LIBBINDER_CLIENT_CACHE))
 
+# Control caching while adding service in libbinder cache
+$(call soong_config_set, libbinder, release_libbinder_addservice_cache, $(RELEASE_LIBBINDER_ADDSERVICE_CACHE))
+
+# Remove static list in libbinder cache
+$(call soong_config_set, libbinder, release_libbinder_remove_cache_static_list, $(RELEASE_LIBBINDER_REMOVE_CACHE_STATIC_LIST))
+
 # Use the configured release of sqlite
 $(call soong_config_set, libsqlite3, release_package_libsqlite3, $(RELEASE_PACKAGE_LIBSQLITE3))
 
diff --git a/target/product/default_art_config.mk b/target/product/default_art_config.mk
index 83d9215..d857e04 100644
--- a/target/product/default_art_config.mk
+++ b/target/product/default_art_config.mk
@@ -51,7 +51,6 @@
     framework-minus-apex \
     framework-graphics \
     framework-location \
-    framework-connectivity-b \
     ext \
     telephony-common \
     voip-common \
@@ -121,6 +120,17 @@
     $(call soong_config_set,bootclasspath,release_ranging_stack,true)
 endif
 
+# Check if VCN should be built into the tethering module or not
+ifeq ($(RELEASE_MOVE_VCN_TO_MAINLINE),true)
+    PRODUCT_APEX_BOOT_JARS += \
+        com.android.tethering:framework-connectivity-b \
+
+else
+    PRODUCT_BOOT_JARS += \
+        framework-connectivity-b \
+
+endif
+
 # List of system_server classpath jars delivered via apex.
 # Keep the list sorted by module names and then library names.
 # Note: For modules available in Q, DO NOT add new entries here.
diff --git a/target/product/full_x86.mk b/target/product/full_x86.mk
index 07f6472..a1b71ca 100644
--- a/target/product/full_x86.mk
+++ b/target/product/full_x86.mk
@@ -32,11 +32,6 @@
   PRODUCT_VENDOR_PROPERTIES += net.eth0.startonboot=1
 endif
 
-# Ensure we package the BIOS files too.
-PRODUCT_HOST_PACKAGES += \
-	bios.bin \
-	vgabios-cirrus.bin \
-
 # Enable dynamic partition size
 PRODUCT_USE_DYNAMIC_PARTITION_SIZE := true
 
diff --git a/target/product/fullmte.mk b/target/product/fullmte.mk
index b622496..fed66e7 100644
--- a/target/product/fullmte.mk
+++ b/target/product/fullmte.mk
@@ -20,7 +20,7 @@
 # For more details, see:
 # https://source.android.com/docs/security/test/memory-safety/arm-mte
 ifeq ($(filter memtag_heap,$(SANITIZE_TARGET)),)
-  SANITIZE_TARGET := $(strip $(SANITIZE_TARGET) memtag_heap memtag_stack)
+  SANITIZE_TARGET := $(strip $(SANITIZE_TARGET) memtag_heap memtag_stack memtag_globals)
   SANITIZE_TARGET_DIAG := $(strip $(SANITIZE_TARGET_DIAG) memtag_heap)
 endif
 PRODUCT_PRODUCT_PROPERTIES += persist.arm64.memtag.default=sync
diff --git a/target/product/generic/Android.bp b/target/product/generic/Android.bp
index ea49af4..314816d 100644
--- a/target/product/generic/Android.bp
+++ b/target/product/generic/Android.bp
@@ -1,5 +1,4 @@
 generic_rootdirs = [
-    "acct",
     "apex",
     "bootstrap-apex",
     "config",
@@ -56,10 +55,6 @@
         name: "bugreports",
     },
     {
-        target: "/data/cache",
-        name: "cache",
-    },
-    {
         target: "/sys/kernel/debug",
         name: "d",
     },
@@ -125,6 +120,10 @@
         target: "/system_ext",
         name: "system/system_ext",
     },
+    {
+        target: "/data/cache",
+        name: "cache",
+    },
 ]
 
 filegroup {
@@ -346,9 +345,75 @@
     ],
 }
 
-filegroup {
-    name: "system_image_erofs_compress_hints.txt",
-    srcs: ["erofs_compress_hints.txt"],
+android_filesystem_defaults {
+    name: "system_ext_image_defaults",
+    deps: [
+        ///////////////////////////////////////////
+        // base_system_ext
+        ///////////////////////////////////////////
+        "build_flag_system_ext",
+        "fs_config_dirs_system_ext",
+        "fs_config_files_system_ext",
+        "group_system_ext",
+        "passwd_system_ext",
+        "SatelliteClient",
+        "selinux_policy_system_ext",
+        "system_ext_manifest.xml",
+        "system_ext-build.prop",
+        // Base modules when shipping api level is less than or equal to 34
+        "hwservicemanager",
+        "android.hidl.allocator@1.0-service",
+
+        ///////////////////////////////////////////
+        // window_extensions_base
+        ///////////////////////////////////////////
+        "androidx.window.extensions",
+        "androidx.window.sidecar",
+
+        ///////////////////////////////////////////
+        // base_system
+        ///////////////////////////////////////////
+        "charger",
+    ] + select(release_flag("RELEASE_APPFUNCTION_SIDECAR"), {
+        true: [
+            "com.android.extensions.appfunctions",
+            "appfunctions.extension.xml",
+        ],
+        default: [],
+    }),
+}
+
+android_filesystem_defaults {
+    name: "product_image_defaults",
+    deps: [
+        ///////////////////////////////////////////
+        // media_product
+        ///////////////////////////////////////////
+        "webview",
+
+        ///////////////////////////////////////////
+        // base_product
+        ///////////////////////////////////////////
+
+        // Base modules and settings for the product partition.
+        "build_flag_product",
+        "fs_config_dirs_product",
+        "fs_config_files_product",
+        "group_product",
+        "ModuleMetadata",
+        "passwd_product",
+        "product_compatibility_matrix.xml",
+        "product_manifest.xml",
+        "selinux_policy_product",
+        "product-build.prop",
+
+        // AUDIO
+        "frameworks_sounds",
+    ] + select(product_variable("debuggable"), {
+        // Packages included only for eng or userdebug builds, previously debug tagged
+        true: ["adb_keys"],
+        default: [],
+    }),
 }
 
 android_filesystem_defaults {
@@ -374,11 +439,6 @@
         ],
         libs: [":framework-res{.export-package.apk}"],
     },
-    type: "erofs",
-    erofs: {
-        compressor: "lz4hc,9",
-        compress_hints: ":system_image_erofs_compress_hints.txt",
-    },
     build_logtags: true,
     gen_aconfig_flags_pb: true,
 
@@ -391,7 +451,6 @@
 
     deps: [
         "abx",
-        "aconfigd",
         "aconfigd-system",
         "aflags",
         "am",
@@ -482,7 +541,6 @@
         "locksettings", // base_system
         "logcat", // base_system
         "logd", // base_system
-        "logpersist.start",
         "lpdump", // base_system
         "lshal", // base_system
         "make_f2fs", // media_system
@@ -510,6 +568,7 @@
         "pintool", // base_system
         "platform.xml", // base_system
         "pm", // base_system
+        "prefetch", //base_system
         "preinstalled-packages-asl-files.xml", // base_system
         "preinstalled-packages-platform-generic-system.xml", // generic_system
         "preinstalled-packages-platform-handheld-system.xml", // handheld_system
@@ -538,7 +597,6 @@
         "sfdo", // base_system
         "sgdisk", // base_system
         "sm", // base_system
-        "snapshotctl", // base_system
         "snapuserd", // base_system
         "storaged", // base_system
         "surfaceflinger", // base_system
@@ -584,6 +642,7 @@
         default: [],
     }) + select(product_variable("debuggable"), {
         true: [
+            "alloctop",
             "adevice_fingerprint",
             "arping",
             "avbctl",
@@ -596,6 +655,7 @@
             "iperf3",
             "iw",
             "layertracegenerator",
+            "logpersist.start",
             "logtagd.rc",
             "ot-cli-ftd",
             "ot-ctl",
@@ -606,6 +666,7 @@
             "sanitizer-status",
             "servicedispatcher",
             "showmap",
+            "snapshotctl",
             "sqlite3",
             "ss",
             "start_with_lockagent",
@@ -715,7 +776,6 @@
                 "framework-graphics", // base_system
                 "framework-location", // base_system
                 "framework-minus-apex-install-dependencies", // base_system
-                "framework-connectivity-b", // base_system
                 "framework_compatibility_matrix.device.xml",
                 "generic_system_fonts", // ok
                 "hwservicemanager_compat_symlink_module", // base_system
@@ -744,11 +804,21 @@
                     "com.android.profiling", // base_system (RELEASE_PACKAGE_PROFILING_MODULE)
                 ],
                 default: [],
+            }) + select(release_flag("RELEASE_MOVE_VCN_TO_MAINLINE"), {
+                true: [],
+                default: [
+                    "framework-connectivity-b", // base_system
+                ],
             }) + select(release_flag("RELEASE_AVATAR_PICKER_APP"), {
                 true: [
                     "AvatarPicker", // generic_system (RELEASE_AVATAR_PICKER_APP)
                 ],
                 default: [],
+            }) + select(release_flag("RELEASE_UPROBESTATS_MODULE"), {
+                true: [
+                    "com.android.uprobestats", // base_system (RELEASE_UPROBESTATS_MODULE)
+                ],
+                default: [],
             }),
         },
         prefer32: {
@@ -763,7 +833,12 @@
                 "android.system.virtualizationservice-ndk",
                 "libgsi",
                 "servicemanager",
-            ],
+            ] + select(release_flag("RELEASE_UPROBESTATS_MODULE"), {
+                true: [],
+                default: [
+                    "uprobestats", // base_system internal
+                ],
+            }),
         },
         both: {
             deps: [
@@ -836,6 +911,7 @@
                 "libmedia_jni", // base_system
                 "libmediandk", // base_system
                 "libminui", // generic_system
+                "libmonkey_jni", // base_system - internal
                 "libmtp", // base_system
                 "libnetd_client", // base_system
                 "libnetlink", // base_system
@@ -879,14 +955,37 @@
             }) + select(soong_config_variable("ANDROID", "TARGET_DYNAMIC_64_32_MEDIASERVER"), {
                 "true": ["mediaserver"],
                 default: [],
+            }) + select(release_flag("RELEASE_UPROBESTATS_MODULE"), {
+                true: [],
+                default: [
+                    "libuprobestats_client", // base_system internal
+                ],
             }),
         },
     },
+    arch: {
+        arm64: {
+            deps: [
+                "libclang_rt.hwasan",
+                "libc_hwasan",
+            ],
+        },
+    },
 }
 
 android_system_image {
-    name: "generic_system_image",
+    name: "aosp_shared_system_image",
     defaults: ["system_image_defaults"],
     dirs: android_rootdirs,
     symlinks: android_symlinks,
+    type: "erofs",
+    erofs: {
+        compressor: "lz4hc,9",
+        compress_hints: "erofs_compress_hints.txt",
+    },
+    deps: [
+        // DO NOT update this list. Instead, update the system_image_defaults to
+        // sync with the base_system.mk
+        "logpersist.start", // cf only
+    ],
 }
diff --git a/target/product/gsi/Android.bp b/target/product/gsi/Android.bp
index 1b8f2d7..c6fc021 100644
--- a/target/product/gsi/Android.bp
+++ b/target/product/gsi/Android.bp
@@ -71,12 +71,25 @@
         target: "/system/product",
         name: "product",
     },
+    {
+        target: "/odm/odm_dlkm/etc",
+        name: "odm_dlkm/etc",
+    },
+    {
+        target: "/vendor/vendor_dlkm/etc",
+        name: "vendor_dlkm/etc",
+    },
 ]
 
 android_system_image {
     name: "android_gsi",
-    defaults: ["system_image_defaults"],
+    defaults: [
+        "system_image_defaults",
+        "system_ext_image_defaults",
+        "product_image_defaults",
+    ],
     symlinks: gsi_symlinks,
+    dirs: ["cache"],
     deps: [
         ///////////////////////////////////////////
         // gsi_system_ext
@@ -98,28 +111,6 @@
         "system_ext_userdebug_plat_sepolicy.cil",
 
         ///////////////////////////////////////////
-        // base_system_ext
-        ///////////////////////////////////////////
-        "build_flag_system_ext",
-        "fs_config_dirs_system_ext",
-        "fs_config_files_system_ext",
-        "group_system_ext",
-        "passwd_system_ext",
-        "SatelliteClient",
-        "selinux_policy_system_ext",
-        "system_ext_manifest.xml",
-        "system_ext-build.prop",
-        // Base modules when shipping api level is less than or equal to 34
-        "hwservicemanager",
-        "android.hidl.allocator@1.0-service",
-
-        ///////////////////////////////////////////
-        // window_extensions_base
-        ///////////////////////////////////////////
-        "androidx.window.extensions",
-        "androidx.window.sidecar",
-
-        ///////////////////////////////////////////
         // gsi_release
         ///////////////////////////////////////////
         "gsi_skip_mount.cfg",
@@ -152,40 +143,7 @@
         "Dialer",
         "LatinIME",
         "apns-full-conf.xml",
-
-        ///////////////////////////////////////////
-        // media_product
-        ///////////////////////////////////////////
-        "webview",
-
-        ///////////////////////////////////////////
-        // base_product
-        ///////////////////////////////////////////
-
-        // Base modules and settings for the product partition.
-        "build_flag_product",
-        "fs_config_dirs_product",
-        "fs_config_files_product",
-        "group_product",
-        "ModuleMetadata",
-        "passwd_product",
-        "product_compatibility_matrix.xml",
-        "product_manifest.xml",
-        "selinux_policy_product",
-        "product-build.prop",
-
-        // AUDIO
-        "frameworks_sounds",
-
-        ///////////////////////////////////////////
-        // base_system
-        ///////////////////////////////////////////
-        "charger",
-    ] + select(product_variable("debuggable"), {
-        // Packages included only for eng or userdebug builds, previously debug tagged
-        true: ["adb_keys"],
-        default: [],
-    }),
+    ],
     multilib: {
         both: {
             // PRODUCT_PACKAGES_SHIPPING_API_LEVEL_34
@@ -196,4 +154,5 @@
         "true": true,
         default: false,
     }),
+    type: "ext4",
 }
diff --git a/target/product/gsi/current.txt b/target/product/gsi/current.txt
index f771916..cbb8a0e 100644
--- a/target/product/gsi/current.txt
+++ b/target/product/gsi/current.txt
@@ -24,7 +24,7 @@
 VNDK-SP: android.hardware.common-V2-ndk.so
 VNDK-SP: android.hardware.common.fmq-V1-ndk.so
 VNDK-SP: android.hardware.graphics.allocator-V2-ndk.so
-VNDK-SP: android.hardware.graphics.common-V5-ndk.so
+VNDK-SP: android.hardware.graphics.common-V6-ndk.so
 VNDK-SP: android.hardware.graphics.common@1.0.so
 VNDK-SP: android.hardware.graphics.common@1.1.so
 VNDK-SP: android.hardware.graphics.common@1.2.so
diff --git a/target/product/gsi_release.mk b/target/product/gsi_release.mk
index 39428d2..115b355 100644
--- a/target/product/gsi_release.mk
+++ b/target/product/gsi_release.mk
@@ -79,9 +79,15 @@
 PRODUCT_BUILD_SYSTEM_DLKM_IMAGE := false
 PRODUCT_EXPORT_BOOT_IMAGE_TO_DIST := true
 
+# Build pvmfw with GSI: b/376363989, pvmfw currently only supports AArch64
+ifneq (,$(filter %_arm64,$(TARGET_PRODUCT)))
+PRODUCT_BUILD_PVMFW_IMAGE := true
+endif
+
 # Additional settings used in all GSI builds
 PRODUCT_PRODUCT_PROPERTIES += \
     ro.crypto.metadata_init_delete_all_keys.enabled=false \
+    debug.codec2.bqpool_dealloc_after_stop=1 \
 
 # Window Extensions
 ifneq ($(PRODUCT_IS_ATV),true)
diff --git a/target/product/handheld_system.mk b/target/product/handheld_system.mk
index 546bbe7..2b055c7 100644
--- a/target/product/handheld_system.mk
+++ b/target/product/handheld_system.mk
@@ -69,13 +69,19 @@
     SharedStorageBackup \
     SimAppDialog \
     Telecom \
-    TelephonyProvider \
     TeleService \
     Traceur \
     UserDictionaryProvider \
     VpnDialogs \
     vr \
 
+# Choose the correct products based on HSUM status
+ifeq ($(PRODUCT_USE_HSUM),true)
+  PRODUCT_PACKAGES += TelephonyProviderHsum
+else
+  PRODUCT_PACKAGES += TelephonyProvider
+endif
+
 PRODUCT_PACKAGES += $(RELEASE_PACKAGE_VIRTUAL_CAMERA)
 # Set virtual_camera_service_enabled soong config variable based on the
 # RELEASE_PACKAGE_VIRTUAL_CAMERA build. virtual_camera_service_enabled soong config
diff --git a/target/product/hsum_common.mk b/target/product/hsum_common.mk
new file mode 100644
index 0000000..b19bc65
--- /dev/null
+++ b/target/product/hsum_common.mk
@@ -0,0 +1,29 @@
+#
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Contains common default elements for devices running in Headless System User Mode.
+
+# Should generally be inherited first as using an HSUM configuration can affect downstream choices
+# (such as ensuring that the HSUM-variants of packages are selected).
+
+PRODUCT_SYSTEM_DEFAULT_PROPERTIES += \
+    ro.fw.mu.headless_system_user=true
+
+# Variable for elsewhere choosing the appropriate products based on HSUM status.
+PRODUCT_USE_HSUM := true
+
+PRODUCT_PACKAGES += \
+    HsumDefaultConfigOverlay
diff --git a/target/product/media_system_ext.mk b/target/product/media_system_ext.mk
index e79a7eb..1179966 100644
--- a/target/product/media_system_ext.mk
+++ b/target/product/media_system_ext.mk
@@ -22,3 +22,8 @@
 
 # Window Extensions
 $(call inherit-product, $(SRC_TARGET_DIR)/product/window_extensions_base.mk)
+
+# AppFunction Extensions
+ifneq (,$(RELEASE_APPFUNCTION_SIDECAR))
+    $(call inherit-product, $(SRC_TARGET_DIR)/product/app_function_extensions.mk)
+endif
diff --git a/target/product/security/Android.bp b/target/product/security/Android.bp
index ffbec06..214c009 100644
--- a/target/product/security/Android.bp
+++ b/target/product/security/Android.bp
@@ -33,7 +33,13 @@
 // image
 otacerts_zip {
     name: "otacerts",
-    recovery_available: true,
+    relative_install_path: "security",
+    filename: "otacerts.zip",
+}
+
+otacerts_zip {
+    name: "otacerts.recovery",
+    recovery: true,
     relative_install_path: "security",
     filename: "otacerts.zip",
 }
diff --git a/target/product/security/BUILD.bazel b/target/product/security/BUILD.bazel
deleted file mode 100644
index c12be79..0000000
--- a/target/product/security/BUILD.bazel
+++ /dev/null
@@ -1,8 +0,0 @@
-filegroup(
-    name = "android_certificate_directory",
-    srcs = glob([
-        "*.pk8",
-        "*.pem",
-    ]),
-    visibility = ["//visibility:public"],
-)
diff --git a/teams/Android.bp b/teams/Android.bp
index 6307f5c..a2b0d14 100644
--- a/teams/Android.bp
+++ b/teams/Android.bp
@@ -3367,6 +3367,13 @@
 }
 
 team {
+    name: "trendy_team_aaos_display_safety_triage",
+
+    // go/trendy/manage/engineers/6522093663780864
+    trendy_team_id: "6522093663780864",
+}
+
+team {
     name: "trendy_team_camera_htc_lg_qualcomm",
 
     // go/trendy/manage/engineers/6332099480911872
diff --git a/tools/Android.bp b/tools/Android.bp
index 59831a6..243cb56 100644
--- a/tools/Android.bp
+++ b/tools/Android.bp
@@ -123,3 +123,11 @@
         "merge-event-log-tags.py",
     ],
 }
+
+python_binary_host {
+    name: "java-event-log-tags",
+    srcs: [
+        "event_log_tags.py",
+        "java-event-log-tags.py",
+    ],
+}
diff --git a/tools/BUILD.bazel b/tools/BUILD.bazel
deleted file mode 100644
index 9ec0dce..0000000
--- a/tools/BUILD.bazel
+++ /dev/null
@@ -1,35 +0,0 @@
-py_library(
-    name = "event_log_tags",
-    srcs = ["event_log_tags.py"],
-    imports = ["."],
-)
-
-py_binary(
-    name = "java-event-log-tags",
-    srcs = ["java-event-log-tags.py"],
-    python_version = "PY3",
-    visibility = ["//visibility:public"],
-    deps = [":event_log_tags"],
-)
-
-py_binary(
-    name = "merge-event-log-tags",
-    srcs = ["merge-event-log-tags.py"],
-    python_version = "PY3",
-    visibility = ["//visibility:public"],
-    deps = [":event_log_tags"],
-)
-
-py_binary(
-    name = "check_elf_file",
-    srcs = ["check_elf_file.py"],
-    python_version = "PY3",
-    visibility = ["//visibility:public"],
-)
-
-py_binary(
-    name = "auto_gen_test_config",
-    srcs = ["auto_gen_test_config.py"],
-    python_version = "PY3",
-    visibility = ["//visibility:public"],
-)
diff --git a/tools/aconfig/TEST_MAPPING b/tools/aconfig/TEST_MAPPING
index 043a956..a7f0a4f 100644
--- a/tools/aconfig/TEST_MAPPING
+++ b/tools/aconfig/TEST_MAPPING
@@ -102,9 +102,7 @@
     {
       // aconfig_storage file java integration tests
       "name": "aconfig_storage_file.test.java"
-    }
-  ],
-  "postsubmit": [
+    },
     {
       // aconfig_storage read functional test
       "name": "aconfig_storage_read_functional"
diff --git a/tools/aconfig/aconfig/Android.bp b/tools/aconfig/aconfig/Android.bp
index 5e3eb12..cce0ca9 100644
--- a/tools/aconfig/aconfig/Android.bp
+++ b/tools/aconfig/aconfig/Android.bp
@@ -243,6 +243,11 @@
     crate_name: "aconfig_test_rust_library",
     aconfig_declarations: "aconfig.test.flags",
     host_supported: true,
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.configinfrastructure",
+    ],
+    min_sdk_version: "34",
 }
 
 rust_test {
diff --git a/tools/aconfig/aconfig/src/codegen/cpp.rs b/tools/aconfig/aconfig/src/codegen/cpp.rs
index 7a9c382..d7d77c5 100644
--- a/tools/aconfig/aconfig/src/codegen/cpp.rs
+++ b/tools/aconfig/aconfig/src/codegen/cpp.rs
@@ -24,7 +24,7 @@
 
 use crate::codegen;
 use crate::codegen::CodegenMode;
-use crate::commands::OutputFile;
+use crate::commands::{should_include_flag, OutputFile};
 
 pub fn generate_cpp_code<I>(
     package: &str,
@@ -127,6 +127,23 @@
     flag_ids: HashMap<String, u16>,
     rw_count: &mut i32,
 ) -> ClassElement {
+    let no_assigned_offset = !should_include_flag(pf);
+
+    let flag_offset = match flag_ids.get(pf.name()) {
+        Some(offset) => offset,
+        None => {
+            // System/vendor/product RO+disabled flags have no offset in storage files.
+            // Assign placeholder value.
+            if no_assigned_offset {
+                &0
+            }
+            // All other flags _must_ have an offset.
+            else {
+                panic!("{}", format!("missing flag offset for {}", pf.name()));
+            }
+        }
+    };
+
     ClassElement {
         readwrite_idx: if pf.permission() == ProtoFlagPermission::READ_WRITE {
             let index = *rw_count;
@@ -144,7 +161,7 @@
         },
         flag_name: pf.name().to_string(),
         flag_macro: pf.name().to_uppercase(),
-        flag_offset: *flag_ids.get(pf.name()).expect("values checked at flag parse time"),
+        flag_offset: *flag_offset,
         device_config_namespace: pf.namespace().to_string(),
         device_config_flag: codegen::create_device_config_ident(package, pf.name())
             .expect("values checked at flag parse time"),
diff --git a/tools/aconfig/aconfig/src/codegen/java.rs b/tools/aconfig/aconfig/src/codegen/java.rs
index e2bb1ad..d4025e6 100644
--- a/tools/aconfig/aconfig/src/codegen/java.rs
+++ b/tools/aconfig/aconfig/src/codegen/java.rs
@@ -22,7 +22,7 @@
 
 use crate::codegen;
 use crate::codegen::CodegenMode;
-use crate::commands::OutputFile;
+use crate::commands::{should_include_flag, OutputFile};
 use aconfig_protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
 use std::collections::HashMap;
 
@@ -32,6 +32,8 @@
     codegen_mode: CodegenMode,
     flag_ids: HashMap<String, u16>,
     allow_instrumentation: bool,
+    package_fingerprint: u64,
+    new_exported: bool,
 ) -> Result<Vec<OutputFile>>
 where
     I: Iterator<Item = ProtoParsedFlag>,
@@ -46,6 +48,7 @@
     let runtime_lookup_required =
         flag_elements.iter().any(|elem| elem.is_read_write) || library_exported;
     let container = (flag_elements.first().expect("zero template flags").container).to_string();
+    let is_platform_container = matches!(container.as_str(), "system" | "product" | "vendor");
     let context = Context {
         flag_elements,
         namespace_flags,
@@ -56,6 +59,9 @@
         library_exported,
         allow_instrumentation,
         container,
+        is_platform_container,
+        package_fingerprint: format!("0x{:X}L", package_fingerprint),
+        new_exported,
     };
     let mut template = TinyTemplate::new();
     template.add_template("Flags.java", include_str!("../../templates/Flags.java.template"))?;
@@ -123,6 +129,9 @@
     pub library_exported: bool,
     pub allow_instrumentation: bool,
     pub container: String,
+    pub is_platform_container: bool,
+    pub package_fingerprint: String,
+    pub new_exported: bool,
 }
 
 #[derive(Serialize, Debug)]
@@ -152,6 +161,24 @@
 ) -> FlagElement {
     let device_config_flag = codegen::create_device_config_ident(package, pf.name())
         .expect("values checked at flag parse time");
+
+    let no_assigned_offset = !should_include_flag(pf);
+
+    let flag_offset = match flag_offsets.get(pf.name()) {
+        Some(offset) => offset,
+        None => {
+            // System/vendor/product RO+disabled flags have no offset in storage files.
+            // Assign placeholder value.
+            if no_assigned_offset {
+                &0
+            }
+            // All other flags _must_ have an offset.
+            else {
+                panic!("{}", format!("missing flag offset for {}", pf.name()));
+            }
+        }
+    };
+
     FlagElement {
         container: pf.container().to_string(),
         default_value: pf.state() == ProtoFlagState::ENABLED,
@@ -159,7 +186,7 @@
         device_config_flag,
         flag_name: pf.name().to_string(),
         flag_name_constant_suffix: pf.name().to_ascii_uppercase(),
-        flag_offset: *flag_offsets.get(pf.name()).expect("didnt find package offset :("),
+        flag_offset: *flag_offset,
         is_read_write: pf.permission() == ProtoFlagPermission::READ_WRITE,
         method_name: format_java_method_name(pf.name()),
         properties: format_property_name(pf.namespace()),
@@ -502,6 +529,8 @@
             mode,
             flag_ids,
             true,
+            5801144784618221668,
+            false,
         )
         .unwrap();
         let expect_flags_content = EXPECTED_FLAG_COMMON_CONTENT.to_string()
@@ -513,27 +542,31 @@
         package com.android.aconfig.test;
         // TODO(b/303773055): Remove the annotation after access issue is resolved.
         import android.compat.annotation.UnsupportedAppUsage;
-        import android.aconfig.storage.StorageInternalReader;
-
+        import android.os.Build;
+        import android.os.flagging.PlatformAconfigPackageInternal;
+        import android.util.Log;
         /** @hide */
         public final class FeatureFlagsImpl implements FeatureFlags {
+            private static final String TAG = "FeatureFlagsImpl";
             private static volatile boolean isCached = false;
             private static boolean disabledRw = false;
             private static boolean disabledRwExported = false;
             private static boolean disabledRwInOtherNamespace = false;
             private static boolean enabledRw = true;
             private void init() {
-                StorageInternalReader reader = null;
-                boolean foundPackage = true;
                 try {
-                    reader = new StorageInternalReader("system", "com.android.aconfig.test");
+                    PlatformAconfigPackageInternal reader = PlatformAconfigPackageInternal.load("system", "com.android.aconfig.test", 0x5081CE7221C77064L);
+                    disabledRw = reader.getBooleanFlagValue(0);
+                    disabledRwExported = reader.getBooleanFlagValue(1);
+                    enabledRw = reader.getBooleanFlagValue(7);
+                    disabledRwInOtherNamespace = reader.getBooleanFlagValue(2);
                 } catch (Exception e) {
-                    foundPackage = false;
+                    Log.e(TAG, e.toString());
+                } catch (LinkageError e) {
+                    // for mainline module running on older devices.
+                    // This should be replaces to version check, after the version bump.
+                    Log.e(TAG, e.toString());
                 }
-                disabledRw = foundPackage ? reader.getBooleanFlagValue(1) : false;
-                disabledRwExported = foundPackage ? reader.getBooleanFlagValue(2) : false;
-                enabledRw = foundPackage ? reader.getBooleanFlagValue(8) : true;
-                disabledRwInOtherNamespace = foundPackage ? reader.getBooleanFlagValue(3) : false;
                 isCached = true;
             }
 
@@ -652,6 +685,8 @@
             mode,
             flag_ids,
             true,
+            5801144784618221668,
+            false,
         )
         .unwrap();
 
@@ -837,6 +872,195 @@
     }
 
     #[test]
+    fn test_generate_java_code_new_exported() {
+        let parsed_flags = crate::test::parse_test_flags();
+        let mode = CodegenMode::Exported;
+        let modified_parsed_flags =
+            crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap();
+        let flag_ids =
+            assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap();
+        let generated_files = generate_java_code(
+            crate::test::TEST_PACKAGE,
+            modified_parsed_flags.into_iter(),
+            mode,
+            flag_ids,
+            true,
+            5801144784618221668,
+            true,
+        )
+        .unwrap();
+
+        let expect_flags_content = r#"
+        package com.android.aconfig.test;
+        /** @hide */
+        public final class Flags {
+            /** @hide */
+            public static final String FLAG_DISABLED_RW_EXPORTED = "com.android.aconfig.test.disabled_rw_exported";
+            /** @hide */
+            public static final String FLAG_ENABLED_FIXED_RO_EXPORTED = "com.android.aconfig.test.enabled_fixed_ro_exported";
+            /** @hide */
+            public static final String FLAG_ENABLED_RO_EXPORTED = "com.android.aconfig.test.enabled_ro_exported";
+            public static boolean disabledRwExported() {
+                return FEATURE_FLAGS.disabledRwExported();
+            }
+            public static boolean enabledFixedRoExported() {
+                return FEATURE_FLAGS.enabledFixedRoExported();
+            }
+            public static boolean enabledRoExported() {
+                return FEATURE_FLAGS.enabledRoExported();
+            }
+            private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl();
+        }
+        "#;
+
+        let expect_feature_flags_content = r#"
+        package com.android.aconfig.test;
+        /** @hide */
+        public interface FeatureFlags {
+            boolean disabledRwExported();
+            boolean enabledFixedRoExported();
+            boolean enabledRoExported();
+        }
+        "#;
+
+        let expect_feature_flags_impl_content = r#"
+        package com.android.aconfig.test;
+        import android.os.flagging.AconfigPackage;
+        import android.util.Log;
+        /** @hide */
+        public final class FeatureFlagsImpl implements FeatureFlags {
+            private static final String TAG = "FeatureFlagsImplExport";
+            private static volatile boolean isCached = false;
+            private static boolean disabledRwExported = false;
+            private static boolean enabledFixedRoExported = false;
+            private static boolean enabledRoExported = false;
+            private void init() {
+                try {
+                    AconfigPackage reader = AconfigPackage.load("com.android.aconfig.test");
+                    disabledRwExported = reader.getBooleanFlagValue("disabled_rw_exported", false);
+                    enabledFixedRoExported = reader.getBooleanFlagValue("enabled_fixed_ro_exported", false);
+                    enabledRoExported = reader.getBooleanFlagValue("enabled_ro_exported", false);
+                } catch (Exception e) {
+                    // pass
+                    Log.e(TAG, e.toString());
+                } catch (LinkageError e) {
+                    // for mainline module running on older devices.
+                    // This should be replaces to version check, after the version bump.
+                    Log.e(TAG, e.toString());
+                }
+                isCached = true;
+            }
+            @Override
+            public boolean disabledRwExported() {
+                if (!isCached) {
+                    init();
+                }
+                return disabledRwExported;
+            }
+            @Override
+            public boolean enabledFixedRoExported() {
+                if (!isCached) {
+                    init();
+                }
+                return enabledFixedRoExported;
+            }
+            @Override
+            public boolean enabledRoExported() {
+                if (!isCached) {
+                    init();
+                }
+                return enabledRoExported;
+            }
+        }"#;
+
+        let expect_custom_feature_flags_content = r#"
+        package com.android.aconfig.test;
+
+        import java.util.Arrays;
+        import java.util.HashSet;
+        import java.util.List;
+        import java.util.Set;
+        import java.util.function.BiPredicate;
+        import java.util.function.Predicate;
+
+        /** @hide */
+        public class CustomFeatureFlags implements FeatureFlags {
+
+            private BiPredicate<String, Predicate<FeatureFlags>> mGetValueImpl;
+
+            public CustomFeatureFlags(BiPredicate<String, Predicate<FeatureFlags>> getValueImpl) {
+                mGetValueImpl = getValueImpl;
+            }
+
+            @Override
+            public boolean disabledRwExported() {
+                return getValue(Flags.FLAG_DISABLED_RW_EXPORTED,
+                    FeatureFlags::disabledRwExported);
+            }
+            @Override
+            public boolean enabledFixedRoExported() {
+                return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED,
+                    FeatureFlags::enabledFixedRoExported);
+            }
+            @Override
+            public boolean enabledRoExported() {
+                return getValue(Flags.FLAG_ENABLED_RO_EXPORTED,
+                    FeatureFlags::enabledRoExported);
+            }
+
+            protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) {
+                return mGetValueImpl.test(flagName, getter);
+            }
+
+            public List<String> getFlagNames() {
+                return Arrays.asList(
+                    Flags.FLAG_DISABLED_RW_EXPORTED,
+                    Flags.FLAG_ENABLED_FIXED_RO_EXPORTED,
+                    Flags.FLAG_ENABLED_RO_EXPORTED
+                );
+            }
+
+            private Set<String> mReadOnlyFlagsSet = new HashSet<>(
+                Arrays.asList(
+                    ""
+                )
+            );
+        }
+    "#;
+
+        let mut file_set = HashMap::from([
+            ("com/android/aconfig/test/Flags.java", expect_flags_content),
+            ("com/android/aconfig/test/FeatureFlags.java", expect_feature_flags_content),
+            ("com/android/aconfig/test/FeatureFlagsImpl.java", expect_feature_flags_impl_content),
+            (
+                "com/android/aconfig/test/CustomFeatureFlags.java",
+                expect_custom_feature_flags_content,
+            ),
+            (
+                "com/android/aconfig/test/FakeFeatureFlagsImpl.java",
+                EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT,
+            ),
+        ]);
+
+        for file in generated_files {
+            let file_path = file.path.to_str().unwrap();
+            assert!(file_set.contains_key(file_path), "Cannot find {}", file_path);
+            assert_eq!(
+                None,
+                crate::test::first_significant_code_diff(
+                    file_set.get(file_path).unwrap(),
+                    &String::from_utf8(file.contents).unwrap()
+                ),
+                "File {} content is not correct",
+                file_path
+            );
+            file_set.remove(file_path);
+        }
+
+        assert!(file_set.is_empty());
+    }
+
+    #[test]
     fn test_generate_java_code_test() {
         let parsed_flags = crate::test::parse_test_flags();
         let mode = CodegenMode::Test;
@@ -850,6 +1074,8 @@
             mode,
             flag_ids,
             true,
+            5801144784618221668,
+            false,
         )
         .unwrap();
 
@@ -971,6 +1197,8 @@
             mode,
             flag_ids,
             true,
+            5801144784618221668,
+            false,
         )
         .unwrap();
         let expect_featureflags_content = r#"
diff --git a/tools/aconfig/aconfig/src/codegen/rust.rs b/tools/aconfig/aconfig/src/codegen/rust.rs
index 82a6ebc..74da1bc 100644
--- a/tools/aconfig/aconfig/src/codegen/rust.rs
+++ b/tools/aconfig/aconfig/src/codegen/rust.rs
@@ -24,7 +24,7 @@
 
 use crate::codegen;
 use crate::codegen::CodegenMode;
-use crate::commands::OutputFile;
+use crate::commands::{should_include_flag, OutputFile};
 
 pub fn generate_rust_code<I>(
     package: &str,
@@ -88,6 +88,21 @@
 impl TemplateParsedFlag {
     #[allow(clippy::nonminimal_bool)]
     fn new(package: &str, flag_offsets: HashMap<String, u16>, pf: &ProtoParsedFlag) -> Self {
+        let flag_offset = match flag_offsets.get(pf.name()) {
+            Some(offset) => offset,
+            None => {
+                // System/vendor/product RO+disabled flags have no offset in storage files.
+                // Assign placeholder value.
+                if !should_include_flag(pf) {
+                    &0
+                }
+                // All other flags _must_ have an offset.
+                else {
+                    panic!("{}", format!("missing flag offset for {}", pf.name()));
+                }
+            }
+        };
+
         Self {
             readwrite: pf.permission() == ProtoFlagPermission::READ_WRITE,
             default_value: match pf.state() {
@@ -96,7 +111,7 @@
             },
             name: pf.name().to_string(),
             container: pf.container().to_string(),
-            flag_offset: *flag_offsets.get(pf.name()).expect("didnt find package offset :("),
+            flag_offset: *flag_offset,
             device_config_namespace: pf.namespace().to_string(),
             device_config_flag: codegen::create_device_config_ident(package, pf.name())
                 .expect("values checked at flag parse time"),
@@ -287,7 +302,7 @@
                .and_then(|package_offset| {
                    match package_offset {
                        Some(offset) => {
-                           get_boolean_flag_value(&flag_val_map, offset + 1)
+                           get_boolean_flag_value(&flag_val_map, offset + 0)
                                .map_err(|err| format!("failed to get flag: {err}"))
                        },
                        None => {
@@ -327,7 +342,7 @@
                     .and_then(|package_offset| {
                         match package_offset {
                             Some(offset) => {
-                                get_boolean_flag_value(&flag_val_map, offset + 2)
+                                get_boolean_flag_value(&flag_val_map, offset + 1)
                                     .map_err(|err| format!("failed to get flag: {err}"))
                             },
                             None => {
@@ -367,7 +382,7 @@
                     .and_then(|package_offset| {
                         match package_offset {
                             Some(offset) => {
-                                get_boolean_flag_value(&flag_val_map, offset + 3)
+                                get_boolean_flag_value(&flag_val_map, offset + 2)
                                     .map_err(|err| format!("failed to get flag: {err}"))
                             },
                             None => {
@@ -408,7 +423,7 @@
                     .and_then(|package_offset| {
                         match package_offset {
                             Some(offset) => {
-                                get_boolean_flag_value(&flag_val_map, offset + 8)
+                                get_boolean_flag_value(&flag_val_map, offset + 7)
                                     .map_err(|err| format!("failed to get flag: {err}"))
                             },
                             None => {
diff --git a/tools/aconfig/aconfig/src/commands.rs b/tools/aconfig/aconfig/src/commands.rs
index 81119f3..2f96015 100644
--- a/tools/aconfig/aconfig/src/commands.rs
+++ b/tools/aconfig/aconfig/src/commands.rs
@@ -218,13 +218,17 @@
     mut input: Input,
     codegen_mode: CodegenMode,
     allow_instrumentation: bool,
+    new_exported: bool,
 ) -> Result<Vec<OutputFile>> {
     let parsed_flags = input.try_parse_flags()?;
-    let modified_parsed_flags = modify_parsed_flags_based_on_mode(parsed_flags, codegen_mode)?;
+    let modified_parsed_flags =
+        modify_parsed_flags_based_on_mode(parsed_flags.clone(), codegen_mode)?;
     let Some(package) = find_unique_package(&modified_parsed_flags) else {
         bail!("no parsed flags, or the parsed flags use different packages");
     };
     let package = package.to_string();
+    let mut flag_names = extract_flag_names(parsed_flags)?;
+    let package_fingerprint = compute_flags_fingerprint(&mut flag_names);
     let flag_ids = assign_flag_ids(&package, modified_parsed_flags.iter())?;
     generate_java_code(
         &package,
@@ -232,6 +236,8 @@
         codegen_mode,
         flag_ids,
         allow_instrumentation,
+        package_fingerprint,
+        new_exported,
     )
 }
 
@@ -419,25 +425,27 @@
 {
     assert!(parsed_flags_iter.clone().tuple_windows().all(|(a, b)| a.name() <= b.name()));
     let mut flag_ids = HashMap::new();
-    for (id_to_assign, pf) in (0_u32..).zip(parsed_flags_iter) {
+    let mut flag_idx = 0;
+    for pf in parsed_flags_iter {
         if package != pf.package() {
             return Err(anyhow::anyhow!("encountered a flag not in current package"));
         }
 
         // put a cap on how many flags a package can contain to 65535
-        if id_to_assign > u16::MAX as u32 {
+        if flag_idx > u16::MAX as u32 {
             return Err(anyhow::anyhow!("the number of flags in a package cannot exceed 65535"));
         }
 
-        flag_ids.insert(pf.name().to_string(), id_to_assign as u16);
+        if should_include_flag(pf) {
+            flag_ids.insert(pf.name().to_string(), flag_idx as u16);
+            flag_idx += 1;
+        }
     }
     Ok(flag_ids)
 }
 
-#[allow(dead_code)] // TODO: b/316357686 - Use fingerprint in codegen to
-                    // protect hardcoded offset reads.
-                    // Creates a fingerprint of the flag names (which requires sorting the vector).
-                    // Fingerprint is used by both codegen and storage files.
+// Creates a fingerprint of the flag names (which requires sorting the vector).
+// Fingerprint is used by both codegen and storage files.
 pub fn compute_flags_fingerprint(flag_names: &mut Vec<String>) -> u64 {
     flag_names.sort();
 
@@ -448,11 +456,9 @@
     hasher.finish()
 }
 
-#[allow(dead_code)] // TODO: b/316357686 - Use fingerprint in codegen to
-                    // protect hardcoded offset reads.
-                    // Converts ProtoParsedFlags into a vector of strings containing all of the flag
-                    // names. Helper fn for creating fingerprint for codegen files. Flags must all
-                    // belong to the same package.
+// Converts ProtoParsedFlags into a vector of strings containing all of the flag
+// names. Helper fn for creating fingerprint for codegen files. Flags must all
+// belong to the same package.
 fn extract_flag_names(flags: ProtoParsedFlags) -> Result<Vec<String>> {
     let separated_flags: Vec<ProtoParsedFlag> = flags.parsed_flag.into_iter().collect::<Vec<_>>();
 
@@ -461,7 +467,23 @@
         bail!("No parsed flags, or the parsed flags use different packages.");
     };
 
-    Ok(separated_flags.into_iter().map(|flag| flag.name.unwrap()).collect::<Vec<_>>())
+    Ok(separated_flags
+        .into_iter()
+        .filter(should_include_flag)
+        .map(|flag| flag.name.unwrap())
+        .collect::<Vec<_>>())
+}
+
+// Exclude system/vendor/product flags that are RO+disabled.
+pub fn should_include_flag(pf: &ProtoParsedFlag) -> bool {
+    let should_filter_container = pf.container == Some("vendor".to_string())
+        || pf.container == Some("system".to_string())
+        || pf.container == Some("product".to_string());
+
+    let disabled_ro = pf.state == Some(ProtoFlagState::DISABLED.into())
+        && pf.permission == Some(ProtoFlagPermission::READ_ONLY.into());
+
+    !should_filter_container || !disabled_ro
 }
 
 #[cfg(test)]
@@ -472,7 +494,7 @@
     #[test]
     fn test_offset_fingerprint() {
         let parsed_flags = crate::test::parse_test_flags();
-        let expected_fingerprint: u64 = 5801144784618221668;
+        let expected_fingerprint: u64 = 11551379960324242360;
 
         let mut extracted_flags = extract_flag_names(parsed_flags).unwrap();
         let hash_result = compute_flags_fingerprint(&mut extracted_flags);
@@ -492,6 +514,7 @@
             .parsed_flag
             .clone()
             .into_iter()
+            .filter(should_include_flag)
             .map(|flag| flag.name.unwrap())
             .map(String::from)
             .collect::<Vec<_>>();
@@ -887,6 +910,30 @@
     }
 
     #[test]
+    fn test_dump_multiple_filters() {
+        let input = parse_test_flags_as_input();
+        let bytes = dump_parsed_flags(
+            vec![input],
+            DumpFormat::Custom("{fully_qualified_name}".to_string()),
+            &["container:system+state:ENABLED", "container:system+permission:READ_WRITE"],
+            false,
+        )
+        .unwrap();
+        let text = std::str::from_utf8(&bytes).unwrap();
+        let expected_flag_list = &[
+            "com.android.aconfig.test.disabled_rw",
+            "com.android.aconfig.test.disabled_rw_exported",
+            "com.android.aconfig.test.disabled_rw_in_other_namespace",
+            "com.android.aconfig.test.enabled_fixed_ro",
+            "com.android.aconfig.test.enabled_fixed_ro_exported",
+            "com.android.aconfig.test.enabled_ro",
+            "com.android.aconfig.test.enabled_ro_exported",
+            "com.android.aconfig.test.enabled_rw",
+        ];
+        assert_eq!(expected_flag_list.map(|s| format!("{}\n", s)).join(""), text);
+    }
+
+    #[test]
     fn test_dump_textproto_format_dedup() {
         let input = parse_test_flags_as_input();
         let input2 = parse_test_flags_as_input();
@@ -948,15 +995,14 @@
         let package = find_unique_package(&parsed_flags.parsed_flag).unwrap().to_string();
         let flag_ids = assign_flag_ids(&package, parsed_flags.parsed_flag.iter()).unwrap();
         let expected_flag_ids = HashMap::from([
-            (String::from("disabled_ro"), 0_u16),
-            (String::from("disabled_rw"), 1_u16),
-            (String::from("disabled_rw_exported"), 2_u16),
-            (String::from("disabled_rw_in_other_namespace"), 3_u16),
-            (String::from("enabled_fixed_ro"), 4_u16),
-            (String::from("enabled_fixed_ro_exported"), 5_u16),
-            (String::from("enabled_ro"), 6_u16),
-            (String::from("enabled_ro_exported"), 7_u16),
-            (String::from("enabled_rw"), 8_u16),
+            (String::from("disabled_rw"), 0_u16),
+            (String::from("disabled_rw_exported"), 1_u16),
+            (String::from("disabled_rw_in_other_namespace"), 2_u16),
+            (String::from("enabled_fixed_ro"), 3_u16),
+            (String::from("enabled_fixed_ro_exported"), 4_u16),
+            (String::from("enabled_ro"), 5_u16),
+            (String::from("enabled_ro_exported"), 6_u16),
+            (String::from("enabled_rw"), 7_u16),
         ]);
         assert_eq!(flag_ids, expected_flag_ids);
     }
diff --git a/tools/aconfig/aconfig/src/main.rs b/tools/aconfig/aconfig/src/main.rs
index c390288..288786b 100644
--- a/tools/aconfig/aconfig/src/main.rs
+++ b/tools/aconfig/aconfig/src/main.rs
@@ -85,6 +85,12 @@
                         .long("allow-instrumentation")
                         .value_parser(clap::value_parser!(bool))
                         .default_value("false"),
+                )
+                .arg(
+                    Arg::new("new-exported")
+                        .long("new-exported")
+                        .value_parser(clap::value_parser!(bool))
+                        .default_value("false"),
                 ),
         )
         .subcommand(
@@ -267,8 +273,10 @@
             let mode = get_required_arg::<CodegenMode>(sub_matches, "mode")?;
             let allow_instrumentation =
                 get_required_arg::<bool>(sub_matches, "allow-instrumentation")?;
-            let generated_files = commands::create_java_lib(cache, *mode, *allow_instrumentation)
-                .context("failed to create java lib")?;
+            let new_exported = get_required_arg::<bool>(sub_matches, "new-exported")?;
+            let generated_files =
+                commands::create_java_lib(cache, *mode, *allow_instrumentation, *new_exported)
+                    .context("failed to create java lib")?;
             let dir = PathBuf::from(get_required_arg::<String>(sub_matches, "out")?);
             generated_files
                 .iter()
diff --git a/tools/aconfig/aconfig/src/storage/flag_info.rs b/tools/aconfig/aconfig/src/storage/flag_info.rs
index 0b5a67b..0943daa 100644
--- a/tools/aconfig/aconfig/src/storage/flag_info.rs
+++ b/tools/aconfig/aconfig/src/storage/flag_info.rs
@@ -16,7 +16,7 @@
 
 use crate::commands::assign_flag_ids;
 use crate::storage::FlagPackage;
-use aconfig_protos::ProtoFlagPermission;
+use aconfig_protos::{ProtoFlagPermission, ProtoFlagState};
 use aconfig_storage_file::{FlagInfoHeader, FlagInfoList, FlagInfoNode, StorageFileType};
 use anyhow::{anyhow, Result};
 
@@ -36,14 +36,24 @@
     packages: &[FlagPackage],
     version: u32,
 ) -> Result<FlagInfoList> {
-    // create list
-    let num_flags = packages.iter().map(|pkg| pkg.boolean_flags.len() as u32).sum();
+    // Exclude system/vendor/product flags that are RO+disabled.
+    let mut filtered_packages = packages.to_vec();
+    if container == "system" || container == "vendor" || container == "product" {
+        for package in filtered_packages.iter_mut() {
+            package.boolean_flags.retain(|b| {
+                !(b.state == Some(ProtoFlagState::DISABLED.into())
+                    && b.permission == Some(ProtoFlagPermission::READ_ONLY.into()))
+            });
+        }
+    }
+
+    let num_flags = filtered_packages.iter().map(|pkg| pkg.boolean_flags.len() as u32).sum();
 
     let mut is_flag_rw = vec![false; num_flags as usize];
-    for pkg in packages.iter() {
+    for pkg in filtered_packages {
         let start_index = pkg.boolean_start_index as usize;
         let flag_ids = assign_flag_ids(pkg.package_name, pkg.boolean_flags.iter().copied())?;
-        for pf in pkg.boolean_flags.iter() {
+        for pf in pkg.boolean_flags {
             let fid = flag_ids
                 .get(pf.name())
                 .ok_or(anyhow!(format!("missing flag id for {}", pf.name())))?;
diff --git a/tools/aconfig/aconfig/src/storage/flag_table.rs b/tools/aconfig/aconfig/src/storage/flag_table.rs
index ae5a16c..a3b4e8f 100644
--- a/tools/aconfig/aconfig/src/storage/flag_table.rs
+++ b/tools/aconfig/aconfig/src/storage/flag_table.rs
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-use crate::commands::assign_flag_ids;
+use crate::commands::{assign_flag_ids, should_include_flag};
 use crate::storage::FlagPackage;
 use aconfig_protos::ProtoFlagPermission;
 use aconfig_storage_file::{
@@ -62,9 +62,13 @@
     }
 
     fn create_nodes(package: &FlagPackage, num_buckets: u32) -> Result<Vec<Self>> {
+        // Exclude system/vendor/product flags that are RO+disabled.
+        let mut filtered_package = package.clone();
+        filtered_package.boolean_flags.retain(|pf| should_include_flag(pf));
+
         let flag_ids =
-            assign_flag_ids(package.package_name, package.boolean_flags.iter().copied())?;
-        package
+            assign_flag_ids(package.package_name, filtered_package.boolean_flags.iter().copied())?;
+        filtered_package
             .boolean_flags
             .iter()
             .map(|&pf| {
diff --git a/tools/aconfig/aconfig/src/storage/flag_value.rs b/tools/aconfig/aconfig/src/storage/flag_value.rs
index 065b7e3..3cfa447 100644
--- a/tools/aconfig/aconfig/src/storage/flag_value.rs
+++ b/tools/aconfig/aconfig/src/storage/flag_value.rs
@@ -16,7 +16,7 @@
 
 use crate::commands::assign_flag_ids;
 use crate::storage::FlagPackage;
-use aconfig_protos::ProtoFlagState;
+use aconfig_protos::{ProtoFlagPermission, ProtoFlagState};
 use aconfig_storage_file::{FlagValueHeader, FlagValueList, StorageFileType};
 use anyhow::{anyhow, Result};
 
@@ -36,15 +36,22 @@
     packages: &[FlagPackage],
     version: u32,
 ) -> Result<FlagValueList> {
-    // create list
-    let num_flags = packages.iter().map(|pkg| pkg.boolean_flags.len() as u32).sum();
-
+    // Exclude system/vendor/product flags that are RO+disabled.
+    let mut filtered_packages = packages.to_vec();
+    if container == "system" || container == "vendor" || container == "product" {
+        for package in filtered_packages.iter_mut() {
+            package.boolean_flags.retain(|b| {
+                !(b.state == Some(ProtoFlagState::DISABLED.into())
+                    && b.permission == Some(ProtoFlagPermission::READ_ONLY.into()))
+            });
+        }
+    }
+    let num_flags = filtered_packages.iter().map(|pkg| pkg.boolean_flags.len() as u32).sum();
     let mut list = FlagValueList {
         header: new_header(container, num_flags, version),
         booleans: vec![false; num_flags as usize],
     };
-
-    for pkg in packages.iter() {
+    for pkg in filtered_packages {
         let start_index = pkg.boolean_start_index as usize;
         let flag_ids = assign_flag_ids(pkg.package_name, pkg.boolean_flags.iter().copied())?;
         for pf in pkg.boolean_flags.iter() {
diff --git a/tools/aconfig/aconfig/src/storage/mod.rs b/tools/aconfig/aconfig/src/storage/mod.rs
index ef1b4f6..61e65d1 100644
--- a/tools/aconfig/aconfig/src/storage/mod.rs
+++ b/tools/aconfig/aconfig/src/storage/mod.rs
@@ -27,9 +27,10 @@
     flag_info::create_flag_info, flag_table::create_flag_table, flag_value::create_flag_value,
     package_table::create_package_table,
 };
-use aconfig_protos::{ProtoParsedFlag, ProtoParsedFlags};
+use aconfig_protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag, ProtoParsedFlags};
 use aconfig_storage_file::StorageFileType;
 
+#[derive(Clone)]
 pub struct FlagPackage<'a> {
     pub package_name: &'a str,
     pub package_id: u32,
@@ -73,6 +74,17 @@
             if index == packages.len() {
                 packages.push(FlagPackage::new(parsed_flag.package(), index as u32));
             }
+
+            // Exclude system/vendor/product flags that are RO+disabled.
+            if (parsed_flag.container == Some("system".to_string())
+                || parsed_flag.container == Some("vendor".to_string())
+                || parsed_flag.container == Some("product".to_string()))
+                && parsed_flag.permission == Some(ProtoFlagPermission::READ_ONLY.into())
+                && parsed_flag.state == Some(ProtoFlagState::DISABLED.into())
+            {
+                continue;
+            }
+
             packages[index].insert(parsed_flag);
         }
     }
@@ -83,7 +95,7 @@
         p.boolean_start_index = boolean_start_index;
         boolean_start_index += p.boolean_flags.len() as u32;
 
-        if version > 2 {
+        if version >= 2 {
             let mut flag_names_vec =
                 p.flag_names.clone().into_iter().map(String::from).collect::<Vec<_>>();
             let fingerprint = compute_flags_fingerprint(&mut flag_names_vec);
@@ -202,6 +214,7 @@
         assert!(packages[0].flag_names.contains("disabled_rw"));
         assert!(packages[0].flag_names.contains("enabled_ro"));
         assert_eq!(packages[0].boolean_start_index, 0);
+        assert_eq!(packages[0].fingerprint, 0);
 
         assert_eq!(packages[1].package_name, "com.android.aconfig.storage.test_2");
         assert_eq!(packages[1].package_id, 1);
@@ -210,6 +223,7 @@
         assert!(packages[1].flag_names.contains("disabled_rw"));
         assert!(packages[1].flag_names.contains("enabled_fixed_ro"));
         assert_eq!(packages[1].boolean_start_index, 3);
+        assert_eq!(packages[0].fingerprint, 0);
 
         assert_eq!(packages[2].package_name, "com.android.aconfig.storage.test_4");
         assert_eq!(packages[2].package_id, 2);
@@ -217,5 +231,49 @@
         assert!(packages[2].flag_names.contains("enabled_rw"));
         assert!(packages[2].flag_names.contains("enabled_fixed_ro"));
         assert_eq!(packages[2].boolean_start_index, 6);
+        assert_eq!(packages[2].fingerprint, 0);
+    }
+
+    #[test]
+    fn test_flag_package_with_fingerprint() {
+        let caches = parse_all_test_flags();
+        let packages = group_flags_by_package(caches.iter(), 2);
+
+        for pkg in packages.iter() {
+            let pkg_name = pkg.package_name;
+            assert_eq!(pkg.flag_names.len(), pkg.boolean_flags.len());
+            for pf in pkg.boolean_flags.iter() {
+                assert!(pkg.flag_names.contains(pf.name()));
+                assert_eq!(pf.package(), pkg_name);
+            }
+        }
+
+        assert_eq!(packages.len(), 3);
+
+        assert_eq!(packages[0].package_name, "com.android.aconfig.storage.test_1");
+        assert_eq!(packages[0].package_id, 0);
+        assert_eq!(packages[0].flag_names.len(), 3);
+        assert!(packages[0].flag_names.contains("enabled_rw"));
+        assert!(packages[0].flag_names.contains("disabled_rw"));
+        assert!(packages[0].flag_names.contains("enabled_ro"));
+        assert_eq!(packages[0].boolean_start_index, 0);
+        assert_eq!(packages[0].fingerprint, 15248948510590158086u64);
+
+        assert_eq!(packages[1].package_name, "com.android.aconfig.storage.test_2");
+        assert_eq!(packages[1].package_id, 1);
+        assert_eq!(packages[1].flag_names.len(), 3);
+        assert!(packages[1].flag_names.contains("enabled_ro"));
+        assert!(packages[1].flag_names.contains("disabled_rw"));
+        assert!(packages[1].flag_names.contains("enabled_fixed_ro"));
+        assert_eq!(packages[1].boolean_start_index, 3);
+        assert_eq!(packages[1].fingerprint, 4431940502274857964u64);
+
+        assert_eq!(packages[2].package_name, "com.android.aconfig.storage.test_4");
+        assert_eq!(packages[2].package_id, 2);
+        assert_eq!(packages[2].flag_names.len(), 2);
+        assert!(packages[2].flag_names.contains("enabled_rw"));
+        assert!(packages[2].flag_names.contains("enabled_fixed_ro"));
+        assert_eq!(packages[2].boolean_start_index, 6);
+        assert_eq!(packages[2].fingerprint, 16233229917711622375u64);
     }
 }
diff --git a/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template b/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template
index d782f81..ac5270a 100644
--- a/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template
+++ b/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template
@@ -5,11 +5,18 @@
 // TODO(b/303773055): Remove the annotation after access issue is resolved.
 import android.compat.annotation.UnsupportedAppUsage;
 {{ -if runtime_lookup_required }}
-import android.aconfig.storage.StorageInternalReader;
+import android.os.Build;
+{{ if is_platform_container }}
+import android.os.flagging.PlatformAconfigPackageInternal;
+{{ -else }}
+import android.os.flagging.AconfigPackageInternal;
+{{ -endif }}
+import android.util.Log;
 {{ -endif }}
 /** @hide */
 public final class FeatureFlagsImpl implements FeatureFlags \{
 {{ -if runtime_lookup_required }}
+    private static final String TAG = "FeatureFlagsImpl";
     private static volatile boolean isCached = false;
 {{ for flag in flag_elements }}
 {{ -if flag.is_read_write }}
@@ -18,20 +25,26 @@
 {{ -endfor }}
 
     private void init() \{
-        StorageInternalReader reader = null;
-        boolean foundPackage = true;
         try \{
-            reader = new StorageInternalReader("{container}", "{package_name}");
-        } catch (Exception e) \{
-            foundPackage = false;
-        }
-        {{ for namespace_with_flags in namespace_flags }}
+{{ if is_platform_container }}
+            PlatformAconfigPackageInternal reader = PlatformAconfigPackageInternal.load("{container}", "{package_name}", {package_fingerprint});
+{{ -else }}
+            AconfigPackageInternal reader = AconfigPackageInternal.load("{container}", "{package_name}", {package_fingerprint});
+{{ -endif }}
+        {{ -for namespace_with_flags in namespace_flags }}
         {{ -for flag in namespace_with_flags.flags }}
-        {{ if flag.is_read_write }}
-            {flag.method_name} = foundPackage ? reader.getBooleanFlagValue({flag.flag_offset}) : {flag.default_value};
-        {{ endif }}
+        {{ -if flag.is_read_write }}
+            {flag.method_name} = reader.getBooleanFlagValue({flag.flag_offset});
+        {{ -endif }}
         {{ -endfor }}
         {{ -endfor }}
+        } catch (Exception e) \{
+            Log.e(TAG, e.toString());
+        } catch (LinkageError e) \{
+            // for mainline module running on older devices.
+            // This should be replaces to version check, after the version bump.
+            Log.e(TAG, e.toString());
+        }
         isCached = true;
     }
 {{ -endif }}{#- end of runtime_lookup_required #}
@@ -52,6 +65,45 @@
 {{ endfor }}
 }
 {{ -else- }}{#- device config for exproted mode #}
+{{ -if new_exported }}
+import android.os.flagging.AconfigPackage;
+import android.util.Log;
+/** @hide */
+public final class FeatureFlagsImpl implements FeatureFlags \{
+    private static final String TAG = "FeatureFlagsImplExport";
+    private static volatile boolean isCached = false;
+{{ for flag in flag_elements }}
+    private static boolean {flag.method_name} = false;
+{{ -endfor }}
+    private void init() \{
+        try \{
+            AconfigPackage reader = AconfigPackage.load("{package_name}");
+            {{ -for namespace_with_flags in namespace_flags }}
+            {{ -for flag in namespace_with_flags.flags }}
+            {flag.method_name} = reader.getBooleanFlagValue("{flag.flag_name}", {flag.default_value});
+            {{ -endfor }}
+            {{ -endfor }}
+        } catch (Exception e) \{
+            // pass
+            Log.e(TAG, e.toString());
+        } catch (LinkageError e) \{
+            // for mainline module running on older devices.
+            // This should be replaces to version check, after the version bump.
+            Log.e(TAG, e.toString());
+        }
+        isCached = true;
+    }
+{{ -for flag in flag_elements }}
+    @Override
+    public boolean {flag.method_name}() \{
+        if (!isCached) \{
+            init();
+        }
+        return {flag.method_name};
+    }
+{{ endfor }}
+}
+{{ else }}
 import android.os.Binder;
 import android.provider.DeviceConfig;
 import android.provider.DeviceConfig.Properties;
@@ -103,6 +155,7 @@
     }
 {{ endfor }}
 }
+{{ -endif- }} {#- end new_exported mode #}
 {{ -endif- }} {#- end exported mode #}
 {{ else }} {#- else for allow_instrumentation is not enabled #}
 {{ if not library_exported- }}
diff --git a/tools/aconfig/aconfig/templates/cpp_source_file.template b/tools/aconfig/aconfig/templates/cpp_source_file.template
index 9be59e0..325dbdc 100644
--- a/tools/aconfig/aconfig/templates/cpp_source_file.template
+++ b/tools/aconfig/aconfig/templates/cpp_source_file.template
@@ -29,25 +29,103 @@
     private:
         std::unordered_map<std::string, bool> overrides_;
 
+    {{ if allow_instrumentation- }}
+        uint32_t boolean_start_index_;
+
+        std::unique_ptr<aconfig_storage::MappedStorageFile> flag_value_file_;
+
+        bool package_exists_in_storage_;
+    {{ -endif }}
+
     public:
+    {{ if allow_instrumentation- }}
+        flag_provider()
+            : overrides_()
+            , boolean_start_index_()
+            , flag_value_file_(nullptr)
+            , package_exists_in_storage_(true) \{
+
+            auto package_map_file = aconfig_storage::get_mapped_file(
+                 "{container}",
+                 aconfig_storage::StorageFileType::package_map);
+
+            if (!package_map_file.ok()) \{
+                ALOGE("error: failed to get package map file: %s", package_map_file.error().c_str());
+                package_exists_in_storage_ = false;
+                return;
+            }
+
+            auto context = aconfig_storage::get_package_read_context(
+                **package_map_file, "{package}");
+
+            if (!context.ok()) \{
+                ALOGE("error: failed to get package read context: %s", context.error().c_str());
+                package_exists_in_storage_ = false;
+                return;
+            }
+
+            if (!(context->package_exists)) \{
+                package_exists_in_storage_ = false;
+                return;
+            }
+
+            // cache package boolean flag start index
+            boolean_start_index_ = context->boolean_start_index;
+
+            // unmap package map file and free memory
+            delete *package_map_file;
+
+            auto flag_value_file = aconfig_storage::get_mapped_file(
+                "{container}",
+                aconfig_storage::StorageFileType::flag_val);
+            if (!flag_value_file.ok()) \{
+                ALOGE("error: failed to get flag value file: %s", flag_value_file.error().c_str());
+                package_exists_in_storage_ = false;
+                return;
+            }
+
+            // cache flag value file
+            flag_value_file_ = std::unique_ptr<aconfig_storage::MappedStorageFile>(
+                *flag_value_file);
+
+        }
+    {{ -else }}
         flag_provider()
             : overrides_()
         \{}
+    {{ -endif }}
 
 {{ for item in class_elements }}
         virtual bool {item.flag_name}() override \{
             auto it = overrides_.find("{item.flag_name}");
-              if (it != overrides_.end()) \{
-                  return it->second;
+            if (it != overrides_.end()) \{
+                return it->second;
             } else \{
-              {{ if item.readwrite- }}
-              return server_configurable_flags::GetServerConfigurableFlag(
+                {{ if item.readwrite- }}
+                {{ if allow_instrumentation- }}
+                if (!package_exists_in_storage_) \{
+                    return {item.default_value};
+                }
+
+                auto value = aconfig_storage::get_boolean_flag_value(
+                    *flag_value_file_,
+                    boolean_start_index_ + {item.flag_offset});
+
+                if (!value.ok()) \{
+                    ALOGE("error: failed to read flag value: %s", value.error().c_str());
+                    return {item.default_value};
+                } else \{
+                    return *value;
+                }
+                {{ -else }}
+                return server_configurable_flags::GetServerConfigurableFlag(
                   "aconfig_flags.{item.device_config_namespace}",
                   "{item.device_config_flag}",
                   "{item.default_value}") == "true";
-              {{ -else }}
-                  return {item.default_value};
-              {{ -endif }}
+                {{ -endif }}
+                {{ -else }}
+                return {item.default_value};
+                {{ -endif }}
             }
         }
 
@@ -66,15 +144,11 @@
     class flag_provider : public flag_provider_interface \{
     public:
 
-        {{ if allow_instrumentation- }}
         {{ if readwrite- }}
+        {{ if allow_instrumentation- }}
         flag_provider()
-            {{ if readwrite- }}
             : cache_({readwrite_count}, -1)
             , boolean_start_index_()
-            {{ -else- }}
-            : boolean_start_index_()
-            {{ -endif }}
             , flag_value_file_(nullptr)
             , package_exists_in_storage_(true) \{
 
@@ -138,6 +212,7 @@
 
                 if (!value.ok()) \{
                     ALOGE("error: failed to read flag value: %s", value.error().c_str());
+                    return {item.default_value};
                 }
 
                 cache_[{item.readwrite_idx}] = *value;
diff --git a/tools/aconfig/aconfig_device_paths/Android.bp b/tools/aconfig/aconfig_device_paths/Android.bp
index bdf96ed..3531450 100644
--- a/tools/aconfig/aconfig_device_paths/Android.bp
+++ b/tools/aconfig/aconfig_device_paths/Android.bp
@@ -26,7 +26,6 @@
         "libaconfig_protos",
         "libanyhow",
         "libprotobuf",
-        "libregex",
     ],
 }
 
@@ -35,6 +34,11 @@
     crate_name: "aconfig_device_paths",
     host_supported: true,
     defaults: ["libaconfig_device_paths.defaults"],
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.configinfrastructure",
+    ],
+    min_sdk_version: "34",
 }
 
 genrule {
@@ -54,7 +58,9 @@
     sdk_version: "core_platform",
     apex_available: [
         "//apex_available:platform",
+        "com.android.configinfrastructure",
     ],
+    min_sdk_version: "34",
 }
 
 genrule {
@@ -73,3 +79,23 @@
     name: "aconfig_host_device_paths_java",
     srcs: [":libaconfig_java_host_device_paths_src"],
 }
+
+genrule {
+    name: "java_device_paths_test_util_src",
+    srcs: ["src/DeviceProtosTestUtilTemplate.java"],
+    out: ["DeviceProtosTestUtil.java"],
+    tool_files: ["partition_aconfig_flags_paths.txt"],
+    cmd: "sed -e '/TEMPLATE/{r$(location partition_aconfig_flags_paths.txt)' -e 'd}' $(in) > $(out)",
+}
+
+java_library {
+    name: "aconfig_device_paths_java_util",
+    srcs: [":java_device_paths_test_util_src"],
+    static_libs: [
+        "libaconfig_java_proto_nano",
+    ],
+    sdk_version: "core_platform",
+    apex_available: [
+        "//apex_available:platform",
+    ],
+}
diff --git a/tools/aconfig/aconfig_device_paths/src/DeviceProtosTestUtilTemplate.java b/tools/aconfig/aconfig_device_paths/src/DeviceProtosTestUtilTemplate.java
new file mode 100644
index 0000000..2b113df
--- /dev/null
+++ b/tools/aconfig/aconfig_device_paths/src/DeviceProtosTestUtilTemplate.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.aconfig;
+
+import android.aconfig.nano.Aconfig.parsed_flag;
+import android.aconfig.nano.Aconfig.parsed_flags;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/** @hide */
+public class DeviceProtosTestUtil {
+    public static final String[] PATHS = {
+        TEMPLATE
+    };
+
+    private static final String APEX_DIR = "/apex/";
+    private static final String APEX_ACONFIG_PATH_SUFFIX = "/etc/aconfig_flags.pb";
+    private static final String SYSTEM_APEX_DIR = "/system/apex";
+
+    /**
+     * Returns a list of all on-device aconfig protos.
+     *
+     * <p>May throw an exception if the protos can't be read at the call site. For example, some of
+     * the protos are in the apex/ partition, which is mounted somewhat late in the boot process.
+     *
+     * @throws IOException if we can't read one of the protos yet
+     * @return a list of all on-device aconfig protos
+     */
+    public static List<parsed_flag> loadAndParseFlagProtos() throws IOException {
+        ArrayList<parsed_flag> result = new ArrayList();
+
+        for (String path : parsedFlagsProtoPaths()) {
+            try (FileInputStream inputStream = new FileInputStream(path)) {
+                parsed_flags parsedFlags = parsed_flags.parseFrom(inputStream.readAllBytes());
+                for (parsed_flag flag : parsedFlags.parsedFlag) {
+                    result.add(flag);
+                }
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns the list of all on-device aconfig protos paths.
+     *
+     * @hide
+     */
+    public static List<String> parsedFlagsProtoPaths() {
+        ArrayList<String> paths = new ArrayList(Arrays.asList(PATHS));
+
+        File apexDirectory = new File(SYSTEM_APEX_DIR);
+        if (!apexDirectory.isDirectory()) {
+            return paths;
+        }
+
+        File[] subdirs = apexDirectory.listFiles();
+        if (subdirs == null) {
+            return paths;
+        }
+
+        for (File prefix : subdirs) {
+            // For each mainline modules, there are two directories, one <modulepackage>/,
+            // and one <modulepackage>@<versioncode>/. Just read the former.
+            if (!prefix.getAbsolutePath().endsWith(".apex")) {
+                continue;
+            }
+
+            String apexName = prefix.getName().replace("com.google", "com");
+            apexName = apexName.substring(0, apexName.length() - 5);
+
+            File protoPath = new File(APEX_DIR + apexName + APEX_ACONFIG_PATH_SUFFIX);
+            if (!protoPath.exists()) {
+                continue;
+            }
+
+            paths.add(protoPath.getAbsolutePath());
+        }
+        return paths;
+    }
+}
diff --git a/tools/aconfig/aconfig_device_paths/test/Android.bp b/tools/aconfig/aconfig_device_paths/test/Android.bp
new file mode 100644
index 0000000..37f561f
--- /dev/null
+++ b/tools/aconfig/aconfig_device_paths/test/Android.bp
@@ -0,0 +1,35 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_team: "trendy_team_android_core_experiments",
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+    name: "aconfig_device_paths_java_test",
+    srcs: [
+        "src/**/*.java",
+    ],
+    static_libs: [
+        "androidx.test.runner",
+        "junit",
+        "aconfig_device_paths_java_util",
+    ],
+    test_suites: [
+        "general-tests",
+    ],
+    platform_apis: true,
+    certificate: "platform",
+}
diff --git a/tools/aconfig/aconfig_device_paths/test/AndroidManifest.xml b/tools/aconfig/aconfig_device_paths/test/AndroidManifest.xml
new file mode 100644
index 0000000..5e01879
--- /dev/null
+++ b/tools/aconfig/aconfig_device_paths/test/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.aconfig.storage.test">
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.aconfig.storage.test" />
+
+</manifest>
diff --git a/tools/aconfig/aconfig_device_paths/test/src/DeviceProtosTestUtilTest.java b/tools/aconfig/aconfig_device_paths/test/src/DeviceProtosTestUtilTest.java
new file mode 100644
index 0000000..8dd0fd0
--- /dev/null
+++ b/tools/aconfig/aconfig_device_paths/test/src/DeviceProtosTestUtilTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.test;
+
+import static org.junit.Assert.assertTrue;
+
+import android.aconfig.DeviceProtosTestUtil;
+import android.aconfig.nano.Aconfig.parsed_flag;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.List;
+import java.util.Set;
+
+@RunWith(JUnit4.class)
+public class DeviceProtosTestUtilTest {
+
+    private static final Set<String> PLATFORM_CONTAINERS = Set.of("system", "vendor", "product");
+
+    @Test
+    public void testDeviceProtos_loadAndParseFlagProtos() throws Exception {
+        List<parsed_flag> flags = DeviceProtosTestUtil.loadAndParseFlagProtos();
+        int platformFlags = 0;
+        int mainlineFlags = 0;
+        for (parsed_flag pf : flags) {
+            if (PLATFORM_CONTAINERS.contains(pf.container)) {
+                platformFlags++;
+            } else {
+                mainlineFlags++;
+            }
+        }
+
+        assertTrue(platformFlags > 3);
+        assertTrue(mainlineFlags > 3);
+    }
+}
diff --git a/tools/aconfig/aconfig_flags/Android.bp b/tools/aconfig/aconfig_flags/Android.bp
index 4c1fd4e..1b4e148 100644
--- a/tools/aconfig/aconfig_flags/Android.bp
+++ b/tools/aconfig/aconfig_flags/Android.bp
@@ -24,6 +24,11 @@
         "libaconfig_flags_rust",
     ],
     host_supported: true,
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.configinfrastructure",
+    ],
+    min_sdk_version: "34",
 }
 
 aconfig_declarations {
@@ -38,6 +43,11 @@
     crate_name: "aconfig_flags_rust",
     aconfig_declarations: "aconfig_flags",
     host_supported: true,
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.configinfrastructure",
+    ],
+    min_sdk_version: "34",
 }
 
 cc_aconfig_library {
diff --git a/tools/aconfig/aconfig_flags/flags.aconfig b/tools/aconfig/aconfig_flags/flags.aconfig
index b66d282..2488b5c 100644
--- a/tools/aconfig/aconfig_flags/flags.aconfig
+++ b/tools/aconfig/aconfig_flags/flags.aconfig
@@ -16,9 +16,28 @@
 }
 
 flag {
-  name: "enable_system_aconfigd_rust"
+  name: "tools_read_from_new_storage"
   namespace: "core_experiments_team_internal"
-  bug: "378079539"
-  description: "When enabled, the aconfigd cc_binary target becomes a no-op, and the rust_binary aconfigd-system target starts up."
-  is_fixed_read_only: true
+  bug: "370499640"
+  description: "When enabled, tools read directly from the new aconfig storage."
+}
+
+flag {
+  name: "tools_read_from_new_storage_bugfix"
+  namespace: "core_experiments_team_internal"
+  bug: "370499640"
+  description: "When enabled, tools read directly from the new aconfig storage."
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+  name: "invoke_updatable_aflags"
+  namespace: "core_experiments_team_internal"
+  bug: "385383899"
+  description: "When enabled, the system aflags binary invokes the updatable aflags."
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
 }
diff --git a/tools/aconfig/aconfig_flags/src/lib.rs b/tools/aconfig/aconfig_flags/src/lib.rs
index b413c62..dc507ae 100644
--- a/tools/aconfig/aconfig_flags/src/lib.rs
+++ b/tools/aconfig/aconfig_flags/src/lib.rs
@@ -40,9 +40,9 @@
         aconfig_flags_rust::enable_only_new_storage()
     }
 
-    /// Returns the value for the enable_system_aconfigd_rust flag.
-    pub fn enable_system_aconfigd_rust() -> bool {
-        aconfig_flags_rust::enable_system_aconfigd_rust()
+    /// Returns the value for the invoke_updatable_aflags flag.
+    pub fn invoke_updatable_aflags() -> bool {
+        aconfig_flags_rust::invoke_updatable_aflags()
     }
 }
 
@@ -61,8 +61,8 @@
         true
     }
 
-    /// Returns a placeholder value for the enable_system_aconfigd_rust flag.
-    pub fn enable_system_aconfigd_rust() -> bool {
+    /// Returns the value for the invoke_updatable_aflags flag.
+    pub fn invoke_updatable_aflags() -> bool {
         // Used only to enable typechecking and testing with cargo
         true
     }
diff --git a/tools/aconfig/aconfig_protos/Android.bp b/tools/aconfig/aconfig_protos/Android.bp
index d241994..62a2b64 100644
--- a/tools/aconfig/aconfig_protos/Android.bp
+++ b/tools/aconfig/aconfig_protos/Android.bp
@@ -58,6 +58,11 @@
     crate_name: "aconfig_rust_proto",
     source_stem: "aconfig_rust_proto",
     host_supported: true,
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.configinfrastructure",
+    ],
+    min_sdk_version: "34",
 }
 
 rust_defaults {
@@ -81,6 +86,11 @@
     crate_name: "aconfig_protos",
     host_supported: true,
     defaults: ["aconfig_protos.defaults"],
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.configinfrastructure",
+    ],
+    min_sdk_version: "34",
 }
 
 rust_test_host {
diff --git a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/ByteBufferReader.java b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/ByteBufferReader.java
index 9571568..1fbcb85 100644
--- a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/ByteBufferReader.java
+++ b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/ByteBufferReader.java
@@ -63,4 +63,8 @@
     public void position(int newPosition) {
         mByteBuffer.position(newPosition);
     }
+
+    public int position() {
+        return mByteBuffer.position();
+    }
 }
diff --git a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/PackageTable.java b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/PackageTable.java
index a45d12a..1e7c2ca 100644
--- a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/PackageTable.java
+++ b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/PackageTable.java
@@ -19,10 +19,16 @@
 import static java.nio.charset.StandardCharsets.UTF_8;
 
 import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 
 public class PackageTable {
 
+    private static final int FINGERPRINT_BYTES = 8;
+    // int: mPackageId + int: mBooleanStartIndex + int: mNextOffset
+    private static final int NODE_SKIP_BYTES = 12;
+
     private Header mHeader;
     private ByteBufferReader mReader;
 
@@ -60,6 +66,18 @@
         return null;
     }
 
+    public List<String> getPackageList() {
+        List<String> list = new ArrayList<>(mHeader.mNumPackages);
+        mReader.position(mHeader.mNodeOffset);
+        int fingerprintBytes = mHeader.mVersion == 1 ? 0 : FINGERPRINT_BYTES;
+        int skipBytes = fingerprintBytes + NODE_SKIP_BYTES;
+        for (int i = 0; i < mHeader.mNumPackages; i++) {
+            list.add(mReader.readString());
+            mReader.position(mReader.position() + skipBytes);
+        }
+        return list;
+    }
+
     public Header getHeader() {
         return mHeader;
     }
diff --git a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/StorageFileProvider.java b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/StorageFileProvider.java
index f1a4e26..f75ac36 100644
--- a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/StorageFileProvider.java
+++ b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/StorageFileProvider.java
@@ -39,13 +39,15 @@
     private static final String PMAP_FILE_EXT = ".package.map";
     private static final String FMAP_FILE_EXT = ".flag.map";
     private static final String VAL_FILE_EXT = ".val";
+    private static final StorageFileProvider DEFAULT_INSTANCE =
+            new StorageFileProvider(DEFAULT_MAP_PATH, DEFAULT_BOOT_PATH);
 
     private final String mMapPath;
     private final String mBootPath;
 
     /** @hide */
     public static StorageFileProvider getDefaultProvider() {
-        return new StorageFileProvider(DEFAULT_MAP_PATH, DEFAULT_BOOT_PATH);
+        return DEFAULT_INSTANCE;
     }
 
     /** @hide */
@@ -82,7 +84,9 @@
 
     /** @hide */
     public PackageTable getPackageTable(String container) {
-        return getPackageTable(Paths.get(mMapPath, container + PMAP_FILE_EXT));
+        return PackageTable.fromBytes(
+                mapStorageFile(
+                        Paths.get(mMapPath, container + PMAP_FILE_EXT), FileType.PACKAGE_MAP));
     }
 
     /** @hide */
@@ -97,11 +101,6 @@
                 mapStorageFile(Paths.get(mBootPath, container + VAL_FILE_EXT), FileType.FLAG_VAL));
     }
 
-    /** @hide */
-    public static PackageTable getPackageTable(Path path) {
-        return PackageTable.fromBytes(mapStorageFile(path, FileType.PACKAGE_MAP));
-    }
-
     // Map a storage file given file path
     private static MappedByteBuffer mapStorageFile(Path file, FileType type) {
         FileChannel channel = null;
diff --git a/tools/aconfig/aconfig_storage_file/tests/srcs/PackageTableTest.java b/tools/aconfig/aconfig_storage_file/tests/srcs/PackageTableTest.java
index 5906d8b..812ce35 100644
--- a/tools/aconfig/aconfig_storage_file/tests/srcs/PackageTableTest.java
+++ b/tools/aconfig/aconfig_storage_file/tests/srcs/PackageTableTest.java
@@ -27,6 +27,9 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.util.HashSet;
+import java.util.Set;
+
 @RunWith(JUnit4.class)
 public class PackageTableTest {
 
@@ -121,4 +124,22 @@
         assertEquals(4431940502274857964L, node2.getPackageFingerprint());
         assertEquals(-2213514155997929241L, node4.getPackageFingerprint());
     }
+
+    @Test
+    public void testPackageTable_getPackageList() throws Exception {
+        PackageTable packageTable =
+                PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer(2));
+        Set<String> packages = new HashSet<>(packageTable.getPackageList());
+        assertEquals(3, packages.size());
+        assertTrue(packages.contains("com.android.aconfig.storage.test_1"));
+        assertTrue(packages.contains("com.android.aconfig.storage.test_2"));
+        assertTrue(packages.contains("com.android.aconfig.storage.test_4"));
+
+        packageTable = PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer(1));
+        packages = new HashSet<>(packageTable.getPackageList());
+        assertEquals(3, packages.size());
+        assertTrue(packages.contains("com.android.aconfig.storage.test_1"));
+        assertTrue(packages.contains("com.android.aconfig.storage.test_2"));
+        assertTrue(packages.contains("com.android.aconfig.storage.test_4"));
+    }
 }
diff --git a/tools/aconfig/aconfig_storage_file/tests/srcs/StorageFileProviderTest.java b/tools/aconfig/aconfig_storage_file/tests/srcs/StorageFileProviderTest.java
index a820970..c2720f9 100644
--- a/tools/aconfig/aconfig_storage_file/tests/srcs/StorageFileProviderTest.java
+++ b/tools/aconfig/aconfig_storage_file/tests/srcs/StorageFileProviderTest.java
@@ -58,10 +58,6 @@
                 new StorageFileProvider(TestDataUtils.TESTDATA_PATH, TestDataUtils.TESTDATA_PATH);
         PackageTable pt = p.getPackageTable("mock.v1");
         assertNotNull(pt);
-        pt =
-                StorageFileProvider.getPackageTable(
-                        Paths.get(TestDataUtils.TESTDATA_PATH, "mock.v1.package.map"));
-        assertNotNull(pt);
         FlagTable f = p.getFlagTable("mock.v1");
         assertNotNull(f);
         FlagValueList v = p.getFlagValueList("mock.v1");
diff --git a/tools/aconfig/aconfig_storage_read_api/Android.bp b/tools/aconfig/aconfig_storage_read_api/Android.bp
index 09daeea..16341b9 100644
--- a/tools/aconfig/aconfig_storage_read_api/Android.bp
+++ b/tools/aconfig/aconfig_storage_read_api/Android.bp
@@ -154,40 +154,17 @@
 java_library {
     name: "aconfig_storage_reader_java",
     srcs: [
-        "srcs/android/aconfig/storage/StorageInternalReader.java",
-        "srcs/android/os/flagging/PlatformAconfigPackageInternal.java",
+        "srcs/android/os/flagging/*.java",
     ],
     libs: [
         "unsupportedappusage",
-        "strict_mode_stub",
-        "aconfig_storage_stub",
     ],
     static_libs: [
         "aconfig_storage_file_java",
     ],
-    sdk_version: "core_current",
-    host_supported: true,
-    min_sdk_version: "29",
-    apex_available: [
-        "//apex_available:platform",
-        "//apex_available:anyapex",
+    sdk_version: "current",
+    visibility: [
+        "//frameworks/base",
+        "//build/make/tools/aconfig/aconfig_storage_read_api/tests",
     ],
 }
-
-java_library {
-    name: "aconfig_storage_reader_java_none",
-    srcs: [
-        "srcs/android/aconfig/storage/StorageInternalReader.java",
-        "srcs/android/os/flagging/PlatformAconfigPackageInternal.java",
-    ],
-    libs: [
-        "unsupportedappusage-sdk-none",
-        "fake_device_config",
-    ],
-    static_libs: [
-        "aconfig_storage_file_java_none",
-    ],
-    sdk_version: "none",
-    system_modules: "core-all-system-modules",
-    host_supported: true,
-}
diff --git a/tools/aconfig/aconfig_storage_read_api/src/lib.rs b/tools/aconfig/aconfig_storage_read_api/src/lib.rs
index 6e98fe9..d3cc9d4 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/lib.rs
+++ b/tools/aconfig/aconfig_storage_read_api/src/lib.rs
@@ -433,21 +433,24 @@
             get_package_read_context(&package_mapped_file, "com.android.aconfig.storage.test_1")
                 .unwrap()
                 .unwrap();
-        let expected_package_context = PackageReadContext { package_id: 0, boolean_start_index: 0 };
+        let expected_package_context =
+            PackageReadContext { package_id: 0, boolean_start_index: 0, fingerprint: 0 };
         assert_eq!(package_context, expected_package_context);
 
         let package_context =
             get_package_read_context(&package_mapped_file, "com.android.aconfig.storage.test_2")
                 .unwrap()
                 .unwrap();
-        let expected_package_context = PackageReadContext { package_id: 1, boolean_start_index: 3 };
+        let expected_package_context =
+            PackageReadContext { package_id: 1, boolean_start_index: 3, fingerprint: 0 };
         assert_eq!(package_context, expected_package_context);
 
         let package_context =
             get_package_read_context(&package_mapped_file, "com.android.aconfig.storage.test_4")
                 .unwrap()
                 .unwrap();
-        let expected_package_context = PackageReadContext { package_id: 2, boolean_start_index: 6 };
+        let expected_package_context =
+            PackageReadContext { package_id: 2, boolean_start_index: 6, fingerprint: 0 };
         assert_eq!(package_context, expected_package_context);
     }
 
diff --git a/tools/aconfig/aconfig_storage_read_api/src/package_table_query.rs b/tools/aconfig/aconfig_storage_read_api/src/package_table_query.rs
index a4b63ab..b20668f 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/package_table_query.rs
+++ b/tools/aconfig/aconfig_storage_read_api/src/package_table_query.rs
@@ -28,6 +28,7 @@
 pub struct PackageReadContext {
     pub package_id: u32,
     pub boolean_start_index: u32,
+    pub fingerprint: u64,
 }
 
 /// Query package read context: package id and start index
@@ -62,6 +63,7 @@
             return Ok(Some(PackageReadContext {
                 package_id: interpreted_node.package_id,
                 boolean_start_index: interpreted_node.boolean_start_index,
+                fingerprint: interpreted_node.fingerprint,
             }));
         }
         match interpreted_node.next_offset {
@@ -84,19 +86,58 @@
             find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_1")
                 .unwrap()
                 .unwrap();
-        let expected_package_context = PackageReadContext { package_id: 0, boolean_start_index: 0 };
+        let expected_package_context =
+            PackageReadContext { package_id: 0, boolean_start_index: 0, fingerprint: 0 };
         assert_eq!(package_context, expected_package_context);
         let package_context =
             find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_2")
                 .unwrap()
                 .unwrap();
-        let expected_package_context = PackageReadContext { package_id: 1, boolean_start_index: 3 };
+        let expected_package_context =
+            PackageReadContext { package_id: 1, boolean_start_index: 3, fingerprint: 0 };
         assert_eq!(package_context, expected_package_context);
         let package_context =
             find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_4")
                 .unwrap()
                 .unwrap();
-        let expected_package_context = PackageReadContext { package_id: 2, boolean_start_index: 6 };
+        let expected_package_context =
+            PackageReadContext { package_id: 2, boolean_start_index: 6, fingerprint: 0 };
+        assert_eq!(package_context, expected_package_context);
+    }
+
+    #[test]
+    // this test point locks down table query
+    fn test_package_query_v2() {
+        let package_table = create_test_package_table(2).into_bytes();
+        let package_context =
+            find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_1")
+                .unwrap()
+                .unwrap();
+        let expected_package_context = PackageReadContext {
+            package_id: 0,
+            boolean_start_index: 0,
+            fingerprint: 15248948510590158086u64,
+        };
+        assert_eq!(package_context, expected_package_context);
+        let package_context =
+            find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_2")
+                .unwrap()
+                .unwrap();
+        let expected_package_context = PackageReadContext {
+            package_id: 1,
+            boolean_start_index: 3,
+            fingerprint: 4431940502274857964u64,
+        };
+        assert_eq!(package_context, expected_package_context);
+        let package_context =
+            find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_4")
+                .unwrap()
+                .unwrap();
+        let expected_package_context = PackageReadContext {
+            package_id: 2,
+            boolean_start_index: 6,
+            fingerprint: 16233229917711622375u64,
+        };
         assert_eq!(package_context, expected_package_context);
     }
 
diff --git a/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/StorageInternalReader.java b/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/StorageInternalReader.java
deleted file mode 100644
index 6fbcdb3..0000000
--- a/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/StorageInternalReader.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.aconfig.storage;
-
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.StrictMode;
-
-import java.io.Closeable;
-import java.nio.MappedByteBuffer;
-import java.nio.channels.FileChannel;
-import java.nio.file.Paths;
-import java.nio.file.StandardOpenOption;
-
-/** @hide */
-public class StorageInternalReader {
-
-    private static final String MAP_PATH = "/metadata/aconfig/maps/";
-    private static final String BOOT_PATH = "/metadata/aconfig/boot/";
-
-    private PackageTable mPackageTable;
-    private FlagValueList mFlagValueList;
-
-    private int mPackageBooleanStartOffset;
-
-    @UnsupportedAppUsage
-    public StorageInternalReader(String container, String packageName) {
-        this(packageName, MAP_PATH + container + ".package.map", BOOT_PATH + container + ".val");
-    }
-
-    @UnsupportedAppUsage
-    public StorageInternalReader(String packageName, String packageMapFile, String flagValueFile) {
-        StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
-        mPackageTable = PackageTable.fromBytes(mapStorageFile(packageMapFile));
-        mFlagValueList = FlagValueList.fromBytes(mapStorageFile(flagValueFile));
-        StrictMode.setThreadPolicy(oldPolicy);
-        mPackageBooleanStartOffset = getPackageBooleanStartOffset(packageName);
-    }
-
-    @UnsupportedAppUsage
-    public boolean getBooleanFlagValue(int index) {
-        index += mPackageBooleanStartOffset;
-        return mFlagValueList.getBoolean(index);
-    }
-
-    private int getPackageBooleanStartOffset(String packageName) {
-        PackageTable.Node pNode = mPackageTable.get(packageName);
-        if (pNode == null) {
-            PackageTable.Header header = mPackageTable.getHeader();
-            throw new AconfigStorageException(
-                    String.format(
-                            "Fail to get package %s from container %s",
-                            packageName, header.getContainer()));
-        }
-        return pNode.getBooleanStartIndex();
-    }
-
-    // Map a storage file given file path
-    private static MappedByteBuffer mapStorageFile(String file) {
-        FileChannel channel = null;
-        try {
-            channel = FileChannel.open(Paths.get(file), StandardOpenOption.READ);
-            return channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
-        } catch (Exception e) {
-            throw new AconfigStorageException(
-                    String.format("Fail to mmap storage file %s", file), e);
-        } finally {
-            quietlyDispose(channel);
-        }
-    }
-
-    private static void quietlyDispose(Closeable closable) {
-        try {
-            if (closable != null) {
-                closable.close();
-            }
-        } catch (Exception e) {
-            // no need to care, at least as of now
-        }
-    }
-}
diff --git a/tools/aconfig/aconfig_storage_read_api/srcs/android/os/flagging/PlatformAconfigPackage.java b/tools/aconfig/aconfig_storage_read_api/srcs/android/os/flagging/PlatformAconfigPackage.java
new file mode 100644
index 0000000..5fbe567
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/srcs/android/os/flagging/PlatformAconfigPackage.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.flagging;
+
+import android.aconfig.storage.AconfigStorageException;
+import android.aconfig.storage.FlagTable;
+import android.aconfig.storage.FlagValueList;
+import android.aconfig.storage.PackageTable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.util.Log;
+
+import java.io.Closeable;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * An {@code aconfig} package containing the enabled state of its flags.
+ *
+ * <p><strong>Note: this is intended only to be used by generated code. To determine if a given flag
+ * is enabled in app code, the generated android flags should be used.</strong>
+ *
+ * <p>This class is used to read the flag from platform Aconfig Package.Each instance of this class
+ * will cache information related to one package. To read flags from a different package, a new
+ * instance of this class should be {@link #load loaded}.
+ *
+ * @hide
+ */
+public class PlatformAconfigPackage {
+    private static final String TAG = "PlatformAconfigPackage";
+    private static final String MAP_PATH = "/metadata/aconfig/maps/";
+    private static final String BOOT_PATH = "/metadata/aconfig/boot/";
+
+    private static final Map<String, PackageTable> sPackageTableCache = new HashMap<>();
+
+    private FlagTable mFlagTable;
+    private FlagValueList mFlagValueList;
+
+    private int mPackageBooleanStartOffset = -1;
+    private int mPackageId = -1;
+
+    private PlatformAconfigPackage() {}
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public static final Set<String> PLATFORM_PACKAGE_MAP_FILES =
+            Set.of("system.package.map", "vendor.package.map", "product.package.map");
+
+    static {
+        for (String pf : PLATFORM_PACKAGE_MAP_FILES) {
+            try {
+                PackageTable pTable = PackageTable.fromBytes(mapStorageFile(MAP_PATH + pf));
+                for (String packageName : pTable.getPackageList()) {
+                    sPackageTableCache.put(packageName, pTable);
+                }
+            } catch (Exception e) {
+                // pass
+                Log.w(TAG, e.toString());
+            }
+        }
+    }
+
+    /**
+     * Loads a platform Aconfig Package from Aconfig Storage.
+     *
+     * <p>This method attempts to load the specified platform Aconfig package.
+     *
+     * @param packageName The name of the Aconfig package to load.
+     * @return An instance of {@link PlatformAconfigPackage}, which may be empty if the package is
+     *     not found in the container. Null if the package is not found in platform partitions.
+     * @throws AconfigStorageReadException if there is an error reading from Aconfig Storage, such
+     *     as if the storage system is not found, or there is an error reading the storage file. The
+     *     specific error code can be got using {@link AconfigStorageReadException#getErrorCode()}.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static PlatformAconfigPackage load(String packageName) {
+        try {
+            PlatformAconfigPackage aconfigPackage = new PlatformAconfigPackage();
+            PackageTable pTable = sPackageTableCache.get(packageName);
+            if (pTable == null) {
+                return null;
+            }
+            PackageTable.Node pNode = pTable.get(packageName);
+            String container = pTable.getHeader().getContainer();
+            aconfigPackage.mFlagTable =
+                    FlagTable.fromBytes(mapStorageFile(MAP_PATH + container + ".flag.map"));
+            aconfigPackage.mFlagValueList =
+                    FlagValueList.fromBytes(mapStorageFile(BOOT_PATH + container + ".val"));
+            aconfigPackage.mPackageBooleanStartOffset = pNode.getBooleanStartIndex();
+            aconfigPackage.mPackageId = pNode.getPackageId();
+            return aconfigPackage;
+        } catch (AconfigStorageException e) {
+            throw new AconfigStorageReadException(
+                    e.getErrorCode(), "Fail to create AconfigPackage", e);
+        } catch (Exception e) {
+            throw new AconfigStorageReadException(
+                    AconfigStorageReadException.ERROR_GENERIC,
+                    "Fail to create PlatformAconfigPackage",
+                    e);
+        }
+    }
+
+    /**
+     * Retrieves the value of a boolean flag.
+     *
+     * <p>This method retrieves the value of the specified flag. If the flag exists within the
+     * loaded Aconfig Package, its value is returned. Otherwise, the provided `defaultValue` is
+     * returned.
+     *
+     * @param flagName The name of the flag (excluding any package name prefix).
+     * @param defaultValue The value to return if the flag is not found.
+     * @return The boolean value of the flag, or `defaultValue` if the flag is not found.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public boolean getBooleanFlagValue(String flagName, boolean defaultValue) {
+        FlagTable.Node fNode = mFlagTable.get(mPackageId, flagName);
+        if (fNode == null) {
+            return defaultValue;
+        }
+        return mFlagValueList.getBoolean(fNode.getFlagIndex() + mPackageBooleanStartOffset);
+    }
+
+    // Map a storage file given file path
+    private static MappedByteBuffer mapStorageFile(String file) {
+        FileChannel channel = null;
+        try {
+            channel = FileChannel.open(Paths.get(file), StandardOpenOption.READ);
+            return channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
+        } catch (Exception e) {
+            throw new AconfigStorageReadException(
+                    AconfigStorageReadException.ERROR_CANNOT_READ_STORAGE_FILE,
+                    "Fail to mmap storage",
+                    e);
+        } finally {
+            quietlyDispose(channel);
+        }
+    }
+
+    private static void quietlyDispose(Closeable closable) {
+        try {
+            if (closable != null) {
+                closable.close();
+            }
+        } catch (Exception e) {
+            // no need to care, at least as of now
+        }
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/srcs/android/os/flagging/PlatformAconfigPackageInternal.java b/tools/aconfig/aconfig_storage_read_api/srcs/android/os/flagging/PlatformAconfigPackageInternal.java
index 83310be..854e68b 100644
--- a/tools/aconfig/aconfig_storage_read_api/srcs/android/os/flagging/PlatformAconfigPackageInternal.java
+++ b/tools/aconfig/aconfig_storage_read_api/srcs/android/os/flagging/PlatformAconfigPackageInternal.java
@@ -17,7 +17,6 @@
 package android.os.flagging;
 
 import android.aconfig.storage.AconfigStorageException;
-import android.aconfig.storage.FlagTable;
 import android.aconfig.storage.FlagValueList;
 import android.aconfig.storage.PackageTable;
 import android.aconfig.storage.StorageFileProvider;
@@ -41,75 +40,13 @@
  */
 public class PlatformAconfigPackageInternal {
 
-    private final FlagTable mFlagTable;
     private final FlagValueList mFlagValueList;
-    private final int mPackageId;
     private final int mPackageBooleanStartOffset;
-    private final AconfigStorageException mException;
 
     private PlatformAconfigPackageInternal(
-            FlagValueList flagValueList,
-            FlagTable flagTable,
-            int packageBooleanStartOffset,
-            int packageId,
-            AconfigStorageException exception) {
+            FlagValueList flagValueList, int packageBooleanStartOffset) {
         this.mFlagValueList = flagValueList;
-        this.mFlagTable = flagTable;
         this.mPackageBooleanStartOffset = packageBooleanStartOffset;
-        this.mPackageId = packageId;
-        this.mException = exception;
-    }
-
-    /**
-     * Loads an Aconfig Package from platform Aconfig Storage.
-     *
-     * <p>This method is intended for internal use only and may be changed or removed without
-     * notice.
-     *
-     * <p>This method loads the specified Aconfig Package from the given container.
-     *
-     * <p>AconfigStorageException will be stored if there is an error reading from Aconfig Storage.
-     * The specific error code can be got using {@link #getException()}.
-     *
-     * @param container The name of the container.
-     * @param packageName The name of the Aconfig package to load.
-     * @return An instance of {@link PlatformAconfigPackageInternal}
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public static PlatformAconfigPackageInternal load(String container, String packageName) {
-        return load(container, packageName, StorageFileProvider.getDefaultProvider());
-    }
-
-    /** @hide */
-    public static PlatformAconfigPackageInternal load(
-            String container, String packageName, StorageFileProvider fileProvider) {
-        StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
-        try {
-            PackageTable.Node pNode = fileProvider.getPackageTable(container).get(packageName);
-
-            if (pNode == null) {
-                return createExceptionInstance(
-                        AconfigStorageException.ERROR_PACKAGE_NOT_FOUND,
-                        "package "
-                                + packageName
-                                + " in container "
-                                + container
-                                + " cannot be found on the device");
-            }
-
-            return new PlatformAconfigPackageInternal(
-                    fileProvider.getFlagValueList(container),
-                    fileProvider.getFlagTable(container),
-                    pNode.getBooleanStartIndex(),
-                    pNode.getPackageId(),
-                    null);
-
-        } catch (AconfigStorageException e) {
-            return createExceptionInstance(e.getErrorCode(), e.getMessage());
-        } finally {
-            StrictMode.setThreadPolicy(oldPolicy);
-        }
     }
 
     /**
@@ -118,9 +55,6 @@
      * <p>This method is intended for internal use only and may be changed or removed without
      * notice.
      *
-     * <p>AconfigStorageException will be stored if there is an error reading from Aconfig Storage.
-     * The specific error code can be got using {@link #getException()}.
-     *
      * @param container The name of the container.
      * @param packageName The name of the Aconfig package.
      * @param packageFingerprint The expected fingerprint of the package.
@@ -145,48 +79,38 @@
             long packageFingerprint,
             StorageFileProvider fileProvider) {
         StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+        PackageTable.Node pNode = null;
+        FlagValueList vList = null;
         try {
-            PackageTable.Node pNode = fileProvider.getPackageTable(container).get(packageName);
-
-            if (pNode == null) {
-                return createExceptionInstance(
-                        AconfigStorageException.ERROR_PACKAGE_NOT_FOUND,
-                        "package "
-                                + packageName
-                                + " in container "
-                                + container
-                                + " cannot be found on the device");
-            }
-
-            if (pNode.hasPackageFingerprint()
-                    && packageFingerprint != pNode.getPackageFingerprint()) {
-                return new PlatformAconfigPackageInternal(
-                        fileProvider.getFlagValueList(container),
-                        fileProvider.getFlagTable(container),
-                        pNode.getBooleanStartIndex(),
-                        pNode.getPackageId(),
-                        new AconfigStorageException(
-                                AconfigStorageException.ERROR_FILE_FINGERPRINT_MISMATCH,
-                                "The fingerprint provided for the Aconfig package "
-                                        + packageName
-                                        + " in container "
-                                        + container
-                                        + " does not match"
-                                        + " the fingerprint of the package found on the device."));
-            }
-
-            return new PlatformAconfigPackageInternal(
-                    fileProvider.getFlagValueList(container),
-                    null,
-                    pNode.getBooleanStartIndex(),
-                    0,
-                    null);
-
-        } catch (AconfigStorageException e) {
-            return createExceptionInstance(e.getErrorCode(), e.getMessage());
+            pNode = fileProvider.getPackageTable(container).get(packageName);
+            vList = fileProvider.getFlagValueList(container);
         } finally {
             StrictMode.setThreadPolicy(oldPolicy);
         }
+
+        if (pNode == null || vList == null) {
+            throw new AconfigStorageException(
+                    AconfigStorageException.ERROR_PACKAGE_NOT_FOUND,
+                    String.format(
+                            "package "
+                                    + packageName
+                                    + " in container "
+                                    + container
+                                    + " cannot be found on the device"));
+        }
+
+        if (pNode.hasPackageFingerprint() && packageFingerprint != pNode.getPackageFingerprint()) {
+            throw new AconfigStorageException(
+                    AconfigStorageException.ERROR_FILE_FINGERPRINT_MISMATCH,
+                    String.format(
+                            "package "
+                                    + packageName
+                                    + " in container "
+                                    + container
+                                    + " cannot be found on the device"));
+        }
+
+        return new PlatformAconfigPackageInternal(vList, pNode.getBooleanStartIndex());
     }
 
     /**
@@ -198,10 +122,6 @@
      * <p>This method retrieves the value of a flag within the loaded Aconfig package using its
      * index. The index is generated at build time and may vary between builds.
      *
-     * <p>To ensure you are using the correct index, verify that the package's fingerprint matches
-     * the expected fingerprint before calling this method. If the fingerprints do not match, use
-     * {@link #getBooleanFlagValue(String, boolean)} instead.
-     *
      * @param index The index of the flag within the package.
      * @return The boolean value of the flag.
      * @hide
@@ -210,55 +130,4 @@
     public boolean getBooleanFlagValue(int index) {
         return mFlagValueList.getBoolean(index + mPackageBooleanStartOffset);
     }
-
-    /**
-     * Retrieves the value of a boolean flag using its name.
-     *
-     * <p>This method is intended for internal use only and may be changed or removed without
-     * notice.
-     *
-     * <p>This method retrieves the value of a flag within the loaded Aconfig package using its
-     * name.
-     *
-     * @param flagName The name of the flag.
-     * @param defaultValue The default value to return if the flag is not found.
-     * @return The boolean value of the flag.
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public boolean getBooleanFlagValue(String flagName, boolean defaultValue) {
-        FlagTable.Node fNode = mFlagTable.get(mPackageId, flagName);
-        if (fNode == null) {
-            return defaultValue;
-        }
-        return mFlagValueList.getBoolean(fNode.getFlagIndex() + mPackageBooleanStartOffset);
-    }
-
-    /**
-     * Returns any exception that occurred during the loading of the Aconfig package.
-     *
-     * <p>This method is intended for internal use only and may be changed or removed without
-     * notice.
-     *
-     * @return The exception that occurred, or {@code null} if no exception occurred.
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public Exception getException() {
-        return mException;
-    }
-
-    /**
-     * Creates a new {@link PlatformAconfigPackageInternal} instance with an {@link
-     * AconfigStorageException}.
-     *
-     * @param errorCode The error code for the exception.
-     * @param message The error message for the exception.
-     * @return A new {@link PlatformAconfigPackageInternal} instance with the specified exception.
-     */
-    private static PlatformAconfigPackageInternal createExceptionInstance(
-            int errorCode, String message) {
-        return new PlatformAconfigPackageInternal(
-                null, null, 0, 0, new AconfigStorageException(errorCode, message));
-    }
 }
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/Android.bp b/tools/aconfig/aconfig_storage_read_api/tests/Android.bp
index fb2fc77..702325d 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/Android.bp
+++ b/tools/aconfig/aconfig_storage_read_api/tests/Android.bp
@@ -57,7 +57,6 @@
     static_libs: [
         "aconfig_device_paths_java",
         "aconfig_storage_file_java",
-        "aconfig_storage_reader_java",
         "androidx.test.rules",
         "libaconfig_storage_read_api_java",
         "junit",
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/AconfigStorageReadAPITest.java b/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/AconfigStorageReadAPITest.java
index 6dd1bce..2557048 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/AconfigStorageReadAPITest.java
+++ b/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/AconfigStorageReadAPITest.java
@@ -26,7 +26,6 @@
 import android.aconfig.storage.FlagReadContext.StoredFlagType;
 import android.aconfig.storage.PackageReadContext;
 import android.aconfig.storage.SipHasher13;
-import android.aconfig.storage.StorageInternalReader;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -225,46 +224,4 @@
             assertEquals(rHash, jHash);
         }
     }
-
-    @Test
-    public void testRustJavaEqualFlag() throws IOException {
-        List<parsed_flag> flags = DeviceProtos.loadAndParseFlagProtos();
-
-        String mapPath = "/metadata/aconfig/maps/";
-        String flagsPath = "/metadata/aconfig/boot/";
-
-        for (parsed_flag flag : flags) {
-
-            String container = flag.container;
-            String packageName = flag.package_;
-            String flagName = flag.name;
-            String fullFlagName = packageName + "/" + flagName;
-
-            MappedByteBuffer packageMap =
-                    AconfigStorageReadAPI.mapStorageFile(mapPath + container + ".package.map");
-            MappedByteBuffer flagMap =
-                    AconfigStorageReadAPI.mapStorageFile(mapPath + container + ".flag.map");
-            MappedByteBuffer flagValList =
-                    AconfigStorageReadAPI.mapStorageFile(flagsPath + container + ".val");
-
-            PackageReadContext packageContext =
-                    AconfigStorageReadAPI.getPackageReadContext(packageMap, packageName);
-
-            FlagReadContext flagContext =
-                    AconfigStorageReadAPI.getFlagReadContext(
-                            flagMap, packageContext.mPackageId, flagName);
-
-            boolean rVal =
-                    AconfigStorageReadAPI.getBooleanFlagValue(
-                            flagValList,
-                            packageContext.mBooleanStartIndex + flagContext.mFlagIndex);
-
-            StorageInternalReader reader = new StorageInternalReader(container, packageName);
-            boolean jVal = reader.getBooleanFlagValue(flagContext.mFlagIndex);
-
-            long rHash = AconfigStorageReadAPI.hash(packageName);
-            long jHash = SipHasher13.hash(packageName.getBytes());
-            assertEquals(rVal, jVal);
-        }
-    }
-}
\ No newline at end of file
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/PlatformAconfigPackageInternalTest.java b/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/PlatformAconfigPackageInternalTest.java
index 15cfc3b..e532ff6 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/PlatformAconfigPackageInternalTest.java
+++ b/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/PlatformAconfigPackageInternalTest.java
@@ -16,18 +16,17 @@
 
 package android.aconfig.storage.test;
 
-import static android.aconfig.nano.Aconfig.ENABLED;
-
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
 
 import android.aconfig.DeviceProtos;
+import android.aconfig.nano.Aconfig;
 import android.aconfig.nano.Aconfig.parsed_flag;
-import android.aconfig.storage.AconfigStorageException;
 import android.aconfig.storage.FlagTable;
 import android.aconfig.storage.FlagValueList;
 import android.aconfig.storage.PackageTable;
 import android.aconfig.storage.StorageFileProvider;
+import android.internal.aconfig.storage.AconfigStorageException;
 import android.os.flagging.PlatformAconfigPackageInternal;
 
 import org.junit.Test;
@@ -46,47 +45,15 @@
     private static final Set<String> PLATFORM_CONTAINERS = Set.of("system", "vendor", "product");
 
     @Test
-    public void testAconfigPackageInternal_load() throws IOException {
+    public void testPlatformAconfigPackageInternal_load() throws IOException {
         List<parsed_flag> flags = DeviceProtos.loadAndParseFlagProtos();
         Map<String, PlatformAconfigPackageInternal> readerMap = new HashMap<>();
         StorageFileProvider fp = StorageFileProvider.getDefaultProvider();
 
         for (parsed_flag flag : flags) {
-
-            String container = flag.container;
-            String packageName = flag.package_;
-            String flagName = flag.name;
-            if (!PLATFORM_CONTAINERS.contains(container)) continue;
-
-            PackageTable pTable = fp.getPackageTable(container);
-            PackageTable.Node pNode = pTable.get(packageName);
-            FlagTable fTable = fp.getFlagTable(container);
-            FlagTable.Node fNode = fTable.get(pNode.getPackageId(), flagName);
-            FlagValueList fList = fp.getFlagValueList(container);
-
-            int index = pNode.getBooleanStartIndex() + fNode.getFlagIndex();
-            boolean rVal = fList.getBoolean(index);
-
-            PlatformAconfigPackageInternal reader = readerMap.get(packageName);
-            if (reader == null) {
-                reader = PlatformAconfigPackageInternal.load(container, packageName);
-                assertNull(reader.getException());
-                readerMap.put(packageName, reader);
+            if (flag.permission == Aconfig.READ_ONLY && flag.state == Aconfig.DISABLED) {
+                continue;
             }
-            boolean jVal = reader.getBooleanFlagValue(flagName, !rVal);
-
-            assertEquals(rVal, jVal);
-        }
-    }
-
-    @Test
-    public void testPlatformAconfigPackageInternal_load_with_fingerprint() throws IOException {
-        List<parsed_flag> flags = DeviceProtos.loadAndParseFlagProtos();
-        Map<String, PlatformAconfigPackageInternal> readerMap = new HashMap<>();
-        StorageFileProvider fp = StorageFileProvider.getDefaultProvider();
-
-        for (parsed_flag flag : flags) {
-
             String container = flag.container;
             String packageName = flag.package_;
             String flagName = flag.name;
@@ -106,7 +73,6 @@
             PlatformAconfigPackageInternal reader = readerMap.get(packageName);
             if (reader == null) {
                 reader = PlatformAconfigPackageInternal.load(container, packageName, fingerprint);
-                assertNull(reader.getException());
                 readerMap.put(packageName, reader);
             }
             boolean jVal = reader.getBooleanFlagValue(fNode.getFlagIndex());
@@ -116,19 +82,22 @@
     }
 
     @Test
-    public void testAconfigPackage_load_withError() throws IOException {
+    public void testPlatformAconfigPackage_load_withError() throws IOException {
         // container not found fake_container
-        PlatformAconfigPackageInternal aPackage =
-                PlatformAconfigPackageInternal.load("fake_container", "fake_package", 0);
-        assertEquals(
-                AconfigStorageException.ERROR_CANNOT_READ_STORAGE_FILE,
-                ((AconfigStorageException) aPackage.getException()).getErrorCode());
+        AconfigStorageException e =
+                assertThrows(
+                        AconfigStorageException.class,
+                        () ->
+                                PlatformAconfigPackageInternal.load(
+                                        "fake_container", "fake_package", 0));
+        assertEquals(AconfigStorageException.ERROR_CANNOT_READ_STORAGE_FILE, e.getErrorCode());
 
         // package not found
-        aPackage = PlatformAconfigPackageInternal.load("system", "fake_container", 0);
-        assertEquals(
-                AconfigStorageException.ERROR_PACKAGE_NOT_FOUND,
-                ((AconfigStorageException) aPackage.getException()).getErrorCode());
+        e =
+                assertThrows(
+                        AconfigStorageException.class,
+                        () -> PlatformAconfigPackageInternal.load("system", "fake_container", 0));
+        assertEquals(AconfigStorageException.ERROR_PACKAGE_NOT_FOUND, e.getErrorCode());
 
         // fingerprint doesn't match
         List<parsed_flag> flags = DeviceProtos.loadAndParseFlagProtos();
@@ -138,18 +107,20 @@
 
         String container = flag.container;
         String packageName = flag.package_;
-        boolean value = flag.state == ENABLED;
+        boolean value = flag.state == Aconfig.ENABLED;
 
         PackageTable pTable = fp.getPackageTable(container);
         PackageTable.Node pNode = pTable.get(packageName);
 
         if (pNode.hasPackageFingerprint()) {
             long fingerprint = pNode.getPackageFingerprint();
-            aPackage = PlatformAconfigPackageInternal.load(container, packageName, fingerprint + 1);
-            assertEquals(
-                    // AconfigStorageException.ERROR_FILE_FINGERPRINT_MISMATCH,
-                    5, ((AconfigStorageException) aPackage.getException()).getErrorCode());
-            assertEquals(aPackage.getBooleanFlagValue(flag.name, !value), value);
+            e =
+                    assertThrows(
+                            AconfigStorageException.class,
+                            () ->
+                                    PlatformAconfigPackageInternal.load(
+                                            container, packageName, fingerprint + 1));
+            assertEquals(AconfigStorageException.ERROR_FILE_FINGERPRINT_MISMATCH, e.getErrorCode());
         }
     }
 }
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/PlatformAconfigPackageTest.java b/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/PlatformAconfigPackageTest.java
new file mode 100644
index 0000000..fabc2c9
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/PlatformAconfigPackageTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import android.aconfig.DeviceProtos;
+import android.aconfig.nano.Aconfig;
+import android.aconfig.nano.Aconfig.parsed_flag;
+import android.aconfig.storage.FlagTable;
+import android.aconfig.storage.FlagValueList;
+import android.aconfig.storage.PackageTable;
+import android.aconfig.storage.StorageFileProvider;
+import android.os.flagging.PlatformAconfigPackage;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+@RunWith(JUnit4.class)
+public class PlatformAconfigPackageTest {
+
+    private static final Set<String> PLATFORM_CONTAINERS = Set.of("system", "vendor", "product");
+
+    @Test
+    public void testPlatformAconfigPackage_load() throws IOException {
+        List<parsed_flag> flags = DeviceProtos.loadAndParseFlagProtos();
+        Map<String, PlatformAconfigPackage> readerMap = new HashMap<>();
+        StorageFileProvider fp = StorageFileProvider.getDefaultProvider();
+
+        for (parsed_flag flag : flags) {
+            if (flag.permission == Aconfig.READ_ONLY && flag.state == Aconfig.DISABLED) {
+                continue;
+            }
+            String container = flag.container;
+            String packageName = flag.package_;
+            String flagName = flag.name;
+            if (!PLATFORM_CONTAINERS.contains(container)) continue;
+
+            PackageTable pTable = fp.getPackageTable(container);
+            PackageTable.Node pNode = pTable.get(packageName);
+            FlagTable fTable = fp.getFlagTable(container);
+            FlagTable.Node fNode = fTable.get(pNode.getPackageId(), flagName);
+            FlagValueList fList = fp.getFlagValueList(container);
+
+            int index = pNode.getBooleanStartIndex() + fNode.getFlagIndex();
+            boolean rVal = fList.getBoolean(index);
+
+            long fingerprint = pNode.getPackageFingerprint();
+
+            PlatformAconfigPackage reader = readerMap.get(packageName);
+            if (reader == null) {
+                reader = PlatformAconfigPackage.load(packageName);
+                readerMap.put(packageName, reader);
+            }
+            boolean jVal = reader.getBooleanFlagValue(flagName, !rVal);
+
+            assertEquals(rVal, jVal);
+        }
+    }
+
+    @Test
+    public void testPlatformAconfigPackage_load_withError() throws IOException {
+        // package not found
+        assertNull(PlatformAconfigPackage.load("fake_container"));
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/StorageInternalReaderTest.java b/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/StorageInternalReaderTest.java
deleted file mode 100644
index 8a8f054..0000000
--- a/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/StorageInternalReaderTest.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.aconfig.storage.test;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.aconfig.storage.StorageInternalReader;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class StorageInternalReaderTest {
-
-    private String mStorageDir = "/data/local/tmp/aconfig_java_api_test";
-
-    @Test
-    public void testStorageInternalReader_getFlag() {
-
-        String packageMapFile = mStorageDir + "/maps/mockup.package.map";
-        String flagValueFile = mStorageDir + "/boot/mockup.val";
-
-        StorageInternalReader reader =
-                new StorageInternalReader(
-                        "com.android.aconfig.storage.test_1", packageMapFile, flagValueFile);
-        assertFalse(reader.getBooleanFlagValue(0));
-        assertTrue(reader.getBooleanFlagValue(1));
-    }
-}
\ No newline at end of file
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs
index 5605a41..2a8edf3 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs
+++ b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs
@@ -64,21 +64,67 @@
             get_package_read_context(&package_mapped_file, "com.android.aconfig.storage.test_1")
                 .unwrap()
                 .unwrap();
-        let expected_package_context = PackageReadContext { package_id: 0, boolean_start_index: 0 };
+        let expected_package_context =
+            PackageReadContext { package_id: 0, boolean_start_index: 0, fingerprint: 0 };
         assert_eq!(package_context, expected_package_context);
 
         let package_context =
             get_package_read_context(&package_mapped_file, "com.android.aconfig.storage.test_2")
                 .unwrap()
                 .unwrap();
-        let expected_package_context = PackageReadContext { package_id: 1, boolean_start_index: 3 };
+        let expected_package_context =
+            PackageReadContext { package_id: 1, boolean_start_index: 3, fingerprint: 0 };
         assert_eq!(package_context, expected_package_context);
 
         let package_context =
             get_package_read_context(&package_mapped_file, "com.android.aconfig.storage.test_4")
                 .unwrap()
                 .unwrap();
-        let expected_package_context = PackageReadContext { package_id: 2, boolean_start_index: 6 };
+        let expected_package_context =
+            PackageReadContext { package_id: 2, boolean_start_index: 6, fingerprint: 0 };
+        assert_eq!(package_context, expected_package_context);
+    }
+
+    #[test]
+    fn test_package_context_query_with_fingerprint() {
+        let storage_dir = create_test_storage_files(2);
+        // SAFETY:
+        // The safety here is ensured as the test process will not write to temp storage file
+        let package_mapped_file = unsafe {
+            get_mapped_file(&storage_dir, "mockup", StorageFileType::PackageMap).unwrap()
+        };
+
+        let package_context =
+            get_package_read_context(&package_mapped_file, "com.android.aconfig.storage.test_1")
+                .unwrap()
+                .unwrap();
+        let expected_package_context = PackageReadContext {
+            package_id: 0,
+            boolean_start_index: 0,
+            fingerprint: 15248948510590158086u64,
+        };
+        assert_eq!(package_context, expected_package_context);
+
+        let package_context =
+            get_package_read_context(&package_mapped_file, "com.android.aconfig.storage.test_2")
+                .unwrap()
+                .unwrap();
+        let expected_package_context = PackageReadContext {
+            package_id: 1,
+            boolean_start_index: 3,
+            fingerprint: 4431940502274857964u64,
+        };
+        assert_eq!(package_context, expected_package_context);
+
+        let package_context =
+            get_package_read_context(&package_mapped_file, "com.android.aconfig.storage.test_4")
+                .unwrap()
+                .unwrap();
+        let expected_package_context = PackageReadContext {
+            package_id: 2,
+            boolean_start_index: 6,
+            fingerprint: 16233229917711622375u64,
+        };
         assert_eq!(package_context, expected_package_context);
     }
 
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/unit/srcs/PlatformAconfigPackageInternalTest.java b/tools/aconfig/aconfig_storage_read_api/tests/unit/srcs/PlatformAconfigPackageInternalTest.java
index 392f644..cac3de2 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/unit/srcs/PlatformAconfigPackageInternalTest.java
+++ b/tools/aconfig/aconfig_storage_read_api/tests/unit/srcs/PlatformAconfigPackageInternalTest.java
@@ -18,7 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 
 import android.aconfig.storage.AconfigStorageException;
@@ -46,137 +46,101 @@
 
     @Test
     public void testLoad_container_package() throws Exception {
+        PackageTable packageTable = pr.getPackageTable("mockup");
+
+        PackageTable.Node node1 = packageTable.get("com.android.aconfig.storage.test_1");
+
+        long fingerprint = node1.getPackageFingerprint();
         PlatformAconfigPackageInternal p =
                 PlatformAconfigPackageInternal.load(
-                        "mockup", "com.android.aconfig.storage.test_1", pr);
-        assertNull(p.getException());
+                        "mockup", "com.android.aconfig.storage.test_1", fingerprint, pr);
     }
 
     @Test
     public void testLoad_container_package_error() throws Exception {
+        PackageTable packageTable = pr.getPackageTable("mockup");
+        PackageTable.Node node1 = packageTable.get("com.android.aconfig.storage.test_1");
+        long fingerprint = node1.getPackageFingerprint();
         // cannot find package
-        PlatformAconfigPackageInternal p =
-                PlatformAconfigPackageInternal.load(
-                        "mockup", "com.android.aconfig.storage.test_10", pr);
-
-        assertEquals(
-                AconfigStorageException.ERROR_PACKAGE_NOT_FOUND,
-                ((AconfigStorageException) p.getException()).getErrorCode());
+        AconfigStorageException e =
+                assertThrows(
+                        AconfigStorageException.class,
+                        () ->
+                                PlatformAconfigPackageInternal.load(
+                                        "mockup",
+                                        "com.android.aconfig.storage.test_10",
+                                        fingerprint,
+                                        pr));
+        assertEquals(AconfigStorageException.ERROR_PACKAGE_NOT_FOUND, e.getErrorCode());
 
         // cannot find container
-        p = PlatformAconfigPackageInternal.load(null, "com.android.aconfig.storage.test_1", pr);
-        assertEquals(
-                AconfigStorageException.ERROR_CANNOT_READ_STORAGE_FILE,
-                ((AconfigStorageException) p.getException()).getErrorCode());
-        p = PlatformAconfigPackageInternal.load("test", "com.android.aconfig.storage.test_1", pr);
-        assertEquals(
-                AconfigStorageException.ERROR_CANNOT_READ_STORAGE_FILE,
-                ((AconfigStorageException) p.getException()).getErrorCode());
+        e =
+                assertThrows(
+                        AconfigStorageException.class,
+                        () ->
+                                PlatformAconfigPackageInternal.load(
+                                        null,
+                                        "com.android.aconfig.storage.test_1",
+                                        fingerprint,
+                                        pr));
+        assertEquals(AconfigStorageException.ERROR_CANNOT_READ_STORAGE_FILE, e.getErrorCode());
 
-        // new storage doesn't exist
-        pr = new StorageFileProvider("fake/path/", "fake/path/");
-        p = PlatformAconfigPackageInternal.load("mockup", "com.android.aconfig.storage.test_1", pr);
-        assertEquals(
-                AconfigStorageException.ERROR_CANNOT_READ_STORAGE_FILE,
-                ((AconfigStorageException) p.getException()).getErrorCode());
+        e =
+                assertThrows(
+                        AconfigStorageException.class,
+                        () ->
+                                PlatformAconfigPackageInternal.load(
+                                        "test",
+                                        "com.android.aconfig.storage.test_1",
+                                        fingerprint,
+                                        pr));
+        assertEquals(AconfigStorageException.ERROR_CANNOT_READ_STORAGE_FILE, e.getErrorCode());
 
-        // file read issue
-        pr = new StorageFileProvider(TESTDATA_PATH, "fake/path/");
-        p = PlatformAconfigPackageInternal.load("mockup", "com.android.aconfig.storage.test_1", pr);
-        assertEquals(
-                AconfigStorageException.ERROR_CANNOT_READ_STORAGE_FILE,
-                ((AconfigStorageException) p.getException()).getErrorCode());
-    }
-
-    @Test
-    public void testLoad_container_package_fingerprint() throws Exception {
-        PackageTable packageTable = pr.getPackageTable("mockup");
-
-        PackageTable.Node node1 = packageTable.get("com.android.aconfig.storage.test_1");
-
-        long fingerprint = node1.getPackageFingerprint();
-        PlatformAconfigPackageInternal p =
-                PlatformAconfigPackageInternal.load(
-                        "mockup", "com.android.aconfig.storage.test_1", fingerprint, pr);
-        assertNull(p.getException());
-    }
-
-    @Test
-    public void testLoad_container_package_fingerprint_error() throws Exception {
-
-        PackageTable packageTable = pr.getPackageTable("mockup");
-
-        PackageTable.Node node1 = packageTable.get("com.android.aconfig.storage.test_1");
-
-        long fingerprint = node1.getPackageFingerprint();
-
-        // cannot find package
-        PlatformAconfigPackageInternal p =
-                PlatformAconfigPackageInternal.load(
-                        "mockup", "com.android.aconfig.storage.test_10", fingerprint, pr);
-
-        assertEquals(
-                AconfigStorageException.ERROR_PACKAGE_NOT_FOUND,
-                ((AconfigStorageException) p.getException()).getErrorCode());
-
-        // cannot find container
-        p =
-                PlatformAconfigPackageInternal.load(
-                        null, "com.android.aconfig.storage.test_1", fingerprint, pr);
-        assertEquals(
-                AconfigStorageException.ERROR_CANNOT_READ_STORAGE_FILE,
-                ((AconfigStorageException) p.getException()).getErrorCode());
-        p =
-                PlatformAconfigPackageInternal.load(
-                        "test", "com.android.aconfig.storage.test_1", fingerprint, pr);
-        assertEquals(
-                AconfigStorageException.ERROR_CANNOT_READ_STORAGE_FILE,
-                ((AconfigStorageException) p.getException()).getErrorCode());
         // fingerprint doesn't match
-        p =
-                PlatformAconfigPackageInternal.load(
-                        "mockup", "com.android.aconfig.storage.test_1", fingerprint + 1, pr);
+        e =
+                assertThrows(
+                        AconfigStorageException.class,
+                        () ->
+                                PlatformAconfigPackageInternal.load(
+                                        "mockup",
+                                        "com.android.aconfig.storage.test_1",
+                                        fingerprint + 1,
+                                        pr));
         assertEquals(
                 // AconfigStorageException.ERROR_FILE_FINGERPRINT_MISMATCH,
-                5, ((AconfigStorageException) p.getException()).getErrorCode());
+                5, e.getErrorCode());
 
         // new storage doesn't exist
         pr = new StorageFileProvider("fake/path/", "fake/path/");
-        p =
-                PlatformAconfigPackageInternal.load(
-                        "mockup", "com.android.aconfig.storage.test_1", fingerprint, pr);
-        assertEquals(
-                AconfigStorageException.ERROR_CANNOT_READ_STORAGE_FILE,
-                ((AconfigStorageException) p.getException()).getErrorCode());
+        e =
+                assertThrows(
+                        AconfigStorageException.class,
+                        () ->
+                                PlatformAconfigPackageInternal.load(
+                                        "mockup",
+                                        "com.android.aconfig.storage.test_1",
+                                        fingerprint,
+                                        pr));
+        assertEquals(AconfigStorageException.ERROR_CANNOT_READ_STORAGE_FILE, e.getErrorCode());
 
         // file read issue
         pr = new StorageFileProvider(TESTDATA_PATH, "fake/path/");
-        p =
-                PlatformAconfigPackageInternal.load(
-                        "mockup", "com.android.aconfig.storage.test_1", fingerprint, pr);
-        assertEquals(
-                AconfigStorageException.ERROR_CANNOT_READ_STORAGE_FILE,
-                ((AconfigStorageException) p.getException()).getErrorCode());
-    }
-
-    @Test
-    public void testGetBooleanFlagValue_flagName() throws Exception {
-        PlatformAconfigPackageInternal p =
-                PlatformAconfigPackageInternal.load(
-                        "mockup", "com.android.aconfig.storage.test_1", pr);
-        assertFalse(p.getBooleanFlagValue("disabled_rw", true));
-        assertTrue(p.getBooleanFlagValue("enabled_ro", false));
-        assertTrue(p.getBooleanFlagValue("enabled_rw", false));
-        assertFalse(p.getBooleanFlagValue("fake", false));
+        e =
+                assertThrows(
+                        AconfigStorageException.class,
+                        () ->
+                                PlatformAconfigPackageInternal.load(
+                                        "mockup",
+                                        "com.android.aconfig.storage.test_1",
+                                        fingerprint,
+                                        pr));
+        assertEquals(AconfigStorageException.ERROR_CANNOT_READ_STORAGE_FILE, e.getErrorCode());
     }
 
     @Test
     public void testGetBooleanFlagValue_index() throws Exception {
-
         PackageTable packageTable = pr.getPackageTable("mockup");
-
         PackageTable.Node node1 = packageTable.get("com.android.aconfig.storage.test_1");
-
         long fingerprint = node1.getPackageFingerprint();
         PlatformAconfigPackageInternal p =
                 PlatformAconfigPackageInternal.load(
diff --git a/tools/aconfig/aflags/Android.bp b/tools/aconfig/aflags/Android.bp
index a7aceee..341975d 100644
--- a/tools/aconfig/aflags/Android.bp
+++ b/tools/aconfig/aflags/Android.bp
@@ -31,6 +31,9 @@
     name: "aflags",
     host_supported: true,
     defaults: ["aflags.defaults"],
+    apex_available: [
+        "//apex_available:platform",
+    ],
 }
 
 rust_test_host {
diff --git a/tools/aconfig/aflags/src/aconfig_storage_source.rs b/tools/aconfig/aflags/src/aconfig_storage_source.rs
index 3f593fe..766807a 100644
--- a/tools/aconfig/aflags/src/aconfig_storage_source.rs
+++ b/tools/aconfig/aflags/src/aconfig_storage_source.rs
@@ -17,6 +17,23 @@
 
 pub struct AconfigStorageSource {}
 
+static ACONFIGD_SYSTEM_SOCKET_NAME: &str = "/dev/socket/aconfigd_system";
+static ACONFIGD_MAINLINE_SOCKET_NAME: &str = "/dev/socket/aconfigd_mainline";
+
+enum AconfigdSocket {
+    System,
+    Mainline,
+}
+
+impl AconfigdSocket {
+    pub fn name(&self) -> &str {
+        match self {
+            AconfigdSocket::System => ACONFIGD_SYSTEM_SOCKET_NAME,
+            AconfigdSocket::Mainline => ACONFIGD_MAINLINE_SOCKET_NAME,
+        }
+    }
+}
+
 fn load_flag_to_container() -> Result<HashMap<String, String>> {
     Ok(load_protos::load()?.into_iter().map(|p| (p.qualified_name(), p.container)).collect())
 }
@@ -81,7 +98,7 @@
     })
 }
 
-fn read_from_socket() -> Result<Vec<ProtoFlagQueryReturnMessage>> {
+fn read_from_socket(socket: AconfigdSocket) -> Result<Vec<ProtoFlagQueryReturnMessage>> {
     let messages = ProtoStorageRequestMessages {
         msgs: vec![ProtoStorageRequestMessage {
             msg: Some(ProtoStorageRequestMessageMsg::ListStorageMessage(ProtoListStorageMessage {
@@ -93,12 +110,7 @@
         special_fields: SpecialFields::new(),
     };
 
-    let socket_name = if aconfig_flags::auto_generated::enable_system_aconfigd_rust() {
-        "/dev/socket/aconfigd_system"
-    } else {
-        "/dev/socket/aconfigd"
-    };
-    let mut socket = UnixStream::connect(socket_name)?;
+    let mut socket = UnixStream::connect(socket.name())?;
 
     let message_buffer = messages.write_to_bytes()?;
     let mut message_length_buffer: [u8; 4] = [0; 4];
@@ -132,14 +144,20 @@
 impl FlagSource for AconfigStorageSource {
     fn list_flags() -> Result<Vec<Flag>> {
         let containers = load_flag_to_container()?;
-        read_from_socket()
-            .map(|query_messages| {
-                query_messages
-                    .iter()
-                    .map(|message| convert(message.clone(), &containers))
-                    .collect::<Vec<_>>()
-            })?
+        let system_messages = read_from_socket(AconfigdSocket::System);
+        let mainline_messages = read_from_socket(AconfigdSocket::Mainline);
+
+        let mut all_messages = vec![];
+        if let Ok(system_messages) = system_messages {
+            all_messages.extend_from_slice(&system_messages);
+        }
+        if let Ok(mainline_messages) = mainline_messages {
+            all_messages.extend_from_slice(&mainline_messages);
+        }
+
+        all_messages
             .into_iter()
+            .map(|query_message| convert(query_message.clone(), &containers))
             .collect()
     }
 
diff --git a/tools/aconfig/aflags/src/main.rs b/tools/aconfig/aflags/src/main.rs
index 8173bc2..e4af2a7 100644
--- a/tools/aconfig/aflags/src/main.rs
+++ b/tools/aconfig/aflags/src/main.rs
@@ -16,6 +16,9 @@
 
 //! `aflags` is a device binary to read and write aconfig flags.
 
+use std::env;
+use std::process::{Command as OsCommand, Stdio};
+
 use anyhow::{anyhow, ensure, Result};
 use clap::Parser;
 
@@ -298,7 +301,34 @@
     }
 }
 
+fn invoke_updatable_aflags() {
+    let updatable_command = "/apex/com.android.configinfrastructure/bin/aflags_updatable";
+
+    let args: Vec<String> = env::args().collect();
+    let command_args = if args.len() >= 2 { &args[1..] } else { &["--help".to_string()] };
+
+    let mut child = OsCommand::new(updatable_command);
+    for arg in command_args {
+        child.arg(arg);
+    }
+
+    let output = child
+        .stdin(Stdio::piped())
+        .stdout(Stdio::piped())
+        .spawn()
+        .expect("failed to execute child")
+        .wait_with_output()
+        .expect("failed to execute command");
+
+    println!("{}", String::from_utf8_lossy(&output.stdout).trim());
+}
+
 fn main() -> Result<()> {
+    if aconfig_flags::auto_generated::invoke_updatable_aflags() {
+        invoke_updatable_aflags();
+        return Ok(());
+    }
+
     ensure!(nix::unistd::Uid::current().is_root(), "must be root");
 
     let cli = Cli::parse();
diff --git a/tools/aconfig/fake_device_config/Android.bp b/tools/aconfig/fake_device_config/Android.bp
index 4cd5070..1c5b7c5 100644
--- a/tools/aconfig/fake_device_config/Android.bp
+++ b/tools/aconfig/fake_device_config/Android.bp
@@ -42,3 +42,14 @@
     host_supported: true,
     is_stubs_module: true,
 }
+
+java_library {
+    name: "aconfig_storage_stub_none",
+    srcs: [
+        "src/android/os/flagging/**/*.java",
+    ],
+    sdk_version: "none",
+    system_modules: "core-all-system-modules",
+    host_supported: true,
+    is_stubs_module: true,
+}
diff --git a/tools/aconfig/fake_device_config/src/android/os/Build.java b/tools/aconfig/fake_device_config/src/android/os/Build.java
new file mode 100644
index 0000000..8ec72fb
--- /dev/null
+++ b/tools/aconfig/fake_device_config/src/android/os/Build.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+public class Build {
+    public static class VERSION {
+        public static final int SDK_INT = 0;
+    }
+}
diff --git a/tools/aconfig/fake_device_config/src/android/os/flagging/AconfigPackage.java b/tools/aconfig/fake_device_config/src/android/os/flagging/AconfigPackage.java
new file mode 100644
index 0000000..3cac516
--- /dev/null
+++ b/tools/aconfig/fake_device_config/src/android/os/flagging/AconfigPackage.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.flagging;
+
+/*
+ * This class allows generated aconfig code to compile independently of the framework.
+ */
+public class AconfigPackage {
+    public static AconfigPackage load(String packageName) {
+        throw new UnsupportedOperationException("Stub!");
+    }
+
+    public boolean getBooleanFlagValue(String flagName, boolean defaultValue) {
+        throw new UnsupportedOperationException("Stub!");
+    }
+}
diff --git a/tools/aconfig/fake_device_config/src/android/os/flagging/AconfigPackageInternal.java b/tools/aconfig/fake_device_config/src/android/os/flagging/AconfigPackageInternal.java
new file mode 100644
index 0000000..d084048
--- /dev/null
+++ b/tools/aconfig/fake_device_config/src/android/os/flagging/AconfigPackageInternal.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.flagging;
+
+/*
+ * This class allows generated aconfig code to compile independently of the framework.
+ */
+public class AconfigPackageInternal {
+
+    public static AconfigPackageInternal load(
+            String container, String packageName, long packageFingerprint) {
+        throw new UnsupportedOperationException("Stub!");
+    }
+
+    public boolean getBooleanFlagValue(int index) {
+        throw new UnsupportedOperationException("Stub!");
+    }
+}
diff --git a/tools/aconfig/fake_device_config/src/android/os/flagging/AconfigStorageReadException.java b/tools/aconfig/fake_device_config/src/android/os/flagging/AconfigStorageReadException.java
deleted file mode 100644
index bfec98c..0000000
--- a/tools/aconfig/fake_device_config/src/android/os/flagging/AconfigStorageReadException.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.os.flagging;
-
-public class AconfigStorageReadException extends RuntimeException {
-
-    /** Generic error code indicating an unspecified Aconfig Storage error. */
-    public static final int ERROR_GENERIC = 0;
-
-    /** Error code indicating that the Aconfig Storage system is not found on the device. */
-    public static final int ERROR_STORAGE_SYSTEM_NOT_FOUND = 1;
-
-    /** Error code indicating that the requested configuration package is not found. */
-    public static final int ERROR_PACKAGE_NOT_FOUND = 2;
-
-    /** Error code indicating that the specified container is not found. */
-    public static final int ERROR_CONTAINER_NOT_FOUND = 3;
-
-    /** Error code indicating that there was an error reading the Aconfig Storage file. */
-    public static final int ERROR_CANNOT_READ_STORAGE_FILE = 4;
-
-    public static final int ERROR_FILE_FINGERPRINT_MISMATCH = 5;
-
-    public AconfigStorageReadException(int errorCode, String msg) {
-        super(msg);
-        throw new UnsupportedOperationException("Stub!");
-    }
-
-    public AconfigStorageReadException(int errorCode, String msg, Throwable cause) {
-        super(msg, cause);
-        throw new UnsupportedOperationException("Stub!");
-    }
-
-    public AconfigStorageReadException(int errorCode, Throwable cause) {
-        super(cause);
-        throw new UnsupportedOperationException("Stub!");
-    }
-
-    public int getErrorCode() {
-        throw new UnsupportedOperationException("Stub!");
-    }
-
-    @Override
-    public String getMessage() {
-        throw new UnsupportedOperationException("Stub!");
-    }
-}
diff --git a/tools/aconfig/fake_device_config/src/android/os/flagging/PlatformAconfigPackage.java b/tools/aconfig/fake_device_config/src/android/os/flagging/PlatformAconfigPackage.java
new file mode 100644
index 0000000..ec79f7d
--- /dev/null
+++ b/tools/aconfig/fake_device_config/src/android/os/flagging/PlatformAconfigPackage.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.flagging;
+
+import java.util.Set;
+
+/*
+ * This class allows generated aconfig code to compile independently of the framework.
+ */
+public class PlatformAconfigPackage {
+
+    public static final Set<String> PLATFORM_PACKAGE_MAP_FILES =
+            Set.of("system.package.map", "vendor.package.map", "product.package.map");
+
+    public static PlatformAconfigPackage load(String packageName) {
+        throw new UnsupportedOperationException("Stub!");
+    }
+
+    public boolean getBooleanFlagValue(String flagName, boolean defaultValue) {
+        throw new UnsupportedOperationException("Stub!");
+    }
+}
diff --git a/tools/aconfig/fake_device_config/src/android/os/flagging/PlatformAconfigPackageInternal.java b/tools/aconfig/fake_device_config/src/android/os/flagging/PlatformAconfigPackageInternal.java
new file mode 100644
index 0000000..283b251
--- /dev/null
+++ b/tools/aconfig/fake_device_config/src/android/os/flagging/PlatformAconfigPackageInternal.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.flagging;
+
+/*
+ * This class allows generated aconfig code to compile independently of the framework.
+ */
+public class PlatformAconfigPackageInternal {
+
+    public static PlatformAconfigPackageInternal load(
+            String container, String packageName, long packageFingerprint) {
+        throw new UnsupportedOperationException("Stub!");
+    }
+
+    public boolean getBooleanFlagValue(int index) {
+        throw new UnsupportedOperationException("Stub!");
+    }
+}
diff --git a/tools/aconfig/fake_device_config/src/android/provider/AconfigPackage.java b/tools/aconfig/fake_device_config/src/android/provider/AconfigPackage.java
deleted file mode 100644
index 2f01b8c..0000000
--- a/tools/aconfig/fake_device_config/src/android/provider/AconfigPackage.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.provider;
-
-/*
- * This class allows generated aconfig code to compile independently of the framework.
- */
-public class AconfigPackage {
-
-    /** Flag value is true */
-    public static final int FLAG_BOOLEAN_VALUE_TRUE = 1;
-
-    /** Flag value is false */
-    public static final int FLAG_BOOLEAN_VALUE_FALSE = 0;
-
-    /** Flag value doesn't exist */
-    public static final int FLAG_BOOLEAN_VALUE_NOT_EXIST = 2;
-
-    public static int getBooleanFlagValue(String packageName, String flagName) {
-        return 0;
-    }
-
-    public AconfigPackage(String packageName) {}
-
-    public int getBooleanFlagValue(String flagName) {
-        return 0;
-    }
-}
\ No newline at end of file
diff --git a/tools/aconfig/printflags/Android.bp b/tools/aconfig/printflags/Android.bp
index d50a77d..6f7bca3 100644
--- a/tools/aconfig/printflags/Android.bp
+++ b/tools/aconfig/printflags/Android.bp
@@ -19,6 +19,9 @@
 rust_binary {
     name: "printflags",
     defaults: ["printflags.defaults"],
+    apex_available: [
+        "//apex_available:platform",
+    ],
 }
 
 rust_test_host {
diff --git a/tools/check_elf_file.py b/tools/check_elf_file.py
index 1fd7950..0640041 100755
--- a/tools/check_elf_file.py
+++ b/tools/check_elf_file.py
@@ -42,8 +42,9 @@
 _EM_X86_64 = 62
 _EM_AARCH64 = 183
 
-_KNOWN_MACHINES = {_EM_386, _EM_ARM, _EM_X86_64, _EM_AARCH64}
-
+_32_BIT_MACHINES = {_EM_386, _EM_ARM}
+_64_BIT_MACHINES = {_EM_X86_64, _EM_AARCH64}
+_KNOWN_MACHINES = _32_BIT_MACHINES | _64_BIT_MACHINES
 
 # ELF header struct
 _ELF_HEADER_STRUCT = (
@@ -483,6 +484,11 @@
       sys.exit(2)
 
   def check_max_page_size(self, max_page_size):
+    if self._file_under_test.header.e_machine in _32_BIT_MACHINES:
+      # Skip test on 32-bit machines. 16 KB pages is an arm64 feature
+      # and no 32-bit systems in Android use it.
+      return
+
     for alignment in self._file_under_test.alignments:
       if alignment % max_page_size != 0:
         self._error(f'Load segment has alignment {alignment} but '
diff --git a/tools/edit_monitor/daemon_manager.py b/tools/edit_monitor/daemon_manager.py
index 22782f7..7d666fe 100644
--- a/tools/edit_monitor/daemon_manager.py
+++ b/tools/edit_monitor/daemon_manager.py
@@ -13,6 +13,8 @@
 # limitations under the License.
 
 
+import errno
+import fcntl
 import getpass
 import hashlib
 import logging
@@ -87,7 +89,7 @@
         "edit_monitor",
         self.user_name,
         "ENABLE_ANDROID_EDIT_MONITOR",
-        50,
+        100,
     ):
       logging.warning("Edit monitor is disabled, exiting...")
       return
@@ -100,16 +102,32 @@
       logging.warning("Edit monitor for cog is not supported, exiting...")
       return
 
-    try:
-      self._stop_any_existing_instance()
-      self._write_pid_to_pidfile()
-      self._start_daemon_process()
-    except Exception as e:
-      logging.exception("Failed to start daemon manager with error %s", e)
-      self._send_error_event_to_clearcut(
-          edit_event_pb2.EditEvent.FAILED_TO_START_EDIT_MONITOR
-      )
-      raise e
+    setup_lock_file = pathlib.Path(tempfile.gettempdir()).joinpath(
+        self.pid_file_path.name + ".setup"
+    )
+    logging.info("setup lock file: %s", setup_lock_file)
+    with open(setup_lock_file, "w") as f:
+      try:
+        # Acquire an exclusive lock
+        fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
+        self._stop_any_existing_instance()
+        self._write_pid_to_pidfile()
+        self._start_daemon_process()
+      except Exception as e:
+        if (
+            isinstance(e, IOError) and e.errno == errno.EAGAIN
+        ):  # Failed to acquire the file lock.
+          logging.warning("Another edit monitor is starting, exitinng...")
+          return
+        else:
+          logging.exception("Failed to start daemon manager with error %s", e)
+          self._send_error_event_to_clearcut(
+              edit_event_pb2.EditEvent.FAILED_TO_START_EDIT_MONITOR
+          )
+          raise e
+      finally:
+        # Release the lock
+        fcntl.flock(f, fcntl.LOCK_UN)
 
   def monitor_daemon(
       self,
@@ -149,15 +167,15 @@
             edit_event_pb2.EditEvent.KILLED_DUE_TO_EXCEEDED_MEMORY_USAGE
         )
         logging.error(
-            "Daemon process is consuming too much memory, rebooting...")
+            "Daemon process is consuming too much memory, rebooting..."
+        )
         self.reboot()
 
       if self.max_cpu_usage >= cpu_threshold:
         self._send_error_event_to_clearcut(
             edit_event_pb2.EditEvent.KILLED_DUE_TO_EXCEEDED_CPU_USAGE
         )
-        logging.error(
-            "Daemon process is consuming too much cpu, killing...")
+        logging.error("Daemon process is consuming too much cpu, killing...")
         self._terminate_process(self.daemon_process.pid)
 
     logging.info(
@@ -179,7 +197,7 @@
         self._wait_for_process_terminate(self.daemon_process.pid, 1)
         if self.daemon_process.is_alive():
           self._terminate_process(self.daemon_process.pid)
-      self._remove_pidfile()
+      self._remove_pidfile(self.pid)
       logging.info("Successfully stopped daemon manager.")
     except Exception as e:
       logging.exception("Failed to stop daemon manager with error %s", e)
@@ -253,11 +271,15 @@
     if ex_pid:
       logging.info("Found another instance with pid %d.", ex_pid)
       self._terminate_process(ex_pid)
-      self._remove_pidfile()
+      self._remove_pidfile(ex_pid)
 
-  def _read_pid_from_pidfile(self):
-    with open(self.pid_file_path, "r") as f:
-      return int(f.read().strip())
+  def _read_pid_from_pidfile(self) -> int | None:
+    try:
+      with open(self.pid_file_path, "r") as f:
+        return int(f.read().strip())
+    except FileNotFoundError as e:
+      logging.warning("pidfile %s does not exist.", self.pid_file_path)
+      return None
 
   def _write_pid_to_pidfile(self):
     """Creates a pidfile and writes the current pid to the file.
@@ -333,7 +355,23 @@
       )
       return True
 
-  def _remove_pidfile(self):
+  def _remove_pidfile(self, expected_pid: int):
+    recorded_pid = self._read_pid_from_pidfile()
+
+    if recorded_pid is None:
+      logging.info("pid file %s already removed.", self.pid_file_path)
+      return
+
+    if recorded_pid != expected_pid:
+      logging.warning(
+          "pid file contains pid from a different process, expected pid: %d,"
+          " actual pid: %d.",
+          expected_pid,
+          recorded_pid,
+      )
+      return
+
+    logging.debug("removing pidfile written by process %s", expected_pid)
     try:
       os.remove(self.pid_file_path)
     except FileNotFoundError:
@@ -378,9 +416,7 @@
       uptime_end = float(f.readline().split()[0])
 
     return (
-        (total_end_time - total_start_time)
-        / (uptime_end - uptime_start)
-        * 100
+        (total_end_time - total_start_time) / (uptime_end - uptime_start) * 100
     )
 
   def _get_total_cpu_time(self, pid: int) -> float:
@@ -395,13 +431,19 @@
   def _find_all_instances_pids(self) -> list[int]:
     pids = []
 
-    for file in os.listdir(self.pid_file_path.parent):
-      if file.endswith(".lock"):
-        try:
-          with open(self.pid_file_path.parent.joinpath(file), "r") as f:
-            pids.append(int(f.read().strip()))
-        except (FileNotFoundError, IOError, ValueError, TypeError):
-          logging.exception("Failed to get pid from file path: %s", file)
+    try:
+      output = subprocess.check_output(["ps", "-ef", "--no-headers"], text=True)
+      for line in output.splitlines():
+        parts = line.split()
+        process_path = parts[7]
+        if pathlib.Path(process_path).name == "edit_monitor":
+          pid = int(parts[1])
+          if pid != self.pid:  # exclude the current process
+            pids.append(pid)
+    except Exception:
+      logging.exception(
+          "Failed to get pids of existing edit monitors from ps command."
+      )
 
     return pids
 
diff --git a/tools/edit_monitor/daemon_manager_test.py b/tools/edit_monitor/daemon_manager_test.py
index 350739d..be28965 100644
--- a/tools/edit_monitor/daemon_manager_test.py
+++ b/tools/edit_monitor/daemon_manager_test.py
@@ -14,6 +14,7 @@
 
 """Unittests for DaemonManager."""
 
+import fcntl
 import logging
 import multiprocessing
 import os
@@ -82,7 +83,8 @@
     # tests will be cleaned.
     tempfile.tempdir = self.working_dir.name
     self.patch = mock.patch.dict(
-        os.environ, {'ENABLE_ANDROID_EDIT_MONITOR': 'true'})
+        os.environ, {'ENABLE_ANDROID_EDIT_MONITOR': 'true'}
+    )
     self.patch.start()
 
   def tearDown(self):
@@ -102,6 +104,7 @@
     p = self._create_fake_deamon_process()
 
     self.assert_run_simple_daemon_success()
+    self.assert_no_subprocess_running()
 
   def test_start_success_with_existing_instance_already_dead(self):
     # Create a pidfile with pid that does not exist.
@@ -137,7 +140,9 @@
     # Verify no daemon process is started.
     self.assertIsNone(dm.daemon_process)
 
-  @mock.patch.dict(os.environ, {'ENABLE_ANDROID_EDIT_MONITOR': 'false'}, clear=True)
+  @mock.patch.dict(
+      os.environ, {'ENABLE_ANDROID_EDIT_MONITOR': 'false'}, clear=True
+  )
   def test_start_return_directly_if_disabled(self):
     dm = daemon_manager.DaemonManager(TEST_BINARY_FILE)
     dm.start()
@@ -154,6 +159,25 @@
     # Verify no daemon process is started.
     self.assertIsNone(dm.daemon_process)
 
+  def test_start_failed_other_instance_is_starting(self):
+    f = open(
+        pathlib.Path(self.working_dir.name).joinpath(
+            TEST_PID_FILE_PATH + '.setup'
+        ),
+        'w',
+    )
+    # Acquire an exclusive lock
+    fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
+
+    dm = daemon_manager.DaemonManager(TEST_BINARY_FILE)
+    dm.start()
+
+    # Release the lock
+    fcntl.flock(f, fcntl.LOCK_UN)
+    f.close()
+    # Verify no daemon process is started.
+    self.assertIsNone(dm.daemon_process)
+
   @mock.patch('os.kill')
   def test_start_failed_to_kill_existing_instance(self, mock_kill):
     mock_kill.side_effect = OSError('Unknown OSError')
@@ -177,6 +201,7 @@
         'edit_monitor'
     )
     pid_file_path_dir.mkdir(parents=True, exist_ok=True)
+
     # Makes the directory read-only so write pidfile will fail.
     os.chmod(pid_file_path_dir, 0o555)
 
@@ -216,7 +241,7 @@
         cclient=fake_cclient,
     )
     # set the fake total_memory_size
-    dm.total_memory_size = 100 * 1024 *1024
+    dm.total_memory_size = 100 * 1024 * 1024
     dm.start()
     dm.monitor_daemon(interval=1)
 
@@ -367,6 +392,26 @@
         fake_cclient, edit_event_pb2.EditEvent.FAILED_TO_REBOOT_EDIT_MONITOR
     )
 
+  @mock.patch('subprocess.check_output')
+  def test_cleanup_success(self, mock_check_output):
+    p = self._create_fake_deamon_process()
+    fake_cclient = FakeClearcutClient()
+    mock_check_output.return_value = f'user {p.pid} 1 1 1 1 1 edit_monitor arg'
+
+    dm = daemon_manager.DaemonManager(
+        TEST_BINARY_FILE,
+        daemon_target=long_running_daemon,
+        cclient=fake_cclient,
+    )
+    dm.cleanup()
+
+    self.assertFalse(p.is_alive())
+    self.assertTrue(
+        pathlib.Path(self.working_dir.name)
+        .joinpath(daemon_manager.BLOCK_SIGN_FILE)
+        .exists()
+    )
+
   def assert_run_simple_daemon_success(self):
     damone_output_file = tempfile.NamedTemporaryFile(
         dir=self.working_dir.name, delete=False
@@ -432,7 +477,7 @@
         pass
 
   def _create_fake_deamon_process(
-      self, name: str = ''
+      self, name: str = TEST_PID_FILE_PATH
   ) -> multiprocessing.Process:
     # Create a long running subprocess
     p = multiprocessing.Process(target=long_running_daemon)
@@ -443,7 +488,7 @@
         'edit_monitor'
     )
     pid_file_path_dir.mkdir(parents=True, exist_ok=True)
-    with open(pid_file_path_dir.joinpath(name + 'pid.lock'), 'w') as f:
+    with open(pid_file_path_dir.joinpath(name), 'w') as f:
       f.write(str(p.pid))
     return p
 
diff --git a/tools/edit_monitor/edit_monitor_integration_test.py b/tools/edit_monitor/edit_monitor_integration_test.py
index 3d28274..f39b936 100644
--- a/tools/edit_monitor/edit_monitor_integration_test.py
+++ b/tools/edit_monitor/edit_monitor_integration_test.py
@@ -15,6 +15,7 @@
 """Integration tests for Edit Monitor."""
 
 import glob
+from importlib import resources
 import logging
 import os
 import pathlib
@@ -25,8 +26,6 @@
 import tempfile
 import time
 import unittest
-
-from importlib import resources
 from unittest import mock
 
 
@@ -49,7 +48,8 @@
     self.root_monitoring_path.mkdir()
     self.edit_monitor_binary_path = self._import_executable("edit_monitor")
     self.patch = mock.patch.dict(
-        os.environ, {'ENABLE_ANDROID_EDIT_MONITOR': 'true'})
+        os.environ, {"ENABLE_ANDROID_EDIT_MONITOR": "true"}
+    )
     self.patch.start()
 
   def tearDown(self):
@@ -83,7 +83,21 @@
 
     self.assertEqual(self._get_logged_events_num(), 4)
 
-  def _start_edit_monitor_process(self):
+  def test_start_multiple_edit_monitor_only_one_started(self):
+    p1 = self._start_edit_monitor_process(wait_for_observer_start=False)
+    p2 = self._start_edit_monitor_process(wait_for_observer_start=False)
+    p3 = self._start_edit_monitor_process(wait_for_observer_start=False)
+
+    live_processes = self._get_live_processes([p1, p2, p3])
+
+    # Cleanup all live processes.
+    for p in live_processes:
+      os.kill(p.pid, signal.SIGINT)
+      p.communicate()
+
+    self.assertEqual(len(live_processes), 1)
+
+  def _start_edit_monitor_process(self, wait_for_observer_start=True):
     command = f"""
     export TMPDIR="{self.working_dir.name}"
     {self.edit_monitor_binary_path} --path={self.root_monitoring_path} --dry_run"""
@@ -94,7 +108,9 @@
         start_new_session=True,
         executable="/bin/bash",
     )
-    self._wait_for_observer_start(time_out=5)
+    if wait_for_observer_start:
+      self._wait_for_observer_start(time_out=5)
+
     return p
 
   def _wait_for_observer_start(self, time_out):
@@ -125,6 +141,18 @@
 
     return 0
 
+  def _get_live_processes(self, processes):
+    live_processes = []
+    for p in processes:
+      try:
+        p.wait(timeout=5)
+      except subprocess.TimeoutExpired as e:
+        live_processes.append(p)
+        logging.info("process: %d still alive.", p.pid)
+      else:
+        logging.info("process: %d stopped.", p.pid)
+    return live_processes
+
   def _import_executable(self, executable_name: str) -> pathlib.Path:
     binary_dir = pathlib.Path(self.working_dir.name).joinpath("binary")
     binary_dir.mkdir()
diff --git a/tools/edit_monitor/main.py b/tools/edit_monitor/main.py
index 49385f1..3c2d183 100644
--- a/tools/edit_monitor/main.py
+++ b/tools/edit_monitor/main.py
@@ -72,7 +72,8 @@
   root_logging_dir = tempfile.mkdtemp(prefix='edit_monitor_')
   _, log_path = tempfile.mkstemp(dir=root_logging_dir, suffix='.log')
 
-  log_fmt = '%(asctime)s %(filename)s:%(lineno)s:%(levelname)s: %(message)s'
+
+  log_fmt = '%(asctime)s.%(msecs)03d %(filename)s:%(lineno)s:%(levelname)s: %(message)s'
   date_fmt = '%Y-%m-%d %H:%M:%S'
   log_level = logging.DEBUG if verbose else logging.INFO
 
@@ -101,12 +102,12 @@
       daemon_args=(args.path, args.dry_run),
   )
 
-  if args.force_cleanup:
-    dm.cleanup()
-
   try:
-    dm.start()
-    dm.monitor_daemon()
+    if args.force_cleanup:
+      dm.cleanup()
+    else:
+      dm.start()
+      dm.monitor_daemon()
   except Exception:
     logging.exception('Unexpected exception raised when run daemon.')
   finally:
diff --git a/tools/event_log_tags.py b/tools/event_log_tags.py
index a6ae9f1..e859b6b 100644
--- a/tools/event_log_tags.py
+++ b/tools/event_log_tags.py
@@ -14,21 +14,21 @@
 
 """A module for reading and parsing event-log-tags files."""
 
+import dataclasses
 import re
 import sys
+from typing import Optional
 
-class Tag(object):
-  __slots__ = ["tagnum", "tagname", "description", "filename", "linenum"]
-
-  def __init__(self, tagnum, tagname, description, filename, linenum):
-    self.tagnum = tagnum
-    self.tagname = tagname
-    self.description = description
-    self.filename = filename
-    self.linenum = linenum
+@dataclasses.dataclass
+class Tag:
+  tagnum: int
+  tagname: str
+  description: Optional[str]
+  filename: str
+  linenum: int
 
 
-class TagFile(object):
+class TagFile:
   """Read an input event-log-tags file."""
   def AddError(self, msg, linenum=None):
     if linenum is None:
@@ -76,14 +76,11 @@
           self.options[parts[1]] = parts[2:]
           continue
 
-        if parts[0] == "?":
-          tag = None
-        else:
-          try:
-            tag = int(parts[0])
-          except ValueError:
-            self.AddError("\"%s\" isn't an integer tag or '?'" % (parts[0],))
-            continue
+        try:
+          tag = int(parts[0])
+        except ValueError:
+          self.AddError("\"%s\" isn't an integer tag" % (parts[0],))
+          continue
 
         tagname = parts[1]
         if len(parts) == 3:
@@ -128,8 +125,8 @@
       out = sys.stdout
       output_file = "<stdout>"
     else:
-      out = open(output_file, "wb")
-    out.write(str.encode(data))
+      out = open(output_file, "w")
+    out.write(data)
     out.close()
   except (IOError, OSError) as e:
     print("failed to write %s: %s" % (output_file, e), file=sys.stderr)
diff --git a/tools/finalization/build-step-0-and-m.sh b/tools/finalization/build-step-0-and-m.sh
new file mode 100755
index 0000000..4843800
--- /dev/null
+++ b/tools/finalization/build-step-0-and-m.sh
@@ -0,0 +1,20 @@
+
+#!/bin/bash
+# Copyright 2024 Google Inc. All rights reserved.
+set -ex
+function help() {
+    echo "Finalize VINTF and build a target for test."
+    echo "usage: $(basename "$0") target [goals...]"
+}
+function finalize_main_step0_and_m() {
+    if [ $# == 0 ] ; then
+        help
+        exit 1
+    fi;
+    local top="$(dirname "$0")"/../../../..
+    source $top/build/make/tools/finalization/build-step-0.sh
+    local m="$top/build/soong/soong_ui.bash --make-mode TARGET_PRODUCT=$1 TARGET_RELEASE=fina_0 TARGET_BUILD_VARIANT=userdebug"
+    # This command tests the release state for AIDL.
+    AIDL_FROZEN_REL=true $m ${@:2}
+}
+finalize_main_step0_and_m $@
diff --git a/tools/finalization/build-step-0.sh b/tools/finalization/build-step-0.sh
index f81b720..8826b35 100755
--- a/tools/finalization/build-step-0.sh
+++ b/tools/finalization/build-step-0.sh
@@ -7,11 +7,26 @@
     local top="$(dirname "$0")"/../../../..
     source $top/build/make/tools/finalization/environment.sh
 
+    local need_vintf_finalize=false
     if [ "$FINAL_STATE" = "unfinalized" ] ; then
-        # VINTF finalization
+        need_vintf_finalize=true
+    else
+        # build-step-0.sh tests the vintf finalization step (step-0) when the
+        # FINAL_BOARD_API_LEVEL is the same as the RELEASE_BOARD_API_LEVEL; and
+        # RELEASE_BOARD_API_LEVEL_FROZEN is not true from the fina_0 configuration.
+        # The FINAL_BOARD_API_LEVEL must be the next vendor API level to be finalized.
+        local board_api_level_vars=$(TARGET_RELEASE=fina_0 $top/build/soong/soong_ui.bash --dumpvars-mode -vars "RELEASE_BOARD_API_LEVEL_FROZEN RELEASE_BOARD_API_LEVEL")
+        local target_board_api_level_vars="RELEASE_BOARD_API_LEVEL_FROZEN=''
+RELEASE_BOARD_API_LEVEL='$FINAL_BOARD_API_LEVEL'"
+        if [ "$board_api_level_vars" = "$target_board_api_level_vars" ] ; then
+            echo The target is a finalization candidate.
+            need_vintf_finalize=true
+        fi;
+    fi;
+
+    if [ "$need_vintf_finalize" = true ] ; then        # VINTF finalization
         source $top/build/make/tools/finalization/finalize-vintf-resources.sh
     fi;
 }
 
 finalize_main_step0
-
diff --git a/tools/finalization/command-line-options.sh b/tools/finalization/command-line-options.sh
index d9397c2..3a1e049 100644
--- a/tools/finalization/command-line-options.sh
+++ b/tools/finalization/command-line-options.sh
@@ -3,6 +3,7 @@
 while true; do
     case "$1" in
         --dry-run) repo_upload_dry_run_arg="--dry-run"; repo_branch="finalization-dry-run"; shift ;;
+        --) shift; break;;
         *) break
     esac
 done
diff --git a/tools/finalization/environment.sh b/tools/finalization/environment.sh
index f68a68b..0d3a9e1 100755
--- a/tools/finalization/environment.sh
+++ b/tools/finalization/environment.sh
@@ -29,4 +29,8 @@
 # FINAL versions for VINTF
 # TODO(b/323985297): The version must match with that from the release configuration.
 # Instead of hardcoding the version here, read it from a release configuration.
-export FINAL_BOARD_API_LEVEL='202404'
+export FINAL_BOARD_API_LEVEL='202504'
+export FINAL_CORRESPONDING_VERSION_LETTER='B'
+export FINAL_CORRESPONDING_PLATFORM_VERSION='16'
+export FINAL_NEXT_BOARD_API_LEVEL='202604'
+export FINAL_NEXT_CORRESPONDING_VERSION_LETTER='C'
diff --git a/tools/finalization/finalize-vintf-resources.sh b/tools/finalization/finalize-vintf-resources.sh
index d532b25..6f1a6f6 100755
--- a/tools/finalization/finalize-vintf-resources.sh
+++ b/tools/finalization/finalize-vintf-resources.sh
@@ -33,18 +33,18 @@
 function create_new_compat_matrix_and_kernel_configs() {
     # The compatibility matrix versions are bumped during vFRC
     # These will change every time we have a new vFRC
-    local CURRENT_COMPATIBILITY_MATRIX_LEVEL='202404'
-    local NEXT_COMPATIBILITY_MATRIX_LEVEL='202504'
+    local CURRENT_COMPATIBILITY_MATRIX_LEVEL="$FINAL_BOARD_API_LEVEL"
+    local NEXT_COMPATIBILITY_MATRIX_LEVEL="$FINAL_NEXT_BOARD_API_LEVEL"
     # The kernel configs need the letter of the Android release
-    local CURRENT_RELEASE_LETTER='v'
-    local NEXT_RELEASE_LETTER='w'
+    local CURRENT_RELEASE_LETTER="$FINAL_CORRESPONDING_VERSION_LETTER"
+    local NEXT_RELEASE_LETTER="$FINAL_NEXT_CORRESPONDING_VERSION_LETTER"
 
 
     # build the targets required before touching the Android.bp/Android.mk files
     local build_cmd="$top/build/soong/soong_ui.bash --make-mode"
     $build_cmd bpmodify
 
-    "$top/prebuilts/build-tools/path/linux-x86/python3" "$top/hardware/interfaces/compatibility_matrices/bump.py" "$CURRENT_COMPATIBILITY_MATRIX_LEVEL" "$NEXT_COMPATIBILITY_MATRIX_LEVEL" "$CURRENT_RELEASE_LETTER" "$NEXT_RELEASE_LETTER"
+    "$top/prebuilts/build-tools/path/linux-x86/python3" "$top/hardware/interfaces/compatibility_matrices/bump.py" "$CURRENT_COMPATIBILITY_MATRIX_LEVEL" "$NEXT_COMPATIBILITY_MATRIX_LEVEL" "$CURRENT_RELEASE_LETTER" "$NEXT_RELEASE_LETTER" "$FINAL_CORRESPONDING_PLATFORM_VERSION"
 
     # Freeze the current framework manifest file. This relies on the
     # aosp_cf_x86_64-trunk_staging build target to get the right manifest
diff --git a/tools/ide_query/prober_scripts/jvm/Android.bp b/tools/ide_query/prober_scripts/jvm/Android.bp
new file mode 100644
index 0000000..84d00b5
--- /dev/null
+++ b/tools/ide_query/prober_scripts/jvm/Android.bp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_library {
+    name: "ide_query_proberscript_jvm",
+    srcs: [
+        "Foo.java",
+        "Bar.java",
+        "other/Other.java",
+    ],
+}
diff --git a/tools/ide_query/prober_scripts/jvm/Bar.java b/tools/ide_query/prober_scripts/jvm/Bar.java
new file mode 100644
index 0000000..8d51576
--- /dev/null
+++ b/tools/ide_query/prober_scripts/jvm/Bar.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jvm;
+
+/** Bar class. The class for testing code assist within the same build module. */
+class Bar<K extends Number, V extends Number> {
+  Bar() {
+    foo(new Foo());
+  }
+
+  void foo(Foo f) {}
+
+  void foo(Object o) {}
+
+  void bar(Foo f) {}
+
+  void baz(Object o) {}
+}
\ No newline at end of file
diff --git a/tools/ide_query/prober_scripts/jvm/Foo.java b/tools/ide_query/prober_scripts/jvm/Foo.java
index a043f72..9397bc4 100644
--- a/tools/ide_query/prober_scripts/jvm/Foo.java
+++ b/tools/ide_query/prober_scripts/jvm/Foo.java
@@ -16,22 +16,109 @@
 
 package jvm;
 
-import java.util.ArrayList;
-import java.util.HashSet;
+import jvm.other.Other;
 
 /** Foo class. */
 public final class Foo {
+//               |  | foo_def
+
+  void testParameterInfo() {
+    // Test signature help for type parameters.
+
+    Bar<Integer, Double> b = new Bar<>();
+    //                               ^ ctor
+    //     ^ decl_1
+    //              ^ decl_2
+    System.out.println(b);
+
+    // step at ctor
+    // workspace.waitForReady()
+    // paraminfo.trigger()
+    // assert paraminfo.items.filter(
+    //  label="K extends Number, V extends Number",
+    //  selection="K extends Number",
+    // )
+
+    // step at decl_1
+    // workspace.waitForReady()
+    // paraminfo.trigger()
+    // assert paraminfo.items.filter(
+    //  label="K extends Number, V extends Number",
+    //  selection="K extends Number",
+    // )
+
+    // step at decl_2
+    // workspace.waitForReady()
+    // paraminfo.trigger()
+    // assert paraminfo.items.filter(
+    //  label="K extends Number, V extends Number",
+    //  selection="V extends Number",
+    // )
+
+    // Test signature help for constructor parameters.
+
+    Other other = new Other(123, "foo");
+    //                       ^ param_1
+    //                             ^ param_2
+    System.out.println(other);
+
+    // step at param_1
+    // workspace.waitForReady()
+    // paraminfo.trigger()
+    // assert paraminfo.items.filter(
+    //  label="\\(int first, String second\\)",
+    //  selection="int first",
+    // )
+
+    // step at param_2
+    // workspace.waitForReady()
+    // paraminfo.trigger()
+    // assert paraminfo.items.empty()
+  }
 
   void testCompletion() {
-    ArrayList<Integer> list = new ArrayList<>();
-    System.out.println(list);
+    Bar<Integer, Double> b = new Bar<>();
+    System.out.println(b);
 
     // ^
 
     // step
-    // ; Test completion on the standard types.
-    // type("list.")
+    // ; Test completion on types from the same package.
+    // workspace.waitForReady()
+    // type("b.")
     // completion.trigger()
-    // assert completion.items.filter(label="add.*")
+    // assert completion.items.filter(label="foo.*")
+    // delline()
+
+    Other other = new Other(1, "foo");
+    System.out.println(other);
+
+    // ^
+
+    // step
+    // ; Test completion on types from a different package.
+    // workspace.waitForReady()
+    // type("other.")
+    // completion.trigger()
+    // apply(completion.items.filter(label="other.*").first())
+    // type(".")
+    // completion.trigger()
+    // apply(completion.items.filter(label="other.*").first())
+    // delline()
+  }
+
+  void testDiagnostics() {
+
+    // ^
+
+    // step
+    // ; Test diagnostics about wrong type argument bounds.
+    // workspace.waitForReady()
+    // type("Bar<String, Double> b;")
+    // assert diagnostics.items.filter(
+    //  message="type argument .* is not within bounds .*",
+    //  code="compiler.err.not.within.bounds",
+    // )
+    // delline()
   }
 }
diff --git a/tools/ide_query/prober_scripts/jvm/ide_query.out b/tools/ide_query/prober_scripts/jvm/ide_query.out
new file mode 100644
index 0000000..af9fb86
--- /dev/null
+++ b/tools/ide_query/prober_scripts/jvm/ide_query.out
@@ -0,0 +1,4 @@
+
+out2X
+6build/make/tools/ide_query/prober_scripts/jvm/Foo.javaide_query_proberscript_jvm:Î
+ide_query_proberscript_jvm6build/make/tools/ide_query/prober_scripts/jvm/Foo.java6build/make/tools/ide_query/prober_scripts/jvm/Bar.java>build/make/tools/ide_query/prober_scripts/jvm/other/Other.java
\ No newline at end of file
diff --git a/tools/ide_query/prober_scripts/jvm/other/Other.java b/tools/ide_query/prober_scripts/jvm/other/Other.java
new file mode 100644
index 0000000..822662a
--- /dev/null
+++ b/tools/ide_query/prober_scripts/jvm/other/Other.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jvm.other;
+
+/** Other class */
+public class Other {
+  public Other(int first, String second) {}
+
+  public Other other() {
+    return new Other(0, "");
+  }
+}
diff --git a/tools/java-event-log-tags.py b/tools/java-event-log-tags.py
index bbd65fa..e3dc07e 100755
--- a/tools/java-event-log-tags.py
+++ b/tools/java-event-log-tags.py
@@ -15,16 +15,12 @@
 # limitations under the License.
 
 """
-Usage: java-event-log-tags.py [-o output_file] <input_file> <merged_tags_file>
-
 Generate a java class containing constants for each of the event log
 tags in the given input file.
-
--h to display this usage message and exit.
 """
 
 from io import StringIO
-import getopt
+import argparse
 import os
 import os.path
 import re
@@ -32,57 +28,14 @@
 
 import event_log_tags
 
-output_file = None
+parser = argparse.ArgumentParser(description=__doc__)
+parser.add_argument('-o', dest='output_file')
+parser.add_argument('file')
+args = parser.parse_args()
 
-try:
-  opts, args = getopt.getopt(sys.argv[1:], "ho:")
-except getopt.GetoptError as err:
-  print(str(err))
-  print(__doc__)
-  sys.exit(2)
-
-for o, a in opts:
-  if o == "-h":
-    print(__doc__)
-    sys.exit(2)
-  elif o == "-o":
-    output_file = a
-  else:
-    print("unhandled option %s" % (o,), file=sys.stderr)
-    sys.exit(1)
-
-if len(args) != 1 and len(args) != 2:
-  print("need one or two input files, not %d" % (len(args),))
-  print(__doc__)
-  sys.exit(1)
-
-fn = args[0]
+fn = args.file
 tagfile = event_log_tags.TagFile(fn)
 
-if len(args) > 1:
-  # Load the merged tag file (which should have numbers assigned for all
-  # tags.  Use the numbers from the merged file to fill in any missing
-  # numbers from the input file.
-  merged_fn = args[1]
-  merged_tagfile = event_log_tags.TagFile(merged_fn)
-  merged_by_name = dict([(t.tagname, t) for t in merged_tagfile.tags])
-  for t in tagfile.tags:
-    if t.tagnum is None:
-      if t.tagname in merged_by_name:
-        t.tagnum = merged_by_name[t.tagname].tagnum
-      else:
-        # We're building something that's not being included in the
-        # product, so its tags don't appear in the merged file.  Assign
-        # them all an arbitrary number so we can emit the java and
-        # compile the (unused) package.
-        t.tagnum = 999999
-else:
-  # Not using the merged tag file, so all tags must have manually assigned
-  # numbers
-  for t in tagfile.tags:
-    if t.tagnum is None:
-      tagfilef.AddError("tag \"%s\" has no number" % (tagname,), tag.linenum)
-
 if "java_package" not in tagfile.options:
   tagfile.AddError("java_package option not specified", linenum=0)
 
@@ -141,11 +94,11 @@
 for t in tagfile.tags:
   methodName = javaName("write_" + t.tagname)
   if t.description:
-    args = [arg.strip("() ").split("|") for arg in t.description.split(",")]
+    fn_args = [arg.strip("() ").split("|") for arg in t.description.split(",")]
   else:
-    args = []
-  argTypesNames = ", ".join([javaTypes[int(arg[1])] + " " + javaName(arg[0]) for arg in args])
-  argNames = "".join([", " + javaName(arg[0]) for arg in args])
+    fn_args = []
+  argTypesNames = ", ".join([javaTypes[int(arg[1])] + " " + javaName(arg[0]) for arg in fn_args])
+  argNames = "".join([", " + javaName(arg[0]) for arg in fn_args])
   buffer.write("\n  public static void %s(%s) {" % (methodName, argTypesNames))
   buffer.write("\n    android.util.EventLog.writeEvent(%s%s);" % (t.tagname.upper(), argNames))
   buffer.write("\n  }\n")
@@ -153,8 +106,8 @@
 
 buffer.write("}\n");
 
-output_dir = os.path.dirname(output_file)
+output_dir = os.path.dirname(args.output_file)
 if not os.path.exists(output_dir):
   os.makedirs(output_dir)
 
-event_log_tags.WriteOutput(output_file, buffer)
+event_log_tags.WriteOutput(args.output_file, buffer)
diff --git a/tools/merge-event-log-tags.py b/tools/merge-event-log-tags.py
index 292604c..5730c11 100755
--- a/tools/merge-event-log-tags.py
+++ b/tools/merge-event-log-tags.py
@@ -15,22 +15,13 @@
 # limitations under the License.
 
 """
-Usage: merge-event-log-tags.py [-o output_file] [input_files...]
-
 Merge together zero or more event-logs-tags files to produce a single
 output file, stripped of comments.  Checks that no tag numbers conflict
 and fails if they do.
-
--h to display this usage message and exit.
 """
 
 from io import StringIO
-import getopt
-try:
-  import hashlib
-except ImportError:
-  import md5 as hashlib
-import struct
+import argparse
 import sys
 
 import event_log_tags
@@ -38,32 +29,10 @@
 errors = []
 warnings = []
 
-output_file = None
-pre_merged_file = None
-
-# Tags with a tag number of ? are assigned a tag in the range
-# [ASSIGN_START, ASSIGN_LIMIT).
-ASSIGN_START = 900000
-ASSIGN_LIMIT = 1000000
-
-try:
-  opts, args = getopt.getopt(sys.argv[1:], "ho:m:")
-except getopt.GetoptError as err:
-  print(str(err))
-  print(__doc__)
-  sys.exit(2)
-
-for o, a in opts:
-  if o == "-h":
-    print(__doc__)
-    sys.exit(2)
-  elif o == "-o":
-    output_file = a
-  elif o == "-m":
-    pre_merged_file = a
-  else:
-    print("unhandled option %s" % (o,), file=sys.stderr)
-    sys.exit(1)
+parser = argparse.ArgumentParser(description=__doc__)
+parser.add_argument('-o', dest='output_file')
+parser.add_argument('files', nargs='*')
+args = parser.parse_args()
 
 # Restrictions on tags:
 #
@@ -77,12 +46,7 @@
 by_tagname = {}
 by_tagnum = {}
 
-pre_merged_tags = {}
-if pre_merged_file:
-  for t in event_log_tags.TagFile(pre_merged_file).tags:
-    pre_merged_tags[t.tagname] = t
-
-for fn in args:
+for fn in args.files:
   tagfile = event_log_tags.TagFile(fn)
 
   for t in tagfile.tags:
@@ -93,12 +57,6 @@
     if t.tagname in by_tagname:
       orig = by_tagname[t.tagname]
 
-      # Allow an explicit tag number to define an implicit tag number
-      if orig.tagnum is None:
-        orig.tagnum = t.tagnum
-      elif t.tagnum is None:
-        t.tagnum = orig.tagnum
-
       if (t.tagnum == orig.tagnum and
           t.description == orig.description):
         # if the name and description are identical, issue a warning
@@ -114,7 +72,7 @@
             linenum=t.linenum)
       continue
 
-    if t.tagnum is not None and t.tagnum in by_tagnum:
+    if t.tagnum in by_tagnum:
       orig = by_tagnum[t.tagnum]
 
       if t.tagname != orig.tagname:
@@ -125,8 +83,7 @@
         continue
 
     by_tagname[t.tagname] = t
-    if t.tagnum is not None:
-      by_tagnum[t.tagnum] = t
+    by_tagnum[t.tagnum] = t
 
   errors.extend(tagfile.errors)
   warnings.extend(tagfile.warnings)
@@ -140,38 +97,6 @@
   for fn, ln, msg in warnings:
     print("%s:%d: warning: %s" % (fn, ln, msg), file=sys.stderr)
 
-# Python's hash function (a) isn't great and (b) varies between
-# versions of python.  Using md5 is overkill here but is the same from
-# platform to platform and speed shouldn't matter in practice.
-def hashname(str):
-  d = hashlib.md5(str).digest()[:4]
-  return struct.unpack("!I", d)[0]
-
-# Assign a tag number to all the entries that say they want one
-# assigned.  We do this based on a hash of the tag name so that the
-# numbers should stay relatively stable as tags are added.
-
-# If we were provided pre-merged tags (w/ the -m option), then don't
-# ever try to allocate one, just fail if we don't have a number
-
-for name, t in sorted(by_tagname.items()):
-  if t.tagnum is None:
-    if pre_merged_tags:
-      try:
-        t.tagnum = pre_merged_tags[t.tagname]
-      except KeyError:
-        print("Error: Tag number not defined for tag `%s'. Have you done a full build?" % t.tagname,
-              file=sys.stderr)
-        sys.exit(1)
-    else:
-      while True:
-        x = (hashname(name) % (ASSIGN_LIMIT - ASSIGN_START - 1)) + ASSIGN_START
-        if x not in by_tagnum:
-          t.tagnum = x
-          by_tagnum[x] = t
-          break
-        name = "_" + name
-
 # by_tagnum should be complete now; we've assigned numbers to all tags.
 
 buffer = StringIO()
@@ -181,4 +106,4 @@
   else:
     buffer.write("%d %s\n" % (t.tagnum, t.tagname))
 
-event_log_tags.WriteOutput(output_file, buffer)
+event_log_tags.WriteOutput(args.output_file, buffer)
diff --git a/tools/missing_soong_module_info.py b/tools/missing_soong_module_info.py
new file mode 100755
index 0000000..6fa7f2b
--- /dev/null
+++ b/tools/missing_soong_module_info.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+import os
+import sys
+
+def main():
+    try:
+        product_out = os.environ["ANDROID_PRODUCT_OUT"]
+    except KeyError:
+        sys.stderr.write("Can't get ANDROID_PRODUCT_OUT. Run lunch first.\n")
+        sys.exit(1)
+
+    filename = os.path.join(product_out, "module-info.json")
+    try:
+        with open(filename) as f:
+            modules = json.load(f)
+    except FileNotFoundError:
+        sys.stderr.write(f"File not found: {filename}\n")
+        sys.exit(1)
+    except json.JSONDecodeError:
+        sys.stderr.write(f"Invalid json: {filename}\n")
+        return None
+
+    classes = {}
+
+    for name, info in modules.items():
+        make = info.get("make")
+        make_gen = info.get("make_generated_module_info")
+        if not make and make_gen:
+            classes.setdefault(frozenset(info.get("class")), []).append(name)
+
+    for cl, names in classes.items():
+        print(" ".join(cl))
+        for name in names:
+            print(" ", name)
+
+if __name__ == "__main__":
+    main()
diff --git a/tools/releasetools/apex_utils.py b/tools/releasetools/apex_utils.py
index 54df955..08f2b83 100644
--- a/tools/releasetools/apex_utils.py
+++ b/tools/releasetools/apex_utils.py
@@ -79,15 +79,10 @@
     Returns:
       The repacked apex file containing the signed apk files.
     """
-    if not os.path.exists(self.debugfs_path):
-      raise ApexSigningError(
-          "Couldn't find location of debugfs_static: " +
-          "Path {} does not exist. ".format(self.debugfs_path) +
-          "Make sure bin/debugfs_static can be found in -p <path>")
-    list_cmd = ['deapexer', '--debugfs_path', self.debugfs_path,
-                'list', self.apex_path]
-    entries_names = common.RunAndCheckOutput(list_cmd).split()
-    apk_entries = [name for name in entries_names if name.endswith('.apk')]
+    payload_dir = self.ExtractApexPayload(self.apex_path)
+    apk_entries = []
+    for base_dir, _, files in os.walk(payload_dir):
+      apk_entries.extend(os.path.join(base_dir, file) for file in files if file.endswith('.apk'))
 
     # No need to sign and repack, return the original apex path.
     if not apk_entries and self.sign_tool is None:
@@ -105,16 +100,16 @@
         logger.warning('Apk path does not contain the intended directory name:'
                        ' %s', entry)
 
-    payload_dir, has_signed_content = self.ExtractApexPayloadAndSignContents(
-        apk_entries, apk_keys, payload_key, signing_args)
+    has_signed_content = self.SignContentsInPayload(
+        payload_dir, apk_entries, apk_keys, payload_key, signing_args)
     if not has_signed_content:
       logger.info('No contents has been signed in %s', self.apex_path)
       return self.apex_path
 
     return self.RepackApexPayload(payload_dir, payload_key, signing_args)
 
-  def ExtractApexPayloadAndSignContents(self, apk_entries, apk_keys, payload_key, signing_args):
-    """Extracts the payload image and signs the containing apk files."""
+  def ExtractApexPayload(self, apex_path):
+    """Extracts the contents of an APEX and returns the directory of the contents"""
     if not os.path.exists(self.debugfs_path):
       raise ApexSigningError(
           "Couldn't find location of debugfs_static: " +
@@ -129,9 +124,12 @@
     extract_cmd = ['deapexer', '--debugfs_path', self.debugfs_path,
                    '--fsckerofs_path', self.fsckerofs_path,
                    'extract',
-                   self.apex_path, payload_dir]
+                   apex_path, payload_dir]
     common.RunAndCheckOutput(extract_cmd)
+    return payload_dir
 
+  def SignContentsInPayload(self, payload_dir, apk_entries, apk_keys, payload_key, signing_args):
+    """Signs the contents in payload."""
     has_signed_content = False
     for entry in apk_entries:
       apk_path = os.path.join(payload_dir, entry)
@@ -163,7 +161,7 @@
       common.RunAndCheckOutput(cmd)
       has_signed_content = True
 
-    return payload_dir, has_signed_content
+    return has_signed_content
 
   def RepackApexPayload(self, payload_dir, payload_key, signing_args=None):
     """Rebuilds the apex file with the updated payload directory."""
diff --git a/tools/releasetools/build_image.py b/tools/releasetools/build_image.py
index 464ad9b..b6c96c4 100755
--- a/tools/releasetools/build_image.py
+++ b/tools/releasetools/build_image.py
@@ -677,6 +677,21 @@
       glob_dict["fingerprint"] = fingerprint
       return
 
+def TryParseFingerprintAndTimestamp(glob_dict):
+  """Helper function that parses fingerprint and timestamp from the global dictionary.
+
+  Args:
+    glob_dict: the global dictionary from the build system.
+  """
+  TryParseFingerprint(glob_dict)
+
+  # Set fixed timestamp for building the OTA package.
+  if "use_fixed_timestamp" in glob_dict:
+    glob_dict["timestamp"] = FIXED_FILE_TIMESTAMP
+  if "build.prop" in glob_dict:
+    timestamp = glob_dict["build.prop"].GetProp("ro.build.date.utc")
+    if timestamp:
+      glob_dict["timestamp"] = timestamp
 
 def ImagePropFromGlobalDict(glob_dict, mount_point):
   """Build an image property dictionary from the global dictionary.
@@ -686,15 +701,7 @@
     mount_point: such as "system", "data" etc.
   """
   d = {}
-  TryParseFingerprint(glob_dict)
-
-  # Set fixed timestamp for building the OTA package.
-  if "use_fixed_timestamp" in glob_dict:
-    d["timestamp"] = FIXED_FILE_TIMESTAMP
-  if "build.prop" in glob_dict:
-    timestamp = glob_dict["build.prop"].GetProp("ro.build.date.utc")
-    if timestamp:
-      d["timestamp"] = timestamp
+  TryParseFingerprintAndTimestamp(glob_dict)
 
   def copy_prop(src_p, dest_p):
     """Copy a property from the global dictionary.
@@ -730,6 +737,7 @@
       "avb_avbtool",
       "use_dynamic_partition_size",
       "fingerprint",
+      "timestamp",
   )
   for p in common_props:
     copy_prop(p, p)
@@ -992,6 +1000,7 @@
     # The caller knows the mount point and provides a dictionary needed by
     # BuildImage().
     image_properties = glob_dict
+    TryParseFingerprintAndTimestamp(image_properties)
   else:
     image_filename = os.path.basename(args.out_file)
     mount_point = ""
diff --git a/tools/releasetools/merge_ota.py b/tools/releasetools/merge_ota.py
index fb5957a..e8732a2 100644
--- a/tools/releasetools/merge_ota.py
+++ b/tools/releasetools/merge_ota.py
@@ -226,9 +226,21 @@
     logger.setLevel(logging.INFO)
 
   logger.info(args)
+  if args.java_path:
+    common.OPTIONS.java_path = args.java_path
+
   if args.search_path:
     common.OPTIONS.search_path = args.search_path
 
+  if args.signapk_path:
+    common.OPTIONS.signapk_path = args.signapk_path
+
+  if args.extra_signapk_args:
+    common.OPTIONS.extra_signapk_args = args.extra_signapk_args
+
+  if args.signapk_shared_library_path:
+    common.OPTIONS.signapk_shared_library_path = args.signapk_shared_library_path
+
   metadata_ota = args.packages[-1]
   if args.metadata_ota is not None:
     metadata_ota = args.metadata_ota
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index 6446e1f..76d168c 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -1039,6 +1039,9 @@
 
   # Prepare custom images.
   if OPTIONS.custom_images:
+    if source_file is not None:
+      source_file = GetTargetFilesZipForCustomImagesUpdates(
+           source_file, OPTIONS.custom_images)
     target_file = GetTargetFilesZipForCustomImagesUpdates(
         target_file, OPTIONS.custom_images)
 
@@ -1121,17 +1124,18 @@
   additional_args += ["--enable_lz4diff=" +
                       str(OPTIONS.enable_lz4diff).lower()]
 
+  env_override = {}
   if source_file and OPTIONS.enable_lz4diff:
-    input_tmp = common.UnzipTemp(source_file, ["META/liblz4.so"])
-    liblz4_path = os.path.join(input_tmp, "META", "liblz4.so")
+    liblz4_path = os.path.join(source_file, "META", "liblz4.so")
     assert os.path.exists(
         liblz4_path), "liblz4.so not found in META/ dir of target file {}".format(liblz4_path)
     logger.info("Enabling lz4diff %s", liblz4_path)
-    additional_args += ["--liblz4_path", liblz4_path]
     erofs_compression_param = OPTIONS.target_info_dict.get(
         "erofs_default_compressor")
     assert erofs_compression_param is not None, "'erofs_default_compressor' not found in META/misc_info.txt of target build. This is required to enable lz4diff."
     additional_args += ["--erofs_compression_param", erofs_compression_param]
+    env_override["LD_PRELOAD"] = liblz4_path + \
+        ":" + os.environ.get("LD_PRELOAD", "")
 
   if OPTIONS.disable_vabc:
     additional_args += ["--disable_vabc=true"]
@@ -1141,10 +1145,15 @@
     additional_args += ["--compressor_types", OPTIONS.compressor_types]
   additional_args += ["--max_timestamp", max_timestamp]
 
+  env = dict(os.environ)
+  if env_override:
+    logger.info("Using environment variables %s", env_override)
+    env.update(env_override)
   payload.Generate(
       target_file,
       source_file,
-      additional_args + partition_timestamps_flags
+      additional_args + partition_timestamps_flags,
+      env=env
   )
 
   # Sign the payload.
diff --git a/tools/releasetools/ota_signing_utils.py b/tools/releasetools/ota_signing_utils.py
index 60c8c94..9d04c3b 100644
--- a/tools/releasetools/ota_signing_utils.py
+++ b/tools/releasetools/ota_signing_utils.py
@@ -23,10 +23,18 @@
 
 
 def AddSigningArgumentParse(parser: argparse.ArgumentParser):
+  parser.add_argument('--java_path', type=str,
+                      help='Path to JVM if other than default')
   parser.add_argument('--package_key', type=str,
                       help='Paths to private key for signing payload')
   parser.add_argument('--search_path', '--path', type=str,
                       help='Search path for framework/signapk.jar')
+  parser.add_argument('--signapk_path', type=str,
+                      help='Path to signapk.jar, relative to search_path')
+  parser.add_argument('--extra_signapk_args', type=ParseSignerArgs,
+                      help='Extra arguments for signapk.jar')
+  parser.add_argument('--signapk_shared_library_path', type=str,
+                      help='Path to lib64 libraries used by signapk.jar')
   parser.add_argument('--payload_signer', type=str,
                       help='Path to custom payload signer')
   parser.add_argument('--payload_signer_args', type=ParseSignerArgs,
diff --git a/tools/releasetools/ota_utils.py b/tools/releasetools/ota_utils.py
index 81b53dc..852d62b 100644
--- a/tools/releasetools/ota_utils.py
+++ b/tools/releasetools/ota_utils.py
@@ -845,16 +845,16 @@
     self.is_partial_update = is_partial_update
     self.spl_downgrade = spl_downgrade
 
-  def _Run(self, cmd):  # pylint: disable=no-self-use
+  def _Run(self, cmd, **kwargs):  # pylint: disable=no-self-use
     # Don't pipe (buffer) the output if verbose is set. Let
     # brillo_update_payload write to stdout/stderr directly, so its progress can
     # be monitored.
     if OPTIONS.verbose:
-      common.RunAndCheckOutput(cmd, stdout=None, stderr=None)
+      common.RunAndCheckOutput(cmd, stdout=None, stderr=None, **kwargs)
     else:
-      common.RunAndCheckOutput(cmd)
+      common.RunAndCheckOutput(cmd, **kwargs)
 
-  def Generate(self, target_file, source_file=None, additional_args=None):
+  def Generate(self, target_file, source_file=None, additional_args=None, **kwargs):
     """Generates a payload from the given target-files zip(s).
 
     Args:
@@ -863,6 +863,7 @@
           generating a full OTA.
       additional_args: A list of additional args that should be passed to
           delta_generator binary; or None.
+      kwargs: Any additional args to pass to subprocess.Popen
     """
     if additional_args is None:
       additional_args = []
@@ -918,7 +919,7 @@
     if self.is_partial_update:
       cmd.extend(["--is_partial_update=true"])
     cmd.extend(additional_args)
-    self._Run(cmd)
+    self._Run(cmd, **kwargs)
 
     self.payload_file = payload_file
     self.payload_properties = None
diff --git a/tools/releasetools/sign_target_files_apks.py b/tools/releasetools/sign_target_files_apks.py
index 4ad97e0..2378539 100755
--- a/tools/releasetools/sign_target_files_apks.py
+++ b/tools/releasetools/sign_target_files_apks.py
@@ -862,21 +862,32 @@
 
     # Updates pvmfw embedded public key with the virt APEX payload key.
     elif filename == "PREBUILT_IMAGES/pvmfw.img":
-      # Find the name of the virt APEX in the target files.
+      # Find the path of the virt APEX in the target files.
       namelist = input_tf_zip.namelist()
-      apex_gen = (GetApexFilename(f) for f in namelist if IsApexFile(f))
-      virt_apex_re = re.compile("^com\.([^\.]+\.)?android\.virt\.apex$")
-      virt_apex = next((a for a in apex_gen if virt_apex_re.match(a)), None)
-      if not virt_apex:
+      apex_gen = (f for f in namelist if IsApexFile(f))
+      virt_apex_re = re.compile("^.*com\.([^\.]+\.)?android\.virt\.apex$")
+      virt_apex_path = next(
+        (a for a in apex_gen if virt_apex_re.match(a)), None)
+      if not virt_apex_path:
         print("Removing %s from ramdisk: virt APEX not found" % filename)
       else:
-        print("Replacing %s embedded key with %s key" % (filename, virt_apex))
+        print("Replacing %s embedded key with %s key" % (filename,
+                                                         virt_apex_path))
         # Get the current and new embedded keys.
+        virt_apex = GetApexFilename(virt_apex_path)
         payload_key, container_key, sign_tool = apex_keys[virt_apex]
-        new_pubkey_path = common.ExtractAvbPublicKey(
-            misc_info['avb_avbtool'], payload_key)
-        with open(new_pubkey_path, 'rb') as f:
-          new_pubkey = f.read()
+
+        # b/384813199: handles the pre-signed com.android.virt.apex in GSI.
+        if payload_key == 'PRESIGNED':
+          with input_tf_zip.open(virt_apex_path) as apex_fp:
+            with zipfile.ZipFile(apex_fp) as apex_zip:
+              new_pubkey = apex_zip.read('apex_pubkey')
+        else:
+          new_pubkey_path = common.ExtractAvbPublicKey(
+              misc_info['avb_avbtool'], payload_key)
+          with open(new_pubkey_path, 'rb') as f:
+            new_pubkey = f.read()
+
         pubkey_info = copy.copy(
             input_tf_zip.getinfo("PREBUILT_IMAGES/pvmfw_embedded.avbpubkey"))
         old_pubkey = input_tf_zip.read(pubkey_info.filename)