Set system property from list of backported fixes

Because the caluculation of a BitSet is
to compicated for make a genrule and java are
used to ro.build.backported_fixes.alias_bitset.long_list to the end of
the system property file.

This part of the API for backported fixes.  See go/android-backported-fixes-spec

Bug: 377727445
Test: atest applied_backported_fixes_test backported_fixes_common_test
Change-Id: I2621e2fb52401495f796a5a4db330c5dfafbda40
diff --git a/backported_fixes/Android.bp b/backported_fixes/Android.bp
new file mode 100644
index 0000000..a20f3fc
--- /dev/null
+++ b/backported_fixes/Android.bp
@@ -0,0 +1,115 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// 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"],
+    default_team: "trendy_team_android_media_reliability",
+}
+
+genrule {
+    name: "applied_backported_fixes",
+    tools: ["applied_backported_fixes_main"],
+    srcs: [":applied_backported_fix_binpbs"],
+    out: ["applied_backported_fixes.prop"],
+    cmd: "$(location applied_backported_fixes_main)" +
+        " -p $(location applied_backported_fixes.prop)" +
+        " $(in)",
+}
+
+java_library {
+    name: "backported_fixes_proto",
+    srcs: [
+        "backported_fixes.proto",
+    ],
+    host_supported: true,
+}
+
+java_library {
+    name: "backported_fixes_common",
+    srcs: ["src/java/com/android/build/backportedfixes/common/*.java"],
+    static_libs: [
+        "backported_fixes_proto",
+        "guava",
+    ],
+    host_supported: true,
+}
+
+java_test_host {
+    name: "backported_fixes_common_test",
+    srcs: ["tests/java/com/android/build/backportedfixes/common/*.java"],
+    static_libs: [
+        "backported_fixes_common",
+        "backported_fixes_proto",
+        "junit",
+        "truth",
+        "truth-liteproto-extension",
+        "truth-proto-extension",
+    ],
+    test_options: {
+        unit_test: true,
+    },
+    test_suites: ["general-tests"],
+}
+
+java_library {
+    name: "applied_backported_fixes_lib",
+    srcs: ["src/java/com/android/build/backportedfixes/*.java"],
+    static_libs: [
+        "backported_fixes_common",
+        "backported_fixes_proto",
+        "jcommander",
+        "guava",
+    ],
+    host_supported: true,
+}
+
+java_binary_host {
+    name: "applied_backported_fixes_main",
+    main_class: "com.android.build.backportedfixes.Main",
+    static_libs: [
+        "applied_backported_fixes_lib",
+    ],
+}
+
+java_test_host {
+    name: "applied_backported_fixes_test",
+    srcs: ["tests/java/com/android/build/backportedfixes/*.java"],
+    static_libs: [
+        "applied_backported_fixes_lib",
+        "backported_fixes_proto",
+        "junit",
+        "truth",
+    ],
+    test_options: {
+        unit_test: true,
+    },
+    test_suites: ["general-tests"],
+}
+
+gensrcs {
+    name: "applied_backported_fix_binpbs",
+    tools: ["aprotoc"],
+    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/OWNERS b/backported_fixes/OWNERS
new file mode 100644
index 0000000..ac176bf
--- /dev/null
+++ b/backported_fixes/OWNERS
@@ -0,0 +1,3 @@
+essick@google.com
+nchalko@google.com
+portmannc@google.com
diff --git a/backported_fixes/applied_fixes/ki350037023.txtpb b/backported_fixes/applied_fixes/ki350037023.txtpb
new file mode 100644
index 0000000..456a7ae
--- /dev/null
+++ b/backported_fixes/applied_fixes/ki350037023.txtpb
@@ -0,0 +1,19 @@
+# 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.
+#
+# proto-file: ../backported_fixes.proto
+# proto-message: BackportedFix
+
+known_issue: 350037023
+alias: 1
diff --git a/backported_fixes/backported_fixes.proto b/backported_fixes/backported_fixes.proto
new file mode 100644
index 0000000..91618ee
--- /dev/null
+++ b/backported_fixes/backported_fixes.proto
@@ -0,0 +1,37 @@
+// 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.
+
+syntax = "proto2";
+
+package com.android.build.backportedfixes;
+
+option java_multiple_files = true;
+
+// A list of backported fixes.
+message BackportedFixes {
+  repeated BackportedFix fixes = 1;
+}
+
+// A known issue approved for reporting Build.getBackportedFixStatus
+message BackportedFix {
+
+  // The issue id from the public bug tracker
+  // https://issuetracker.google.com/issues/{known_issue}
+  optional int64 known_issue = 1;
+  // The alias for the known issue.
+  // 1 - 1023 are valid aliases
+  // Must be unique across all backported fixes.
+  optional int32 alias = 2;
+}
+
diff --git a/backported_fixes/src/java/com/android/build/backportedfixes/Main.java b/backported_fixes/src/java/com/android/build/backportedfixes/Main.java
new file mode 100644
index 0000000..79148cc
--- /dev/null
+++ b/backported_fixes/src/java/com/android/build/backportedfixes/Main.java
@@ -0,0 +1,79 @@
+
+/*
+ * 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 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;
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.converters.FileConverter;
+import com.google.common.io.Files;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public final class Main {
+    @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)
+    File propertyFile;
+
+    public static void main(String... argv) throws Exception {
+        Main main = new Main();
+        JCommander.newBuilder().addObject(main).build().parse(argv);
+        main.run();
+    }
+
+    Main() {
+    }
+
+    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());
+            writeFixesAsAliasBitSet(fixes, out);
+        }
+    }
+
+    static void writeFixesAsAliasBitSet(BackportedFixes fixes, Writer out) {
+        PrintWriter printWriter = new PrintWriter(out);
+        printWriter.println("# The following backported fixes have been applied");
+        for (var f : fixes.getFixesList()) {
+            printWriter.printf("# https://issuetracker.google.com/issues/%d with alias %d",
+                    f.getKnownIssue(), f.getAlias());
+            printWriter.println();
+        }
+        var bsArray = Parser.getBitSetArray(
+                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.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
new file mode 100644
index 0000000..75b6730
--- /dev/null
+++ b/backported_fixes/src/java/com/android/build/backportedfixes/common/ClosableCollection.java
@@ -0,0 +1,67 @@
+/*
+ * 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
new file mode 100644
index 0000000..6b08b8f
--- /dev/null
+++ b/backported_fixes/src/java/com/android/build/backportedfixes/common/Parser.java
@@ -0,0 +1,71 @@
+/*
+ * 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.android.build.backportedfixes.BackportedFix;
+import com.android.build.backportedfixes.BackportedFixes;
+
+import com.google.common.collect.ImmutableList;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.BitSet;
+import java.util.List;
+
+
+/** Static utilities for working with {@link BackportedFixes}. */
+public final class Parser {
+
+    /** Creates list of FileInputStreams for a list of files. */
+    public static ImmutableList<FileInputStream> getFileInputStreams(List<File> fixFiles) throws
+            FileNotFoundException {
+        var streams = ImmutableList.<FileInputStream>builder();
+        for (var f : fixFiles) {
+            streams.add(new FileInputStream(f));
+        }
+        return streams.build();
+    }
+
+    /** Converts a list of backported fix aliases into a long array representing a {@link BitSet} */
+    public static long[] getBitSetArray(int[] aliases) {
+        BitSet bs = new BitSet();
+        for (int a : aliases) {
+            bs.set(a);
+        }
+        return bs.toLongArray();
+    }
+
+    /**
+     * 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();
+        }
+        return fixes.build();
+    }
+
+    private Parser() {
+    }
+}
diff --git a/backported_fixes/tests/java/com/android/build/backportedfixes/MainTest.java b/backported_fixes/tests/java/com/android/build/backportedfixes/MainTest.java
new file mode 100644
index 0000000..84061e1
--- /dev/null
+++ b/backported_fixes/tests/java/com/android/build/backportedfixes/MainTest.java
@@ -0,0 +1,64 @@
+
+/*
+ * 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.PrintWriter;
+import java.io.StringWriter;
+
+/** Tests for {@link Main}. */
+public class MainTest {
+
+
+    @Test
+    public void writeFixesAsAliasBitSet_default() {
+        BackportedFixes fixes = BackportedFixes.newBuilder().build();
+        var result = new StringWriter();
+
+        Main.writeFixesAsAliasBitSet(fixes, new PrintWriter(result));
+
+        Truth.assertThat(result.toString())
+                .isEqualTo("""
+                        # The following backported fixes have been applied
+                        ro.build.backported_fixes.alias_bitset.long_list=
+                        """);
+    }
+
+    @Test
+    public void writeFixesAsAliasBitSet_some() {
+        BackportedFixes fixes = BackportedFixes.newBuilder()
+                .addFixes(BackportedFix.newBuilder().setKnownIssue(1234L).setAlias(1))
+                .addFixes(BackportedFix.newBuilder().setKnownIssue(3L).setAlias(65))
+                .addFixes(BackportedFix.newBuilder().setKnownIssue(4L).setAlias(67))
+                .build();
+        var result = new StringWriter();
+
+        Main.writeFixesAsAliasBitSet(fixes, new PrintWriter(result));
+
+        Truth.assertThat(result.toString())
+                .isEqualTo("""
+                        # The following backported fixes have been applied
+                        # https://issuetracker.google.com/issues/1234 with alias 1
+                        # https://issuetracker.google.com/issues/3 with alias 65
+                        # https://issuetracker.google.com/issues/4 with alias 67
+                        ro.build.backported_fixes.alias_bitset.long_list=2,10
+                        """);
+    }
+}
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
new file mode 100644
index 0000000..d3d84a8
--- /dev/null
+++ b/backported_fixes/tests/java/com/android/build/backportedfixes/common/CloseableCollectionTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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
new file mode 100644
index 0000000..444e694
--- /dev/null
+++ b/backported_fixes/tests/java/com/android/build/backportedfixes/common/ParserTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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 static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
+
+import com.android.build.backportedfixes.BackportedFix;
+import com.android.build.backportedfixes.BackportedFixes;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+
+/** Tests for {@link Parser}.*/
+public class ParserTest {
+
+    @Test
+    public void getFileInputStreams() throws IOException {
+        var results = Parser.getFileInputStreams(
+                ImmutableList.of(Files.createTempFile("test", null).toFile()));
+        assertThat(results).isNotEmpty();
+    }
+
+
+    @Test
+    public void getBitSetArray_empty() {
+        var results = Parser.getBitSetArray(new int[]{});
+        assertThat(results).isEmpty();
+    }
+
+    @Test
+    public void getBitSetArray_2_3_64() {
+        var results = Parser.getBitSetArray(new int[]{2,3,64});
+        assertThat(results).asList().containsExactly(12L,1L).inOrder();
+    }
+
+    @Test
+    public void parseBackportedFixes_empty() throws IOException {
+        var result = Parser.parseBackportedFixes(ImmutableList.of());
+        assertThat(result).isEqualTo(BackportedFixes.getDefaultInstance());
+    }
+
+    @Test
+    public void parseBackportedFixes_oneBlank() throws IOException {
+        var result = Parser.parseBackportedFixes(
+                ImmutableList.of(inputStream(BackportedFix.getDefaultInstance())));
+
+        assertThat(result).isEqualTo(
+                BackportedFixes.newBuilder()
+                        .addFixes(BackportedFix.getDefaultInstance())
+                        .build());
+    }
+
+    @Test
+    public void parseBackportedFixes_two() throws IOException {
+        BackportedFix ki123 = BackportedFix.newBuilder()
+                .setKnownIssue(123)
+                .setAlias(1)
+                .build();
+        BackportedFix ki456 = BackportedFix.newBuilder()
+                .setKnownIssue(456)
+                .setAlias(2)
+                .build();
+        var result = Parser.parseBackportedFixes(
+                ImmutableList.of(inputStream(ki123), inputStream(ki456)));
+        assertThat(result).isEqualTo(
+                BackportedFixes.newBuilder()
+                        .addFixes(ki123)
+                        .addFixes(ki456)
+                        .build());
+    }
+
+    private static ByteArrayInputStream inputStream(BackportedFix f) {
+        return new ByteArrayInputStream(f.toByteArray());
+    }
+}