Merge "Connect fingerprint build flag to v2." into main
diff --git a/backported_fixes/Android.bp b/backported_fixes/Android.bp
index a20f3fc..243e77e 100644
--- a/backported_fixes/Android.bp
+++ b/backported_fixes/Android.bp
@@ -19,19 +19,24 @@
 
 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,
 }
 
@@ -63,7 +68,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 +80,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 +119,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_test_suites.py b/ci/build_test_suites.py
index 443e872..addad15 100644
--- a/ci/build_test_suites.py
+++ b/ci/build_test_suites.py
@@ -35,7 +35,7 @@
 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'])
+REQUIRED_BUILD_TARGETS = frozenset(['dist', 'droid', 'checkbuild'])
 
 
 class Error(Exception):
@@ -112,6 +112,9 @@
         if optimization_rationale:
           get_metrics_agent().report_unoptimized_target(target, optimization_rationale)
           continue
+        if target in REQUIRED_BUILD_TARGETS:
+          get_metrics_agent().report_unoptimized_target(target, 'Required build target.')
+          continue
         try:
           regex = r'\b(%s.*)\b' % re.escape(target)
           if any(re.search(regex, opt) for opt in test_discovery_zip_regexes):
@@ -154,6 +157,7 @@
     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
 
       regex = r'\b(%s.*)\b' % re.escape(target)
diff --git a/core/Makefile b/core/Makefile
index 1180073..3ba931c 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -3504,18 +3504,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))
@@ -3556,7 +3558,7 @@
   PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \
       $(BUILD_IMAGE) \
           $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(systemimage_intermediates)/file_list.txt) \
-          $(TARGET_OUT) $(systemimage_intermediates)/system_image_info.txt $(1) $(TARGET_OUT) \
+          $(TARGET_OUT) $(systemimage_intermediates)/system_image_info.txt $(1) $(TARGET_OUT) --build_datetime_file $(BUILD_DATETIME_FILE) \
           || ( mkdir -p $${DIST_DIR}; \
                cp $(INSTALLED_FILES_FILE) $${DIST_DIR}/installed-files-rescued.txt; \
                exit 1 )
@@ -3707,7 +3709,7 @@
   PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \
       $(BUILD_IMAGE) \
           $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(userdataimage_intermediates)/file_list.txt) \
-          $(TARGET_OUT_DATA) $(userdataimage_intermediates)/userdata_image_info.txt \
+          $(TARGET_OUT_DATA) $(userdataimage_intermediates)/userdata_image_info.txt --build_datetime_file $(BUILD_DATETIME_FILE) \
           $(INSTALLED_USERDATAIMAGE_TARGET) $(TARGET_OUT)
   $(call assert-max-image-size,$(INSTALLED_USERDATAIMAGE_TARGET),$(BOARD_USERDATAIMAGE_PARTITION_SIZE))
 endef
@@ -3769,7 +3771,7 @@
   PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \
       $(BUILD_IMAGE) \
           $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(cacheimage_intermediates)/file_list.txt) \
-          $(TARGET_OUT_CACHE) $(cacheimage_intermediates)/cache_image_info.txt \
+          $(TARGET_OUT_CACHE) $(cacheimage_intermediates)/cache_image_info.txt --build_datetime_file $(BUILD_DATETIME_FILE) \
           $(INSTALLED_CACHEIMAGE_TARGET) $(TARGET_OUT)
   $(call assert-max-image-size,$(INSTALLED_CACHEIMAGE_TARGET),$(BOARD_CACHEIMAGE_PARTITION_SIZE))
 endef
@@ -3856,7 +3858,7 @@
   PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \
       $(BUILD_IMAGE) \
           $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(systemotherimage_intermediates)/file_list.txt) \
-          $(TARGET_OUT_SYSTEM_OTHER) $(systemotherimage_intermediates)/system_other_image_info.txt \
+          $(TARGET_OUT_SYSTEM_OTHER) $(systemotherimage_intermediates)/system_other_image_info.txt --build_datetime_file $(BUILD_DATETIME_FILE) \
           $(INSTALLED_SYSTEMOTHERIMAGE_TARGET) $(TARGET_OUT)
   $(call assert-max-image-size,$(INSTALLED_SYSTEMOTHERIMAGE_TARGET),$(BOARD_SYSTEMIMAGE_PARTITION_SIZE))
 endef
@@ -3962,7 +3964,7 @@
   PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \
       $(BUILD_IMAGE) \
           $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(vendorimage_intermediates)/file_list.txt) \
-          $(TARGET_OUT_VENDOR) $(vendorimage_intermediates)/vendor_image_info.txt \
+          $(TARGET_OUT_VENDOR) $(vendorimage_intermediates)/vendor_image_info.txt --build_datetime_file $(BUILD_DATETIME_FILE) \
           $(INSTALLED_VENDORIMAGE_TARGET) $(TARGET_OUT)
   $(call assert-max-image-size,$(INSTALLED_VENDORIMAGE_TARGET) $(RECOVERY_FROM_BOOT_PATCH),$(BOARD_VENDORIMAGE_PARTITION_SIZE))
 endef
@@ -4050,7 +4052,7 @@
   PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \
       $(BUILD_IMAGE) \
           $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(productimage_intermediates)/file_list.txt) \
-          $(TARGET_OUT_PRODUCT) $(productimage_intermediates)/product_image_info.txt \
+          $(TARGET_OUT_PRODUCT) $(productimage_intermediates)/product_image_info.txt --build_datetime_file $(BUILD_DATETIME_FILE) \
           $(INSTALLED_PRODUCTIMAGE_TARGET) $(TARGET_OUT)
   $(call assert-max-image-size,$(INSTALLED_PRODUCTIMAGE_TARGET),$(BOARD_PRODUCTIMAGE_PARTITION_SIZE))
 endef
@@ -4119,7 +4121,7 @@
       $(BUILD_IMAGE) \
           $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(system_extimage_intermediates)/file_list.txt) \
           $(TARGET_OUT_SYSTEM_EXT) \
-          $(system_extimage_intermediates)/system_ext_image_info.txt \
+          $(system_extimage_intermediates)/system_ext_image_info.txt --build_datetime_file $(BUILD_DATETIME_FILE) \
           $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) \
           $(TARGET_OUT)
   $(call assert-max-image-size,$(INSTALLED_PRODUCT_SERVICESIMAGE_TARGET),$(BOARD_PRODUCT_SERVICESIMAGE_PARTITION_SIZE))
@@ -4209,7 +4211,7 @@
   PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \
       $(BUILD_IMAGE) \
           $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(odmimage_intermediates)/file_list.txt) \
-          $(TARGET_OUT_ODM) $(odmimage_intermediates)/odm_image_info.txt \
+          $(TARGET_OUT_ODM) $(odmimage_intermediates)/odm_image_info.txt --build_datetime_file $(BUILD_DATETIME_FILE) \
           $(INSTALLED_ODMIMAGE_TARGET) $(TARGET_OUT)
   $(call assert-max-image-size,$(INSTALLED_ODMIMAGE_TARGET),$(BOARD_ODMIMAGE_PARTITION_SIZE))
 endef
@@ -4278,7 +4280,7 @@
   PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \
       $(BUILD_IMAGE) \
           $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(vendor_dlkmimage_intermediates)/file_list.txt) \
-          $(TARGET_OUT_VENDOR_DLKM) $(vendor_dlkmimage_intermediates)/vendor_dlkm_image_info.txt \
+          $(TARGET_OUT_VENDOR_DLKM) $(vendor_dlkmimage_intermediates)/vendor_dlkm_image_info.txt --build_datetime_file $(BUILD_DATETIME_FILE) \
           $(INSTALLED_VENDOR_DLKMIMAGE_TARGET) $(TARGET_OUT)
   $(call assert-max-image-size,$(INSTALLED_VENDOR_DLKMIMAGE_TARGET),$(BOARD_VENDOR_DLKMIMAGE_PARTITION_SIZE))
 endef
@@ -4347,7 +4349,7 @@
   PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \
       $(BUILD_IMAGE) \
           $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(odm_dlkmimage_intermediates)/file_list.txt) \
-          $(TARGET_OUT_ODM_DLKM) $(odm_dlkmimage_intermediates)/odm_dlkm_image_info.txt \
+          $(TARGET_OUT_ODM_DLKM) $(odm_dlkmimage_intermediates)/odm_dlkm_image_info.txt --build_datetime_file $(BUILD_DATETIME_FILE) \
           $(INSTALLED_ODM_DLKMIMAGE_TARGET) $(TARGET_OUT)
   $(call assert-max-image-size,$(INSTALLED_ODM_DLKMIMAGE_TARGET),$(BOARD_ODM_DLKMIMAGE_PARTITION_SIZE))
 endef
@@ -4418,7 +4420,7 @@
   PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \
       $(BUILD_IMAGE) \
           $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(system_dlkmimage_intermediates)/file_list.txt) \
-          $(TARGET_OUT_SYSTEM_DLKM) $(system_dlkmimage_intermediates)/system_dlkm_image_info.txt \
+          $(TARGET_OUT_SYSTEM_DLKM) $(system_dlkmimage_intermediates)/system_dlkm_image_info.txt --build_datetime_file $(BUILD_DATETIME_FILE) \
           $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET) $(TARGET_OUT)
   $(call assert-max-image-size,$(INSTALLED_SYSTEM_DLKMIMAGE_TARGET),$(BOARD_SYSTEM_DLKMIMAGE_PARTITION_SIZE))
 endef
diff --git a/core/android_soong_config_vars.mk b/core/android_soong_config_vars.mk
index 6040ad3..f17d8c6 100644
--- a/core/android_soong_config_vars.mk
+++ b/core/android_soong_config_vars.mk
@@ -78,9 +78,10 @@
 $(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))
 
 ifdef TARGET_BOARD_AUTO
   $(call add_soong_config_var_value, ANDROID, target_board_auto, $(TARGET_BOARD_AUTO))
@@ -99,6 +100,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
@@ -198,6 +202,17 @@
 # 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))
diff --git a/core/config.mk b/core/config.mk
index 20ebeeb..454c0e5 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -1333,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/main.mk b/core/main.mk
index 4b76971..624df49 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -277,7 +277,10 @@
 # Include all of the makefiles in the system
 #
 
-subdir_makefiles := $(SOONG_OUT_DIR)/installs-$(TARGET_PRODUCT)$(COVERAGE_SUFFIX).mk $(SOONG_ANDROID_MK)
+subdir_makefiles := \
+    $(SOONG_OUT_DIR)/installs-$(TARGET_PRODUCT)$(COVERAGE_SUFFIX).mk \
+    $(SOONG_ANDROID_MK) \
+    build/make/target/board/android-info.mk
 
 # Android.mk files are only used on Linux builds, Mac only supports Android.bp
 ifeq ($(HOST_OS),linux)
diff --git a/core/soong_config.mk b/core/soong_config.mk
index 4485072..ace025e 100644
--- a/core/soong_config.mk
+++ b/core/soong_config.mk
@@ -447,6 +447,10 @@
   $(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))
diff --git a/core/sysprop.mk b/core/sysprop.mk
index e5f1267..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).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 :=
-
 KNOWN_OEM_THUMBPRINT_PROPERTIES := \
     ro.product.brand \
     ro.product.name \
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/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/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/Android.mk b/target/board/android-info.mk
similarity index 100%
rename from target/board/Android.mk
rename to target/board/android-info.mk
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 2907e07..52498ee 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -178,6 +178,7 @@
     libmedia \
     libmedia_jni \
     libmediandk \
+    libmonkey_jni \
     libmtp \
     libnetd_client \
     libnetlink \
@@ -204,6 +205,7 @@
     libstdc++ \
     libsysutils \
     libui \
+    libuprobestats_client \
     libusbhost \
     libutils \
     libvintf_jni \
@@ -293,7 +295,6 @@
     uiautomator \
     uinput \
     uncrypt \
-    uprobestats \
     usbd \
     vdc \
     vintf \
@@ -311,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 += \
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 66bf694..75af062 100644
--- a/target/product/generic/Android.bp
+++ b/target/product/generic/Android.bp
@@ -1,5 +1,4 @@
 generic_rootdirs = [
-    "acct",
     "apex",
     "bootstrap-apex",
     "config",
@@ -743,6 +742,11 @@
                     "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: {
@@ -757,7 +761,12 @@
                 "android.system.virtualizationservice-ndk",
                 "libgsi",
                 "servicemanager",
-            ],
+            ] + select(release_flag("RELEASE_UPROBESTATS_MODULE"), {
+                true: [],
+                default: [
+                    "uprobestats", // base_system internal
+                ],
+            }),
         },
         both: {
             deps: [
@@ -830,6 +839,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
@@ -873,6 +883,11 @@
             }) + 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
+                ],
             }),
         },
     },
diff --git a/target/product/gsi_release.mk b/target/product/gsi_release.mk
index f00c38c..115b355 100644
--- a/target/product/gsi_release.mk
+++ b/target/product/gsi_release.mk
@@ -79,6 +79,11 @@
 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 \
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/tools/aconfig/aconfig/src/commands.rs b/tools/aconfig/aconfig/src/commands.rs
index 4c06462..547076f 100644
--- a/tools/aconfig/aconfig/src/commands.rs
+++ b/tools/aconfig/aconfig/src/commands.rs
@@ -439,7 +439,7 @@
         // Exclude system/vendor/product flags that are RO+disabled.
         let should_filter_container = pf.container == Some("vendor".to_string())
             || pf.container == Some("system".to_string())
-            || pf.container == Some("vendor".to_string());
+            || pf.container == Some("product".to_string());
         if !(should_filter_container
             && pf.state == Some(ProtoFlagState::DISABLED.into())
             && pf.permission == Some(ProtoFlagPermission::READ_ONLY.into()))
diff --git a/tools/aconfig/aconfig_flags/flags.aconfig b/tools/aconfig/aconfig_flags/flags.aconfig
index 0a004ca..96bb81a 100644
--- a/tools/aconfig/aconfig_flags/flags.aconfig
+++ b/tools/aconfig/aconfig_flags/flags.aconfig
@@ -14,3 +14,20 @@
   bug: "369808805"
   description: "When enabled, launch aconfigd from config infra module."
 }
+
+flag {
+  name: "tools_read_from_new_storage"
+  namespace: "core_experiments_team_internal"
+  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
+  }
+}
\ No newline at end of file
diff --git a/tools/aconfig/aconfig_storage_read_api/Android.bp b/tools/aconfig/aconfig_storage_read_api/Android.bp
index fa41ff0..16341b9 100644
--- a/tools/aconfig/aconfig_storage_read_api/Android.bp
+++ b/tools/aconfig/aconfig_storage_read_api/Android.bp
@@ -154,18 +154,15 @@
 java_library {
     name: "aconfig_storage_reader_java",
     srcs: [
-        "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,
+    sdk_version: "current",
     visibility: [
         "//frameworks/base",
         "//build/make/tools/aconfig/aconfig_storage_read_api/tests",
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/tests/functional/srcs/PlatformAconfigPackageInternalTest.java b/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/PlatformAconfigPackageInternalTest.java
index 2b0aeae..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
@@ -45,7 +45,7 @@
     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();
@@ -82,7 +82,7 @@
     }
 
     @Test
-    public void testAconfigPackage_load_withError() throws IOException {
+    public void testPlatformAconfigPackage_load_withError() throws IOException {
         // container not found fake_container
         AconfigStorageException e =
                 assertThrows(
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/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/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/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/environment.sh b/tools/finalization/environment.sh
index f68a68b..cf3e61b 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='W'
+export FINAL_CORRESPONDING_PLATFORM_VERSION='16'
+export FINAL_NEXT_BOARD_API_LEVEL='202604'
+export FINAL_NEXT_CORRESPONDING_VERSION_LETTER='X'
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/releasetools/build_image.py b/tools/releasetools/build_image.py
index b6c96c4..25bcb52 100755
--- a/tools/releasetools/build_image.py
+++ b/tools/releasetools/build_image.py
@@ -977,6 +977,8 @@
     "will install in by default, so there could be files in the input_directory that are not "
     "actually supposed to be part of the partition. The paths in this file must be relative to "
     "input_directory.")
+  parser.add_argument("--build_datetime_file",
+                      help="a file containing the build id timestamp")
   parser.add_argument("input_directory",
     help="the staging directory to be converted to an image file")
   parser.add_argument("properties_file",
@@ -996,6 +998,11 @@
   common.InitLogging()
 
   glob_dict = LoadGlobalDict(args.properties_file)
+  if args.build_datetime_file:
+    # Parse the timestamp from build_datetime_file.
+    with open(args.build_datetime_file, "r") as f:
+      glob_dict["timestamp"] = int(f.read())
+
   if "mount_point" in glob_dict:
     # The caller knows the mount point and provides a dictionary needed by
     # BuildImage().