Add satellite ConfigDataGenerator tool

This tool generates a binary file of TelephonyConfigProto

Bug: 348112315
Bug: 342287366
Flag: EXEMPT adding a new host-based tool
Test: check generated telephony_config.pb
Change-Id: I14869dec7f64ab1b71156ddf757420c7031c1360
diff --git a/utils/satellite/README.md b/utils/satellite/README.md
index 77ee0fb..34a8794 100644
--- a/utils/satellite/README.md
+++ b/utils/satellite/README.md
@@ -15,11 +15,15 @@
   for dumping the binary file into human-readable format.
 - `src/test` Contains the test code for the tools.
 
+`configdatagenerator`
+- `src/main` Contains the tool for generating satellite configdata protobuf file.
+- `src/test` Contains the test code for the configdatagenerator tool.
+
 Run unit tests
 =
 - Build the tools and test code: Go to the tool directory (`packages/services/Telephony/tools/
   satellite`) in the local workspace and run `mm`, e.g.,
-- Run unit tests: `$atest SatelliteToolsTests`
+- Run unit tests: `$atest SatelliteToolsTests`, `$atest SatelliteGenerateProtoTests`
 
 Data file generate tools
 =
@@ -43,6 +47,55 @@
 - Example run command: `$satellite_createsats2file --input-file s2cells.txt --s2-level 12
   --is-allowed-list true --output-file sats2.dat`
 
+`satellite_generateprotobuf`
+- Runs the `satellite_generateprotobuf` to create a binary file of TelephonyConfigProto whose format
+  is defined in telephony_config_update.proto
+- Command: `satellite_generateprotobuf --input-file <input.xml> --output-file <telephony_config.pb>`
+  - `--input-file` input XML file contains input information such as carrier id, carrier plmn,
+  allowed service list and country code list. This is example of input file.
+    ```xml
+    <satelliteconfig>
+      <!-- version -->
+       <version>14</version>
+
+      <!-- CarrierSupportedSatelliteServicesProto -->
+      <carriersupportedservices>
+        <carrier_id>1</carrier_id>
+          <providercapability>
+            <carrier_plmn>310160</carrier_plmn>
+            <service>1</service>
+          </providercapability>
+          <providercapability>
+            <carrier_plmn>310240</carrier_plmn>
+            <service>6</service>
+          </providercapability>
+      </carriersupportedservices>
+
+      <carriersupportedservices>
+        <carrier_id>1891</carrier_id>
+        <providercapability>
+          <carrier_plmn>45005</carrier_plmn>
+          <service>1</service>
+          <service>2</service>
+        </providercapability>
+      </carriersupportedservices>
+
+      <!-- SatelliteRegionProto -->
+      <satelliteregion>
+        <s2_cell_file>sats2.dat</s2_cell_file>
+        <country_code>US</country_code>
+        <country_code>KR</country_code>
+        <is_allowed>TRUE</is_allowed>
+      </satelliteregion>
+    </satelliteconfig>
+    ```
+  - `--output-file` The created binary TelephonyConfigProto file, which will be used by
+  the `ConfigUpdater` module for Satellite Project.
+- Build the tools: Go to the tool directory (`packages/services/Telephony/tools/satellite`)
+  in the local workspace and run `mm`.
+- Example run command: `satellite_generateprotobuf --input-file input.xml --output-file
+  telephony_config.pb`
+
 Debug tools
 =
 
@@ -64,4 +117,4 @@
 `satellite_location_lookup`
 - Check if a location is present in the input satellite S2 file.
 - Run the tool: `$satellite_location_lookup --input-file <...> --lat-degrees <...>
-  --lng-degrees <...>`
\ No newline at end of file
+  --lng-degrees <...>`
diff --git a/utils/satellite/configdatagenerator/Android.bp b/utils/satellite/configdatagenerator/Android.bp
new file mode 100644
index 0000000..b64b941
--- /dev/null
+++ b/utils/satellite/configdatagenerator/Android.bp
@@ -0,0 +1,49 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_team: "trendy_team_fwk_telephony",
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_library_host {
+    name: "satellite-generateproto-lib",
+    srcs: [
+        "src/main/java/**/*.java",
+    ],
+    static_libs: [
+        "telephony-config-update-proto-lite",
+        "jcommander",
+    ],
+}
+
+// A tool to generate configdata protubuf file
+java_binary_host {
+    name: "satellite_generateprotobuf",
+    main_class: "com.android.telephony.tools.configdatagenerate.ConfigDataGenerator",
+    static_libs: [
+        "satellite-generateproto-lib",
+    ],
+}
+
+// Tests for ConfigDataGenerator.
+java_test_host {
+    name: "SatelliteGenerateProtoTests",
+    srcs: ["src/test/java/**/*.java"],
+    static_libs: [
+        "junit",
+        "satellite-generateproto-lib",
+    ],
+    test_suites: ["general-tests"],
+}
diff --git a/utils/satellite/configdatagenerator/TEST_MAPPING b/utils/satellite/configdatagenerator/TEST_MAPPING
new file mode 100644
index 0000000..13a3e13
--- /dev/null
+++ b/utils/satellite/configdatagenerator/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+    "postsubmit": [
+        {
+            "name": "SatelliteGenerateProtoTests"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/utils/satellite/configdatagenerator/src/main/java/com/android/telephony/tools/configdatagenerate/ConfigDataGenerator.java b/utils/satellite/configdatagenerator/src/main/java/com/android/telephony/tools/configdatagenerate/ConfigDataGenerator.java
new file mode 100644
index 0000000..7e29e9a
--- /dev/null
+++ b/utils/satellite/configdatagenerator/src/main/java/com/android/telephony/tools/configdatagenerate/ConfigDataGenerator.java
@@ -0,0 +1,233 @@
+/*
+ * 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.telephony.tools.configdatagenerate;
+
+import com.beust.jcommander.JCommander;
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.ParameterException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+/** Creates a protubuf file **/
+public class ConfigDataGenerator {
+    public static final String TAG_SATELLITE_CONFIG = "satelliteconfig";
+    public static final String TAG_VERSION = "version";
+    public static final String TAG_SUPPORTED_SERVICES = "carriersupportedservices";
+    public static final String TAG_CARRIER_ID = "carrier_id";
+    public static final String TAG_PROVIDER_CAPABILITY = "providercapability";
+    public static final String TAG_CARRIER_PLMN = "carrier_plmn";
+    public static final String TAG_SERVICE = "service";
+    public static final String TAG_SATELLITE_REGION =  "satelliteregion";
+    public static final String TAG_S2_CELL_FILE = "s2_cell_file";
+    public static final String TAG_COUNTRY_CODE = "country_code";
+    public static final String TAG_IS_ALLOWED = "is_allowed";
+
+    /**
+     * Creates a protubuf file with user inputs
+     */
+    public static void main(String[] args) {
+        Arguments arguments = new Arguments();
+        JCommander.newBuilder()
+                .addObject(arguments)
+                .build()
+                .parse(args);
+        // Refer to the README file for an example of the input XML file
+        String inputFile = arguments.inputFile;
+        String outputFile = arguments.outputFile;
+        SatelliteConfigProtoGenerator.sProtoResultFile = outputFile;
+
+        Document doc = getDocumentFromInput(inputFile);
+
+        setSatelliteConfigVersion(doc);
+        createStarlinkConfigProto(doc);
+        createSkyloConfigProto(doc);
+
+        SatelliteConfigProtoGenerator.generateProto();
+
+        System.out.print("\n" + SatelliteConfigProtoGenerator.sProtoResultFile + " is generated\n");
+    }
+
+    private static class Arguments {
+        @Parameter(names = "--input-file",
+                description = "input xml file",
+                required = true)
+        public String inputFile;
+
+        @Parameter(names = "--output-file",
+                description = "out protobuf file",
+                required = false)
+        public String outputFile = SatelliteConfigProtoGenerator.sProtoResultFile;
+    }
+
+    private static Document getDocumentFromInput(String inputFile) {
+        File xmlFile = new File(inputFile);
+        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
+        DocumentBuilder dBuilder = null;
+        Document doc = null;
+        try {
+            dBuilder = dbFactory.newDocumentBuilder();
+            doc = dBuilder.parse(xmlFile);
+        } catch (ParserConfigurationException | SAXException | IOException e) {
+            throw new RuntimeException("getDocumentFromInput: e=" + e);
+        }
+        doc.getDocumentElement().normalize();
+        return doc;
+    }
+
+    /**
+     * Set version after getting version from the input document
+     *
+     * @param doc the input document. Format of document should be
+     * <pre>
+     * &lt;version&gt;value1&lt;/version&gt;
+     * </pre>
+     */
+    public static void setSatelliteConfigVersion(Document doc) {
+        NodeList versionList = doc.getElementsByTagName(TAG_VERSION);
+        Node versionNode = versionList.item(0);
+        System.out.println("Version: " + versionNode.getTextContent());
+        SatelliteConfigProtoGenerator.sVersion = Integer.parseInt(versionNode.getTextContent());
+    }
+
+
+    /**
+     * Creates a list of ServiceProto from the input document
+     *
+     * @param doc the input document. Format of document should be
+     * <pre>
+     * &lt;carriersupportedservices&gt;
+     *   &lt;carrier_id&gt;value1&lt;/carrier_id&gt;
+     *   &lt;providercapability&gt;
+     *     &lt;carrier_plmn&gt;value2&lt;/carrier_plmn&gt;
+     *     &lt;service&gt;value3&lt;/service&gt;
+     *   &lt;/providercapability&gt;
+     * &lt;/carriersupportedservices&gt;
+     * </pre>
+     */
+    public static void createStarlinkConfigProto(Document doc) {
+        NodeList carrierServicesList = doc.getElementsByTagName(TAG_SUPPORTED_SERVICES);
+        SatelliteConfigProtoGenerator.sServiceProtoList = new ArrayList<>();
+        for (int i = 0; i < carrierServicesList.getLength(); i++) {
+            Node carrierServiceNode = carrierServicesList.item(i);
+            if (carrierServiceNode.getNodeType() == Node.ELEMENT_NODE) {
+                Element carrierServiceElement = (Element) carrierServiceNode;
+                String carrierId = carrierServiceElement.getElementsByTagName(TAG_CARRIER_ID)
+                        .item(0).getTextContent();
+                System.out.println("\nCarrier ID: " + carrierId);
+
+                NodeList providerCapabilityList = carrierServiceElement.getElementsByTagName(
+                        TAG_PROVIDER_CAPABILITY);
+                ProviderCapabilityProto[] capabilityProtoList =
+                        new ProviderCapabilityProto[providerCapabilityList.getLength()];
+                for (int j = 0; j < providerCapabilityList.getLength(); j++) {
+                    Node providerCapabilityNode = providerCapabilityList.item(j);
+                    if (providerCapabilityNode.getNodeType() == Node.ELEMENT_NODE) {
+                        Element providerCapabilityElement = (Element) providerCapabilityNode;
+                        String carrierPlmn = providerCapabilityElement.getElementsByTagName(
+                                TAG_CARRIER_PLMN).item(0).getTextContent();
+                        System.out.println("  Carrier PLMN: " + carrierPlmn);
+                        if (!Util.isValidPlmn(carrierPlmn)) {
+                            throw new ParameterException("Invalid plmn:" + carrierPlmn);
+                        }
+
+                        NodeList allowedServicesList = providerCapabilityElement
+                                .getElementsByTagName(TAG_SERVICE);
+                        System.out.print("    Allowed services: ");
+                        int[] allowedServiceArray = new int[allowedServicesList.getLength()];
+                        for (int k = 0; k < allowedServicesList.getLength(); k++) {
+                            int service = Integer.parseInt(allowedServicesList.item(k)
+                                    .getTextContent());
+                            System.out.print(service + " ");
+                            if (!Util.isValidService(service)) {
+                                throw new ParameterException("Invalid service:" + service);
+                            }
+                            allowedServiceArray[k] = service;
+                        }
+                        System.out.println();
+                        ProviderCapabilityProto capabilityProto =
+                                new ProviderCapabilityProto(carrierPlmn, allowedServiceArray);
+                        capabilityProtoList[j] = capabilityProto;
+                    }
+                }
+                ServiceProto serviceProto = new ServiceProto(Integer.parseInt(carrierId),
+                        capabilityProtoList);
+                SatelliteConfigProtoGenerator.sServiceProtoList.add(serviceProto);
+            }
+        }
+    }
+
+    /**
+     * Creates a RegionProto from the input document
+     *
+     * @param doc the input document. Format of document should be
+     * <pre>
+     * &lt;satelliteregion&gt;
+     *   &lt;s2_cell_file&gt;value1&lt;/s2_cell_file&gt;
+     *   &lt;country_code&gt;value2&lt;/country_code&gt;
+     *   &lt;country_code&gt;value3&lt;/country_code&gt;
+     *   &lt;is_allowed&gt;value4&lt;/is_allowed&gt;
+     * &lt;/satelliteregion&gt;
+     * </pre>
+     */
+    public static void createSkyloConfigProto(Document doc) {
+        NodeList satelliteRegionList = doc.getElementsByTagName(TAG_SATELLITE_REGION);
+        Node satelliteRegionNode = satelliteRegionList.item(0);
+        if (satelliteRegionNode != null && satelliteRegionNode.getNodeType() == Node.ELEMENT_NODE) {
+            Element satelliteRegionElement = (Element) satelliteRegionNode;
+            String s2CellFileName = satelliteRegionElement.getElementsByTagName(TAG_S2_CELL_FILE)
+                    .item(0).getTextContent();
+            String isAllowedString = satelliteRegionElement.getElementsByTagName(TAG_IS_ALLOWED)
+                    .item(0).getTextContent();
+            boolean isAllowed = false;
+            if (isAllowedString.equals("TRUE")) {
+                isAllowed = true;
+            }
+            System.out.println("\nSatellite Region:");
+            System.out.println("  S2 Cell File: " + s2CellFileName);
+            System.out.println("  Is Allowed: " + isAllowed);
+
+            NodeList countryCodesList = satelliteRegionElement.getElementsByTagName(
+                    TAG_COUNTRY_CODE);
+            String[] listCountryCode = new String[countryCodesList.getLength()];
+            System.out.print("  Country Codes: ");
+            for (int k = 0; k < countryCodesList.getLength(); k++) {
+                String countryCode = countryCodesList.item(k).getTextContent();
+                System.out.print(countryCode + " ");
+                if (!Util.isValidCountryCode(countryCode)) {
+                    throw new ParameterException("Invalid countryCode:" + countryCode);
+                }
+                listCountryCode[k] = countryCode;
+            }
+            System.out.println();
+            SatelliteConfigProtoGenerator.sRegionProto =
+                    new RegionProto(s2CellFileName, listCountryCode, isAllowed);
+        }
+    }
+}
+
diff --git a/utils/satellite/configdatagenerator/src/main/java/com/android/telephony/tools/configdatagenerate/ProviderCapabilityProto.java b/utils/satellite/configdatagenerator/src/main/java/com/android/telephony/tools/configdatagenerate/ProviderCapabilityProto.java
new file mode 100644
index 0000000..9fe692d
--- /dev/null
+++ b/utils/satellite/configdatagenerator/src/main/java/com/android/telephony/tools/configdatagenerate/ProviderCapabilityProto.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.telephony.tools.configdatagenerate;
+
+public class ProviderCapabilityProto {
+
+    public String mPlmn;
+    public int[] mAllowedServices;
+
+    public ProviderCapabilityProto(String plmn, int[] allowedServices) {
+        mPlmn = plmn;
+        mAllowedServices = allowedServices;
+    }
+}
diff --git a/utils/satellite/configdatagenerator/src/main/java/com/android/telephony/tools/configdatagenerate/RegionProto.java b/utils/satellite/configdatagenerator/src/main/java/com/android/telephony/tools/configdatagenerate/RegionProto.java
new file mode 100644
index 0000000..be3b0cc
--- /dev/null
+++ b/utils/satellite/configdatagenerator/src/main/java/com/android/telephony/tools/configdatagenerate/RegionProto.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.telephony.tools.configdatagenerate;
+
+public class RegionProto {
+
+    String mS2CellFileName;
+    String[] mCountryCodeList;
+    boolean mIsAllowed;
+
+    public RegionProto(String s2CellFileName, String[] countryCodeList, boolean isAllowed) {
+        mS2CellFileName = s2CellFileName;
+        mCountryCodeList = countryCodeList;
+        mIsAllowed = isAllowed;
+    }
+}
diff --git a/utils/satellite/configdatagenerator/src/main/java/com/android/telephony/tools/configdatagenerate/SatelliteConfigProtoGenerator.java b/utils/satellite/configdatagenerator/src/main/java/com/android/telephony/tools/configdatagenerate/SatelliteConfigProtoGenerator.java
new file mode 100644
index 0000000..740e2ea
--- /dev/null
+++ b/utils/satellite/configdatagenerator/src/main/java/com/android/telephony/tools/configdatagenerate/SatelliteConfigProtoGenerator.java
@@ -0,0 +1,155 @@
+/*
+ * 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.telephony.tools.configdatagenerate;
+
+import com.android.internal.telephony.satellite.SatelliteConfigData;
+
+import com.google.protobuf.ByteString;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+public class SatelliteConfigProtoGenerator {
+
+    private static final String TAG = "ProtoGenerator";
+    public static String sProtoResultFile = "telephony_config.pb";
+    public static int sVersion;
+    public static ArrayList<ServiceProto> sServiceProtoList;
+    public static RegionProto sRegionProto;
+
+    /**
+     * Generate Protobuf.
+     *
+     * The output file is a binary file of TelephonyConfigProto.
+     *
+     * The format of TelephonyConfigProto is defined in
+     * https://source.corp.google.com/android/frameworks/opt/telephony/proto/src/
+     * telephony_config_update.proto
+     */
+    public static void generateProto() {
+        SatelliteConfigData.TelephonyConfigProto.Builder telephonyConfigBuilder =
+                SatelliteConfigData.TelephonyConfigProto.newBuilder();
+        SatelliteConfigData.SatelliteConfigProto.Builder satelliteConfigBuilder =
+                SatelliteConfigData.SatelliteConfigProto.newBuilder();
+
+        satelliteConfigBuilder.setVersion(sVersion);    // Input version
+
+        if (sServiceProtoList != null) {
+            // carrierSupportedSatelliteServiceBuilder
+            SatelliteConfigData.CarrierSupportedSatelliteServicesProto.Builder
+                    carrierSupportedSatelliteServiceBuilder =
+                    SatelliteConfigData.CarrierSupportedSatelliteServicesProto.newBuilder();
+            for (int i = 0; i < sServiceProtoList.size(); i++) {
+                ServiceProto proto = sServiceProtoList.get(i);
+                carrierSupportedSatelliteServiceBuilder.setCarrierId(proto.mCarrierId);
+                SatelliteConfigData.SatelliteProviderCapabilityProto.Builder
+                        satelliteProviderCapabilityBuilder =
+                        SatelliteConfigData.SatelliteProviderCapabilityProto.newBuilder();
+                ProviderCapabilityProto[] capabilityProtoList = proto.mCapabilityProtoList;
+                for (int j = 0; j < capabilityProtoList.length; j++) {
+                    ProviderCapabilityProto capabilityProto = capabilityProtoList[j];
+                    satelliteProviderCapabilityBuilder.setCarrierPlmn(capabilityProto.mPlmn);
+                    int[] allowedServiceList = capabilityProto.mAllowedServices;
+                    for (int k = 0; k < allowedServiceList.length; k++) {
+                        satelliteProviderCapabilityBuilder
+                                .addAllowedServices(allowedServiceList[k]);
+                    }
+                    carrierSupportedSatelliteServiceBuilder
+                            .addSupportedSatelliteProviderCapabilities(
+                                    satelliteProviderCapabilityBuilder);
+                    satelliteProviderCapabilityBuilder.clear();
+                }
+                satelliteConfigBuilder.addCarrierSupportedSatelliteServices(
+                        carrierSupportedSatelliteServiceBuilder);
+                carrierSupportedSatelliteServiceBuilder.clear();
+            }
+        } else {
+            System.out.print("ServiceProtoList does not exist");
+        }
+
+        if (sRegionProto != null) {
+            // satelliteRegionBuilder
+            SatelliteConfigData.SatelliteRegionProto.Builder satelliteRegionBuilder =
+                    SatelliteConfigData.SatelliteRegionProto.newBuilder();
+            byte[] binaryData;
+            try {
+                binaryData = readFileToByteArray(sRegionProto.mS2CellFileName);
+            } catch (IOException e) {
+                throw new RuntimeException("Got exception in reading the file "
+                        + sRegionProto.mS2CellFileName + ", e=" + e);
+            }
+            if (binaryData != null) {
+                satelliteRegionBuilder.setS2CellFile(ByteString.copyFrom(binaryData));
+            }
+
+            String[] countryCodeList = sRegionProto.mCountryCodeList;
+            for (int i = 0; i < countryCodeList.length; i++) {
+                satelliteRegionBuilder.addCountryCodes(countryCodeList[i]);
+            }
+            satelliteRegionBuilder.setIsAllowed(sRegionProto.mIsAllowed);
+            satelliteConfigBuilder.setDeviceSatelliteRegion(satelliteRegionBuilder);
+        } else {
+            System.out.print("RegionProto does not exist");
+        }
+
+        telephonyConfigBuilder.setSatellite(satelliteConfigBuilder);
+
+        writeToResultFile(telephonyConfigBuilder);
+    }
+
+    private static void writeToResultFile(SatelliteConfigData
+            .TelephonyConfigProto.Builder telephonyConfigBuilder) {
+        try {
+            File file = new File(sProtoResultFile);
+            if (file.exists()) {
+                file.delete();
+            }
+            FileOutputStream fos = new FileOutputStream(file);
+            SatelliteConfigData.TelephonyConfigProto telephonyConfigData =
+                    telephonyConfigBuilder.build();
+            telephonyConfigData.writeTo(fos);
+
+            fos.close();
+        } catch (Exception e) {
+            throw new RuntimeException("Got exception in writing the file "
+                    + sProtoResultFile + ", e=" + e);
+        }
+    }
+
+    private static byte[] readFileToByteArray(String fileName) throws IOException {
+        File sat2File = new File(fileName);
+        if (!sat2File.exists()) {
+            throw new IOException("sat2File " + fileName + " does not exist");
+        }
+
+        if (sat2File.exists() && sat2File.canRead()) {
+            FileInputStream fileInputStream = new FileInputStream(sat2File);
+            long fileSize = fileInputStream.available();
+            byte[] bytes = new byte[(int) fileSize];
+            int bytesRead = fileInputStream.read(bytes);
+            fileInputStream.close();
+            if (bytesRead != fileSize) {
+                throw new IOException("file read fail: " + sat2File.getCanonicalPath());
+            }
+            return bytes;
+        }
+        return null;
+    }
+}
diff --git a/utils/satellite/configdatagenerator/src/main/java/com/android/telephony/tools/configdatagenerate/ServiceProto.java b/utils/satellite/configdatagenerator/src/main/java/com/android/telephony/tools/configdatagenerate/ServiceProto.java
new file mode 100644
index 0000000..a17e1dd
--- /dev/null
+++ b/utils/satellite/configdatagenerator/src/main/java/com/android/telephony/tools/configdatagenerate/ServiceProto.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.telephony.tools.configdatagenerate;
+
+public class ServiceProto {
+
+    public int mCarrierId;
+    public ProviderCapabilityProto[] mCapabilityProtoList;
+
+    public ServiceProto(int carrierId, ProviderCapabilityProto[] capabilityProtolist) {
+        mCarrierId = carrierId;
+        mCapabilityProtoList = capabilityProtolist;
+    }
+}
diff --git a/utils/satellite/configdatagenerator/src/main/java/com/android/telephony/tools/configdatagenerate/Util.java b/utils/satellite/configdatagenerator/src/main/java/com/android/telephony/tools/configdatagenerate/Util.java
new file mode 100644
index 0000000..925e828
--- /dev/null
+++ b/utils/satellite/configdatagenerator/src/main/java/com/android/telephony/tools/configdatagenerate/Util.java
@@ -0,0 +1,68 @@
+/*
+ * 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.telephony.tools.configdatagenerate;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class Util {
+
+    public static final int SERVICE_TYPE_VOICE = 1;
+    public static final int SERVICE_TYPE_MMS = 6;
+
+    private static final int FIRST_SERVICE_TYPE = SERVICE_TYPE_VOICE;
+    private static final int LAST_SERVICE_TYPE = SERVICE_TYPE_MMS;
+
+    private static boolean isValidPattern(String input, String regex) {
+        if ((input == null) || (regex == null)) {
+            return false;
+        }
+        Pattern pattern = Pattern.compile(regex);
+        Matcher matcher = pattern.matcher(input);
+        if (!matcher.matches()) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * @param countryCode two letters country code based on the ISO 3166-1.
+     * @return {@code true} if the countryCode is valid {@code false} otherwise.
+     */
+    public static boolean isValidCountryCode(String countryCode) {
+        return isValidPattern(countryCode, "^[A-Za-z]{2}$");
+    }
+
+    /**
+     * @param plmn target plmn for validation.
+     * @return {@code true} if the target plmn is valid {@code false} otherwise.
+     */
+    public static boolean isValidPlmn(String plmn) {
+        return isValidPattern(plmn, "^(?:[0-9]{3})(?:[0-9]{2}|[0-9]{3})$");
+    }
+
+    /**
+     * @param serviceType target serviceType for validation.
+     * @return {@code true} if the target serviceType is valid {@code false} otherwise.
+     */
+    public static boolean isValidService(int serviceType) {
+        if (serviceType < FIRST_SERVICE_TYPE || serviceType > LAST_SERVICE_TYPE) {
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/utils/satellite/configdatagenerator/src/test/java/com/android/telephony/tools/configdatagenerate/ConfigDataGeneratorTest.java b/utils/satellite/configdatagenerator/src/test/java/com/android/telephony/tools/configdatagenerate/ConfigDataGeneratorTest.java
new file mode 100644
index 0000000..f588815
--- /dev/null
+++ b/utils/satellite/configdatagenerator/src/test/java/com/android/telephony/tools/configdatagenerate/ConfigDataGeneratorTest.java
@@ -0,0 +1,324 @@
+/*
+ * 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.telephony.tools.configdatagenerate;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+
+import com.android.internal.telephony.satellite.SatelliteConfigData.CarrierSupportedSatelliteServicesProto;
+import com.android.internal.telephony.satellite.SatelliteConfigData.SatelliteConfigProto;
+import com.android.internal.telephony.satellite.SatelliteConfigData.SatelliteProviderCapabilityProto;
+import com.android.internal.telephony.satellite.SatelliteConfigData.SatelliteRegionProto;
+import com.android.internal.telephony.satellite.SatelliteConfigData.TelephonyConfigProto;
+
+import com.google.protobuf.ByteString;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+public class ConfigDataGeneratorTest {
+    private Path mTempDirPath;
+
+    @Before
+    public void setUp() throws IOException {
+        mTempDirPath = createTempDir(this.getClass());
+    }
+
+    @After
+    public void tearDown() throws IOException {
+        if (mTempDirPath != null) {
+            deleteDirectory(mTempDirPath);
+        }
+    }
+
+    @Test
+    public void testConfigDataGeneratorWithInvalidPlmn() throws Exception {
+        Path inputDirPath = mTempDirPath.resolve("input");
+        Files.createDirectory(inputDirPath);
+        Path inputFilePath = inputDirPath.resolve("test_input.xml");
+        Path inputS2CellFilePath = inputDirPath.resolve("sats2.dat");
+
+        Path outputDirPath = mTempDirPath.resolve("output");
+        Files.createDirectory(outputDirPath);
+        Path outputFilePath = outputDirPath.resolve("test_out.pb");
+        String inputfileName = inputFilePath.toAbsolutePath().toString();
+        String inputS2CellFileName = inputS2CellFilePath.toAbsolutePath().toString();
+        File inputFile = new File(inputfileName);
+        ByteString inputByteStringForS2Cell = ByteString.copyFromUtf8("Test ByteString!");
+        writeByteStringToFile(inputS2CellFileName, inputByteStringForS2Cell);
+
+        createInputXml(inputFile, 14, 1, "310062222", 1,
+                "US", true, inputS2CellFileName);
+        String[] args = {
+                "--input-file", inputFilePath.toAbsolutePath().toString(),
+                "--output-file", outputFilePath.toAbsolutePath().toString()
+        };
+        try {
+            ConfigDataGenerator.main(args);
+        } catch (Exception ex) {
+            // Expected exception because input plmn is invalid
+            return;
+        }
+        fail("Exception should have been caught");
+    }
+
+    @Test
+    public void testConfigDataGeneratorWithInvalidService() throws Exception {
+        Path inputDirPath = mTempDirPath.resolve("input");
+        Files.createDirectory(inputDirPath);
+        Path inputFilePath = inputDirPath.resolve("test_input.xml");
+        Path inputS2CellFilePath = inputDirPath.resolve("sats2.dat");
+
+        Path outputDirPath = mTempDirPath.resolve("output");
+        Files.createDirectory(outputDirPath);
+        Path outputFilePath = outputDirPath.resolve("test_out.pb");
+        String inputfileName = inputFilePath.toAbsolutePath().toString();
+        String inputS2CellFileName = inputS2CellFilePath.toAbsolutePath().toString();
+        File inputFile = new File(inputfileName);
+        ByteString inputByteStringForS2Cell = ByteString.copyFromUtf8("Test ByteString!");
+        writeByteStringToFile(inputS2CellFileName, inputByteStringForS2Cell);
+
+        createInputXml(inputFile, 14, 1, "31006", -1,
+                "US", true, inputS2CellFileName);
+        String[] args = {
+                "--input-file", inputFilePath.toAbsolutePath().toString(),
+                "--output-file", outputFilePath.toAbsolutePath().toString()
+        };
+        try {
+            ConfigDataGenerator.main(args);
+        } catch (Exception ex) {
+            // Expected exception because input allowed service is invalid
+            return;
+        }
+        fail("Exception should have been caught");
+    }
+
+    @Test
+    public void testConfigDataGeneratorWithInvalidCountryCode() throws Exception {
+        Path inputDirPath = mTempDirPath.resolve("input");
+        Files.createDirectory(inputDirPath);
+        Path inputFilePath = inputDirPath.resolve("test_input.xml");
+        Path inputS2CellFilePath = inputDirPath.resolve("sats2.dat");
+
+        Path outputDirPath = mTempDirPath.resolve("output");
+        Files.createDirectory(outputDirPath);
+        Path outputFilePath = outputDirPath.resolve("test_out.pb");
+        String inputfileName = inputFilePath.toAbsolutePath().toString();
+        String inputS2CellFileName = inputS2CellFilePath.toAbsolutePath().toString();
+        File inputFile = new File(inputfileName);
+        ByteString inputByteStringForS2Cell = ByteString.copyFromUtf8("Test ByteString!");
+        writeByteStringToFile(inputS2CellFileName, inputByteStringForS2Cell);
+
+        createInputXml(inputFile, 14, 1, "31006", 1,
+                "USSSS", true, inputS2CellFileName);
+        String[] args = {
+                "--input-file", inputFilePath.toAbsolutePath().toString(),
+                "--output-file", outputFilePath.toAbsolutePath().toString()
+        };
+        try {
+            ConfigDataGenerator.main(args);
+        } catch (Exception ex) {
+            // Expected exception because input country code is invalid
+            return;
+        }
+        fail("Exception should have been caught");
+    }
+
+    @Test
+    public void testConfigDataGeneratorWithValidInput() throws Exception {
+        Path inputDirPath = mTempDirPath.resolve("input");
+        Files.createDirectory(inputDirPath);
+        Path inputFilePath = inputDirPath.resolve("test_input.xml");
+        Path inputS2CellFilePath = inputDirPath.resolve("sats2.dat");
+
+        Path outputDirPath = mTempDirPath.resolve("output");
+        Files.createDirectory(outputDirPath);
+        Path outputFilePath = outputDirPath.resolve("test_out.pb");
+        String inputfileName = inputFilePath.toAbsolutePath().toString();
+        String inputS2CellFileName = inputS2CellFilePath.toAbsolutePath().toString();
+        File inputFile = new File(inputfileName);
+        String outputFileName = outputFilePath.toAbsolutePath().toString();
+
+
+        int inputVersion = 14;
+        int inputCarrierId = 1;
+        String inputPlmn = "31006";
+        int inputAllowedService = 1;
+        String inputCountryCode = "US";
+        boolean inputIsAllowed = true;
+        ByteString inputByteStringForS2Cell = ByteString.copyFromUtf8("Test ByteString!");
+        writeByteStringToFile(inputS2CellFileName, inputByteStringForS2Cell);
+        createInputXml(inputFile, inputVersion, inputCarrierId, inputPlmn, inputAllowedService,
+                inputCountryCode, inputIsAllowed, inputS2CellFileName);
+        String[] args = {
+                "--input-file", inputFilePath.toAbsolutePath().toString(),
+                "--output-file", outputFilePath.toAbsolutePath().toString()
+        };
+        try {
+            ConfigDataGenerator.main(args);
+        } catch (Exception ex) {
+            fail("Unexpected exception when executing the tool ex=" + ex);
+        }
+
+        Path filePath = Paths.get(outputFileName);
+        byte[] fileBytes = Files.readAllBytes(filePath);
+        TelephonyConfigProto telephonyConfigProto = TelephonyConfigProto.parseFrom(fileBytes);
+        SatelliteConfigProto satelliteConfigProto = telephonyConfigProto.getSatellite();
+        int version  = satelliteConfigProto.getVersion();
+        assertEquals(inputVersion, version);
+        CarrierSupportedSatelliteServicesProto serviceProto =
+                satelliteConfigProto.getCarrierSupportedSatelliteServices(0);
+        int carrierId = serviceProto.getCarrierId();
+        assertEquals(inputCarrierId, carrierId);
+        SatelliteProviderCapabilityProto providerCapabilityProto =
+                serviceProto.getSupportedSatelliteProviderCapabilities(0);
+        String plmn = providerCapabilityProto.getCarrierPlmn();
+        assertEquals(inputPlmn, plmn);
+        int allowedService = providerCapabilityProto.getAllowedServices(0);
+        assertEquals(inputAllowedService, allowedService);
+
+        SatelliteRegionProto regionProto = satelliteConfigProto.getDeviceSatelliteRegion();
+        String countryCode = regionProto.getCountryCodes(0);
+        assertEquals(inputCountryCode, countryCode);
+        ByteString s2cellfile = regionProto.getS2CellFile();
+        byte[] fileBytesForInputS2CellFile = Files.readAllBytes(Paths.get(inputS2CellFileName));
+        ByteString inputS2CellFile = ByteString.copyFrom(fileBytesForInputS2CellFile);
+        assertEquals(inputS2CellFile, s2cellfile);
+        boolean isAllowed = regionProto.getIsAllowed();
+        assertEquals(inputIsAllowed, isAllowed);
+    }
+
+    private void createInputXml(File outputFile, int version, int carrierId, String plmn,
+            int allowedService, String countryCode, boolean isAllowed, String inputS2CellFileName) {
+        try {
+            DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
+            DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
+
+            // Create Document and Root Element
+            Document doc = docBuilder.newDocument();
+            Element rootElement = doc.createElement(ConfigDataGenerator.TAG_SATELLITE_CONFIG);
+            doc.appendChild(rootElement);
+
+            // Add <version>
+            Element versionElement = doc.createElement(ConfigDataGenerator.TAG_VERSION);
+            versionElement.appendChild(doc.createTextNode(String.valueOf(version)));
+            rootElement.appendChild(versionElement);
+
+            // Add <carriersupportedservices>
+            rootElement.appendChild(
+                    createCarrierSupportedServices(doc, carrierId, plmn, allowedService));
+
+            // Add <satelliteregion>
+            Element satelliteRegion = doc.createElement(ConfigDataGenerator.TAG_SATELLITE_REGION);
+            satelliteRegion.appendChild(
+                    createElementWithText(doc, ConfigDataGenerator.TAG_S2_CELL_FILE,
+                            inputS2CellFileName));
+            satelliteRegion.appendChild(
+                    createElementWithText(doc, ConfigDataGenerator.TAG_COUNTRY_CODE, countryCode));
+            satelliteRegion.appendChild(
+                    createElementWithText(doc, ConfigDataGenerator.TAG_IS_ALLOWED,
+                            isAllowed ? "TRUE" : "FALSE"));
+            rootElement.appendChild(satelliteRegion);
+
+            // Write XML to File
+            TransformerFactory transformerFactory = TransformerFactory.newInstance();
+            Transformer transformer = transformerFactory.newTransformer();
+            DOMSource source = new DOMSource(doc);
+            StreamResult result = new StreamResult(outputFile);
+            transformer.transform(source, result);
+
+        } catch (Exception e) {
+            throw new RuntimeException("Got exception in creating input file , e=" + e);
+        }
+    }
+
+    private static Element createCarrierSupportedServices(Document doc, int carrierId,
+            String carrierPlmn, int... services) {
+        Element carrierSupportedServices = doc.createElement(
+                ConfigDataGenerator.TAG_SUPPORTED_SERVICES);
+        carrierSupportedServices.appendChild(createElementWithText(doc,
+                ConfigDataGenerator.TAG_CARRIER_ID, String.valueOf(carrierId)));
+
+        Element providerCapability = doc.createElement(ConfigDataGenerator.TAG_PROVIDER_CAPABILITY);
+        providerCapability.appendChild(createElementWithText(doc,
+                ConfigDataGenerator.TAG_CARRIER_PLMN, carrierPlmn));
+        for (int service : services) {
+            providerCapability.appendChild(createElementWithText(doc,
+                    ConfigDataGenerator.TAG_SERVICE, String.valueOf(service)));
+        }
+        carrierSupportedServices.appendChild(providerCapability);
+
+        return carrierSupportedServices;
+    }
+
+    private static Element createElementWithText(Document doc, String tagName, String textContent) {
+        Element element = doc.createElement(tagName);
+        element.appendChild(doc.createTextNode(textContent));
+        return element;
+    }
+
+    private static Path createTempDir(Class<?> testClass) throws IOException {
+        return Files.createTempDirectory(testClass.getSimpleName());
+    }
+
+    private static void deleteDirectory(Path dir) throws IOException {
+        Files.walkFileTree(dir, new SimpleFileVisitor<>() {
+            @Override
+            public FileVisitResult visitFile(Path path, BasicFileAttributes basicFileAttributes)
+                    throws IOException {
+                Files.deleteIfExists(path);
+                return FileVisitResult.CONTINUE;
+            }
+
+            @Override
+            public FileVisitResult postVisitDirectory(Path path, IOException e) throws IOException {
+                Files.delete(path);
+                return FileVisitResult.CONTINUE;
+            }
+        });
+        assertFalse(Files.exists(dir));
+    }
+
+    private void writeByteStringToFile(String fileName, ByteString byteString) {
+        try (FileOutputStream fos = new FileOutputStream(fileName)) {
+            fos.write(byteString.toByteArray());
+        } catch (IOException e) {
+            System.err.println("Error writing to file: " + e.getMessage());
+        }
+    }
+}