diff --git a/.github/BOTMETA.yml b/.github/BOTMETA.yml
index 6896106906..2be4619ecb 100644
--- a/.github/BOTMETA.yml
+++ b/.github/BOTMETA.yml
@@ -309,6 +309,8 @@ files:
     maintainers: delineaKrehl tylerezimmerman
   $module_utils/:
     labels: module_utils
+  $module_utils/android_sdkmanager.py:
+    maintainers: shamilovstas
   $module_utils/btrfs.py:
     maintainers: gnfzdz
   $module_utils/cmd_runner_fmt.py:
@@ -420,6 +422,8 @@ files:
     ignore: DavidWittman jiuka
     labels: alternatives
     maintainers: mulby
+  $modules/android_sdk.py:
+    maintainers: shamilovstas
   $modules/ansible_galaxy_install.py:
     maintainers: russoz
   $modules/apache2_mod_proxy.py:
diff --git a/plugins/module_utils/android_sdkmanager.py b/plugins/module_utils/android_sdkmanager.py
new file mode 100644
index 0000000000..9cbb2df6b0
--- /dev/null
+++ b/plugins/module_utils/android_sdkmanager.py
@@ -0,0 +1,148 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2024, Stanislav Shamilov <shamilovstas@protonmail.com>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import re
+
+from ansible_collections.community.general.plugins.module_utils.cmd_runner import CmdRunner, cmd_runner_fmt
+
+__state_map = {
+    "present": "--install",
+    "absent": "--uninstall"
+}
+
+# sdkmanager --help 2>&1 | grep -A 2 -- --channel
+__channel_map = {
+    "stable": 0,
+    "beta": 1,
+    "dev": 2,
+    "canary": 3
+}
+
+
+def __map_channel(channel_name):
+    if channel_name not in __channel_map:
+        raise ValueError("Unknown channel name '%s'" % channel_name)
+    return __channel_map[channel_name]
+
+
+def sdkmanager_runner(module, **kwargs):
+    return CmdRunner(
+        module,
+        command='sdkmanager',
+        arg_formats=dict(
+            state=cmd_runner_fmt.as_map(__state_map),
+            name=cmd_runner_fmt.as_list(),
+            installed=cmd_runner_fmt.as_fixed("--list_installed"),
+            list=cmd_runner_fmt.as_fixed('--list'),
+            newer=cmd_runner_fmt.as_fixed("--newer"),
+            sdk_root=cmd_runner_fmt.as_opt_eq_val("--sdk_root"),
+            channel=cmd_runner_fmt.as_func(lambda x: ["{0}={1}".format("--channel", __map_channel(x))])
+        ),
+        force_lang="C.UTF-8",  # Without this, sdkmanager binary crashes
+        **kwargs
+    )
+
+
+class Package:
+    def __init__(self, name):
+        self.name = name
+
+    def __hash__(self):
+        return hash(self.name)
+
+    def __ne__(self, other):
+        if not isinstance(other, Package):
+            return True
+        return self.name != other.name
+
+    def __eq__(self, other):
+        if not isinstance(other, Package):
+            return False
+
+        return self.name == other.name
+
+
+class SdkManagerException(Exception):
+    pass
+
+
+class AndroidSdkManager(object):
+    _RE_INSTALLED_PACKAGES_HEADER = re.compile(r'^Installed packages:$')
+    _RE_UPDATABLE_PACKAGES_HEADER = re.compile(r'^Available Updates:$')
+
+    # Example: '  platform-tools     | 27.0.0  | Android SDK Platform-Tools 27 | platform-tools  '
+    _RE_INSTALLED_PACKAGE = re.compile(r'^\s*(?P<name>\S+)\s*\|\s*[0-9][^|]*\b\s*\|\s*.+\s*\|\s*(\S+)\s*$')
+
+    # Example: '   platform-tools | 27.0.0    | 35.0.2'
+    _RE_UPDATABLE_PACKAGE = re.compile(r'^\s*(?P<name>\S+)\s*\|\s*[0-9][^|]*\b\s*\|\s*[0-9].*\b\s*$')
+
+    _RE_UNKNOWN_PACKAGE = re.compile(r'^Warning: Failed to find package \'(?P<package>\S+)\'\s*$')
+    _RE_ACCEPT_LICENSE = re.compile(r'^The following packages can not be installed since their licenses or those of '
+                                    r'the packages they depend on were not accepted')
+
+    def __init__(self, module):
+        self.runner = sdkmanager_runner(module)
+
+    def get_installed_packages(self):
+        with self.runner('installed sdk_root channel') as ctx:
+            rc, stdout, stderr = ctx.run()
+            return self._parse_packages(stdout, self._RE_INSTALLED_PACKAGES_HEADER, self._RE_INSTALLED_PACKAGE)
+
+    def get_updatable_packages(self):
+        with self.runner('list newer sdk_root channel') as ctx:
+            rc, stdout, stderr = ctx.run()
+            return self._parse_packages(stdout, self._RE_UPDATABLE_PACKAGES_HEADER, self._RE_UPDATABLE_PACKAGE)
+
+    def apply_packages_changes(self, packages, accept_licenses=False):
+        """ Install or delete packages, depending on the `module.vars.state` parameter """
+        if len(packages) == 0:
+            return 0, '', ''
+
+        if accept_licenses:
+            license_prompt_answer = 'y'
+        else:
+            license_prompt_answer = 'N'
+        for package in packages:
+            with self.runner('state name sdk_root channel', data=license_prompt_answer) as ctx:
+                rc, stdout, stderr = ctx.run(name=package.name)
+
+                for line in stdout.splitlines():
+                    if self._RE_ACCEPT_LICENSE.match(line):
+                        raise SdkManagerException("Licenses for some packages were not accepted")
+
+                if rc != 0:
+                    self._try_parse_stderr(stderr)
+                    return rc, stdout, stderr
+        return 0, '', ''
+
+    def _try_parse_stderr(self, stderr):
+        data = stderr.splitlines()
+        for line in data:
+            unknown_package_regex = self._RE_UNKNOWN_PACKAGE.match(line)
+            if unknown_package_regex:
+                package = unknown_package_regex.group('package')
+                raise SdkManagerException("Unknown package %s" % package)
+
+    @staticmethod
+    def _parse_packages(stdout, header_regexp, row_regexp):
+        data = stdout.splitlines()
+
+        section_found = False
+        packages = set()
+
+        for line in data:
+            if not section_found:
+                section_found = header_regexp.match(line)
+                continue
+            else:
+                p = row_regexp.match(line)
+                if p:
+                    packages.add(Package(p.group('name')))
+        return packages
diff --git a/plugins/modules/android_sdk.py b/plugins/modules/android_sdk.py
new file mode 100644
index 0000000000..9851a84fc2
--- /dev/null
+++ b/plugins/modules/android_sdk.py
@@ -0,0 +1,213 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2024, Stanislav Shamilov <shamilovstas@protonmail.com>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = r'''
+---
+module: android_sdk
+short_description: Manages Android SDK packages
+description:
+    - Manages Android SDK packages.
+    - Allows installation from different channels (stable, beta, dev, canary).
+    - Allows installation of packages to a non-default SDK root directory.
+author: Stanislav Shamilov (@shamilovstas)
+extends_documentation_fragment:
+  - community.general.attributes
+attributes:
+  check_mode:
+    support: full
+  diff_mode:
+    support: none
+version_added: 10.2.0
+options:
+  accept_licenses:
+    description:
+      - If this is set to B(true), the module will try to accept license prompts generated by C(sdkmanager) during
+        package installation. Otherwise, every license prompt will be rejected.
+    type: bool
+    default: false
+  name:
+    description:
+      - A name of an Android SDK package (for instance, V(build-tools;34.0.0)).
+    aliases: ['package', 'pkg']
+    type: list
+    elements: str
+  state:
+    description:
+      - Indicates the desired package(s) state.
+      - V(present) ensures that package(s) is/are present.
+      - V(absent) ensures that package(s) is/are absent.
+      - V(latest) ensures that package(s) is/are installed and updated to the latest version(s).
+    choices: ['present', 'absent', 'latest']
+    default: present
+    type: str
+  sdk_root:
+    description:
+      - Provides path for an alternative directory to install Android SDK packages to. By default, all packages
+        are installed to the directory where C(sdkmanager) is installed.
+    type: path
+  channel:
+    description:
+      - Indicates what channel must C(sdkmanager) use for installation of packages.
+    choices: ['stable', 'beta', 'dev', 'canary']
+    default: stable
+    type: str
+requirements:
+  - C(java) >= 17
+  - C(sdkmanager) Command line tool for installing Android SDK packages.
+notes:
+  - For some of the packages installed by C(sdkmanager) is it necessary to accept licenses. Usually it is done through
+    command line prompt in a form of a Y/N question when a licensed package is requested to be installed. If there are
+    several packages requested for installation and at least two of them belong to different licenses, the C(sdkmanager)
+    tool will prompt for these licenses in a loop.
+    In order to install packages, the module must be able to answer these license prompts. Currently, it is only
+    possible to answer one license prompt at a time, meaning that instead of installing multiple packages as a single
+    invocation of the C(sdkmanager --install) command, it will be done by executing the command independently for each
+    package. This makes sure that at most only one license prompt will need to be answered.
+    At the time of writing this module, a C(sdkmanager)'s package may belong to at most one license type that needs to
+    be accepted. However, if this is changes in the future, the module may hang as there might be more prompts generated
+    by the C(sdkmanager) tool which the module will not be able to answer. If this is the case, file an issue and in the
+    meantime, consider accepting all the licenses in advance, as it is described in the C(sdkmanager)
+    L(documentation,https://developer.android.com/tools/sdkmanager#accept-licenses), for instance, using the
+    M(ansible.builtin.command) module.
+seealso:
+  - name: sdkmanager tool documentation
+    description: Detailed information of how to install and use sdkmanager command line tool.
+    link: https://developer.android.com/tools/sdkmanager
+'''
+
+EXAMPLES = r'''
+- name: Install build-tools;34.0.0
+  community.general.android_sdk:
+    name: build-tools;34.0.0
+    accept_licenses: true
+    state: present
+
+- name: Install build-tools;34.0.0 and platform-tools
+  community.general.android_sdk:
+    name:
+      - build-tools;34.0.0
+      - platform-tools
+    accept_licenses: true
+    state: present
+
+- name: Delete build-tools;34.0.0
+  community.general.android_sdk:
+    name: build-tools;34.0.0
+    state: absent
+
+- name: Install platform-tools or update if installed
+  community.general.android_sdk:
+    name: platform-tools
+    accept_licenses: true
+    state: latest
+
+- name: Install build-tools;34.0.0 to a different SDK root
+  community.general.android_sdk:
+    name: build-tools;34.0.0
+    accept_licenses: true
+    state: present
+    sdk_root: "/path/to/new/root"
+
+- name: Install a package from another channel
+  community.general.android_sdk:
+    name: some-package-present-in-canary-channel
+    accept_licenses: true
+    state: present
+    channel: canary
+'''
+
+RETURN = r'''
+installed:
+    description: a list of packages that have been installed
+    returned: when packages have changed
+    type: list
+    sample: ['build-tools;34.0.0', 'platform-tools']
+
+removed:
+    description: a list of packages that have been removed
+    returned: when packages have changed
+    type: list
+    sample: ['build-tools;34.0.0', 'platform-tools']
+'''
+
+from ansible_collections.community.general.plugins.module_utils.mh.module_helper import StateModuleHelper
+from ansible_collections.community.general.plugins.module_utils.android_sdkmanager import Package, AndroidSdkManager
+
+
+class AndroidSdk(StateModuleHelper):
+    module = dict(
+        argument_spec=dict(
+            state=dict(type='str', default='present', choices=['present', 'absent', 'latest']),
+            package=dict(type='list', elements='str', aliases=['pkg', 'name']),
+            sdk_root=dict(type='path'),
+            channel=dict(type='str', default='stable', choices=['stable', 'beta', 'dev', 'canary']),
+            accept_licenses=dict(type='bool', default=False)
+        ),
+        supports_check_mode=True
+    )
+    use_old_vardict = False
+
+    def __init_module__(self):
+        self.sdkmanager = AndroidSdkManager(self.module)
+        self.vars.set('installed', [], change=True)
+        self.vars.set('removed', [], change=True)
+
+    def _parse_packages(self):
+        arg_pkgs = set(self.vars.package)
+        if len(arg_pkgs) < len(self.vars.package):
+            self.do_raise("Packages may not repeat")
+        return set([Package(p) for p in arg_pkgs])
+
+    def state_present(self):
+        packages = self._parse_packages()
+        installed = self.sdkmanager.get_installed_packages()
+        pending_installation = packages.difference(installed)
+
+        self.vars.installed = AndroidSdk._map_packages_to_names(pending_installation)
+        if not self.check_mode:
+            rc, stdout, stderr = self.sdkmanager.apply_packages_changes(pending_installation, self.vars.accept_licenses)
+            if rc != 0:
+                self.do_raise("Could not install packages: %s" % stderr)
+
+    def state_absent(self):
+        packages = self._parse_packages()
+        installed = self.sdkmanager.get_installed_packages()
+        to_be_deleted = packages.intersection(installed)
+        self.vars.removed = AndroidSdk._map_packages_to_names(to_be_deleted)
+        if not self.check_mode:
+            rc, stdout, stderr = self.sdkmanager.apply_packages_changes(to_be_deleted)
+            if rc != 0:
+                self.do_raise("Could not uninstall packages: %s" % stderr)
+
+    def state_latest(self):
+        packages = self._parse_packages()
+        installed = self.sdkmanager.get_installed_packages()
+        updatable = self.sdkmanager.get_updatable_packages()
+        not_installed = packages.difference(installed)
+        to_be_installed = not_installed.union(updatable)
+        self.vars.installed = AndroidSdk._map_packages_to_names(to_be_installed)
+
+        if not self.check_mode:
+            rc, stdout, stderr = self.sdkmanager.apply_packages_changes(to_be_installed, self.vars.accept_licenses)
+            if rc != 0:
+                self.do_raise("Could not install packages: %s" % stderr)
+
+    @staticmethod
+    def _map_packages_to_names(packages):
+        return [x.name for x in packages]
+
+
+def main():
+    AndroidSdk.execute()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/tests/integration/targets/android_sdk/aliases b/tests/integration/targets/android_sdk/aliases
new file mode 100644
index 0000000000..bb79889366
--- /dev/null
+++ b/tests/integration/targets/android_sdk/aliases
@@ -0,0 +1,7 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+azp/posix/3
+destructive
+needs/root
\ No newline at end of file
diff --git a/tests/integration/targets/android_sdk/meta/main.yml b/tests/integration/targets/android_sdk/meta/main.yml
new file mode 100644
index 0000000000..d7c152feeb
--- /dev/null
+++ b/tests/integration/targets/android_sdk/meta/main.yml
@@ -0,0 +1,8 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+dependencies:
+  - setup_pkg_mgr
+  - setup_remote_tmp_dir
\ No newline at end of file
diff --git a/tests/integration/targets/android_sdk/tasks/default-tests.yml b/tests/integration/targets/android_sdk/tasks/default-tests.yml
new file mode 100644
index 0000000000..b8cb6df54d
--- /dev/null
+++ b/tests/integration/targets/android_sdk/tasks/default-tests.yml
@@ -0,0 +1,92 @@
+---
+####################################################################
+# WARNING: These are designed specifically for Ansible tests       #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Install build-tools;34.0.0
+  android_sdk:
+    accept_licenses: true
+    name: build-tools;34.0.0
+    state: present
+  register: build_tools_installed
+
+- name: Install build-tools;34.0.0 second time
+  android_sdk:
+    name: build-tools;34.0.0
+    state: present
+  register: build_tools_installed2
+
+- name: Stat build-tools
+  stat:
+    path: "{{ android_sdk_location }}/build-tools/34.0.0"
+  register: build_tools_34_0_0
+
+- name: Delete build-tools;34.0.0
+  android_sdk:
+    name: build-tools;34.0.0
+    state: absent
+  register: build_tools_deleted
+
+- name: Delete build-tools;34.0.0 second time
+  android_sdk:
+    name: build-tools;34.0.0
+    state: absent
+  register: build_tools_deleted2
+
+- name: Download old platform-tools
+  unarchive:
+    src: https://dl.google.com/android/repository/platform-tools_r27.0.0-linux.zip
+    remote_src: true
+    dest: "{{ android_sdk_location }}"
+
+- name: Try installing platform-tools from sdkmanager
+  android_sdk:
+    name: platform-tools
+    accept_licenses: true
+    state: present
+  register: platform_tools_present
+
+- name: Install (update) platform-tools
+  android_sdk:
+    name: platform-tools
+    state: latest
+  register: platform_tools_updated
+
+- name: Install a package to a new root
+  android_sdk:
+    name: build-tools;34.0.0
+    accept_licenses: true
+    state: present
+    sdk_root: "{{ remote_tmp_dir }}"
+  register: new_root_package
+
+- name: Check package is installed
+  stat:
+    path: "{{ remote_tmp_dir }}/build-tools/34.0.0"
+  register: new_root_package_stat
+
+- name: Install a package from canary channel
+  android_sdk:
+    name: build-tools;33.0.0
+    state: present
+    channel: canary
+  register: package_canary
+
+- name: Run tests
+  assert:
+    that:
+      - build_tools_34_0_0.stat.exists
+      - build_tools_installed is changed
+      - build_tools_installed2 is not changed
+      - build_tools_deleted is changed
+      - build_tools_deleted2 is not changed
+      - platform_tools_present is not changed
+      - platform_tools_updated is changed
+      - new_root_package is changed
+      - new_root_package_stat.stat.exists
+      - package_canary is changed
\ No newline at end of file
diff --git a/tests/integration/targets/android_sdk/tasks/freebsd-tests.yml b/tests/integration/targets/android_sdk/tasks/freebsd-tests.yml
new file mode 100644
index 0000000000..f1886f245d
--- /dev/null
+++ b/tests/integration/targets/android_sdk/tasks/freebsd-tests.yml
@@ -0,0 +1,72 @@
+---
+####################################################################
+# WARNING: These are designed specifically for Ansible tests       #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Install sources;android-26 (FreeBSD)
+  android_sdk:
+    name: sources;android-26
+    accept_licenses: true
+    state: present
+  register: sources_android_26_installed
+
+- name: Install sources;android-26 (FreeBSD)
+  android_sdk:
+    name: sources;android-26
+    state: present
+  register: sources_android_26_installed2
+
+- name: Stat build-tools (FreeBSD)
+  stat:
+    path: "{{ android_sdk_location }}/sources/android-26"
+  register: sources_android_26
+
+- name: Delete sources;android-26 (FreeBSD)
+  android_sdk:
+    name: sources;android-26
+    state: absent
+  register: sources_android_26_deleted
+
+- name: Delete sources;android-26 second time (FreeBSD)
+  android_sdk:
+    name: sources;android-26
+    state: absent
+  register: sources_android_26_deleted2
+
+- name: Install a package to a new root (FreeBSD)
+  android_sdk:
+    name: sources;android-26
+    accept_licenses: true
+    state: present
+    sdk_root: "{{ remote_tmp_dir }}"
+  register: new_root_package
+
+- name: Check package is installed (FreeBSD)
+  stat:
+    path: "{{ remote_tmp_dir }}/sources/android-26"
+  register: new_root_package_stat
+
+- name: Install a package from canary channel (FreeBSD)
+  android_sdk:
+    name: sources;android-26
+    accept_licenses: true
+    state: present
+    channel: canary
+  register: package_canary
+
+- name: Run tests (FreeBSD)
+  assert:
+    that:
+      - sources_android_26.stat.exists
+      - sources_android_26_installed is changed
+      - sources_android_26_installed2 is not changed
+      - sources_android_26_deleted is changed
+      - sources_android_26_deleted2 is not changed
+      - new_root_package is changed
+      - new_root_package_stat.stat.exists
+      - package_canary is changed
diff --git a/tests/integration/targets/android_sdk/tasks/main.yml b/tests/integration/targets/android_sdk/tasks/main.yml
new file mode 100644
index 0000000000..46cf3192e1
--- /dev/null
+++ b/tests/integration/targets/android_sdk/tasks/main.yml
@@ -0,0 +1,31 @@
+---
+####################################################################
+# WARNING: These are designed specifically for Ansible tests       #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# java >= 17 is not available in RHEL and CentOS7 repos, which is required for sdkmanager to run
+- name: Bail out if not supported
+  when:
+  - "ansible_os_family == 'RedHat' and ansible_distribution_version is version('8.0', '<')"
+  ansible.builtin.meta: end_play
+
+- name: Run android_sdk tests
+  environment:
+    PATH: '{{ ansible_env.PATH }}:{{ android_sdk_location }}/cmdline-tools/latest/bin'
+  block:
+  - import_tasks: setup.yml
+
+  - name: Run default tests
+    import_tasks: default-tests.yml
+    when: ansible_os_family != 'FreeBSD'
+
+  # Most of the important Android SDK packages are not available on FreeBSD (like, build-tools, platform-tools and so on),
+  # but at least some of the functionality can be tested (like, downloading sources)
+  - name: Run FreeBSD tests
+    import_tasks: freebsd-tests.yml
+    when: ansible_os_family == 'FreeBSD'
diff --git a/tests/integration/targets/android_sdk/tasks/setup.yml b/tests/integration/targets/android_sdk/tasks/setup.yml
new file mode 100644
index 0000000000..ff2e3eb3cf
--- /dev/null
+++ b/tests/integration/targets/android_sdk/tasks/setup.yml
@@ -0,0 +1,86 @@
+---
+####################################################################
+# WARNING: These are designed specifically for Ansible tests       #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Include OS-specific variables
+  include_vars: '{{ ansible_os_family }}.yml'
+
+- name: Install dependencies
+  become: true
+  package:
+    name:
+      - "{{ openjdk_pkg }}"
+      - unzip
+    state: present
+  when: ansible_os_family != 'Darwin'
+
+- name: Install dependencies (OSX)
+  block:
+    - name: Find brew binary
+      command: which brew
+      register: brew_which
+    - name: Get owner of brew binary
+      stat:
+        path: "{{ brew_which.stdout }}"
+      register: brew_stat
+    - name: "Install package"
+      homebrew:
+        name:
+          - "{{ openjdk_pkg }}"
+          - unzip
+        state: present
+        update_homebrew: false
+      become: true
+      become_user: "{{ brew_stat.stat.pw_name }}"
+      environment:
+        HOMEBREW_NO_AUTO_UPDATE: "True"
+    - name: Symlink java
+      become: true
+      file:
+        src: "/usr/local/opt/openjdk@17/libexec/openjdk.jdk"
+        dest: "/Library/Java/JavaVirtualMachines/openjdk-17.jdk"
+        state: link
+  when:
+    - ansible_os_family == 'Darwin'
+
+- name: Create Android SDK directory
+  file:
+    path: "{{ android_sdk_location }}"
+    state: directory
+
+- name: Check that sdkmanager is installed
+  stat:
+    path: "{{ android_sdk_location }}/cmdline-tools/latest/bin/sdkmanager"
+  register: sdkmanager_installed
+
+- name: Install Android command line tools
+  when: not sdkmanager_installed.stat.exists
+  block:
+    - name: Create Android SDK dir structure
+      file:
+        path: "{{ item.path }}"
+        state: "{{ item.state }}"
+      with_items:
+        - { path: "{{ android_cmdline_temp_dir }}", state: "directory" }
+        - { path: "{{ android_sdk_location }}/cmdline-tools/latest", state: "directory" }
+
+    - name: Download Android command line tools
+      unarchive:
+        src: "{{ commandline_tools_link }}"
+        dest: "{{ android_cmdline_temp_dir }}"
+        remote_src: yes
+        creates: "{{ android_cmdline_temp_dir }}/cmdline-tools"
+      when: not sdkmanager_installed.stat.exists
+
+
+    - name: Fix directory structure
+      copy:
+        src: "{{ android_cmdline_temp_dir }}/cmdline-tools/"
+        dest: "{{ android_sdk_location }}/cmdline-tools/latest"
+        remote_src: yes
diff --git a/tests/integration/targets/android_sdk/vars/Alpine.yml b/tests/integration/targets/android_sdk/vars/Alpine.yml
new file mode 100644
index 0000000000..593925f043
--- /dev/null
+++ b/tests/integration/targets/android_sdk/vars/Alpine.yml
@@ -0,0 +1,6 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+openjdk_pkg: openjdk17-jre-headless
diff --git a/tests/integration/targets/android_sdk/vars/Archlinux.yml b/tests/integration/targets/android_sdk/vars/Archlinux.yml
new file mode 100644
index 0000000000..ff46870671
--- /dev/null
+++ b/tests/integration/targets/android_sdk/vars/Archlinux.yml
@@ -0,0 +1,6 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+openjdk_pkg: jre17-openjdk-headless
diff --git a/tests/integration/targets/android_sdk/vars/Darwin.yml b/tests/integration/targets/android_sdk/vars/Darwin.yml
new file mode 100644
index 0000000000..696bf39a75
--- /dev/null
+++ b/tests/integration/targets/android_sdk/vars/Darwin.yml
@@ -0,0 +1,6 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+openjdk_pkg: openjdk@17
diff --git a/tests/integration/targets/android_sdk/vars/Debian.yml b/tests/integration/targets/android_sdk/vars/Debian.yml
new file mode 100644
index 0000000000..ddcfaaf1e3
--- /dev/null
+++ b/tests/integration/targets/android_sdk/vars/Debian.yml
@@ -0,0 +1,6 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+openjdk_pkg: openjdk-17-jre-headless
diff --git a/tests/integration/targets/android_sdk/vars/FreeBSD.yml b/tests/integration/targets/android_sdk/vars/FreeBSD.yml
new file mode 100644
index 0000000000..61c1858423
--- /dev/null
+++ b/tests/integration/targets/android_sdk/vars/FreeBSD.yml
@@ -0,0 +1,6 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+openjdk_pkg: openjdk17-jre
diff --git a/tests/integration/targets/android_sdk/vars/RedHat.yml b/tests/integration/targets/android_sdk/vars/RedHat.yml
new file mode 100644
index 0000000000..40f44bd773
--- /dev/null
+++ b/tests/integration/targets/android_sdk/vars/RedHat.yml
@@ -0,0 +1,6 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+openjdk_pkg: java-17-openjdk-headless
diff --git a/tests/integration/targets/android_sdk/vars/Suse.yml b/tests/integration/targets/android_sdk/vars/Suse.yml
new file mode 100644
index 0000000000..40f44bd773
--- /dev/null
+++ b/tests/integration/targets/android_sdk/vars/Suse.yml
@@ -0,0 +1,6 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+openjdk_pkg: java-17-openjdk-headless
diff --git a/tests/integration/targets/android_sdk/vars/main.yml b/tests/integration/targets/android_sdk/vars/main.yml
new file mode 100644
index 0000000000..9ba619a6d5
--- /dev/null
+++ b/tests/integration/targets/android_sdk/vars/main.yml
@@ -0,0 +1,8 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+android_cmdline_temp_dir: "/tmp/cmdlinetools"
+android_sdk_location: "/tmp/androidsdk"
+commandline_tools_link: https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip