Merge "Export PRODUCT_VENDOR_LINKER_CONFIG_FRAGMENTS to soong" into main
diff --git a/core/Makefile b/core/Makefile
index 638d2b9..25429e7 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -5298,7 +5298,7 @@
 	  --dirmap /system_ext:$(TARGET_OUT_SYSTEM_EXT) \
 	  --dirmap /product:$(TARGET_OUT_PRODUCT) \
 	  --dirmap /apex:$(APEX_OUT) \
-	  $(VINTF_FRAMEWORK_MANIFEST_FROZEN_DIR) > $@ 2>&1 ) || ( cat $@ && exit 1 )
+	  system/libhidl/vintfdata/frozen > $@ 2>&1 ) || ( cat $@ && exit 1 )
 
 $(call declare-1p-target,$(vintffm_log))
 
diff --git a/core/android_soong_config_vars.mk b/core/android_soong_config_vars.mk
index 48b6ff2..10d365c 100644
--- a/core/android_soong_config_vars.mk
+++ b/core/android_soong_config_vars.mk
@@ -102,6 +102,10 @@
 $(call add_soong_config_var_value,ANDROID,avf_microdroid_guest_gki_version,$(PRODUCT_AVF_MICRODROID_GUEST_GKI_VERSION))
 endif
 
+ifdef TARGET_BOOTS_16K
+$(call soong_config_set_bool,ANDROID,target_boots_16k,$(filter true,$(TARGET_BOOTS_16K)))
+endif
+
 ifdef PRODUCT_MEMCG_V2_FORCE_ENABLED
 $(call add_soong_config_var_value,ANDROID,memcg_v2_force_enabled,$(PRODUCT_MEMCG_V2_FORCE_ENABLED))
 endif
@@ -234,3 +238,18 @@
 ifdef BOARD_USE_MAX_SECURE_RESOURCE
   $(call soong_config_set,video_codec,board_use_max_secure_resource,$(BOARD_USE_MAX_SECURE_RESOURCE))
 endif
+
+# Export related variables to soong for hardware/google/graphics/common/libacryl:libacryl
+ifdef BOARD_LIBACRYL_DEFAULT_COMPOSITOR
+  $(call soong_config_set,acryl,libacryl_default_compositor,$(BOARD_LIBACRYL_DEFAULT_COMPOSITOR))
+endif
+ifdef BOARD_LIBACRYL_DEFAULT_SCALER
+  $(call soong_config_set,acryl,libacryl_default_scaler,$(BOARD_LIBACRYL_DEFAULT_SCALER))
+endif
+ifdef BOARD_LIBACRYL_DEFAULT_BLTER
+  $(call soong_config_set,acryl,libacryl_default_blter,$(BOARD_LIBACRYL_DEFAULT_BLTER))
+endif
+ifdef BOARD_LIBACRYL_G2D_HDR_PLUGIN
+  #BOARD_LIBACRYL_G2D_HDR_PLUGIN is set in each board config
+  $(call soong_config_set_bool,acryl,libacryl_use_g2d_hdr_plugin,true)
+endif
diff --git a/core/soong_config.mk b/core/soong_config.mk
index b608d2f..ac49aee 100644
--- a/core/soong_config.mk
+++ b/core/soong_config.mk
@@ -352,6 +352,7 @@
 $(call add_json_list, SystemExtPropFiles, $(TARGET_SYSTEM_EXT_PROP))
 $(call add_json_list, ProductPropFiles, $(TARGET_PRODUCT_PROP))
 $(call add_json_list, OdmPropFiles, $(TARGET_ODM_PROP))
+$(call add_json_list, VendorPropFiles, $(TARGET_VENDOR_PROP))
 
 $(call add_json_str, ExtraAllowedDepsTxt, $(EXTRA_ALLOWED_DEPS_TXT))
 
@@ -437,6 +438,26 @@
 
 $(call end_json_map)
 
+# For converting vintf_data
+$(call add_json_list, DeviceMatrixFile, $(DEVICE_MATRIX_FILE))
+$(call add_json_list, ProductManifestFiles, $(PRODUCT_MANIFEST_FILES))
+$(call add_json_list, SystemManifestFile, $(DEVICE_FRAMEWORK_MANIFEST_FILE))
+SYSTEM_EXT_HWSERVICE_FILES :=
+ifeq ($(PRODUCT_HIDL_ENABLED),true)
+  ifneq ($(filter hwservicemanager,$(PRODUCT_PACKAGES)),)
+    SYSTEM_EXT_HWSERVICE_FILES += system/hwservicemanager/hwservicemanager_no_max.xml
+  else
+    $(error If PRODUCT_HIDL_ENABLED is set, hwservicemanager must be added to PRODUCT_PACKAGES explicitly)
+  endif
+else
+  ifneq ($(filter hwservicemanager,$(PRODUCT_PACKAGES)),)
+    SYSTEM_EXT_HWSERVICE_FILES += system/hwservicemanager/hwservicemanager.xml
+  else ifneq ($(filter hwservicemanager,$(PRODUCT_PACKAGES_SHIPPING_API_LEVEL_34)),)
+    SYSTEM_EXT_HWSERVICE_FILES += system/hwservicemanager/hwservicemanager.xml
+  endif
+endif
+$(call add_json_list, SystemExtManifestFiles, $(SYSTEM_EXT_MANIFEST_FILES) $(SYSTEM_EXT_HWSERVICE_FILES))
+
 $(call json_end)
 
 $(file >$(SOONG_VARIABLES).tmp,$(json_contents))
diff --git a/core/soong_extra_config.mk b/core/soong_extra_config.mk
index 00b5c0f..2ff83a1 100644
--- a/core/soong_extra_config.mk
+++ b/core/soong_extra_config.mk
@@ -43,6 +43,7 @@
 $(call add_json_list, PRODUCT_PRODUCT_PROPERTIES,        $(call collapse-prop-pairs,PRODUCT_PRODUCT_PROPERTIES))
 $(call add_json_list, PRODUCT_ODM_PROPERTIES,            $(call collapse-prop-pairs,PRODUCT_ODM_PROPERTIES))
 $(call add_json_list, PRODUCT_PROPERTY_OVERRIDES,        $(call collapse-prop-pairs,PRODUCT_PROPERTY_OVERRIDES))
+$(call add_json_list, PRODUCT_DEFAULT_PROPERTY_OVERRIDES,        $(call collapse-prop-pairs,PRODUCT_DEFAULT_PROPERTY_OVERRIDES))
 
 $(call add_json_str, BootloaderBoardName, $(TARGET_BOOTLOADER_BOARD_NAME))
 
diff --git a/target/product/base_system.mk b/target/product/base_system.mk
index 4a27b7d..5ce21e2 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -274,7 +274,6 @@
     Shell \
     shell_and_utilities_system \
     sm \
-    snapshotctl \
     snapuserd \
     storaged \
     surfaceflinger \
@@ -492,6 +491,7 @@
     record_binder \
     servicedispatcher \
     showmap \
+    snapshotctl \
     sqlite3 \
     ss \
     start_with_lockagent \
@@ -505,10 +505,6 @@
     unwind_reg_info \
     unwind_symbols \
 
-# For Remotely Provisioned Certificate Processor
-PRODUCT_SYSTEM_PROPERTIES += \
-    remote_provisioning.use_cert_processor=false
-
 # The set of packages whose code can be loaded by the system server.
 PRODUCT_SYSTEM_SERVER_APPS += \
     SettingsProvider \
diff --git a/tools/aconfig/aconfig/src/codegen/java.rs b/tools/aconfig/aconfig/src/codegen/java.rs
index 47d4042..81c7d00 100644
--- a/tools/aconfig/aconfig/src/codegen/java.rs
+++ b/tools/aconfig/aconfig/src/codegen/java.rs
@@ -561,6 +561,8 @@
                         + "flag declaration.",
                         e
                     );
+                } catch (SecurityException e) {
+                    // for isolated process case, skip loading flag value from the storage, use the default
                 }
                 aconfig_test_is_cached = true;
             }
@@ -579,6 +581,8 @@
                         + "flag declaration.",
                         e
                     );
+                } catch (SecurityException e) {
+                    // for isolated process case, skip loading flag value from the storage, use the default
                 }
                 other_namespace_is_cached = true;
             }
@@ -787,6 +791,8 @@
                         + "flag declaration.",
                         e
                     );
+                } catch (SecurityException e) {
+                    // for isolated process case, skip loading flag value from the storage, use the default
                 }
                 aconfig_test_is_cached = true;
             }
diff --git a/tools/aconfig/aconfig/src/codegen/rust.rs b/tools/aconfig/aconfig/src/codegen/rust.rs
index d318b96..569a34b 100644
--- a/tools/aconfig/aconfig/src/codegen/rust.rs
+++ b/tools/aconfig/aconfig/src/codegen/rust.rs
@@ -297,7 +297,7 @@
                             },
                             None => {
                                 log!(Level::Error, "no context found for package com.android.aconfig.test");
-                                Ok(false)
+                                Err(format!("failed to flag package com.android.aconfig.test"))
                             }
                         }
                     })
@@ -309,7 +309,7 @@
             },
             Err(err) => {
                 log!(Level::Error, "aconfig_rust_codegen: error: {err}");
-                panic!("failed to read flag value: {err}");
+                return false;
             }
         }
     } else {
@@ -344,7 +344,7 @@
                             },
                             None => {
                                 log!(Level::Error, "no context found for package com.android.aconfig.test");
-                                Ok(false)
+                                Err(format!("failed to flag package com.android.aconfig.test"))
                             }
                         }
                     })
@@ -356,7 +356,7 @@
             },
             Err(err) => {
                 log!(Level::Error, "aconfig_rust_codegen: error: {err}");
-                panic!("failed to read flag value: {err}");
+                return false;
             }
         }
     } else {
@@ -391,7 +391,7 @@
                             },
                             None => {
                                 log!(Level::Error, "no context found for package com.android.aconfig.test");
-                                Ok(false)
+                                Err(format!("failed to flag package com.android.aconfig.test"))
                             }
                         }
                     })
@@ -403,7 +403,7 @@
             },
             Err(err) => {
                 log!(Level::Error, "aconfig_rust_codegen: error: {err}");
-                panic!("failed to read flag value: {err}");
+                return false;
             }
         }
     } else {
@@ -439,7 +439,7 @@
                             },
                             None => {
                                 log!(Level::Error, "no context found for package com.android.aconfig.test");
-                                Ok(true)
+                                Err(format!("failed to flag package com.android.aconfig.test"))
                             }
                         }
                     })
@@ -451,7 +451,7 @@
             },
             Err(err) => {
                 log!(Level::Error, "aconfig_rust_codegen: error: {err}");
-                panic!("failed to read flag value: {err}");
+                return true;
             }
         }
     } else {
diff --git a/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template b/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template
index 26d3069..8c7b3fa 100644
--- a/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template
+++ b/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template
@@ -73,6 +73,8 @@
                 + "flag declaration.",
                 e
             );
+        } catch (SecurityException e) \{
+            // for isolated process case, skip loading flag value from the storage, use the default
         }
         {namespace_with_flags.namespace}_is_cached = true;
     }
diff --git a/tools/aconfig/aconfig/templates/cpp_source_file.template b/tools/aconfig/aconfig/templates/cpp_source_file.template
index eaaf86f..df3b10d 100644
--- a/tools/aconfig/aconfig/templates/cpp_source_file.template
+++ b/tools/aconfig/aconfig/templates/cpp_source_file.template
@@ -92,17 +92,21 @@
                  aconfig_storage::StorageFileType::package_map);
             if (!package_map_file.ok()) \{
                 ALOGE("error: failed to get package map file: %s", package_map_file.error().c_str());
+                package_exists_in_storage_ = false;
+                return;
             }
 
             auto context = aconfig_storage::get_package_read_context(
                 **package_map_file, "{package}");
             if (!context.ok()) \{
                 ALOGE("error: failed to get package read context: %s", context.error().c_str());
+                package_exists_in_storage_ = false;
+                return;
             }
 
             if (!(context->package_exists)) \{
-               package_exists_in_storage_ = false;
-               return;
+                package_exists_in_storage_ = false;
+                return;
             }
 
             // cache package boolean flag start index
@@ -116,6 +120,8 @@
                 aconfig_storage::StorageFileType::flag_val);
             if (!flag_value_file.ok()) \{
                 ALOGE("error: failed to get flag value file: %s", flag_value_file.error().c_str());
+                package_exists_in_storage_ = false;
+                return;
             }
 
             // cache flag value file
diff --git a/tools/aconfig/aconfig/templates/rust.template b/tools/aconfig/aconfig/templates/rust.template
index 6456360..d0079d4 100644
--- a/tools/aconfig/aconfig/templates/rust.template
+++ b/tools/aconfig/aconfig/templates/rust.template
@@ -53,7 +53,7 @@
                             },
                             None => \{
                                  log!(Level::Error, "no context found for package {package}");
-                                 Ok({flag.default_value})
+                                 Err(format!("failed to flag package {package}"))
                             }
                         }
                     })
@@ -65,7 +65,7 @@
             },
             Err(err) => \{
                 log!(Level::Error, "aconfig_rust_codegen: error: \{err}");
-                panic!("failed to read flag value: \{err}");
+                return {flag.default_value};
             }
         }
     } else \{
diff --git a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/ByteBufferReader.java b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/ByteBufferReader.java
index 4bea083..9571568 100644
--- a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/ByteBufferReader.java
+++ b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/ByteBufferReader.java
@@ -41,8 +41,16 @@
         return this.mByteBuffer.getInt();
     }
 
+    public long readLong() {
+        return this.mByteBuffer.getLong();
+    }
+
     public String readString() {
         int length = readInt();
+        if (length > 1024) {
+            throw new AconfigStorageException(
+                    "String length exceeds maximum allowed size (1024 bytes): " + length);
+        }
         byte[] bytes = new byte[length];
         mByteBuffer.get(bytes, 0, length);
         return new String(bytes, StandardCharsets.UTF_8);
diff --git a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/PackageTable.java b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/PackageTable.java
index 39b7e59..a45d12a 100644
--- a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/PackageTable.java
+++ b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/PackageTable.java
@@ -124,8 +124,10 @@
 
         private String mPackageName;
         private int mPackageId;
+        private long mPackageFingerprint;
         private int mBooleanStartIndex;
         private int mNextOffset;
+        private boolean mHasPackageFingerprint;
 
         private static Node fromBytes(ByteBufferReader reader, int version) {
             switch (version) {
@@ -153,9 +155,11 @@
             Node node = new Node();
             node.mPackageName = reader.readString();
             node.mPackageId = reader.readInt();
+            node.mPackageFingerprint = reader.readLong();
             node.mBooleanStartIndex = reader.readInt();
             node.mNextOffset = reader.readInt();
             node.mNextOffset = node.mNextOffset == 0 ? -1 : node.mNextOffset;
+            node.mHasPackageFingerprint = true;
             return node;
         }
 
@@ -189,6 +193,10 @@
             return mPackageId;
         }
 
+        public long getPackageFingerprint() {
+            return mPackageFingerprint;
+        }
+
         public int getBooleanStartIndex() {
             return mBooleanStartIndex;
         }
@@ -196,5 +204,9 @@
         public int getNextOffset() {
             return mNextOffset;
         }
+
+        public boolean hasPackageFingerprint() {
+            return mHasPackageFingerprint;
+        }
     }
 }
diff --git a/tools/aconfig/aconfig_storage_file/tests/srcs/PackageTableTest.java b/tools/aconfig/aconfig_storage_file/tests/srcs/PackageTableTest.java
index e7e19d8..dc2ad92 100644
--- a/tools/aconfig/aconfig_storage_file/tests/srcs/PackageTableTest.java
+++ b/tools/aconfig/aconfig_storage_file/tests/srcs/PackageTableTest.java
@@ -17,6 +17,7 @@
 package android.aconfig.storage.test;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 
 import android.aconfig.storage.FileType;
 import android.aconfig.storage.PackageTable;
@@ -66,5 +67,9 @@
         assertEquals(159, node1.getNextOffset());
         assertEquals(-1, node2.getNextOffset());
         assertEquals(-1, node4.getNextOffset());
+
+        assertFalse(node1.hasPackageFingerprint());
+        assertFalse(node2.hasPackageFingerprint());
+        assertFalse(node4.hasPackageFingerprint());
     }
 }
diff --git a/tools/aconfig/aconfig_storage_read_api/Android.bp b/tools/aconfig/aconfig_storage_read_api/Android.bp
index 80b8ece..666c5ba 100644
--- a/tools/aconfig/aconfig_storage_read_api/Android.bp
+++ b/tools/aconfig/aconfig_storage_read_api/Android.bp
@@ -154,6 +154,8 @@
 java_library {
     name: "aconfig_storage_reader_java",
     srcs: [
+        "srcs/android/aconfig/storage/AconfigPackageImpl.java",
+        "srcs/android/aconfig/storage/StorageFileProvider.java",
         "srcs/android/aconfig/storage/StorageInternalReader.java",
     ],
     libs: [
@@ -175,6 +177,8 @@
 java_library {
     name: "aconfig_storage_reader_java_none",
     srcs: [
+        "srcs/android/aconfig/storage/AconfigPackageImpl.java",
+        "srcs/android/aconfig/storage/StorageFileProvider.java",
         "srcs/android/aconfig/storage/StorageInternalReader.java",
     ],
     libs: [
diff --git a/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/AconfigPackageImpl.java b/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/AconfigPackageImpl.java
new file mode 100644
index 0000000..4d020cf
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/AconfigPackageImpl.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage;
+
+import android.os.StrictMode;
+
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+
+/** @hide */
+public class AconfigPackageImpl {
+    private FlagTable mFlagTable;
+    private FlagValueList mFlagValueList;
+    private PackageTable.Node mPNode;
+
+    /** @hide */
+    public static AconfigPackageImpl load(String packageName, StorageFileProvider fileProvider) {
+        AconfigPackageImpl aPackage = new AconfigPackageImpl();
+        if (!aPackage.init(null, packageName, fileProvider)) {
+            return null;
+        }
+        return aPackage;
+    }
+
+    /** @hide */
+    public static AconfigPackageImpl load(
+            String container, String packageName, StorageFileProvider fileProvider) {
+        if (container == null) {
+            return null;
+        }
+        AconfigPackageImpl aPackage = new AconfigPackageImpl();
+        if (!aPackage.init(container, packageName, fileProvider)) {
+            return null;
+        }
+        return aPackage;
+    }
+
+    /** @hide */
+    public boolean getBooleanFlagValue(String flagName, boolean defaultValue) {
+        FlagTable.Node fNode = mFlagTable.get(mPNode.getPackageId(), flagName);
+        // no such flag in this package
+        if (fNode == null) return defaultValue;
+        int index = fNode.getFlagIndex() + mPNode.getBooleanStartIndex();
+        return mFlagValueList.getBoolean(index);
+    }
+
+    /** @hide */
+    public boolean getBooleanFlagValue(int index) {
+        return mFlagValueList.getBoolean(index + mPNode.getBooleanStartIndex());
+    }
+
+    /** @hide */
+    public long getPackageFingerprint() {
+        return mPNode.getPackageFingerprint();
+    }
+
+    /** @hide */
+    public boolean hasPackageFingerprint() {
+        return mPNode.hasPackageFingerprint();
+    }
+
+    private boolean init(
+            String containerName, String packageName, StorageFileProvider fileProvider) {
+        StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+        String container = containerName;
+        try {
+            // for devices don't have new storage directly return
+            if (!fileProvider.containerFileExists(null)) {
+                return false;
+            }
+            PackageTable.Node pNode = null;
+
+            if (container == null) {
+                PackageTable pTable = null;
+                // check if device has flag files on the system partition
+                // if the device has then search system partition first
+                container = "system";
+                if (fileProvider.containerFileExists(container)) {
+                    pTable = fileProvider.getPackageTable(container);
+                    pNode = pTable.get(packageName);
+                }
+                List<Path> mapFiles = new ArrayList<>();
+                if (pNode == null) {
+                    mapFiles = fileProvider.listPackageMapFiles();
+                    if (mapFiles.isEmpty()) return false;
+                }
+
+                for (Path p : mapFiles) {
+                    pTable = StorageFileProvider.getPackageTable(p);
+                    pNode = pTable.get(packageName);
+                    if (pNode != null) {
+                        container = pTable.getHeader().getContainer();
+                        break;
+                    }
+                }
+            } else {
+                pNode = fileProvider.getPackageTable(container).get(packageName);
+            }
+
+            if (pNode == null) {
+                // for the case package is not found in all container, return instead of throwing
+                // error
+                return false;
+            }
+
+            mFlagTable = fileProvider.getFlagTable(container);
+            mFlagValueList = fileProvider.getFlagValueList(container);
+            mPNode = pNode;
+        } catch (Exception e) {
+            throw new AconfigStorageException(
+                    String.format(
+                            "cannot load package %s, from container %s", packageName, container),
+                    e);
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
+        return true;
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/StorageFileProvider.java b/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/StorageFileProvider.java
new file mode 100644
index 0000000..6a6f007
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/StorageFileProvider.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage;
+
+import java.io.Closeable;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.List;
+
+/** @hide */
+public class StorageFileProvider {
+
+    private static final String DEFAULT_MAP_PATH = "/metadata/aconfig/maps/";
+    private static final String DEFAULT_BOOT_PATH = "/metadata/aconfig/boot/";
+    private static final String PMAP_FILE_EXT = ".package.map";
+    private static final String FMAP_FILE_EXT = ".flag.map";
+    private static final String VAL_FILE_EXT = ".val";
+
+    private final String mMapPath;
+    private final String mBootPath;
+
+    /** @hide */
+    public StorageFileProvider getDefaultProvider() {
+        return new StorageFileProvider(DEFAULT_MAP_PATH, DEFAULT_BOOT_PATH);
+    }
+
+    /** @hide */
+    public StorageFileProvider(String mapPath, String bootPath) {
+        mMapPath = mapPath;
+        mBootPath = bootPath;
+    }
+
+    /** @hide */
+    public boolean containerFileExists(String container) {
+        if (container == null) {
+            return Files.exists(Paths.get(DEFAULT_MAP_PATH));
+        }
+        return Files.exists(Paths.get(mMapPath, container + PMAP_FILE_EXT));
+    }
+
+    /** @hide */
+    public List<Path> listPackageMapFiles() {
+        List<Path> result = new ArrayList<>();
+        try {
+            DirectoryStream<Path> stream =
+                    Files.newDirectoryStream(Paths.get(mMapPath), "*" + PMAP_FILE_EXT);
+            for (Path entry : stream) {
+                result.add(entry);
+                // sb.append(entry. toString());
+            }
+        } catch (Exception e) {
+            throw new AconfigStorageException(
+                    String.format("Fail to list map files in path %s", mMapPath), e);
+        }
+
+        return result;
+    }
+
+    /** @hide */
+    public PackageTable getPackageTable(String container) {
+        return getPackageTable(Paths.get(mMapPath, container + PMAP_FILE_EXT));
+    }
+
+    /** @hide */
+    public FlagTable getFlagTable(String container) {
+        return FlagTable.fromBytes(mapStorageFile(Paths.get(mMapPath, container + FMAP_FILE_EXT)));
+    }
+
+    /** @hide */
+    public FlagValueList getFlagValueList(String container) {
+        return FlagValueList.fromBytes(
+                mapStorageFile(Paths.get(mBootPath, container + VAL_FILE_EXT)));
+    }
+
+    /** @hide */
+    public static PackageTable getPackageTable(Path path) {
+        return PackageTable.fromBytes(mapStorageFile(path));
+    }
+
+    // Map a storage file given file path
+    private static MappedByteBuffer mapStorageFile(Path file) {
+        FileChannel channel = null;
+        try {
+            channel = FileChannel.open(file, StandardOpenOption.READ);
+            return channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
+        } catch (Exception e) {
+            throw new AconfigStorageException(
+                    String.format("Fail to mmap storage file %s", file), e);
+        } finally {
+            quietlyDispose(channel);
+        }
+    }
+
+    private static void quietlyDispose(Closeable closable) {
+        try {
+            if (closable != null) {
+                closable.close();
+            }
+        } catch (Exception e) {
+            // no need to care, at least as of now
+        }
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/java/AconfigPackageImplTest.java b/tools/aconfig/aconfig_storage_read_api/tests/java/AconfigPackageImplTest.java
new file mode 100644
index 0000000..0f3be7a
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/java/AconfigPackageImplTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage.test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+
+import android.aconfig.storage.AconfigPackageImpl;
+import android.aconfig.storage.AconfigStorageException;
+import android.aconfig.storage.StorageFileProvider;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class AconfigPackageImplTest {
+
+    @Test
+    public void testLoad_onlyPackageName() throws Exception {
+        StorageFileProvider pr =
+                new StorageFileProvider(TestDataUtils.TESTDATA_PATH, TestDataUtils.TESTDATA_PATH);
+        AconfigPackageImpl p = AconfigPackageImpl.load("com.android.aconfig.storage.test_1", pr);
+        assertNotNull(p);
+    }
+
+    @Test
+    public void testLoad_groupNameFingerprint() throws Exception {
+        StorageFileProvider pr =
+                new StorageFileProvider(TestDataUtils.TESTDATA_PATH, TestDataUtils.TESTDATA_PATH);
+        AconfigPackageImpl p =
+                AconfigPackageImpl.load("mockup", "com.android.aconfig.storage.test_1", pr);
+        assertNotNull(p);
+
+        assertThrows(
+                AconfigStorageException.class,
+                () -> AconfigPackageImpl.load("test", "com.android.aconfig.storage.test_1", pr));
+    }
+
+    @Test
+    public void testGetBooleanFlagValue_flagName() throws Exception {
+        StorageFileProvider pr =
+                new StorageFileProvider(TestDataUtils.TESTDATA_PATH, TestDataUtils.TESTDATA_PATH);
+        AconfigPackageImpl p =
+                AconfigPackageImpl.load("mockup", "com.android.aconfig.storage.test_1", pr);
+        assertFalse(p.getBooleanFlagValue("disabled_rw", true));
+        assertTrue(p.getBooleanFlagValue("enabled_ro", false));
+        assertTrue(p.getBooleanFlagValue("enabled_rw", false));
+        assertFalse(p.getBooleanFlagValue("fake", false));
+    }
+
+    @Test
+    public void testGetBooleanFlagValue_index() throws Exception {
+        StorageFileProvider pr =
+                new StorageFileProvider(TestDataUtils.TESTDATA_PATH, TestDataUtils.TESTDATA_PATH);
+        AconfigPackageImpl p =
+                AconfigPackageImpl.load("mockup", "com.android.aconfig.storage.test_1", pr);
+        assertFalse(p.getBooleanFlagValue(0));
+        assertTrue(p.getBooleanFlagValue(1));
+        assertTrue(p.getBooleanFlagValue(2));
+    }
+
+    @Test
+    public void testHasPackageFingerprint() throws Exception {
+        StorageFileProvider pr =
+                new StorageFileProvider(TestDataUtils.TESTDATA_PATH, TestDataUtils.TESTDATA_PATH);
+        AconfigPackageImpl p =
+                AconfigPackageImpl.load("mockup", "com.android.aconfig.storage.test_1", pr);
+        assertFalse(p.hasPackageFingerprint());
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/java/Android.bp b/tools/aconfig/aconfig_storage_read_api/tests/java/Android.bp
index 3d4e9ad..9c88122 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/java/Android.bp
+++ b/tools/aconfig/aconfig_storage_read_api/tests/java/Android.bp
@@ -22,3 +22,28 @@
     ],
     team: "trendy_team_android_core_experiments",
 }
+
+android_test {
+    name: "aconfig_storage_package",
+    team: "trendy_team_android_core_experiments",
+    srcs: [
+        "AconfigPackageImplTest.java",
+        "StorageFileProviderTest.java",
+        "TestDataUtils.java",
+    ],
+    static_libs: [
+        "androidx.test.runner",
+        "junit",
+        "aconfig_storage_reader_java",
+    ],
+    test_config: "AndroidStorageJaveTest.xml",
+    manifest: "AndroidPackageTestManifest.xml",
+    sdk_version: "test_current",
+    data: [
+        ":read_api_test_storage_files",
+    ],
+    test_suites: [
+        "general-tests",
+    ],
+    jarjar_rules: "jarjar.txt",
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidPackageTestManifest.xml b/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidPackageTestManifest.xml
new file mode 100644
index 0000000..5e01879
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidPackageTestManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.aconfig.storage.test">
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.aconfig.storage.test" />
+
+</manifest>
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidStorageJaveTest.xml b/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidStorageJaveTest.xml
new file mode 100644
index 0000000..3d5bb8e
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidStorageJaveTest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<configuration description="Test aconfig storage java tests">
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="aconfig_storage_package.apk" />
+    </target_preparer>
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true" />
+        <option name="push" value="package.map->/data/local/tmp/aconfig_storage_package/testdata/mockup.package.map" />
+        <option name="push" value="flag.map->/data/local/tmp/aconfig_storage_package/testdata/mockup.flag.map" />
+        <option name="push" value="flag.val->/data/local/tmp/aconfig_storage_package/testdata/mockup.val" />
+        <option name="push" value="flag.info->/data/local/tmp/aconfig_storage_package/testdata/mockup.info" />
+        <option name="post-push" value="chmod +r /data/local/tmp/aconfig_storage_package/testdata/" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.aconfig.storage.test" />
+        <option name="runtime-hint" value="1m" />
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/java/StorageFileProviderTest.java b/tools/aconfig/aconfig_storage_read_api/tests/java/StorageFileProviderTest.java
new file mode 100644
index 0000000..ba1ae9e
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/java/StorageFileProviderTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.aconfig.storage.FlagTable;
+import android.aconfig.storage.FlagValueList;
+import android.aconfig.storage.PackageTable;
+import android.aconfig.storage.StorageFileProvider;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public class StorageFileProviderTest {
+
+    @Test
+    public void testContainerFileExists() throws Exception {
+        StorageFileProvider p =
+                new StorageFileProvider(TestDataUtils.TESTDATA_PATH, TestDataUtils.TESTDATA_PATH);
+        assertTrue(p.containerFileExists(null));
+        assertTrue(p.containerFileExists("mockup"));
+        assertFalse(p.containerFileExists("fake"));
+    }
+
+    @Test
+    public void testListpackageMapFiles() throws Exception {
+        StorageFileProvider p =
+                new StorageFileProvider(TestDataUtils.TESTDATA_PATH, TestDataUtils.TESTDATA_PATH);
+        // throw new Exception(Environment.getExternalStorageDirectory().getAbsolutePath());
+        List<Path> file = p.listPackageMapFiles();
+        assertEquals(1, file.size());
+        assertTrue(
+                file.get(0)
+                        .equals(
+                                Paths.get(
+                                        TestDataUtils.TESTDATA_PATH,
+                                        TestDataUtils.TEST_PACKAGE_MAP_PATH)));
+    }
+
+    @Test
+    public void testLoadFiles() throws Exception {
+        StorageFileProvider p =
+                new StorageFileProvider(TestDataUtils.TESTDATA_PATH, TestDataUtils.TESTDATA_PATH);
+        PackageTable pt = p.getPackageTable("mockup");
+        assertNotNull(pt);
+        pt =
+                StorageFileProvider.getPackageTable(
+                        Paths.get(
+                                TestDataUtils.TESTDATA_PATH, TestDataUtils.TEST_PACKAGE_MAP_PATH));
+        assertNotNull(pt);
+        FlagTable f = p.getFlagTable("mockup");
+        assertNotNull(f);
+        FlagValueList v = p.getFlagValueList("mockup");
+        assertNotNull(v);
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/java/TestDataUtils.java b/tools/aconfig/aconfig_storage_read_api/tests/java/TestDataUtils.java
new file mode 100644
index 0000000..d5cddc7
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/java/TestDataUtils.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage.test;
+
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+
+public final class TestDataUtils {
+    public static final String TEST_PACKAGE_MAP_PATH = "mockup.package.map";
+    public static final String TEST_FLAG_MAP_PATH = "mockup.flag.map";
+    public static final String TEST_FLAG_VAL_PATH = "mockup.val";
+    public static final String TEST_FLAG_INFO_PATH = "mockup.info";
+
+    public static final String TESTDATA_PATH =
+            "/data/local/tmp/aconfig_storage_package/testdata/";
+
+    public static ByteBuffer getTestPackageMapByteBuffer() throws Exception {
+        return readFile(TESTDATA_PATH + TEST_PACKAGE_MAP_PATH);
+    }
+
+    public static ByteBuffer getTestFlagMapByteBuffer() throws Exception {
+        return readFile(TESTDATA_PATH + TEST_FLAG_MAP_PATH);
+    }
+
+    public static ByteBuffer getTestFlagValByteBuffer() throws Exception {
+        return readFile(TESTDATA_PATH + TEST_FLAG_VAL_PATH);
+    }
+
+    public static ByteBuffer getTestFlagInfoByteBuffer() throws Exception {
+        return readFile(TESTDATA_PATH + TEST_FLAG_INFO_PATH);
+    }
+
+    private static ByteBuffer readFile(String fileName) throws Exception {
+        InputStream input = new FileInputStream(fileName);
+        return ByteBuffer.wrap(input.readAllBytes());
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/java/jarjar.txt b/tools/aconfig/aconfig_storage_read_api/tests/java/jarjar.txt
new file mode 100644
index 0000000..64ba09c
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/java/jarjar.txt
@@ -0,0 +1,17 @@
+rule android.aconfig.storage.AconfigStorageException android.aconfig.storage.test.AconfigStorageException
+rule android.aconfig.storage.FlagTable android.aconfig.storage.test.FlagTable
+rule android.aconfig.storage.PackageTable android.aconfig.storage.test.PackageTable
+rule android.aconfig.storage.ByteBufferReader android.aconfig.storage.test.ByteBufferReader
+rule android.aconfig.storage.FlagType android.aconfig.storage.test.FlagType
+rule android.aconfig.storage.SipHasher13 android.aconfig.storage.test.SipHasher13
+rule android.aconfig.storage.FileType android.aconfig.storage.test.FileType
+rule android.aconfig.storage.FlagValueList android.aconfig.storage.test.FlagValueList
+rule android.aconfig.storage.TableUtils android.aconfig.storage.test.TableUtils
+rule android.aconfig.storage.Package android.aconfig.storage.test.Package
+rule android.aconfig.storage.StorageFileProvider android.aconfig.storage.test.StorageFileProvider
+
+
+rule android.aconfig.storage.FlagTable$* android.aconfig.storage.test.FlagTable$@1
+rule android.aconfig.storage.PackageTable$* android.aconfig.storage.test.PackageTable$@1
+rule android.aconfig.storage.FlagValueList$* android.aconfig.storage.test.FlagValueList@1
+rule android.aconfig.storage.SipHasher13$* android.aconfig.storage.test.SipHasher13@1
diff --git a/tools/aconfig/aflags/src/load_protos.rs b/tools/aconfig/aflags/src/load_protos.rs
index f201d8f..c5ac8ff 100644
--- a/tools/aconfig/aflags/src/load_protos.rs
+++ b/tools/aconfig/aflags/src/load_protos.rs
@@ -51,7 +51,10 @@
 
     let paths = aconfig_device_paths::parsed_flags_proto_paths()?;
     for path in paths {
-        let bytes = fs::read(path.clone())?;
+        let Ok(bytes) = fs::read(&path) else {
+            eprintln!("warning: failed to read {:?}", path);
+            continue;
+        };
         let parsed_flags: ProtoParsedFlags = protobuf::Message::parse_from_bytes(&bytes)?;
         for flag in parsed_flags.parsed_flag {
             // TODO(b/334954748): enforce one-container-per-flag invariant.