├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── data └── icons │ └── desktop_icon.svg ├── docs ├── android-x86-manual.md └── bliss-os-manual.md ├── launcher.sh ├── modules ├── core.sh └── drive-utils.sh └── vm.conf /.gitignore: -------------------------------------------------------------------------------- 1 | drives/ 2 | images/ 3 | mnt/ 4 | vm.user.conf 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2023-2024, Max Gashutin "maximilionus" 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | APP_NAME := Android Qemu Launcher 2 | ENTRY_NAME := android-qemu-launcher 3 | SCRIPT_NAME := launcher.sh 4 | ICON_PATH := data/icons/desktop_icon.svg 5 | DEST_DIR := $(HOME)/.local/share/applications 6 | .DEFAULT_GOAL := help 7 | 8 | install: 9 | @echo "[Desktop Entry]" > $(DEST_DIR)/$(ENTRY_NAME).desktop 10 | @echo "Name=$(APP_NAME)" >> $(DEST_DIR)/$(ENTRY_NAME).desktop 11 | @echo "Exec=$(realpath $(SCRIPT_NAME))" >> $(DEST_DIR)/$(ENTRY_NAME).desktop 12 | @echo "Icon=$(realpath $(ICON_PATH))" >> $(DEST_DIR)/$(ENTRY_NAME).desktop 13 | @echo "Type=Application" >> $(DEST_DIR)/$(ENTRY_NAME).desktop 14 | @echo "Categories=Graphics;" >> $(DEST_DIR)/$(ENTRY_NAME).desktop 15 | 16 | @echo "Desktop entry created successfully." 17 | 18 | uninstall: 19 | @rm -f $(DEST_DIR)/$(ENTRY_NAME).desktop 20 | 21 | @echo "Desktop entry removed successfully." 22 | 23 | help: 24 | @echo "Usage: make [target]" 25 | @echo "" 26 | @echo "Targets:" 27 | @echo " install Create the desktop entry." 28 | @echo " uninstall Remove the desktop entry." 29 | @echo " help Display this help message." 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## About 2 | Utility and documentation to run hardware-accelerated x86_64 Android images on Linux QEMU KVM 3 | 4 | ## Supported ROM 5 | This list contains the names of Android images that are officially supported and tested with this launcher and have their own manuals in this repository, under the [docs](./docs/) directory. 6 | 7 | - Bliss OS : [Manual](./docs/bliss-os-manual.md) 8 | - Android x86 : [Manual](./docs/android-x86-manual.md) 9 | 10 | 11 | ## Install 12 | ### Requirements 13 | - Qemu 14 | - [Virtio](https://www.linux-kvm.org/page/Virtio) drivers (Installed as a Qemu dependency in most package managers by default) 15 | 16 | 17 | ### Steps 18 | > **Note** 19 | > If your ROM is [officially supported](#supported-rom) by this launcher, then be sure to read the manuals in the [docs](./docs/) directory. 20 | 21 | 1. Clone this repository 22 | > **Note** 23 | > Yes, you can simply download this repo, but using git is the preferred way if you want to get all the new updates from the upstream. 24 | 25 | ```sh 26 | git clone https://github.com/maximilionus/android-qemu-launcher.git 27 | ``` 28 | 2. Download the desired Android image, but prefer the [officially supported](#supported-rom) one. 29 | 3. Run the [launcher](./launcher.sh) with argument `init` to prepare the file structure and drives for VM: 30 | > **Warning** 31 | > Be ready to provide user input. 32 | 33 | ```sh 34 | ./launcher.sh init 35 | ``` 36 | 37 | Output: 38 | ```sh 39 | Directories initialized. 40 | Enter VM drive size (default: 20G): [USER-INPUT] 41 | Formatting './drives/...', [...] 42 | Everything is done [...] 43 | ``` 44 | 45 | 4. Run the [launcher](./launcher.sh) with the **install** argument and the path to the Android image from step 2 next to it and proceed with basic Android installation on MBR (DOS) drive layout and GRUB bootloader enabled: 46 | ```sh 47 | ./launcher.sh install 48 | 49 | # Example 50 | ./launcher.sh install ~/Downloads/android-x86-rom.iso 51 | ``` 52 | 53 | 5. Shut down the virtual machine after the installer reports a successful installation, do not reboot it. 54 | 55 | 6. That's it. From now on, you can run the [launcher](./launcher.sh) to start the VM: 56 | ```sh 57 | ./launcher.sh 58 | 59 | # or 60 | 61 | ./launcher.sh run 62 | ``` 63 | 64 | 65 | ## Update 66 | To update the launcher, simply use this command 67 | ```sh 68 | git pull 69 | ``` 70 | 71 | 72 | ## Configuration 73 | ### Default 74 | The default configuration file is located in the root of this project and is named [`vm.conf`](./vm.conf). It contains all modifiable variables with their corresponding descriptions and default values. Every value in this file can be changed or even deleted at any point in the development process, so you should prefer using the user configuration file to make any tweaks. 75 | 76 | ### User 77 | This configuration file must be created manually by the user and placed in the root of this project under the name `vm.user.conf`. Launcher will automatically load it on each run, overwriting the modified variables from the [default configuration](#default). 78 | 79 | #### Example 80 | 1. Create the `vm.user.conf` in the root of this project. 81 | 2. Add modified CPU and RAM values to it. It should look something like this: 82 | ``` 83 | RAM_SIZE=8192 84 | CPU_CORES=8 85 | ADB_ENABLE=true 86 | ``` 87 | 3. Now VM will be allowed to use **8 CPU cores**, **8GB of RAM** and **Android Debug Bridge port forwarding enabled** on each start. 88 | 89 | > **Note** 90 | > You can also modify the path to the user configuration file by changing the `CUSTOM_CONFIG_PATH` variable value in the [default configuration](#default). 91 | 92 | 93 | ## Desktop Entry 94 | This project provides a convenient way to manage the desktop entry for the [launcher](./launcher.sh), making it easier to access from your desktop environment. 95 | 96 | ### Prerequisites 97 | - A desktop environment that supports `.desktop` entries *(e.g., GNOME, KDE, Xfce, etc.)*. 98 | - `GNU Make` utility installed on your system. 99 | 100 | ### Usage 101 | - To create the desktop entry: 102 | ```bash 103 | make install 104 | ``` 105 | 106 | - To remove the desktop entry: 107 | ```bash 108 | make uninstall 109 | ``` 110 | 111 | 112 | ## Android Debug Bridge (adb) 113 | Virtual Machine can also be accessed with `adb` from the host machine, but this feature is disabled by default in [configuration file](./vm.conf). To enable it, follow the steps below. 114 | 115 | 1. Create the [user configuration file](#user). 116 | 2. Add the `ADB_ENABLE=true` variable to it. 117 | > **Note** 118 | > You can also change the port forwarding by adding the `ADB_PORT=VALUE`. 119 | 3. Launch the VM and ensure that [**Developer mode**](https://developer.android.com/studio/debug/dev-options#enable) with [**USB Debugging**](https://developer.android.com/studio/debug/dev-options#Enable-debugging) are enabled. 120 | 4. Now you can connect to the VM with `adb` by executing the command below: 121 | ```sh 122 | adb connect localhost:4444 123 | ``` 124 | If you are connecting for the first time, you will be prompted to allow debugging in the VM. Allow the connection and run the command above again. 125 | -------------------------------------------------------------------------------- /data/icons/desktop_icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 15 | 17 | 38 | 41 | 45 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /docs/android-x86-manual.md: -------------------------------------------------------------------------------- 1 | # About 2 | This ROM is originally what this launcher started with and is therefore fully supported. However, after installation you need to do some tweaking if you want to get the full Android VM feature set - for example ARM support. It is also based on the old Android 9.0, making it difficult to use for the modern tasks and overall stability for graphical tasks is quite terrible. 3 | 4 | 5 | # Downloads 6 | > **Warning** 7 | > Only the versions from the list below have been tested with this launcher. You are free to use whatever version you want, but any kind of issues may occur. 8 | 9 | > **Note** 10 | > You can find all the official releases for this ROM [here](https://www.android-x86.org/download) 11 | 12 | - `9.0-r2` (Android 9.0) - [GAPPS](https://sourceforge.net/projects/android-x86/files/Release%209.0/android-x86_64-9.0-r2.iso/download) 13 | 14 | 15 | # Install 16 | Installation is quite easy, just follow the [basic guide](../README.md#install). You can also read the more detailed [official installation docs](https://www.android-x86.org/installhowto.html) of this ROM. 17 | 18 | 19 | # Extra 20 | ## ARM Support 21 | > **Warning** 22 | > For this method to work, you should install your system in **read/write** mode. You're basically asked to do so on the installer, right after formatting the drive. 23 | 24 | 25 | ### Automatic 26 | 1. Start the Android emulator with [main script](../launcher.sh). 27 | 2. Inside emulator, go to **Settings App -> Android x86 Options**, switch on the **Enable native bridge** and wait before automatic download+install finish (Check notifications for result). 28 | 3. Try launching the ARM app. If everything works fine - congratulations! If not - proceed to the [guide below](#extra). 29 | 30 | 31 | ### Extra 32 | > **Note** 33 | > Below are instructions for those who have not been able to get the arm translator to work in auto-setup mode. 34 | 35 | 1. Install the requirements: 36 | - [android-tools](https://developer.android.com/tools/releases/platform-tools) 37 | 38 | 2. [Download](http://dl.android-x86.org/houdini/9_y/houdini.sfs) the **x86_64 -> ARM** translation and put it somewhere you know. Rename the downloaded file from `houdini.sfs` to `houdini9_y.sfs`. 39 | > **Note** 40 | > Replace the `` with real path in steps below. 41 | 42 | 3. Follow the [main guide](../README.md#android-debug-bridge-adb) to enable the `adb`. 43 | 44 | 4. Connect to emulator with **adb**: 45 | ```sh 46 | adb connect localhost:4444 47 | ``` 48 | *Output:* 49 | ```Log 50 | connected to localhost:4444 51 | ``` 52 | 53 | 5. Use **adb** to push the translation file to device: 54 | ```sh 55 | adb push /houdini9_y.sfs /sdcard/arm/ 56 | ``` 57 | *Output:* 58 | ```Log 59 | /houdini9_y.sfs: 1 file pushed, 0 skipped. 351.3 MB/s (42778624 bytes in 0.116s) 60 | ``` 61 | 62 | 6. Connect to device shell and request **SU**: 63 | ```sh 64 | adb shell 65 | ``` 66 | ```sh 67 | su 68 | ``` 69 | 70 | 7. Execute the built-in script to patch the `/system/`: 71 | ```sh 72 | enable_nativebridge 73 | ``` 74 | > **Note** 75 | > Script does not provide any echo output, which means that after it completes and does not return an error code is a success 76 | 77 | 8. That's it, now reboot the emulator, and you're ready to go: 78 | ```sh 79 | reboot -f 80 | ``` 81 | -------------------------------------------------------------------------------- /docs/bliss-os-manual.md: -------------------------------------------------------------------------------- 1 | # About 2 | One of the most stable and feature-rich ROMs on the modern Android versions. Comes with everything out of the box, even the ARM support. Amazing stability and graphics performance. 3 | 4 | 5 | # Download 6 | > **Warning** 7 | > Only the versions from the list below have been tested with this launcher. You are free to use whatever version you want, but any kind of issues may occur. 8 | 9 | > **Note** 10 | > You can find all the official releases for this ROM [here](https://blissos.org/) 11 | 12 | - `14.10` (Android 11) 13 | - `15.8.6` (Android 12L) 14 | - `16.9.4` (Android 13) 15 | 16 | 17 | # Install 18 | 1. Follow the [basic guide](../README.md#install) to install the OS. 19 | 2. Each VM startup, in GRUB, you should select the `BlissOS-XX.XX YYYY-MM-DD QEMO/KVM` boot entry. This will improve graphics performance and overall VM stability. It will also allow you to change the screen resolution and aspect ratio using the `video=WxH` [kernel parameter](https://wiki.archlinux.org/title/Kernel_parameters). 20 | -------------------------------------------------------------------------------- /launcher.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Exit on first error 4 | set -e 5 | 6 | # Change the workdir to the script location 7 | cd "$(dirname "$0")" 8 | 9 | # Load the configuration files 10 | . ./vm.conf 11 | 12 | if [ -f "$CUSTOM_CONFIG_PATH" ]; then 13 | . $CUSTOM_CONFIG_PATH 14 | fi 15 | 16 | # Load modules 17 | source ./modules/core.sh 18 | source ./modules/drive-utils.sh 19 | 20 | # Default args for qemu 21 | arguments_list=( 22 | "-enable-kvm" 23 | "-M" "q35" 24 | "-m" "$RAM_SIZE" 25 | "-smp" "$CPU_CORES" 26 | "-cpu" "host" 27 | "-drive" "file=$DRIVE_PATH,if=virtio" 28 | "-usb" 29 | "-device" "virtio-tablet" 30 | "-device" "virtio-keyboard" 31 | "-device" "qemu-xhci,id=xhci" 32 | "-device" "virtio-vga-gl" 33 | "-display" "sdl,gl=on" 34 | "-machine" "vmport=off" 35 | "-net" "nic,model=virtio-net-pci" 36 | ) 37 | 38 | # Add config-based args 39 | if [ "$ADB_ENABLE" = true ] ; then 40 | arguments_list+=("-net" "user,hostfwd=tcp::$ADB_PORT-:5555") 41 | else 42 | arguments_list+=("-net" "user") 43 | fi 44 | 45 | # Process params 46 | # TODO: Split implementation to module 47 | if ([ $# -eq 0 ] || [ "$1" = "run" ]); then 48 | # Launch the VM in default mode 49 | arguments_list+=( 50 | "-name" "$WINDOW_TITLE" 51 | "-audio" "pa,model=ac97" 52 | ) 53 | echo "Starting the VM in normal mode" 54 | elif [ "$1" = "install" ]; then 55 | # Launch installation mode 56 | if [ -z "$2" ]; then 57 | echo "Error: No path to Android image provided. Exiting." 58 | exit 1 59 | fi 60 | 61 | arguments_list+=( 62 | "-name" "$WINDOW_TITLE - Install" 63 | "-cdrom" "$2" 64 | ) 65 | 66 | echo "Starting the VM in installation mode" 67 | echo 68 | echo "Please read the manual in ./docs/ for your ROM if its" 69 | echo "officially supported by this launcher" 70 | echo 71 | echo "NOTE: Be sure to select the MBR (DOS) layout for the drive with" 72 | echo " ext4 formatting and GRUB bootloader enabled." 73 | elif [ "$1" = "init" ]; then 74 | # Initialize file structure for this launcher 75 | mkdir -p -v $(dirname "$DRIVE_PATH") 76 | 77 | echo "Directories initialized." 78 | read -p "Enter VM drive size (default: 20G): " _qemu_drive_size 79 | _qemu_drive_size="${_qemu_drive_size:-20G}" 80 | qemu-img create -f qcow2 "$DRIVE_PATH" $_qemu_drive_size 81 | 82 | echo 83 | echo "Everything is done. Now you should download the desired" 84 | echo "Android (x86_64 arch) image and launch this scipt with \"install\"" 85 | echo "argument, providing the path to the image." 86 | echo 87 | echo "EXAMPLE:" 88 | echo " ./launcher.sh install ~/Downloads/downloaded-android-image.iso" 89 | exit 0 90 | elif [ "$1" = "drive" ]; then 91 | driveutils_cli_process_args "${@:2}" 92 | elif [ "$1" = "d_args" ]; then 93 | echo "Composed arguments array:" 94 | echo "${arguments_list[@]}" 95 | exit 0 96 | elif [ "$1" = "help" ]; then 97 | # Show help messages 98 | echo "Usage: ./launcher.sh [COMMAND]" 99 | echo 100 | echo "COMMANDS:" 101 | echo " run : (Default) Run the Virtual Machine in normal mode." 102 | echo " init : Prepare everything for VM, initialize drives." 103 | echo " install : Run the Virtual Machine in installation mode with" 104 | echo " path to the Android image to be" 105 | echo " installed." 106 | echo " drive : Set of utilities to manage the VM drive." 107 | echo " help : Show this help message." 108 | echo 109 | echo "DEBUG COMMANDS:" 110 | echo " d_args : Print the composed array of arguments and exit." 111 | echo 112 | echo "NOTES:" 113 | echo " \"(Default)\" argument will be selected automatically, if no arguments" 114 | echo " are provided to the script." 115 | exit 0 116 | else 117 | echo "Error: Invalid argument: $1" 118 | exit 1 119 | fi 120 | 121 | qemu-system-x86_64 "${arguments_list[@]}" 122 | -------------------------------------------------------------------------------- /modules/core.sh: -------------------------------------------------------------------------------- 1 | # Core components for global use within the scope of this project. 2 | 3 | 4 | require_root () { 5 | if [[ $(/usr/bin/id -u) -ne 0 ]]; then 6 | echo "This action reqires the root privileges" 7 | exit 1 8 | fi 9 | } 10 | -------------------------------------------------------------------------------- /modules/drive-utils.sh: -------------------------------------------------------------------------------- 1 | # Drive image (qcow2) managing utilities 2 | 3 | 4 | __driveutils_load_modules () { 5 | echo "[ Loading the kernel modules ]" 6 | modprobe -v nbd max_part=8 7 | } 8 | 9 | __driveutils_unload_modules () { 10 | echo "[ Unloading the kernel modules ]" 11 | rmmod -v nbd 12 | } 13 | 14 | __driveutils_nbd_connect () { 15 | echo "[ Connecting the drive to NBD ]" 16 | qemu-nbd --connect=/dev/nbd0 $DRIVE_PATH 17 | } 18 | 19 | __driveutils_nbd_disconnect () { 20 | echo "[ Disconnecting the drive from NBD ]" 21 | qemu-nbd --disconnect /dev/nbd0 22 | } 23 | 24 | __driveutils_nbd_mount () { 25 | echo "[ Mounting the drive image to the '$(realpath $DRIVE_MOUNT_PATH)' directory]" 26 | 27 | mount_tries=1 28 | until mount /dev/nbd0p1 "$DRIVE_MOUNT_PATH"; do 29 | echo "- Attempting to mount the drive. Attempt: $mount_tries" 30 | 31 | if ((mount_tries >= 5)); then 32 | echo "[ Can not mount the drive. Shutting down. ]" 33 | set +e 34 | __driveutils_nbd_umount 35 | __driveutils_nbd_disconnect 36 | __driveutils_unload_modules 37 | __driveutils_mountdir_delete 38 | exit 1 39 | fi 40 | 41 | ((mount_tries++)) 42 | sleep 2 43 | done 44 | 45 | echo "[ Drive was successfully mounted ]" 46 | } 47 | 48 | __driveutils_nbd_umount () { 49 | echo "[ Unmounting the drive image from the '$(realpath $DRIVE_MOUNT_PATH)' directory]" 50 | umount -v "$DRIVE_MOUNT_PATH" 51 | echo "[ Drive was successfully unmounted ]" 52 | } 53 | 54 | __driveutils_mountdir_create () { 55 | echo "[ Creating the mount dir ]" 56 | mkdir -v -p "$DRIVE_MOUNT_PATH" 57 | } 58 | 59 | __driveutils_mountdir_delete () { 60 | echo "[ Removing the mount dir ]" 61 | rm -rfv "$DRIVE_MOUNT_PATH" 62 | } 63 | 64 | 65 | # Load kernel modules and mount the drive to a local folder inside the root 66 | # of current project 67 | driveutils_mount () { 68 | require_root 69 | 70 | __driveutils_load_modules 71 | __driveutils_nbd_connect 72 | __driveutils_mountdir_create 73 | __driveutils_nbd_mount 74 | } 75 | 76 | # Unmount the drive and unload the kernel modules 77 | driveutils_umount () { 78 | require_root 79 | 80 | __driveutils_nbd_umount 81 | __driveutils_nbd_disconnect 82 | __driveutils_unload_modules 83 | __driveutils_mountdir_delete 84 | } 85 | 86 | # Process the CLI args 87 | driveutils_cli_process_args () { 88 | arguments_arr=("${@}") 89 | 90 | if ([ $# -eq 0 ] || [ "${arguments_arr[0]}" = "help" ]); then 91 | echo "Usage: ./launcher.sh drive [COMMAND]" 92 | echo "About: Set of utilities to manage the VM drive." 93 | echo 94 | echo "COMMANDS:" 95 | echo " help : (Default) Show this help message." 96 | echo " mount : Mount the VM drive to the path provided with" 97 | echo " 'DRIVE_MOUNT_PATH' var in the configuration file." 98 | echo " umount : Unmount the VM drive and unload all modules." 99 | echo "" 100 | echo "NOTES:" 101 | echo " The \"mount\" and \"umount\" commands require root privileges" 102 | echo " to execute." 103 | elif ([ "${arguments_arr[0]}" == "mount" ]); then 104 | driveutils_mount 105 | elif ([ "${arguments_arr[0]}" == "umount" ]); then 106 | driveutils_umount 107 | else 108 | echo "Error: Invalid argument: \"${arguments_arr[0]}\"" 109 | exit 1 110 | fi 111 | 112 | exit 0 113 | } 114 | -------------------------------------------------------------------------------- /vm.conf: -------------------------------------------------------------------------------- 1 | # Path to user-defined configuration file. 2 | # Creating this file and changing any values in it will overwrite the default 3 | # values from this file. 4 | CUSTOM_CONFIG_PATH="./vm.user.conf" 5 | 6 | # VM window title 7 | WINDOW_TITLE="Android VM" 8 | 9 | # Size of max RAM, allocated for this VM in MB. 10 | # The default is 4GB, and that's more than enough. 11 | RAM_SIZE=4096 12 | 13 | # Number of CPU cores available for VM. 14 | # By default will use 75% of cores. 15 | CPU_CORES=$(( $(nproc) - $(nproc) / 4 )) 16 | 17 | # Use the Android Debug Bridge port forwarding to the host machine. 18 | ADB_ENABLE=false 19 | 20 | # Android Debug Bridge port forwards to this port on localhost. 21 | # This is the only way to use adb with VM, works only with ADB_ENABLED=true. 22 | ADB_PORT=4444 23 | 24 | # Path to the VM drive default location. 25 | # All dirs will be created automatically on "init" call. 26 | DRIVE_PATH="./drives/android-image.qcow2.img" 27 | 28 | # Path to the VM drive mount location when using the "drive" CLI commands. 29 | DRIVE_MOUNT_PATH="./mnt/" 30 | --------------------------------------------------------------------------------