Add get ipv4/ipv6 address function to `apf_utils`

Test: atest NetworkStaticLibHostPythonTests
Change-Id: I535bdca481f600b95603206bf368a106cbb45b42
diff --git a/staticlibs/tests/unit/host/python/apf_utils_test.py b/staticlibs/tests/unit/host/python/apf_utils_test.py
index b5a941b..96b967b 100644
--- a/staticlibs/tests/unit/host/python/apf_utils_test.py
+++ b/staticlibs/tests/unit/host/python/apf_utils_test.py
@@ -25,6 +25,8 @@
     get_apf_capabilities,
     get_apf_counter,
     get_apf_counters_from_dumpsys,
+    get_ipv4_address,
+    get_ipv6_address,
     get_hardware_address,
     is_send_raw_packet_downstream_supported,
     send_raw_packet_downstream,
@@ -112,6 +114,46 @@
       get_hardware_address(self.mock_ad, "wlan0")
 
   @patch("net_tests_utils.host.python.adb_utils.adb_shell")
+  def test_get_ipv4_address_success(
+      self, mock_adb_shell: MagicMock
+  ) -> None:
+    mock_adb_shell.return_value = """
+54: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 1000
+inet 192.168.195.162/24 brd 192.168.195.255 scope global wlan0
+valid_lft forever preferred_lft forever
+"""
+    ip_address = get_ipv4_address(self.mock_ad, "wlan0")
+    asserts.assert_equal(ip_address, "192.168.195.162")
+
+  @patch("net_tests_utils.host.python.adb_utils.adb_shell")
+  def test_get_ipv4_address_not_found(
+      self, mock_adb_shell: MagicMock
+  ) -> None:
+     mock_adb_shell.return_value = ""
+     with asserts.assert_raises(PatternNotFoundException):
+       get_ipv4_address(self.mock_ad, "wlan0")
+
+  @patch("net_tests_utils.host.python.adb_utils.adb_shell")
+  def test_get_ipv6_address_success(
+      self, mock_adb_shell: MagicMock
+  ) -> None:
+    mock_adb_shell.return_value = """
+54: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 1000
+inet6 fe80::10a3:5dff:fe52:de32/64 scope link
+valid_lft forever preferred_lft forever
+"""
+    ip_address = get_ipv6_address(self.mock_ad, "wlan0")
+    asserts.assert_equal(ip_address, "fe80::10a3:5dff:fe52:de32")
+
+  @patch("net_tests_utils.host.python.adb_utils.adb_shell")
+  def test_get_ipv6_address_not_found(
+          self, mock_adb_shell: MagicMock
+  ) -> None:
+      mock_adb_shell.return_value = ""
+      with asserts.assert_raises(PatternNotFoundException):
+          get_ipv6_address(self.mock_ad, "wlan0")
+
+  @patch("net_tests_utils.host.python.adb_utils.adb_shell")
   def test_send_raw_packet_downstream_success(
       self, mock_adb_shell: MagicMock
   ) -> None:
diff --git a/staticlibs/testutils/host/python/apf_utils.py b/staticlibs/testutils/host/python/apf_utils.py
index c3330d2..b312bcf 100644
--- a/staticlibs/testutils/host/python/apf_utils.py
+++ b/staticlibs/testutils/host/python/apf_utils.py
@@ -83,6 +83,80 @@
   ad.log.debug("Getting apf counters: " + str(result))
   return result
 
+def get_ipv4_address(
+    ad: android_device.AndroidDevice, iface_name: str
+) -> str:
+  """Retrieves the IPv4 address of a given interface on an Android device.
+
+  This function executes an ADB shell command (`ip -4 address show`) to get the
+  network interface information and extracts the IPv4 address from the output.
+  If devices has multiple IPv4 addresses, return the first one.
+  If devices have no IPv4 address, raise PatternNotFoundException.
+
+  Args:
+      ad: The Android device object.
+      iface_name: The name of the network interface (e.g., "wlan0").
+
+  Returns:
+      The IPv4 address of the interface as a string.
+
+  Raises:
+      PatternNotFoundException: If the IPv4 address is not found in the command
+      output.
+  """
+  # output format:
+  # 54: wlan2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
+  # inet 192.168.195.162/24 brd 192.168.195.255 scope global wlan2
+  # valid_lft forever preferred_lft forever
+  output = adb_utils.adb_shell(ad, f"ip -4 address show {iface_name}")
+  pattern = r"inet\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\/\d+"
+  match = re.search(pattern, output)
+
+  if match:
+    return match.group(1)  # Extract the IPv4 address string.
+  else:
+    raise PatternNotFoundException(
+      "Cannot get hardware address for " + iface_name
+    )
+
+def get_ipv6_address(
+    ad: android_device.AndroidDevice, iface_name: str
+) -> str:
+  """Retrieves the IPv6 address of a given interface on an Android device.
+
+  This function executes an ADB shell command (`ip -6 address show`) to get the
+  network interface information and extracts the IPv6 address from the output.
+  If devices has multiple IPv6 addresses, return the first one.
+  If devices have no IPv6 address, raise PatternNotFoundException.
+
+  Args:
+      ad: The Android device object.
+      iface_name: The name of the network interface (e.g., "wlan0").
+
+  Returns:
+      The IPv6 address of the interface as a string.
+
+  Raises:
+      PatternNotFoundException: If the IPv6 address is not found in the command
+      output.
+  """
+  # output format
+  # 54: wlan2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 1000
+  # inet6 fe80::10a3:5dff:fe52:de32/64 scope link
+  # valid_lft forever preferred_lft forever
+  output = adb_utils.adb_shell(ad, f"ip -6 address show {iface_name}")
+  if output is "":
+    raise PatternNotFoundException(
+      "Cannot get ipv6 address for " + iface_name
+    )
+  pattern = r"inet6\s+([0-9a-fA-F:]+)\/\d+"
+  match = re.search(pattern, output)
+  if match:
+    return match.group(1)  # Extract the IPv6 address string.
+  else:
+    raise PatternNotFoundException(
+      "Cannot get IPv6 address for " + iface_name
+    )
 
 def get_hardware_address(
     ad: android_device.AndroidDevice, iface_name: str