Add errorcode in AconfigStorageException

This chagne adds errorCode in AconfigStorageException, so when the
caller catch this error, the caller have the ability to know the detail
about the error

Test: atest aconfig_storage_file.test.java aconfig_storage_package
Bug: 367765164
Flag: EXEMPT refactor
Change-Id: I1da3b4383e2875e60933803c94ca2da8f9c6ad06
diff --git a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/AconfigStorageException.java b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/AconfigStorageException.java
index 86a75f2..b1c7ee7 100644
--- a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/AconfigStorageException.java
+++ b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/AconfigStorageException.java
@@ -16,12 +16,118 @@
 
 package android.aconfig.storage;
 
+/**
+ * Exception thrown when an error occurs while accessing Aconfig Storage.
+ *
+ * <p>This exception indicates a general problem with Aconfig Storage, such as an inability to read
+ * or write data.
+ */
 public class AconfigStorageException 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;
+
+    private final int mErrorCode;
+
+    /**
+     * Constructs a new {@code AconfigStorageException} with a generic error code and the specified
+     * detail message.
+     *
+     * @param msg The detail message for this exception.
+     */
     public AconfigStorageException(String msg) {
         super(msg);
+        mErrorCode = ERROR_GENERIC;
     }
 
+    /**
+     * Constructs a new {@code AconfigStorageException} with a generic error code, the specified
+     * detail message, and cause.
+     *
+     * @param msg The detail message for this exception.
+     * @param cause The cause of this exception.
+     */
     public AconfigStorageException(String msg, Throwable cause) {
         super(msg, cause);
+        mErrorCode = ERROR_GENERIC;
+    }
+
+    /**
+     * Constructs a new {@code AconfigStorageException} with the specified error code and detail
+     * message.
+     *
+     * @param errorCode The error code for this exception.
+     * @param msg The detail message for this exception.
+     */
+    public AconfigStorageException(int errorCode, String msg) {
+        super(msg);
+        mErrorCode = errorCode;
+    }
+
+    /**
+     * Constructs a new {@code AconfigStorageException} with the specified error code, detail
+     * message, and cause.
+     *
+     * @param errorCode The error code for this exception.
+     * @param msg The detail message for this exception.
+     * @param cause The cause of this exception.
+     */
+    public AconfigStorageException(int errorCode, String msg, Throwable cause) {
+        super(msg, cause);
+        mErrorCode = errorCode;
+    }
+
+    /**
+     * Returns the error code associated with this exception.
+     *
+     * @return The error code.
+     */
+    public int getErrorCode() {
+        return mErrorCode;
+    }
+
+    /**
+     * Returns the error message for this exception, including the error code and the original
+     * message.
+     *
+     * @return The error message.
+     */
+    @Override
+    public String getMessage() {
+        return errorString() + ": " + super.getMessage();
+    }
+
+    /**
+     * Returns a string representation of the error code.
+     *
+     * @return The error code string.
+     */
+    private String errorString() {
+        switch (mErrorCode) {
+            case ERROR_GENERIC:
+                return "ERROR_GENERIC";
+            case ERROR_STORAGE_SYSTEM_NOT_FOUND:
+                return "ERROR_STORAGE_SYSTEM_NOT_FOUND";
+            case ERROR_PACKAGE_NOT_FOUND:
+                return "ERROR_PACKAGE_NOT_FOUND";
+            case ERROR_CONTAINER_NOT_FOUND:
+                return "ERROR_CONTAINER_NOT_FOUND";
+            case ERROR_CANNOT_READ_STORAGE_FILE:
+                return "ERROR_CANNOT_READ_STORAGE_FILE";
+            default:
+                return "<Unknown error code " + mErrorCode + ">";
+        }
     }
 }
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
index 5d8e7cb..c2bef31 100644
--- 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
@@ -27,29 +27,26 @@
     private PackageTable.Node mPNode;
 
     /** @hide */
-    public static final int ERROR_NEW_STORAGE_SYSTEM_NOT_FOUND = 1;
+    private AconfigPackageImpl() {}
 
     /** @hide */
-    public static final int ERROR_PACKAGE_NOT_FOUND = 2;
-
-    /** @hide */
-    public static final int ERROR_CONTAINER_NOT_FOUND = 3;
-
-    /** @hide */
-    public AconfigPackageImpl() {}
-
-    /** @hide */
-    public int load(String packageName, StorageFileProvider fileProvider) {
-        return init(null, packageName, fileProvider);
+    public static AconfigPackageImpl load(String packageName, StorageFileProvider fileProvider) {
+        AconfigPackageImpl impl = new AconfigPackageImpl();
+        impl.init(null, packageName, fileProvider);
+        return impl;
     }
 
     /** @hide */
-    public int load(String container, String packageName, StorageFileProvider fileProvider) {
+    public static AconfigPackageImpl load(
+            String container, String packageName, StorageFileProvider fileProvider) {
         if (container == null) {
-            return ERROR_CONTAINER_NOT_FOUND;
+            throw new AconfigStorageException(
+                    AconfigStorageException.ERROR_CONTAINER_NOT_FOUND,
+                    "container null cannot be found on the device");
         }
-
-        return init(container, packageName, fileProvider);
+        AconfigPackageImpl impl = new AconfigPackageImpl();
+        impl.init(container, packageName, fileProvider);
+        return impl;
     }
 
     /** @hide */
@@ -76,13 +73,15 @@
         return mPNode.hasPackageFingerprint();
     }
 
-    private int init(String containerName, String packageName, StorageFileProvider fileProvider) {
+    private void 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 ERROR_NEW_STORAGE_SYSTEM_NOT_FOUND;
+                throw new AconfigStorageException(
+                        AconfigStorageException.ERROR_STORAGE_SYSTEM_NOT_FOUND,
+                        "aconfig storage cannot be found on the device");
             }
             PackageTable.Node pNode = null;
 
@@ -107,7 +106,9 @@
                 }
             } else {
                 if (!fileProvider.containerFileExists(container)) {
-                    return ERROR_CONTAINER_NOT_FOUND;
+                    throw new AconfigStorageException(
+                            AconfigStorageException.ERROR_CONTAINER_NOT_FOUND,
+                            "container " + container + " cannot be found on the device");
                 }
                 pNode = fileProvider.getPackageTable(container).get(packageName);
             }
@@ -115,7 +116,13 @@
             if (pNode == null) {
                 // for the case package is not found in all container, return instead of throwing
                 // error
-                return ERROR_PACKAGE_NOT_FOUND;
+                throw new AconfigStorageException(
+                        AconfigStorageException.ERROR_PACKAGE_NOT_FOUND,
+                        "package "
+                                + packageName
+                                + " in container "
+                                + container
+                                + " cannot be found on the device");
             }
 
             mFlagTable = fileProvider.getFlagTable(container);
@@ -126,6 +133,5 @@
         } finally {
             StrictMode.setThreadPolicy(oldPolicy);
         }
-        return 0;
     }
 }
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
index 1f84a51..b28341e 100644
--- 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
@@ -105,7 +105,9 @@
             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);
+                    AconfigStorageException.ERROR_CANNOT_READ_STORAGE_FILE,
+                    String.format("Fail to mmap storage file %s", file),
+                    e);
         } finally {
             quietlyDispose(channel);
         }
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/java/AconfigPackageImplTest.java b/tools/aconfig/aconfig_storage_read_api/tests/java/AconfigPackageImplTest.java
index 8a72f0a..18f70e8 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/java/AconfigPackageImplTest.java
+++ b/tools/aconfig/aconfig_storage_read_api/tests/java/AconfigPackageImplTest.java
@@ -43,53 +43,73 @@
 
     @Test
     public void testLoad_onlyPackageName() throws Exception {
-        AconfigPackageImpl p = new AconfigPackageImpl();
-        p.load("com.android.aconfig.storage.test_1", pr);
+        AconfigPackageImpl p = AconfigPackageImpl.load("com.android.aconfig.storage.test_1", pr);
         assertNotNull(p);
     }
 
     @Test
     public void testLoad_groupNameFingerprint() throws Exception {
-        AconfigPackageImpl p = new AconfigPackageImpl();
-        p.load("mockup", "com.android.aconfig.storage.test_1", pr);
+        AconfigPackageImpl p =
+                AconfigPackageImpl.load("mockup", "com.android.aconfig.storage.test_1", pr);
         assertNotNull(p);
     }
 
     @Test
     public void testLoad_error() throws Exception {
-        AconfigPackageImpl p = new AconfigPackageImpl();
+        AconfigPackageImpl p;
+        AconfigStorageException e =
+                assertThrows(
+                        AconfigStorageException.class,
+                        () ->
+                                AconfigPackageImpl.load(
+                                        "mockup", "com.android.aconfig.storage.test_10", pr));
         // cannot find package
-        assertEquals(
-                AconfigPackageImpl.ERROR_PACKAGE_NOT_FOUND,
-                p.load("mockup", "com.android.aconfig.storage.test_10", pr));
+        assertEquals(AconfigStorageException.ERROR_PACKAGE_NOT_FOUND, e.getErrorCode());
         // cannot find package
-        assertEquals(
-                AconfigPackageImpl.ERROR_PACKAGE_NOT_FOUND,
-                p.load("com.android.aconfig.storage.test_10", pr));
+        e =
+                assertThrows(
+                        AconfigStorageException.class,
+                        () -> AconfigPackageImpl.load("com.android.aconfig.storage.test_10", pr));
+        assertEquals(AconfigStorageException.ERROR_PACKAGE_NOT_FOUND, e.getErrorCode());
         // cannot find container
-        assertEquals(
-                AconfigPackageImpl.ERROR_CONTAINER_NOT_FOUND,
-                p.load(null, "com.android.aconfig.storage.test_1", pr));
-        assertEquals(
-                AconfigPackageImpl.ERROR_CONTAINER_NOT_FOUND,
-                p.load("test", "com.android.aconfig.storage.test_1", pr));
+        e =
+                assertThrows(
+                        AconfigStorageException.class,
+                        () ->
+                                AconfigPackageImpl.load(
+                                        null, "com.android.aconfig.storage.test_1", pr));
+        assertEquals(AconfigStorageException.ERROR_CONTAINER_NOT_FOUND, e.getErrorCode());
+        e =
+                assertThrows(
+                        AconfigStorageException.class,
+                        () ->
+                                AconfigPackageImpl.load(
+                                        "test", "com.android.aconfig.storage.test_1", pr));
+        assertEquals(AconfigStorageException.ERROR_CONTAINER_NOT_FOUND, e.getErrorCode());
 
         // new storage doesn't exist
         pr = new StorageFileProvider("fake/path/", "fake/path/");
-        assertEquals(
-                AconfigPackageImpl.ERROR_NEW_STORAGE_SYSTEM_NOT_FOUND, p.load("fake_package", pr));
+        e =
+                assertThrows(
+                        AconfigStorageException.class,
+                        () -> AconfigPackageImpl.load("fake_package", pr));
+        assertEquals(AconfigStorageException.ERROR_STORAGE_SYSTEM_NOT_FOUND, e.getErrorCode());
 
         // file read issue
         pr = new StorageFileProvider(TestDataUtils.TESTDATA_PATH, "fake/path/");
-        assertThrows(
-                AconfigStorageException.class,
-                () -> p.load("mockup", "com.android.aconfig.storage.test_1", pr));
+        e =
+                assertThrows(
+                        AconfigStorageException.class,
+                        () ->
+                                AconfigPackageImpl.load(
+                                        "mockup", "com.android.aconfig.storage.test_1", pr));
+        assertEquals(AconfigStorageException.ERROR_CANNOT_READ_STORAGE_FILE, e.getErrorCode());
     }
 
     @Test
     public void testGetBooleanFlagValue_flagName() throws Exception {
-        AconfigPackageImpl p = new AconfigPackageImpl();
-        p.load("mockup", "com.android.aconfig.storage.test_1", pr);
+        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));
@@ -98,8 +118,8 @@
 
     @Test
     public void testGetBooleanFlagValue_index() throws Exception {
-        AconfigPackageImpl p = new AconfigPackageImpl();
-        p.load("mockup", "com.android.aconfig.storage.test_1", pr);
+        AconfigPackageImpl p =
+                AconfigPackageImpl.load("mockup", "com.android.aconfig.storage.test_1", pr);
         assertFalse(p.getBooleanFlagValue(0));
         assertTrue(p.getBooleanFlagValue(1));
         assertTrue(p.getBooleanFlagValue(2));
@@ -107,8 +127,8 @@
 
     @Test
     public void testHasPackageFingerprint() throws Exception {
-        AconfigPackageImpl p = new AconfigPackageImpl();
-        p.load("mockup", "com.android.aconfig.storage.test_1", pr);
+        AconfigPackageImpl p =
+                AconfigPackageImpl.load("mockup", "com.android.aconfig.storage.test_1", pr);
         assertFalse(p.hasPackageFingerprint());
     }
 }