From 1f0393876d891d0e32a995d0bf7259cc6505afdc Mon Sep 17 00:00:00 2001 From: Erfan Abdi Date: Sat, 17 Jul 2021 11:23:14 +0430 Subject: [PATCH] Waydroid: Initial commit --- .gitignore | 132 +++++ LICENSE | 674 ++++++++++++++++++++++++++ README.md | 40 ++ data/AppIcon.png | Bin 0 -> 193086 bytes data/configs/config_1 | 25 + data/configs/config_2 | 25 + data/scripts/waydroid-net.sh | 269 ++++++++++ tools/__init__.py | 135 ++++++ tools/actions/__init__.py | 8 + tools/actions/app_manager.py | 113 +++++ tools/actions/container_manager.py | 227 +++++++++ tools/actions/initializer.py | 88 ++++ tools/actions/session_manager.py | 51 ++ tools/actions/status.py | 19 + tools/actions/upgrader.py | 35 ++ tools/config/__init__.py | 79 +++ tools/config/load.py | 69 +++ tools/config/save.py | 19 + tools/helpers/__init__.py | 10 + tools/helpers/arch.py | 17 + tools/helpers/arguments.py | 153 ++++++ tools/helpers/drivers.py | 135 ++++++ tools/helpers/http.py | 89 ++++ tools/helpers/images.py | 109 +++++ tools/helpers/logging.py | 99 ++++ tools/helpers/lxc.py | 269 ++++++++++ tools/helpers/mount.py | 137 ++++++ tools/helpers/props.py | 50 ++ tools/helpers/run.py | 78 +++ tools/helpers/run_core.py | 346 +++++++++++++ tools/interfaces/IClipboard.py | 48 ++ tools/interfaces/IHardware.py | 66 +++ tools/interfaces/IPlatform.py | 292 +++++++++++ tools/interfaces/IStatusBarService.py | 59 +++ tools/interfaces/IUserMonitor.py | 51 ++ tools/services/__init__.py | 5 + tools/services/clipboard_manager.py | 41 ++ tools/services/hardware_manager.py | 44 ++ tools/services/user_manager.py | 98 ++++ waydroid.py | 9 + 40 files changed, 4213 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 data/AppIcon.png create mode 100644 data/configs/config_1 create mode 100644 data/configs/config_2 create mode 100755 data/scripts/waydroid-net.sh create mode 100644 tools/__init__.py create mode 100644 tools/actions/__init__.py create mode 100644 tools/actions/app_manager.py create mode 100644 tools/actions/container_manager.py create mode 100644 tools/actions/initializer.py create mode 100644 tools/actions/session_manager.py create mode 100644 tools/actions/status.py create mode 100644 tools/actions/upgrader.py create mode 100644 tools/config/__init__.py create mode 100644 tools/config/load.py create mode 100644 tools/config/save.py create mode 100644 tools/helpers/__init__.py create mode 100644 tools/helpers/arch.py create mode 100644 tools/helpers/arguments.py create mode 100644 tools/helpers/drivers.py create mode 100644 tools/helpers/http.py create mode 100644 tools/helpers/images.py create mode 100644 tools/helpers/logging.py create mode 100644 tools/helpers/lxc.py create mode 100644 tools/helpers/mount.py create mode 100644 tools/helpers/props.py create mode 100644 tools/helpers/run.py create mode 100644 tools/helpers/run_core.py create mode 100644 tools/interfaces/IClipboard.py create mode 100644 tools/interfaces/IHardware.py create mode 100644 tools/interfaces/IPlatform.py create mode 100644 tools/interfaces/IStatusBarService.py create mode 100644 tools/interfaces/IUserMonitor.py create mode 100644 tools/services/__init__.py create mode 100644 tools/services/clipboard_manager.py create mode 100644 tools/services/hardware_manager.py create mode 100644 tools/services/user_manager.py create mode 100755 waydroid.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7a68a05 --- /dev/null +++ b/.gitignore @@ -0,0 +1,132 @@ +# Pycharm +/.idea + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md new file mode 100644 index 0000000..9fe872d --- /dev/null +++ b/README.md @@ -0,0 +1,40 @@ +# Waydroid + +Waydroid is a container-based approach to boot a full Android system on a +regular GNU/Linux system like Ubuntu. + +## Overview + +Waydroid uses Linux namespaces (user, pid, uts, net, mount, ipc) to run a +full Android system in a container and provide Android applications on +any GNU/Linux-based platform. + +The Android inside the container has direct access to needed hardwares. + +The Android runtime environment ships with a minimal customized Android system +image based on the [LineageOS](https://lineageos.org/). +The used image is currently based on Android 10 + +## Install and Run Android Applications + +You can install Android applications from the command line. + +```sh +waydroid app install xyz.apk +``` + +The apk files you will sometimes find on the internet tend to only have arm +support, and will therefore not work on x86\_64. + +You may want to install [F-Droid](https://f-droid.org/) to get applications +graphically. Note that the Google Play Store will not work as is, because it +relies on the proprietary Google Play Services, which are not installed. + +## Reporting bugs + +If you have found an issue with Waydroid, please [file a bug](https://github.com/Waydroid/waydroid/issues/new). + +## Get in Touch + +If you want to get in contact with the developers please feel free to join the +*WayDroid* groups in [Matrix](https://matrix.to/#/#waydroid:connolly.tech) or [Telegram](https://t.me/WayDroid). diff --git a/data/AppIcon.png b/data/AppIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..0bd597d589ce507c37976ed8c5ba131272995fea GIT binary patch literal 193086 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

1=|A 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ⅆ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 /dev/null 2>&1 && [ "$LXC_USE_NFT" = "true" ] +} + +NFT="$(which nft)" +if ! use_nft; then + use_iptables_lock="-w" + $IPTABLES_BIN -w -L -n > /dev/null 2>&1 || use_iptables_lock="" +fi + +_netmask2cidr () +{ + # Assumes there's no "255." after a non-255 byte in the mask + local x=${1##*255.} + set -- 0^^^128^192^224^240^248^252^254^ $(( (${#1} - ${#x})*2 )) ${x%%.*} + x=${1%%$3*} + echo $(( $2 + (${#x}/4) )) +} + +_ifdown() { + ip addr flush dev ${LXC_BRIDGE} + ip link set dev ${LXC_BRIDGE} down +} + +_ifup() { + MASK=`_netmask2cidr ${LXC_NETMASK}` + CIDR_ADDR="${LXC_ADDR}/${MASK}" + ip addr add ${CIDR_ADDR} broadcast + dev ${LXC_BRIDGE} + ip link set dev ${LXC_BRIDGE} address $LXC_BRIDGE_MAC + ip link set dev ${LXC_BRIDGE} up +} + +start_ipv6() { + LXC_IPV6_ARG="" + if [ -n "$LXC_IPV6_ADDR" ] && [ -n "$LXC_IPV6_MASK" ] && [ -n "$LXC_IPV6_NETWORK" ]; then + echo 1 > /proc/sys/net/ipv6/conf/all/forwarding + echo 0 > /proc/sys/net/ipv6/conf/${LXC_BRIDGE}/autoconf + ip -6 addr add dev ${LXC_BRIDGE} ${LXC_IPV6_ADDR}/${LXC_IPV6_MASK} + LXC_IPV6_ARG="--dhcp-range=${LXC_IPV6_ADDR},ra-only --listen-address ${LXC_IPV6_ADDR}" + fi +} + +start_iptables() { + start_ipv6 + if [ -n "$LXC_IPV6_ARG" ] && [ "$LXC_IPV6_NAT" = "true" ]; then + $IP6TABLES_BIN $use_iptables_lock -t nat -A POSTROUTING -s ${LXC_IPV6_NETWORK} ! -d ${LXC_IPV6_NETWORK} -j MASQUERADE + fi + $IPTABLES_BIN $use_iptables_lock -I INPUT -i ${LXC_BRIDGE} -p udp --dport 67 -j ACCEPT + $IPTABLES_BIN $use_iptables_lock -I INPUT -i ${LXC_BRIDGE} -p tcp --dport 67 -j ACCEPT + $IPTABLES_BIN $use_iptables_lock -I INPUT -i ${LXC_BRIDGE} -p udp --dport 53 -j ACCEPT + $IPTABLES_BIN $use_iptables_lock -I INPUT -i ${LXC_BRIDGE} -p tcp --dport 53 -j ACCEPT + $IPTABLES_BIN $use_iptables_lock -I FORWARD -i ${LXC_BRIDGE} -j ACCEPT + $IPTABLES_BIN $use_iptables_lock -I FORWARD -o ${LXC_BRIDGE} -j ACCEPT + $IPTABLES_BIN $use_iptables_lock -t nat -A POSTROUTING -s ${LXC_NETWORK} ! -d ${LXC_NETWORK} -j MASQUERADE + $IPTABLES_BIN $use_iptables_lock -t mangle -A POSTROUTING -o ${LXC_BRIDGE} -p udp -m udp --dport 68 -j CHECKSUM --checksum-fill +} + +start_nftables() { + start_ipv6 + NFT_RULESET="" + if [ -n "$LXC_IPV6_ARG" ] && [ "$LXC_IPV6_NAT" = "true" ]; then + NFT_RULESET="${NFT_RULESET} +add table ip6 lxc; +flush table ip6 lxc; +add chain ip6 lxc postrouting { type nat hook postrouting priority 100; }; +add rule ip6 lxc postrouting ip saddr ${LXC_IPV6_NETWORK} ip daddr != ${LXC_IPV6_NETWORK} counter masquerade; +" + fi + NFT_RULESET="${NFT_RULESET}; +add table inet lxc; +flush table inet lxc; +add chain inet lxc input { type filter hook input priority 0; }; +add rule inet lxc input iifname ${LXC_BRIDGE} udp dport { 53, 67 } accept; +add rule inet lxc input iifname ${LXC_BRIDGE} tcp dport { 53, 67 } accept; +add chain inet lxc forward { type filter hook forward priority 0; }; +add rule inet lxc forward iifname ${LXC_BRIDGE} accept; +add rule inet lxc forward oifname ${LXC_BRIDGE} accept; +add table ip lxc; +flush table ip lxc; +add chain ip lxc postrouting { type nat hook postrouting priority 100; }; +add rule ip lxc postrouting ip saddr ${LXC_NETWORK} ip daddr != ${LXC_NETWORK} counter masquerade" + nft "${NFT_RULESET}" +} + +start() { + [ "x$USE_LXC_BRIDGE" = "xtrue" ] || { exit 0; } + + [ ! -f "${varrun}/network_up" ] || { echo "waydroid-net is already running"; exit 1; } + + if [ -d /sys/class/net/${LXC_BRIDGE} ]; then + stop force || true + fi + + FAILED=1 + + cleanup() { + set +e + if [ "$FAILED" = "1" ]; then + echo "Failed to setup waydroid-net." >&2 + stop force + exit 1 + fi + } + + trap cleanup EXIT HUP INT TERM + set -e + + # set up the lxc network + [ ! -d /sys/class/net/${LXC_BRIDGE} ] && ip link add dev ${LXC_BRIDGE} type bridge + echo 1 > /proc/sys/net/ipv4/ip_forward + echo 0 > /proc/sys/net/ipv6/conf/${LXC_BRIDGE}/accept_dad || true + + # if we are run from systemd on a system with selinux enabled, + # the mkdir will create /run/lxc as init_var_run_t which dnsmasq + # can't write its pid into, so we restorecon it (to var_run_t) + if [ ! -d "${varrun}" ]; then + mkdir -p "${varrun}" + if which restorecon >/dev/null 2>&1; then + restorecon "${varrun}" + fi + fi + + _ifup + + if use_nft; then + start_nftables + else + start_iptables + fi + + LXC_DOMAIN_ARG="" + if [ -n "$LXC_DOMAIN" ]; then + LXC_DOMAIN_ARG="-s $LXC_DOMAIN -S /$LXC_DOMAIN/" + fi + + # lxc's dnsmasq should be hermetic and not read `/etc/dnsmasq.conf` (which + # it does by default if `--conf-file` is not present + LXC_DHCP_CONFILE_ARG="--conf-file=${LXC_DHCP_CONFILE:-/dev/null}" + + # https://lists.linuxcontainers.org/pipermail/lxc-devel/2014-October/010561.html + for DNSMASQ_USER in lxc-dnsmasq dnsmasq nobody + do + if getent passwd ${DNSMASQ_USER} >/dev/null; then + break + fi + done + + LXC_DHCP_PING_ARG="" + if [ "x$LXC_DHCP_PING" = "xfalse" ]; then + LXC_DHCP_PING_ARG="--no-ping" + fi + + dnsmasq $LXC_DHCP_CONFILE_ARG $LXC_DOMAIN_ARG $LXC_DHCP_PING_ARG -u ${DNSMASQ_USER} \ + --strict-order --bind-interfaces --pid-file="${varrun}"/dnsmasq.pid \ + --listen-address ${LXC_ADDR} --dhcp-range ${LXC_DHCP_RANGE} \ + --dhcp-lease-max=${LXC_DHCP_MAX} --dhcp-no-override \ + --except-interface=lo --interface=${LXC_BRIDGE} \ + --dhcp-leasefile="${varlib}"/misc/dnsmasq.${LXC_BRIDGE}.leases \ + --dhcp-authoritative $LXC_IPV6_ARG || cleanup + + touch "${varrun}"/network_up + FAILED=0 +} + +stop_iptables() { + $IPTABLES_BIN $use_iptables_lock -D INPUT -i ${LXC_BRIDGE} -p udp --dport 67 -j ACCEPT + $IPTABLES_BIN $use_iptables_lock -D INPUT -i ${LXC_BRIDGE} -p tcp --dport 67 -j ACCEPT + $IPTABLES_BIN $use_iptables_lock -D INPUT -i ${LXC_BRIDGE} -p udp --dport 53 -j ACCEPT + $IPTABLES_BIN $use_iptables_lock -D INPUT -i ${LXC_BRIDGE} -p tcp --dport 53 -j ACCEPT + $IPTABLES_BIN $use_iptables_lock -D FORWARD -i ${LXC_BRIDGE} -j ACCEPT + $IPTABLES_BIN $use_iptables_lock -D FORWARD -o ${LXC_BRIDGE} -j ACCEPT + $IPTABLES_BIN $use_iptables_lock -t nat -D POSTROUTING -s ${LXC_NETWORK} ! -d ${LXC_NETWORK} -j MASQUERADE + $IPTABLES_BIN $use_iptables_lock -t mangle -D POSTROUTING -o ${LXC_BRIDGE} -p udp -m udp --dport 68 -j CHECKSUM --checksum-fill + if [ "$LXC_IPV6_NAT" = "true" ]; then + $IP6TABLES_BIN $use_iptables_lock -t nat -D POSTROUTING -s ${LXC_IPV6_NETWORK} ! -d ${LXC_IPV6_NETWORK} -j MASQUERADE + fi +} + +stop_nftables() { + # Adding table before removing them is just to avoid + # delete error for non-existent table + NFT_RULESET="add table inet lxc; +delete table inet lxc; +add table ip lxc; +delete table ip lxc; +" + if [ "$LXC_IPV6_NAT" = "true" ]; then + NFT_RULESET="${NFT_RULESET}; +add table ip6 lxc; +delete table ip6 lxc;" + fi + nft "${NFT_RULESET}" +} + +stop() { + [ "x$USE_LXC_BRIDGE" = "xtrue" ] || { exit 0; } + + [ -f "${varrun}/network_up" ] || [ "$1" = "force" ] || { echo "waydroid-net isn't running"; exit 1; } + + if [ -d /sys/class/net/${LXC_BRIDGE} ]; then + _ifdown + if use_nft; then + stop_nftables + else + stop_iptables + fi + + pid=`cat "${varrun}"/dnsmasq.pid 2>/dev/null` && kill -9 $pid + rm -f "${varrun}"/dnsmasq.pid + # if $LXC_BRIDGE has attached interfaces, don't destroy the bridge + ls /sys/class/net/${LXC_BRIDGE}/brif/* > /dev/null 2>&1 || ip link delete ${LXC_BRIDGE} + fi + + rm -f "${varrun}"/network_up +} + +# See how we were called. +case "$1" in + start) + start + ;; + + stop) + stop + ;; + + restart|reload|force-reload) + $0 stop + $0 start + ;; + + *) + echo "Usage: $0 {start|stop|restart|reload|force-reload}" + exit 2 +esac + +exit $? diff --git a/tools/__init__.py b/tools/__init__.py new file mode 100644 index 0000000..555ea00 --- /dev/null +++ b/tools/__init__.py @@ -0,0 +1,135 @@ +# Copyright 2021 Oliver Smith +# SPDX-License-Identifier: GPL-3.0-or-later +# PYTHON_ARGCOMPLETE_OK +import sys +import logging +import os +import traceback + +from . import actions +from . import config +from . import helpers +from .helpers import logging as tools_logging + + +def main(): + def actionNeedRoot(action): + if os.geteuid() != 0: + raise RuntimeError( + "Action \"{}\" needs root access".format(action)) + + # Wrap everything to display nice error messages + args = None + try: + os.umask(0o000) + # Parse arguments, set up logging + args = helpers.arguments() + args.cache = {} + args.work = config.defaults["work"] + args.config = args.work + "/waydroid.cfg" + args.log = args.work + "/tools.log" + args.sudo_timer = True + args.timeout = 1800 + + if not os.path.isfile(args.config): + if args.action and args.action != "init": + print('ERROR: WayDroid is not initialized, run "waydroid init"') + return 0 + elif os.geteuid() == 0 and args.action == "init": + os.mkdir(args.work) + else: + args.log = "/tmp/tools.log" + + tools_logging.init(args) + + # Initialize or require config + if args.action == "init": + actionNeedRoot(args.action) + actions.init(args) + elif args.action == "upgrade": + actionNeedRoot(args.action) + actions.upgrade(args) + elif args.action == "session": + if args.subaction == "start": + actions.session_manager.start(args) + elif args.subaction == "stop": + actions.session_manager.stop(args) + else: + logging.info( + "Run waydroid {} -h for usage information.".format(args.action)) + elif args.action == "container": + actionNeedRoot(args.action) + if args.subaction == "start": + actions.container_manager.start(args) + elif args.subaction == "stop": + actions.container_manager.stop(args) + elif args.subaction == "freeze": + actions.container_manager.freeze(args) + elif args.subaction == "unfreeze": + actions.container_manager.unfreeze(args) + else: + logging.info( + "Run waydroid {} -h for usage information.".format(args.action)) + elif args.action == "app": + if args.subaction == "install": + actions.app_manager.install(args) + elif args.subaction == "remove": + actions.app_manager.remove(args) + elif args.subaction == "launch": + actions.app_manager.launch(args) + elif args.subaction == "list": + actions.app_manager.list(args) + else: + logging.info( + "Run waydroid {} -h for usage information.".format(args.action)) + elif args.action == "prop": + if args.subaction == "get": + ret = helpers.props.get(args, args.key) + if ret: + print(ret) + elif args.subaction == "set": + helpers.props.set(args, args.key, args.value) + else: + logging.info( + "Run waydroid {} -h for usage information.".format(args.action)) + elif args.action == "shell": + actionNeedRoot(args.action) + helpers.lxc.shell(args) + elif args.action == "logcat": + actionNeedRoot(args.action) + helpers.lxc.logcat(args) + elif args.action == "show-full-ui": + actions.app_manager.showFullUI(args) + elif args.action == "status": + actions.status.print_status(args) + elif args.action == "log": + if args.clear_log: + helpers.run.user(args, ["truncate", "-s", "0", args.log]) + helpers.run.user( + args, ["tail", "-n", args.lines, "-F", args.log], output="tui") + else: + logging.info("Run waydroid -h for usage information.") + + #logging.info("Done") + + except Exception as e: + # Dump log to stdout when args (and therefore logging) init failed + if not args: + logging.getLogger().setLevel(logging.DEBUG) + + logging.info("ERROR: " + str(e)) + logging.info("See also: ") + logging.debug(traceback.format_exc()) + + # Hints about the log file (print to stdout only) + log_hint = "Run 'waydroid log' for details." + if not args or not os.path.exists(args.log): + log_hint += (" Alternatively you can use '--details-to-stdout' to" + " get more output, e.g. 'waydroid" + " --details-to-stdout init'.") + print(log_hint) + return 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/actions/__init__.py b/tools/actions/__init__.py new file mode 100644 index 0000000..558ed83 --- /dev/null +++ b/tools/actions/__init__.py @@ -0,0 +1,8 @@ +# Copyright 2021 Erfan Abdi +# SPDX-License-Identifier: GPL-3.0-or-later +from tools.actions.initializer import init +from tools.actions.upgrader import upgrade +from tools.actions.session_manager import start, stop +from tools.actions.container_manager import start, stop, freeze, unfreeze +from tools.actions.app_manager import install, remove, launch, list +from tools.actions.status import print_status diff --git a/tools/actions/app_manager.py b/tools/actions/app_manager.py new file mode 100644 index 0000000..7bb19a2 --- /dev/null +++ b/tools/actions/app_manager.py @@ -0,0 +1,113 @@ +# Copyright 2021 Erfan Abdi +# SPDX-License-Identifier: GPL-3.0-or-later +import logging +import os +import shutil +import time +import tools.config +import tools.helpers.props +from tools.interfaces import IPlatform +from tools.interfaces import IStatusBarService + +def install(args): + if os.path.exists(tools.config.session_defaults["config_path"]): + session_cfg = tools.config.load_session() + if session_cfg["session"]["state"] == "RUNNING": + tmp_dir = session_cfg["session"]["waydroid_data"] + "/waydroid_tmp" + if not os.path.exists(tmp_dir): + os.makedirs(tmp_dir) + + shutil.copyfile(args.PACKAGE, tmp_dir + "/base.apk") + platformService = IPlatform.get_service(args) + if platformService: + platformService.installApp("/data/waydroid_tmp/base.apk") + shutil.rmtree(tmp_dir) + else: + logging.error("WayDroid container is {}".format( + session_cfg["session"]["state"])) + else: + logging.error("WayDroid session is stopped") + +def remove(args): + if os.path.exists(tools.config.session_defaults["config_path"]): + session_cfg = tools.config.load_session() + if session_cfg["session"]["state"] == "RUNNING": + platformService = IPlatform.get_service(args) + if platformService: + ret = platformService.removeApp(args.PACKAGE) + if ret != 0: + logging.error("Failed to uninstall package: {}".format(args.PACKAGE)) + else: + logging.error("Failed to access IPlatform service") + else: + logging.error("WayDroid container is {}".format( + session_cfg["session"]["state"])) + else: + logging.error("WayDroid session is stopped") + +def launch(args): + def justLaunch(): + platformService = IPlatform.get_service(args) + if platformService: + platformService.setprop("waydroid.active_apps", args.PACKAGE) + ret = platformService.launchApp(args.PACKAGE) + multiwin = platformService.getprop( + "persist.waydroid.multi_windows", "false") + if multiwin == "false": + platformService.settingsPutString( + 2, "policy_control", "immersive.status=*") + else: + platformService.settingsPutString( + 2, "policy_control", "immersive.full=*") + else: + logging.error("Failed to access IPlatform service") + + if os.path.exists(tools.config.session_defaults["config_path"]): + session_cfg = tools.config.load_session() + + if session_cfg["session"]["state"] == "RUNNING": + justLaunch() + elif session_cfg["session"]["state"] == "FROZEN" or session_cfg["session"]["state"] == "UNFREEZE": + session_cfg["session"]["state"] = "UNFREEZE" + tools.config.save_session(session_cfg) + while session_cfg["session"]["state"] != "RUNNING": + session_cfg = tools.config.load_session() + justLaunch() + else: + logging.error("WayDroid container is {}".format( + session_cfg["session"]["state"])) + else: + logging.error("WayDroid session is stopped") + +def list(args): + if os.path.exists(tools.config.session_defaults["config_path"]): + session_cfg = tools.config.load_session() + if session_cfg["session"]["state"] == "RUNNING": + platformService = IPlatform.get_service(args) + if platformService: + appsList = platformService.getAppsInfo() + for app in appsList: + print("Name: " + app["name"]) + print("packageName: " + app["packageName"]) + print("categories:") + for cat in app["categories"]: + print("\t" + cat) + else: + logging.error("Failed to access IPlatform service") + else: + logging.error("WayDroid container is {}".format( + session_cfg["session"]["state"])) + else: + logging.error("WayDroid session is stopped") + +def showFullUI(args): + platformService = IPlatform.get_service(args) + if platformService: + platformService.setprop("waydroid.active_apps", "Waydroid") + platformService.settingsPutString(2, "policy_control", "null*") + #HACK: Refresh display contents + statusBarService = IStatusBarService.get_service(args) + if statusBarService: + statusBarService.expand() + time.sleep(0.5) + statusBarService.collapse() diff --git a/tools/actions/container_manager.py b/tools/actions/container_manager.py new file mode 100644 index 0000000..994dcea --- /dev/null +++ b/tools/actions/container_manager.py @@ -0,0 +1,227 @@ +# Copyright 2021 Erfan Abdi +# SPDX-License-Identifier: GPL-3.0-or-later +from shutil import which +import logging +import os +import time +import glob +import signal +import sys +import tools.config +from tools import helpers +from tools import services + + +def start(args): + def make_prop(full_props_path): + def add_prop(key, cfg_key): + value = session_cfg["session"][cfg_key] + if value != "None": + props.append(key + "=" + session_cfg["session"][cfg_key]) + + if not os.path.isfile(args.work + "/waydroid_base.prop"): + raise RuntimeError("waydroid_base.prop Not found") + with open(args.work + "/waydroid_base.prop") as f: + props = f.read().splitlines() + if not props: + raise RuntimeError("waydroid_base.prop is broken!!?") + + add_prop("waydroid.host.user", "user_name") + add_prop("waydroid.host.uid", "user_id") + add_prop("waydroid.host.gid", "group_id") + add_prop("waydroid.xdg_runtime_dir", "xdg_runtime_dir") + add_prop("waydroid.pulse_runtime_path", "pulse_runtime_path") + add_prop("waydroid.wayland_display", "wayland_display") + if which("waydroid-sensord") is None: + props.append("waydroid.stub_sensors_hal=1") + dpi = session_cfg["session"]["lcd_density"] + if dpi != "0": + props.append("ro.sf.lcd_density=" + dpi) + + final_props = open(full_props_path, "w") + for prop in props: + final_props.write(prop + "\n") + final_props.close() + os.chmod(full_props_path, 0o644) + + def set_permissions(perm_list=None, mode="777"): + def chmod(path, mode): + if os.path.exists(path): + command = ["chmod", mode, "-R", path] + tools.helpers.run.root(args, command, check=False) + + # Nodes list + if not perm_list: + perm_list = [ + "/dev/ashmem", + + # sw_sync for HWC + "/dev/sw_sync", + "/sys/kernel/debug/sync/sw_sync", + + # Media + "/dev/Vcodec", + "/dev/MTK_SMI", + "/dev/mdp_sync", + "/dev/mtk_cmdq", + "/dev/video32", + "/dev/video33", + + # Graphics + "/dev/dri", + "/dev/graphics", + + # Wayland and pulse socket permissions + session_cfg["session"]["pulse_runtime_path"], + session_cfg["session"]["xdg_runtime_dir"] + ] + + # Framebuffers + perm_list.extend(glob.glob("/dev/fb*")) + + for path in perm_list: + chmod(path, mode) + + def signal_handler(sig, frame): + services.hardware_manager.stop(args) + stop(args) + sys.exit(0) + + status = helpers.lxc.status(args) + if status == "STOPPED": + # Load binder and ashmem drivers + cfg = tools.config.load(args) + if cfg["waydroid"]["vendor_type"] == "MAINLINE": + if helpers.drivers.probeBinderDriver(args) != 0: + logging.error("Failed to load Binder driver") + if helpers.drivers.probeAshmemDriver(args) != 0: + logging.error("Failed to load Ashmem driver") + helpers.drivers.loadBinderNodes(args) + set_permissions([ + "/dev/" + args.BINDER_DRIVER, + "/dev/" + args.VNDBINDER_DRIVER, + "/dev/" + args.HWBINDER_DRIVER + ], "666") + + if os.path.exists(tools.config.session_defaults["config_path"]): + session_cfg = tools.config.load_session() + if session_cfg["session"]["state"] != "STOPPED": + logging.warning("Found session config on state: {}, restart session".format( + session_cfg["session"]["state"])) + os.remove(tools.config.session_defaults["config_path"]) + logging.debug("Container manager is waiting for session to load") + while not os.path.exists(tools.config.session_defaults["config_path"]): + time.sleep(1) + + # Load session configs + session_cfg = tools.config.load_session() + + # Generate props + make_prop(args.work + "/waydroid.prop") + + # Networking + command = [tools.config.tools_src + + "/data/scripts/waydroid-net.sh", "start"] + tools.helpers.run.root(args, command, check=False) + + # Sensors + tools.helpers.run.root( + args, ["waydroid-sensord", args.HWBINDER_DRIVER], output="background") + + # Mount rootfs + helpers.images.mount_rootfs(args, cfg["waydroid"]["images_path"]) + + # Mount data + helpers.mount.bind(args, session_cfg["session"]["waydroid_data"], + tools.config.defaults["data"]) + + # Cgroup hacks + if which("start"): + command = ["start", "cgroup-lite"] + tools.helpers.run.root(args, command, check=False) + helpers.mount.umount_all(args, "/sys/fs/cgroup/schedtune") + + #TODO: remove NFC hacks + if which("stop"): + command = ["stop", "nfcd"] + tools.helpers.run.root(args, command, check=False) + + # Set permissions + set_permissions() + + helpers.lxc.start(args) + session_cfg["session"]["state"] = helpers.lxc.status(args) + tools.config.save_session(session_cfg) + + if not hasattr(args, 'hardwareLoop'): + services.hardware_manager.start(args) + + signal.signal(signal.SIGINT, signal_handler) + while os.path.exists(tools.config.session_defaults["config_path"]): + session_cfg = tools.config.load_session() + if session_cfg["session"]["state"] == "STOPPED": + services.hardware_manager.stop(args) + sys.exit(0) + elif session_cfg["session"]["state"] == "UNFREEZE": + session_cfg["session"]["state"] = helpers.lxc.status(args) + tools.config.save_session(session_cfg) + unfreeze(args) + time.sleep(1) + + logging.warning("session manager stopped, stopping container and waiting...") + stop(args) + start(args) + else: + logging.error("WayDroid container is {}".format(status)) + +def stop(args): + status = helpers.lxc.status(args) + if status != "STOPPED": + helpers.lxc.stop(args) + if os.path.exists(tools.config.session_defaults["config_path"]): + session_cfg = tools.config.load_session() + session_cfg["session"]["state"] = helpers.lxc.status(args) + tools.config.save_session(session_cfg) + + # Networking + command = [tools.config.tools_src + + "/data/scripts/waydroid-net.sh", "stop"] + tools.helpers.run.root(args, command, check=False) + + #TODO: remove NFC hacks + if which("start"): + command = ["start", "nfcd"] + tools.helpers.run.root(args, command, check=False) + + # Sensors + if which("waydroid-sensord"): + command = ["pidof", "waydroid-sensord"] + pid = tools.helpers.run.root(args, command, check=False, output_return=True) + if pid: + command = ["killall", pid] + tools.helpers.run.root(args, command, check=False) + + else: + logging.error("WayDroid container is {}".format(status)) + +def freeze(args): + status = helpers.lxc.status(args) + if status == "RUNNING": + helpers.lxc.freeze(args) + if os.path.exists(tools.config.session_defaults["config_path"]): + session_cfg = tools.config.load_session() + session_cfg["session"]["state"] = helpers.lxc.status(args) + tools.config.save_session(session_cfg) + else: + logging.error("WayDroid container is {}".format(status)) + +def unfreeze(args): + status = helpers.lxc.status(args) + if status == "FROZEN": + helpers.lxc.unfreeze(args) + if os.path.exists(tools.config.session_defaults["config_path"]): + session_cfg = tools.config.load_session() + session_cfg["session"]["state"] = helpers.lxc.status(args) + tools.config.save_session(session_cfg) + else: + logging.error("WayDroid container is {}".format(status)) diff --git a/tools/actions/initializer.py b/tools/actions/initializer.py new file mode 100644 index 0000000..c5783a4 --- /dev/null +++ b/tools/actions/initializer.py @@ -0,0 +1,88 @@ +# Copyright 2021 Erfan Abdi +# SPDX-License-Identifier: GPL-3.0-or-later +import logging +import os +import requests +from tools import helpers +import tools.config + + +def get_vendor_type(args): + vndk_str = helpers.props.host_get(args, "ro.vndk.version") + ret = "MAINLINE" + if vndk_str != "": + vndk = int(vndk_str) + if vndk > 19: + ret = "HALIUM_" + str(vndk - 19) + + return ret + +def setup_config(args): + cfg = tools.config.load(args) + args.arch = helpers.arch.host() + cfg["waydroid"]["arch"] = args.arch + cfg["waydroid"]["images_path"] = args.images_path + + channels_cfg = tools.config.load_channels() + if not args.system_channel: + args.system_channel = channels_cfg["channels"]["system_channel"] + if not args.vendor_channel: + args.vendor_channel = channels_cfg["channels"]["vendor_channel"] + if not args.rom_type: + args.rom_type = channels_cfg["channels"]["rom_type"] + if not args.system_type: + args.system_type = channels_cfg["channels"]["system_type"] + + args.system_ota = args.system_channel + "/" + args.rom_type + \ + "/waydroid_" + args.arch + "/" + args.system_type + ".json" + system_request = requests.get(args.system_ota) + if system_request.status_code != 200: + raise ValueError( + "Failed to get system OTA channel: {}".format(args.system_ota)) + + device_codename = helpers.props.host_get(args, "ro.product.device") + args.vendor_type = None + for vendor in [device_codename, get_vendor_type(args)]: + vendor_ota = args.vendor_channel + "/waydroid_" + \ + args.arch + "/" + vendor + ".json" + vendor_request = requests.get(vendor_ota) + if vendor_request.status_code == 200: + args.vendor_type = vendor + args.vendor_ota = vendor_ota + break + + if not args.vendor_type: + raise ValueError( + "Failed to get vendor OTA channel: {}".format(vendor_ota)) + + cfg["waydroid"]["vendor_type"] = args.vendor_type + cfg["waydroid"]["system_ota"] = args.system_ota + cfg["waydroid"]["vendor_ota"] = args.vendor_ota + helpers.drivers.setupBinderNodes(args) + cfg["waydroid"]["binder"] = args.BINDER_DRIVER + cfg["waydroid"]["vndbinder"] = args.VNDBINDER_DRIVER + cfg["waydroid"]["hwbinder"] = args.HWBINDER_DRIVER + tools.config.save(args, cfg) + +def init(args): + if not os.path.isfile(args.config) or args.force: + setup_config(args) + status = "STOPPED" + if os.path.exists(tools.config.defaults["lxc"] + "/waydroid"): + status = helpers.lxc.status(args) + if status != "STOPPED": + logging.info("Stopping container") + helpers.lxc.stop(args) + helpers.images.umount_rootfs(args) + helpers.images.get(args) + if not os.path.isdir(tools.config.defaults["rootfs"]): + os.mkdir(tools.config.defaults["rootfs"]) + helpers.lxc.setup_host_perms(args) + helpers.lxc.set_lxc_config(args) + helpers.lxc.make_base_props(args) + if status != "STOPPED": + logging.info("Starting container") + helpers.images.mount_rootfs(args, args.images_path) + helpers.lxc.start(args) + else: + logging.info("Already initialized") diff --git a/tools/actions/session_manager.py b/tools/actions/session_manager.py new file mode 100644 index 0000000..7d89757 --- /dev/null +++ b/tools/actions/session_manager.py @@ -0,0 +1,51 @@ +# Copyright 2021 Erfan Abdi +# SPDX-License-Identifier: GPL-3.0-or-later +import logging +import os +import time +import signal +import sys +import tools.config +from tools import services + + +def start(args): + def signal_handler(sig, frame): + stop(args) + sys.exit(0) + + xdg_session = os.getenv("XDG_SESSION_TYPE") + if xdg_session != "wayland": + logging.warning('XDG Session is not "wayland"') + + cfg = tools.config.load_session() + waydroid_data = cfg["session"]["waydroid_data"] + if not os.path.isdir(waydroid_data): + os.makedirs(waydroid_data) + dpi = tools.helpers.props.host_get(args, "ro.sf.lcd_density") + if dpi == "": + dpi = os.getenv("GRID_UNIT_PX") + if dpi is not None: + dpi = str(int(dpi) * 20) + else: + dpi = "0" + cfg["session"]["lcd_density"] = dpi + tools.config.save_session(cfg) + + services.user_manager.start(args) + services.clipboard_manager.start(args) + + signal.signal(signal.SIGINT, signal_handler) + while os.path.exists(tools.config.session_defaults["config_path"]): + time.sleep(1) + services.user_manager.stop(args) + services.clipboard_manager.stop(args) + +def stop(args): + config_path = tools.config.session_defaults["config_path"] + if os.path.isfile(config_path): + services.user_manager.stop(args) + services.clipboard_manager.stop(args) + os.remove(config_path) + else: + logging.error("WayDroid session is not started") diff --git a/tools/actions/status.py b/tools/actions/status.py new file mode 100644 index 0000000..00261ce --- /dev/null +++ b/tools/actions/status.py @@ -0,0 +1,19 @@ +# Copyright 2021 Erfan Abdi +# SPDX-License-Identifier: GPL-3.0-or-later +import os +import tools.config + +def print_status(args): + cfg = tools.config.load(args) + if os.path.exists(tools.config.session_defaults["config_path"]): + session_cfg = tools.config.load_session() + print("Session:\tRUNNING") + print("Container:\t" + session_cfg["session"]["state"]) + print("Vendor type:\t" + cfg["waydroid"]["vendor_type"]) + print("Session user:\t{}({})".format( + session_cfg["session"]["user_name"], session_cfg["session"]["user_id"])) + print("Wayland display:\t" + + session_cfg["session"]["wayland_display"]) + else: + print("Session:\tSTOPPED") + print("Vendor type:\t" + cfg["waydroid"]["vendor_type"]) diff --git a/tools/actions/upgrader.py b/tools/actions/upgrader.py new file mode 100644 index 0000000..cb1aaa5 --- /dev/null +++ b/tools/actions/upgrader.py @@ -0,0 +1,35 @@ +# Copyright 2021 Erfan Abdi +# SPDX-License-Identifier: GPL-3.0-or-later +import logging +import os +from tools import helpers +import tools.config + + +def get_config(args): + cfg = tools.config.load(args) + args.arch = cfg["waydroid"]["arch"] + args.images_path = cfg["waydroid"]["images_path"] + args.vendor_type = cfg["waydroid"]["vendor_type"] + args.system_ota = cfg["waydroid"]["system_ota"] + args.vendor_ota = cfg["waydroid"]["vendor_ota"] + +def upgrade(args): + get_config(args) + status = "STOPPED" + if os.path.exists(tools.config.defaults["lxc"] + "/waydroid"): + status = helpers.lxc.status(args) + if status != "STOPPED": + logging.info("Stopping container") + helpers.lxc.stop(args) + helpers.images.umount_rootfs(args) + helpers.drivers.loadBinderNodes(args) + if not args.offline: + helpers.images.get(args) + helpers.lxc.setup_host_perms(args) + helpers.lxc.set_lxc_config(args) + helpers.lxc.make_base_props(args) + if status != "STOPPED": + logging.info("Starting container") + helpers.images.mount_rootfs(args, args.images_path) + helpers.lxc.start(args) diff --git a/tools/config/__init__.py b/tools/config/__init__.py new file mode 100644 index 0000000..4c16de2 --- /dev/null +++ b/tools/config/__init__.py @@ -0,0 +1,79 @@ +# Copyright 2021 Oliver Smith +# SPDX-License-Identifier: GPL-3.0-or-later +import os +import pwd + +# +# Exported functions +# +from tools.config.load import load, load_session, load_channels +from tools.config.save import save, save_session + +# +# Exported variables (internal configuration) +# +version = "1.0.0" +tools_src = os.path.normpath(os.path.realpath(__file__) + "/../../..") + +# Keys saved in the config file (mostly what we ask in 'waydroid init') +config_keys = ["arch", + "images_path", + "vendor_type", + "system_datetime", + "vendor_datetime"] + +session_config_keys = ["user_name", + "user_id", + "group_id", + "host_user", + "waydroid_data", + "xdg_runtime_dir", + "wayland_display", + "pulse_runtime_path", + "state", + "lcd_density"] + +# Config file/commandline default values +# $WORK gets replaced with the actual value for args.work (which may be +# overridden on the commandline) +defaults = { + "arch": "arm64", + "work": "/home/.waydroid", + "vendor_type": "MAINLINE", + "system_datetime": "0", + "vendor_datetime": "0" +} +defaults["images_path"] = defaults["work"] + "/images" +defaults["rootfs"] = defaults["work"] + "/rootfs" +defaults["data"] = defaults["work"] + "/data" +defaults["lxc"] = defaults["work"] + "/lxc" +defaults["host_perms"] = defaults["work"] + "/host-permissions" + +session_defaults = { + "user_name": pwd.getpwuid(os.getuid()).pw_name, + "user_id": str(os.getuid()), + "group_id": str(os.getgid()), + "host_user": os.path.expanduser("~"), + "xdg_runtime_dir": str(os.environ.get('XDG_RUNTIME_DIR')), + "wayland_display": str(os.environ.get('WAYLAND_DISPLAY')), + "pulse_runtime_path": str(os.environ.get('PULSE_RUNTIME_PATH')), + "state": "STOPPED", + "lcd_density": "0" +} +session_defaults["config_path"] = defaults["work"] + "/session.cfg" +session_defaults["waydroid_data"] = session_defaults["host_user"] + \ + "/waydroid/data" +if session_defaults["pulse_runtime_path"] == "None": + session_defaults["pulse_runtime_path"] = session_defaults["xdg_runtime_dir"] + "/pulse" + +channels_defaults = { + "config_path": "/usr/share/waydroid-extra/channels.cfg", + "system_channel": "https://raw.githubusercontent.com/waydroid/OTA/master/systems", + "vendor_channel": "https://raw.githubusercontent.com/waydroid/OTA/master/vendor", + "rom_type": "lineage", + "system_type": "VANILLA" +} +channels_config_keys = ["system_channel", + "vendor_channel", + "rom_type", + "system_type"] diff --git a/tools/config/load.py b/tools/config/load.py new file mode 100644 index 0000000..6826c21 --- /dev/null +++ b/tools/config/load.py @@ -0,0 +1,69 @@ +# Copyright 2021 Oliver Smith +# SPDX-License-Identifier: GPL-3.0-or-later +import logging +import configparser +import os +import tools.config + + +def load(args): + cfg = configparser.ConfigParser() + if os.path.isfile(args.config): + cfg.read(args.config) + + if "waydroid" not in cfg: + cfg["waydroid"] = {} + + for key in tools.config.defaults: + if key in tools.config.config_keys and key not in cfg["waydroid"]: + cfg["waydroid"][key] = str(tools.config.defaults[key]) + + # We used to save default values in the config, which can *not* be + # configured in "waydroid init". That doesn't make sense, we always + # want to use the defaults from tools/config/__init__.py in that case, + if key not in tools.config.config_keys and key in cfg["waydroid"]: + logging.debug("Ignored unconfigurable and possibly outdated" + " default value from config: {}".format(cfg['waydroid'][key])) + del cfg["waydroid"][key] + + return cfg + +def load_session(): + config_path = tools.config.session_defaults["config_path"] + cfg = configparser.ConfigParser() + if os.path.isfile(config_path): + cfg.read(config_path) + + if "session" not in cfg: + cfg["session"] = {} + + for key in tools.config.session_defaults: + if key in tools.config.session_config_keys and key not in cfg["session"]: + cfg["session"][key] = str(tools.config.session_defaults[key]) + + if key not in tools.config.session_config_keys and key in cfg["session"]: + logging.debug("Ignored unconfigurable and possibly outdated" + " default value from config: {}".format(cfg['session'][key])) + del cfg["session"][key] + + return cfg + +def load_channels(): + config_path = tools.config.channels_defaults["config_path"] + cfg = configparser.ConfigParser() + if os.path.isfile(config_path): + cfg.read(config_path) + + if "channels" not in cfg: + cfg["channels"] = {} + + for key in tools.config.channels_defaults: + if key in tools.config.channels_config_keys and key not in cfg["channels"]: + cfg["channels"][key] = str(tools.config.channels_defaults[key]) + + if key not in tools.config.channels_config_keys and key in cfg["channels"]: + logging.debug("Ignored unconfigurable and possibly outdated" + " default value from config: {}".format(cfg['channels'][key])) + del cfg["channels"][key] + + return cfg diff --git a/tools/config/save.py b/tools/config/save.py new file mode 100644 index 0000000..67e25ea --- /dev/null +++ b/tools/config/save.py @@ -0,0 +1,19 @@ +# Copyright 2021 Oliver Smith +# SPDX-License-Identifier: GPL-3.0-or-later +import os +import logging +import tools.config + + +def save(args, cfg): + logging.debug("Save config: " + args.config) + os.makedirs(os.path.dirname(args.config), 0o700, True) + with open(args.config, "w") as handle: + cfg.write(handle) + +def save_session(cfg): + config_path = tools.config.session_defaults["config_path"] + logging.debug("Save session config: " + config_path) + os.makedirs(os.path.dirname(config_path), 0o700, True) + with open(config_path, "w") as handle: + cfg.write(handle) diff --git a/tools/helpers/__init__.py b/tools/helpers/__init__.py new file mode 100644 index 0000000..21357cf --- /dev/null +++ b/tools/helpers/__init__.py @@ -0,0 +1,10 @@ +# Copyright 2021 Oliver Smith +# SPDX-License-Identifier: GPL-3.0-or-later +from tools.helpers.arguments import arguments +import tools.helpers.arch +import tools.helpers.props +import tools.helpers.lxc +import tools.helpers.images +import tools.helpers.drivers +import tools.helpers.mount +import tools.helpers.http diff --git a/tools/helpers/arch.py b/tools/helpers/arch.py new file mode 100644 index 0000000..3c0bb76 --- /dev/null +++ b/tools/helpers/arch.py @@ -0,0 +1,17 @@ +# Copyright 2021 Oliver Smith +# SPDX-License-Identifier: GPL-3.0-or-later +import platform + +def host(): + machine = platform.machine() + + mapping = { + "i686": "x86", + "x86_64": "x86_64", + "aarch64": "arm64", + "armv7l": "arm" + } + if machine in mapping: + return mapping[machine] + raise ValueError("platform.machine '" + machine + "'" + " architecture is not supported") diff --git a/tools/helpers/arguments.py b/tools/helpers/arguments.py new file mode 100644 index 0000000..4c95287 --- /dev/null +++ b/tools/helpers/arguments.py @@ -0,0 +1,153 @@ +# Copyright 2021 Oliver Smith +# SPDX-License-Identifier: GPL-3.0-or-later +import argparse + +try: + import argcomplete +except ImportError: + argcomplete = False + +import tools.config + +""" This file is about parsing command line arguments passed to waydroid, as + well as generating the help pages (waydroid -h). All this is done with + Python's argparse. The parsed arguments get extended and finally stored in + the "args" variable, which is prominently passed to most functions all + over the waydroid code base. + + See tools/helpers/args.py for more information about the args variable. """ + +def arguments_init(subparser): + ret = subparser.add_parser("init", help="set up waydroid specific" + " configs and install images") + ret.add_argument("-i", "--images_path", + default=tools.config.defaults["images_path"], + help="custom path to waeiod images (default in" + " /home/.waydroid/images)") + ret.add_argument("-f", "--force", action="store_true", + help="re-initialize configs and images") + ret.add_argument("-c", "--system_channel", + help="custom system channel (options: OTA channel URL; default is Official OTA server)") + ret.add_argument("-v", "--vendor_channel", + help="custom vendor channel (options: OTA channel URL; default is Official OTA server)") + ret.add_argument("-r", "--rom_type", + help="rom type (options: \"lineage\", \"bliss\" or OTA channel URL; default is LineageOS)") + ret.add_argument("-s", "--system_type", + help="system type (options: VANILLA, FOSS or GAPPS; default is VANILLA)") + return ret + +def arguments_status(subparser): + ret = subparser.add_parser("status", + help="quick check for the waydroid") + return ret + +def arguments_upgrade(subparser): + ret = subparser.add_parser("upgrade", help="upgrade images") + ret.add_argument("-o", "--offline", action="store_true", + help="just for updating configs") + return ret + +def arguments_log(subparser): + ret = subparser.add_parser("log", help="follow the waydroid logfile") + ret.add_argument("-n", "--lines", default="60", + help="count of initial output lines") + ret.add_argument("-c", "--clear", help="clear the log", + action="store_true", dest="clear_log") + return ret + +def arguments_session(subparser): + ret = subparser.add_parser("session", help="session controller") + sub = ret.add_subparsers(title="subaction", dest="subaction") + sub.add_parser("start", help="start session") + sub.add_parser("stop", help="start session") + return ret + +def arguments_container(subparser): + ret = subparser.add_parser("container", help="container controller") + sub = ret.add_subparsers(title="subaction", dest="subaction") + sub.add_parser("start", help="start container") + sub.add_parser("stop", help="start container") + sub.add_parser("freeze", help="freeze container") + sub.add_parser("unfreeze", help="unfreeze container") + return ret + +def arguments_app(subparser): + ret = subparser.add_parser("app", help="applications controller") + sub = ret.add_subparsers(title="subaction", dest="subaction") + install = sub.add_parser( + "install", help="push a single package to the container and install it") + install.add_argument('PACKAGE', help="path to apk file") + remove = sub.add_parser( + "remove", help="remove single app package from the container") + remove.add_argument('PACKAGE', help="package name of app to remove") + launch = sub.add_parser("launch", help="start single application") + launch.add_argument('PACKAGE', help="package name of app to launch") + sub.add_parser("list", help="list installed applications") + return ret + +def arguments_prop(subparser): + ret = subparser.add_parser("prop", help="android properties controller") + sub = ret.add_subparsers(title="subaction", dest="subaction") + get = sub.add_parser( + "get", help="get value of property from container") + get.add_argument('key', help="key of the property to get") + set = sub.add_parser( + "set", help="set value to property on container") + set.add_argument('key', help="key of the property to set") + set.add_argument('value', help="value of the property to set") + return ret + +def arguments_fullUI(subparser): + ret = subparser.add_parser("show-full-ui", help="show android full screen in window") + return ret + +def arguments_shell(subparser): + ret = subparser.add_parser("shell", help="run remote shell command") + ret.add_argument('COMMAND', nargs='?', help="command to run") + return ret + +def arguments_logcat(subparser): + ret = subparser.add_parser("logcat", help="show android logcat") + return ret + +def arguments(): + parser = argparse.ArgumentParser(prog="waydroid") + + # Other + parser.add_argument("-V", "--version", action="version", + version=tools.config.version) + + # Logging + parser.add_argument("-l", "--log", dest="log", default=None, + help="path to log file") + parser.add_argument("--details-to-stdout", dest="details_to_stdout", + help="print details (e.g. build output) to stdout," + " instead of writing to the log", + action="store_true") + parser.add_argument("-v", "--verbose", dest="verbose", + action="store_true", help="write even more to the" + " logfiles (this may reduce performance)") + parser.add_argument("-q", "--quiet", dest="quiet", action="store_true", + help="do not output any log messages") + + # Actions + sub = parser.add_subparsers(title="action", dest="action") + + arguments_status(sub) + arguments_log(sub) + arguments_init(sub) + arguments_upgrade(sub) + arguments_session(sub) + arguments_container(sub) + arguments_app(sub) + arguments_prop(sub) + arguments_fullUI(sub) + arguments_shell(sub) + arguments_logcat(sub) + + if argcomplete: + argcomplete.autocomplete(parser, always_complete_options="long") + + # Parse and extend arguments (also backup unmodified result from argparse) + args = parser.parse_args() + return args diff --git a/tools/helpers/drivers.py b/tools/helpers/drivers.py new file mode 100644 index 0000000..e5a0335 --- /dev/null +++ b/tools/helpers/drivers.py @@ -0,0 +1,135 @@ +# Copyright 2021 Erfan Abdi +# SPDX-License-Identifier: GPL-3.0-or-later +import logging +import os +import glob +import tools.config +import tools.helpers.run + + +BINDER_DRIVERS = [ + "anbox-binder", + "puddlejumper", + "binder" +] +VNDBINDER_DRIVERS = [ + "anbox-vndbinder", + "vndpuddlejumper", + "vndbinder" +] +HWBINDER_DRIVERS = [ + "anbox-hwbinder", + "hwpuddlejumper", + "hwbinder" +] + +def probeBinderDriver(args): + binder_dev_nodes = [] + has_binder = False + has_vndbinder = False + has_hwbinder = False + for node in BINDER_DRIVERS: + if os.path.exists("/dev/" + node): + has_binder = True + if not has_binder: + binder_dev_nodes.append(BINDER_DRIVERS[0]) + for node in VNDBINDER_DRIVERS: + if os.path.exists("/dev/" + node): + has_vndbinder = True + if not has_vndbinder: + binder_dev_nodes.append(VNDBINDER_DRIVERS[0]) + for node in HWBINDER_DRIVERS: + if os.path.exists("/dev/" + node): + has_hwbinder = True + if not has_hwbinder: + binder_dev_nodes.append(HWBINDER_DRIVERS[0]) + + if len(binder_dev_nodes) > 0: + devices = ','.join(binder_dev_nodes) + command = ["modprobe", "binder_linux", "devices=\"{}\"".format(devices)] + output = tools.helpers.run.root(args, command, check=False, output_return=True) + if output: + logging.error("Failed to load binder driver for devices: {}".format(devices)) + logging.error(output.strip()) + else: + command = ["mkdir", "-p", "/dev/binderfs"] + tools.helpers.run.root(args, command, check=False) + command = ["mount", "-t", "binder", "binder", "/dev/binderfs"] + tools.helpers.run.root(args, command, check=False) + command = ["ln", "-s"] + command.extend(glob.glob("/dev/binderfs/*")) + command.append("/dev/") + tools.helpers.run.root(args, command, check=False) + + for node in binder_dev_nodes: + if not os.path.exists("/dev/" + node): + return -1 + + return 0 + +def probeAshmemDriver(args): + if not os.path.exists("/dev/ashmem"): + command = ["modprobe", "ashmem_linux"] + output = tools.helpers.run.root(args, command, check=False, output_return=True) + if output: + logging.error("Failed to load ashmem driver") + logging.error(output.strip()) + + if not os.path.exists("/dev/ashmem"): + return -1 + + return 0 + +def setupBinderNodes(args): + has_binder = False + has_vndbinder = False + has_hwbinder = False + if args.vendor_type == "MAINLINE": + probeBinderDriver(args) + for node in BINDER_DRIVERS: + if os.path.exists("/dev/" + node): + has_binder = True + args.BINDER_DRIVER = node + if not has_binder: + raise OSError('Binder node "binder" for waydroid not found') + + for node in VNDBINDER_DRIVERS: + if os.path.exists("/dev/" + node): + has_vndbinder = True + args.VNDBINDER_DRIVER = node + if not has_vndbinder: + raise OSError('Binder node "vndbinder" for waydroid not found') + + for node in HWBINDER_DRIVERS: + if os.path.exists("/dev/" + node): + has_hwbinder = True + args.HWBINDER_DRIVER = node + if not has_hwbinder: + raise OSError('Binder node "hwbinder" for waydroid not found') + else: + for node in BINDER_DRIVERS[:-1]: + if os.path.exists("/dev/" + node): + has_binder = True + args.BINDER_DRIVER = node + if not has_binder: + raise OSError('Binder node "binder" for waydroid not found') + + for node in VNDBINDER_DRIVERS[:-1]: + if os.path.exists("/dev/" + node): + has_vndbinder = True + args.VNDBINDER_DRIVER = node + if not has_vndbinder: + raise OSError('Binder node "vndbinder" for waydroid not found') + + for node in HWBINDER_DRIVERS[:-1]: + if os.path.exists("/dev/" + node): + has_hwbinder = True + args.HWBINDER_DRIVER = node + if not has_hwbinder: + raise OSError('Binder node "hwbinder" for waydroid not found') + +def loadBinderNodes(args): + cfg = tools.config.load(args) + args.BINDER_DRIVER = cfg["waydroid"]["binder"] + args.VNDBINDER_DRIVER = cfg["waydroid"]["vndbinder"] + args.HWBINDER_DRIVER = cfg["waydroid"]["hwbinder"] diff --git a/tools/helpers/http.py b/tools/helpers/http.py new file mode 100644 index 0000000..d05522b --- /dev/null +++ b/tools/helpers/http.py @@ -0,0 +1,89 @@ +# Copyright 2021 Oliver Smith +# SPDX-License-Identifier: GPL-3.0-or-later +import hashlib +import json +import logging +import os +import shutil +import urllib.request + +import tools.helpers.run + + +def download(args, url, prefix, cache=True, loglevel=logging.INFO, + allow_404=False): + """ Download a file to disk. + + :param url: the http(s) address of to the file to download + :param prefix: for the cache, to make it easier to find (cache files + get a hash of the URL after the prefix) + :param cache: if True, and url is cached, do not download it again + :param loglevel: change to logging.DEBUG to only display the download + message in 'waydroid log', not in stdout. We use + this when downloading many APKINDEX files at once, no + point in showing a dozen messages. + :param allow_404: do not raise an exception when the server responds + with a 404 Not Found error. Only display a warning on + stdout (no matter if loglevel is changed). + :returns: path to the downloaded file in the cache or None on 404 """ + # Create cache folder + if not os.path.exists(args.work + "/cache_http"): + tools.helpers.run.user(args, ["mkdir", "-p", args.work + "/cache_http"]) + + # Check if file exists in cache + prefix = prefix.replace("/", "_") + path = (args.work + "/cache_http/" + prefix + "_" + + hashlib.sha256(url.encode("utf-8")).hexdigest()) + if os.path.exists(path): + if cache: + return path + tools.helpers.run.user(args, ["rm", path]) + + # Download the file + logging.log(loglevel, "Download " + url) + try: + with urllib.request.urlopen(url) as response: + with open(path, "wb") as handle: + shutil.copyfileobj(response, handle) + # Handle 404 + except urllib.error.HTTPError as e: + if e.code == 404 and allow_404: + logging.warning("WARNING: file not found: " + url) + return None + raise + + # Return path in cache + return path + + +def retrieve(url, headers=None, allow_404=False): + """ Fetch the content of a URL and returns it as string. + + :param url: the http(s) address of to the resource to fetch + :param headers: dict of HTTP headers to use + :param allow_404: do not raise an exception when the server responds + with a 404 Not Found error. Only display a warning + :returns: str with the content of the response + """ + # Download the file + logging.verbose("Retrieving " + url) + + if headers is None: + headers = {} + + req = urllib.request.Request(url, headers=headers) + try: + with urllib.request.urlopen(req) as response: + return response.read() + # Handle 404 + except urllib.error.HTTPError as e: + if e.code == 404 and allow_404: + logging.warning("WARNING: failed to retrieve content from: " + url) + return None + raise + + +def retrieve_json(*args, **kwargs): + """ Fetch the contents of a URL, parse it as JSON and return it. See + retrieve() for the list of all parameters. """ + return json.loads(retrieve(*args, **kwargs)) diff --git a/tools/helpers/images.py b/tools/helpers/images.py new file mode 100644 index 0000000..aaeadff --- /dev/null +++ b/tools/helpers/images.py @@ -0,0 +1,109 @@ +# Copyright 2021 Erfan Abdi +# SPDX-License-Identifier: GPL-3.0-or-later +import logging +import zipfile +import requests +import hashlib +import os +import tools.config +from tools import helpers + + +def sha256sum(filename): + h = hashlib.sha256() + b = bytearray(128*1024) + mv = memoryview(b) + with open(filename, 'rb', buffering=0) as f: + for n in iter(lambda: f.readinto(mv), 0): + h.update(mv[:n]) + return h.hexdigest() + + +def get(args): + cfg = tools.config.load(args) + system_ota = cfg["waydroid"]["system_ota"] + system_request = requests.get(system_ota) + if system_request.status_code != 200: + raise ValueError( + "Failed to get system OTA channel: {}".format(system_ota)) + system_responses = system_request.json()["response"] + if len(system_responses) < 1: + raise ValueError("No images found on system channel") + + for system_response in system_responses: + if system_response['datetime'] > int(cfg["waydroid"]["system_datetime"]): + images_zip = helpers.http.download( + args, system_response['url'], system_response['filename'], cache=False) + logging.info("Validating system image") + if sha256sum(images_zip) != system_response['id']: + raise ValueError("Downloaded system image hash doesn't match, expected: {}".format( + system_response['id'])) + logging.info("Extracting to " + args.images_path) + with zipfile.ZipFile(images_zip, 'r') as zip_ref: + zip_ref.extractall(args.images_path) + cfg["waydroid"]["system_datetime"] = str(system_response['datetime']) + tools.config.save(args, cfg) + os.remove(images_zip) + break + + vendor_ota = cfg["waydroid"]["vendor_ota"] + vendor_request = requests.get(vendor_ota) + if vendor_request.status_code != 200: + raise ValueError( + "Failed to get vendor OTA channel: {}".format(vendor_ota)) + vendor_responses = vendor_request.json()["response"] + if len(vendor_responses) < 1: + raise ValueError("No images found on vendor channel") + + for vendor_response in vendor_responses: + if vendor_response['datetime'] > int(cfg["waydroid"]["vendor_datetime"]): + images_zip = helpers.http.download( + args, vendor_response['url'], vendor_response['filename'], cache=False) + logging.info("Validating vendor image") + if sha256sum(images_zip) != vendor_response['id']: + raise ValueError("Downloaded vendor image hash doesn't match, expected: {}".format( + vendor_response['id'])) + logging.info("Extracting to " + args.images_path) + with zipfile.ZipFile(images_zip, 'r') as zip_ref: + zip_ref.extractall(args.images_path) + cfg["waydroid"]["vendor_datetime"] = str(vendor_response['datetime']) + tools.config.save(args, cfg) + os.remove(images_zip) + break + +def replace(args, system_zip, system_time, vendor_zip, vendor_time): + cfg = tools.config.load(args) + args.images_path = cfg["waydroid"]["images_path"] + if os.path.exists(system_zip): + with zipfile.ZipFile(system_zip, 'r') as zip_ref: + zip_ref.extractall(args.images_path) + cfg["waydroid"]["system_datetime"] = str(system_time) + tools.config.save(args, cfg) + if os.path.exists(vendor_zip): + with zipfile.ZipFile(vendor_zip, 'r') as zip_ref: + zip_ref.extractall(args.images_path) + cfg["waydroid"]["vendor_datetime"] = str(vendor_time) + tools.config.save(args, cfg) + + +def mount_rootfs(args, images_dir): + helpers.mount.mount(args, images_dir + "/system.img", + tools.config.defaults["rootfs"], umount=True) + helpers.mount.mount(args, images_dir + "/vendor.img", + tools.config.defaults["rootfs"] + "/vendor") + for egl_path in ["/vendor/lib/egl", "/vendor/lib64/egl"]: + if os.path.isdir(egl_path): + helpers.mount.bind( + args, egl_path, tools.config.defaults["rootfs"] + egl_path) + if helpers.mount.ismount("/odm"): + helpers.mount.bind( + args, "/odm", tools.config.defaults["rootfs"] + "/odm_extra") + else: + if os.path.isdir("/vendor/odm"): + helpers.mount.bind( + args, "/vendor/odm", tools.config.defaults["rootfs"] + "/odm_extra") + helpers.mount.bind_file(args, args.work + "/waydroid.prop", + tools.config.defaults["rootfs"] + "/vendor/waydroid.prop") + +def umount_rootfs(args): + helpers.mount.umount_all(args, tools.config.defaults["rootfs"]) diff --git a/tools/helpers/logging.py b/tools/helpers/logging.py new file mode 100644 index 0000000..094bf82 --- /dev/null +++ b/tools/helpers/logging.py @@ -0,0 +1,99 @@ +# Copyright 2021 Oliver Smith +# SPDX-License-Identifier: GPL-3.0-or-later +import logging +import os +import sys + + +class log_handler(logging.StreamHandler): + """ + Write to stdout and to the already opened log file. + """ + _args = None + + def emit(self, record): + try: + msg = self.format(record) + + # INFO or higher: Write to stdout + if (not self._args.details_to_stdout and + not self._args.quiet and + record.levelno >= logging.INFO): + stream = self.stream + stream.write(msg) + stream.write(self.terminator) + self.flush() + + # Everything: Write to logfd + msg = "(" + str(os.getpid()).zfill(6) + ") " + msg + self._args.logfd.write(msg + "\n") + self._args.logfd.flush() + + except (KeyboardInterrupt, SystemExit): + raise + except BaseException: + self.handleError(record) + + +def add_verbose_log_level(): + """ + Add a new log level "verbose", which is below "debug". Also monkeypatch + logging, so it can be used with logging.verbose(). + + This function is based on work by Voitek Zylinski and sleepycal: + https://stackoverflow.com/a/20602183 + All stackoverflow user contributions are licensed as CC-BY-SA: + https://creativecommons.org/licenses/by-sa/3.0/ + """ + logging.VERBOSE = 5 + logging.addLevelName(logging.VERBOSE, "VERBOSE") + logging.Logger.verbose = lambda inst, msg, * \ + args, **kwargs: inst.log(logging.VERBOSE, msg, *args, **kwargs) + logging.verbose = lambda msg, *args, **kwargs: logging.log(logging.VERBOSE, + msg, *args, + **kwargs) + + +def init(args): + """ + Set log format and add the log file descriptor to args.logfd, add the + verbose log level. + """ + # Set log file descriptor (logfd) + if args.details_to_stdout: + setattr(args, "logfd", sys.stdout) + else: + # Require containing directory to exist (so we don't create the work + # folder and break the folder migration logic, which needs to set the + # version upon creation) + dir = os.path.dirname(args.log) + if os.path.exists(dir): + setattr(args, "logfd", open(args.log, "a+")) + else: + setattr(args, "logfd", open(os.devnull, "a+")) + if args.action != "init": + print("WARNING: Can't create log file in '" + dir + "', path" + " does not exist!") + + # Set log format + root_logger = logging.getLogger() + root_logger.handlers = [] + formatter = logging.Formatter("[%(asctime)s] %(message)s", + datefmt="%H:%M:%S") + + # Set log level + add_verbose_log_level() + root_logger.setLevel(logging.DEBUG) + if args.verbose: + root_logger.setLevel(logging.VERBOSE) + + # Add a custom log handler + handler = log_handler() + log_handler._args = args + handler.setFormatter(formatter) + root_logger.addHandler(handler) + + +def disable(): + logger = logging.getLogger() + logger.disabled = True diff --git a/tools/helpers/lxc.py b/tools/helpers/lxc.py new file mode 100644 index 0000000..c46f2a7 --- /dev/null +++ b/tools/helpers/lxc.py @@ -0,0 +1,269 @@ +# Copyright 2021 Erfan Abdi +# SPDX-License-Identifier: GPL-3.0-or-later +import subprocess +import os +import re +import logging +import glob +import shutil +import platform +import tools.config +import tools.helpers.run + + +def get_lxc_version(args): + if shutil.which("lxc-info") is not None: + command = ["lxc-info", "--version"] + version_str = tools.helpers.run.user(args, command, output_return=True) + return int(version_str[0]) + else: + return 0 + + +def generate_nodes_lxc_config(args): + def make_entry(src, dist=None, mnt_type="none", options="bind,create=file,optional 0 0", check=True): + if check and not os.path.exists(src): + return False + entry = "lxc.mount.entry = " + entry += src + " " + if dist is None: + dist = src[1:] + entry += dist + " " + entry += mnt_type + " " + entry += options + nodes.append(entry) + return True + + nodes = [] + # Necessary dev nodes + make_entry("tmpfs", "dev", "tmpfs", "nosuid 0 0", False) + make_entry("/dev/zero") + make_entry("/dev/full") + make_entry("/dev/ashmem", check=False) + make_entry("/dev/fuse") + make_entry("/dev/ion") + make_entry("/dev/char", options="bind,create=dir,optional 0 0") + + # Graphic dev nodes + make_entry("/dev/kgsl-3d0") + make_entry("/dev/mali0") + make_entry("/dev/pvr_sync") + make_entry("/dev/pmsg0") + make_entry("/dev/fb0") + make_entry("/dev/graphics/fb0") + make_entry("/dev/fb1") + make_entry("/dev/graphics/fb1") + make_entry("/dev/fb2") + make_entry("/dev/graphics/fb2") + make_entry("/dev/dri", options="bind,create=dir,optional 0 0") + + # Binder dev nodes + make_entry("/dev/" + args.BINDER_DRIVER, "dev/binder") + make_entry("/dev/" + args.VNDBINDER_DRIVER, "dev/vndbinder") + make_entry("/dev/" + args.HWBINDER_DRIVER, "dev/hwbinder") + + if args.vendor_type != "MAINLINE": + if not make_entry("/dev/hwbinder", "dev/host_hwbinder"): + raise OSError('Binder node "hwbinder" of host not found') + make_entry("/vendor", "vendor_extra", options="bind,optional 0 0") + + # Necessary device nodes for adb + make_entry("none", "dev/pts", "devpts", "defaults,mode=644,ptmxmode=666,create=dir 0 0", False) + make_entry("/dev/uhid") + + # Low memory killer sys node + make_entry("/sys/module/lowmemorykiller", options="bind,create=dir,optional 0 0") + + # Mount /data + make_entry("tmpfs", "mnt", "tmpfs", "mode=0755,uid=0,gid=1000", False) + make_entry(tools.config.defaults["data"], "data", options="bind 0 0", check=False) + + # Mount host permissions + make_entry(tools.config.defaults["host_perms"], + "vendor/etc/host-permissions", options="bind,optional 0 0") + + # Recursive mount /run to provide necessary host sockets + make_entry("/run", options="rbind,create=dir 0 0") + + # Necessary sw_sync node for HWC + make_entry("/dev/sw_sync") + make_entry("/sys/kernel/debug", options="rbind,create=dir,optional 0 0") + + # Media dev nodes (for Mediatek) + make_entry("/dev/Vcodec") + make_entry("/dev/MTK_SMI") + make_entry("/dev/mdp_sync") + make_entry("/dev/mtk_cmdq") + + # Media dev nodes (for Qcom) + make_entry("/dev/video32") + make_entry("/dev/video33") + + return nodes + + +def set_lxc_config(args): + lxc_path = tools.config.defaults["lxc"] + "/waydroid" + config_file = "config_2" + lxc_ver = get_lxc_version(args) + if lxc_ver == 0: + raise OSError("LXC is not installed") + elif lxc_ver <= 2: + config_file = "config_1" + config_path = tools.config.tools_src + "/data/configs/" + config_file + + command = ["mkdir", "-p", lxc_path] + tools.helpers.run.root(args, command) + command = ["cp", "-fpr", config_path, lxc_path + "/config"] + tools.helpers.run.root(args, command) + command = ["sed", "-i", "s/LXCARCH/{}/".format(platform.machine()), lxc_path + "/config"] + tools.helpers.run.root(args, command) + + nodes = generate_nodes_lxc_config(args) + config_nodes_tmp_path = args.work + "/config_nodes" + config_nodes = open(config_nodes_tmp_path, "w") + for node in nodes: + config_nodes.write(node + "\n") + config_nodes.close() + command = ["mv", config_nodes_tmp_path, lxc_path] + tools.helpers.run.root(args, command) + + +def make_base_props(args): + def find_hal(hardware): + hardware_props = [ + "ro.hardware." + hardware, + "ro.hardware", + "ro.product.board", + "ro.arch", + "ro.board.platform"] + for p in hardware_props: + prop = tools.helpers.props.host_get(args, p) + hal_prop = "" + if prop != "": + for lib in ["lib", "lib64"]: + hal_file = "/vendor/" + lib + "/hw/" + hardware + "." + prop + ".so" + command = ["readlink", "-f", hal_file] + hal_file_path = tools.helpers.run.root(args, command, output_return=True).strip() + if os.path.isfile(hal_file_path): + hal_prop = re.sub(".*" + hardware + ".", "", hal_file_path) + hal_prop = re.sub(".so", "", hal_prop) + if hal_prop != "": + return hal_prop + if hal_prop != "": + return hal_prop + return "" + + props = [] + gralloc = find_hal("gralloc") + if gralloc == "": + gralloc = "gbm" + props.append("ro.hardware.egl=mesa") + props.append("debug.stagefright.ccodec=0") + props.append("ro.hardware.gralloc=" + gralloc) + + egl = tools.helpers.props.host_get(args, "ro.hardware.egl") + if egl != "": + props.append("ro.hardware.egl=" + egl) + + media_profiles = tools.helpers.props.host_get(args, "media.settings.xml") + if media_profiles != "": + media_profiles = media_profiles.replace("vendor/", "vendor_extra/") + media_profiles = media_profiles.replace("odm/", "odm_extra/") + props.append("media.settings.xml=" + media_profiles) + + ccodec = tools.helpers.props.host_get(args, "debug.stagefright.ccodec") + if ccodec != "": + props.append("debug.stagefright.ccodec=" + ccodec) + + ext_library = tools.helpers.props.host_get(args, "ro.vendor.extension_library") + if ext_library != "": + ext_library = ext_library.replace("vendor/", "vendor_extra/") + ext_library = ext_library.replace("odm/", "odm_extra/") + props.append("ro.vendor.extension_library=" + ext_library) + + vulkan = find_hal("vulkan") + if vulkan != "": + props.append("ro.hardware.vulkan=" + vulkan) + + opengles = tools.helpers.props.host_get(args, "ro.opengles.version") + if opengles == "": + opengles = "196608" + props.append("ro.opengles.version=" + opengles) + + props.append("waydroid.system_ota=" + args.system_ota) + props.append("waydroid.vendor_ota=" + args.vendor_ota) + props.append("waydroid.tools_version=" + tools.config.version) + + base_props = open(args.work + "/waydroid_base.prop", "w") + for prop in props: + base_props.write(prop + "\n") + base_props.close() + + +def setup_host_perms(args): + sku = tools.helpers.props.host_get(args, "ro.boot.product.hardware.sku") + copy_list = [] + copy_list.extend( + glob.glob("/vendor/etc/permissions/android.hardware.nfc.*")) + if os.path.exists("/vendor/etc/permissions/android.hardware.consumerir.xml"): + copy_list.append("/vendor/etc/permissions/android.hardware.consumerir.xml") + copy_list.extend( + glob.glob("/odm/etc/permissions/android.hardware.nfc.*")) + if os.path.exists("/odm/etc/permissions/android.hardware.consumerir.xml"): + copy_list.append("/odm/etc/permissions/android.hardware.consumerir.xml") + if sku != "": + copy_list.extend( + glob.glob("/odm/etc/permissions/sku_{}/android.hardware.nfc.*".format(sku))) + if os.path.exists("/odm/etc/permissions/sku_{}/android.hardware.consumerir.xml".format(sku)): + copy_list.append( + "/odm/etc/permissions/sku_{}/android.hardware.consumerir.xml".format(sku)) + + if not os.path.exists(tools.config.defaults["host_perms"]): + os.mkdir(tools.config.defaults["host_perms"]) + + for filename in copy_list: + shutil.copy(filename, tools.config.defaults["host_perms"]) + +def status(args): + command = ["sudo", "lxc-info", "-P", tools.config.defaults["lxc"], "-n", "waydroid", "-sH"] + return subprocess.run(command, stdout=subprocess.PIPE).stdout.decode('utf-8').strip() + +def start(args): + command = ["lxc-start", "-P", tools.config.defaults["lxc"], + "-F", "-n", "waydroid", "--", "/init"] + tools.helpers.run.root(args, command, output="background") + +def stop(args): + command = ["lxc-stop", "-P", + tools.config.defaults["lxc"], "-n", "waydroid", "-k"] + tools.helpers.run.root(args, command) + +def freeze(args): + command = ["lxc-freeze", "-P", tools.config.defaults["lxc"], "-n", "waydroid"] + tools.helpers.run.root(args, command) + +def unfreeze(args): + command = ["lxc-unfreeze", "-P", + tools.config.defaults["lxc"], "-n", "waydroid"] + tools.helpers.run.root(args, command) + +def shell(args): + if status(args) != "RUNNING": + logging.error("WayDroid container is {}".format(status(args))) + return + command = ["lxc-attach", "-P", tools.config.defaults["lxc"], + "-n", "waydroid", "--"] + if args.COMMAND: + command.append(args.COMMAND) + else: + command.append("/system/bin/sh") + subprocess.run(command) + +def logcat(args): + if status(args) != "RUNNING": + logging.error("WayDroid container is {}".format(status(args))) + return + command = ["lxc-attach", "-P", tools.config.defaults["lxc"], + "-n", "waydroid", "--", "/system/bin/logcat"] + subprocess.run(command) diff --git a/tools/helpers/mount.py b/tools/helpers/mount.py new file mode 100644 index 0000000..4d77206 --- /dev/null +++ b/tools/helpers/mount.py @@ -0,0 +1,137 @@ +# Copyright 2021 Oliver Smith +# SPDX-License-Identifier: GPL-3.0-or-later +import os +import tools.helpers.run + + +def ismount(folder): + """ + Ismount() implementation, that works for mount --bind. + Workaround for: https://bugs.python.org/issue29707 + """ + folder = os.path.realpath(os.path.realpath(folder)) + with open("/proc/mounts", "r") as handle: + for line in handle: + words = line.split() + if len(words) >= 2 and words[1] == folder: + return True + if words[0] == folder: + return True + return False + + +def bind(args, source, destination, create_folders=True, umount=False): + """ + Mount --bind a folder and create necessary directory structure. + :param umount: when destination is already a mount point, umount it first. + """ + # Check/umount destination + if ismount(destination): + if umount: + umount_all(args, destination) + else: + return + + # Check/create folders + for path in [source, destination]: + if os.path.exists(path): + continue + if create_folders: + tools.helpers.run.root(args, ["mkdir", "-p", path]) + else: + raise RuntimeError("Mount failed, folder does not exist: " + + path) + + # Actually mount the folder + tools.helpers.run.root(args, ["mount", "-o", "bind", source, destination]) + + # Verify, that it has worked + if not ismount(destination): + raise RuntimeError("Mount failed: " + source + " -> " + destination) + + +def bind_file(args, source, destination, create_folders=False): + """ + Mount a file with the --bind option, and create the destination file, + if necessary. + """ + # Skip existing mountpoint + if ismount(destination): + return + + # Create empty file + if not os.path.exists(destination): + if create_folders: + dir = os.path.dirname(destination) + if not os.path.isdir(dir): + tools.helpers.run.root(args, ["mkdir", "-p", dir]) + + tools.helpers.run.root(args, ["touch", destination]) + + # Mount + tools.helpers.run.root(args, ["mount", "-o", "bind", source, + destination]) + + +def umount_all_list(prefix, source="/proc/mounts"): + """ + Parses `/proc/mounts` for all folders beginning with a prefix. + :source: can be changed for testcases + :returns: a list of folders, that need to be umounted + """ + ret = [] + prefix = os.path.realpath(prefix) + with open(source, "r") as handle: + for line in handle: + words = line.split() + if len(words) < 2: + raise RuntimeError("Failed to parse line in " + source + ": " + + line) + mountpoint = words[1] + if mountpoint.startswith(prefix): + # Remove "\040(deleted)" suffix (#545) + deleted_str = r"\040(deleted)" + if mountpoint.endswith(deleted_str): + mountpoint = mountpoint[:-len(deleted_str)] + ret.append(mountpoint) + ret.sort(reverse=True) + return ret + + +def umount_all(args, folder): + """ + Umount all folders, that are mounted inside a given folder. + """ + for mountpoint in umount_all_list(folder): + tools.helpers.run.root(args, ["umount", mountpoint]) + if ismount(mountpoint): + raise RuntimeError("Failed to umount: " + mountpoint) + +def mount(args, source, destination, create_folders=True, umount=False, readonly=True): + """ + Mount and create necessary directory structure. + :param umount: when destination is already a mount point, umount it first. + """ + # Check/umount destination + if ismount(destination): + if umount: + umount_all(args, destination) + else: + return + + # Check/create folders + if not os.path.exists(destination): + if create_folders: + tools.helpers.run.root(args, ["mkdir", "-p", destination]) + else: + raise RuntimeError("Mount failed, folder does not exist: " + + destination) + + # Actually mount the folder + tools.helpers.run.root(args, ["mount", source, destination]) + if readonly: + tools.helpers.run.root(args, ["mount", "-o", "remount,ro", source, destination]) + + # Verify, that it has worked + if not ismount(destination): + raise RuntimeError("Mount failed: " + source + " -> " + destination) diff --git a/tools/helpers/props.py b/tools/helpers/props.py new file mode 100644 index 0000000..bcc7c34 --- /dev/null +++ b/tools/helpers/props.py @@ -0,0 +1,50 @@ +# Copyright 2021 Oliver Smith +# SPDX-License-Identifier: GPL-3.0-or-later +from shutil import which +import logging +import os +import tools.helpers.run +from tools.interfaces import IPlatform + + +def host_get(args, prop): + if which("getprop") is not None: + command = ["getprop", prop] + return tools.helpers.run.user(args, command, output_return=True).strip() + else: + return "" + +def host_set(args, prop, value): + if which("setprop") is not None: + command = ["setprop", prop, value] + tools.helpers.run.user(args, command) + +def get(args, prop): + if os.path.exists(tools.config.session_defaults["config_path"]): + session_cfg = tools.config.load_session() + if session_cfg["session"]["state"] == "RUNNING": + platformService = IPlatform.get_service(args) + if platformService: + return platformService.getprop(prop, "") + else: + logging.error("Failed to access IPlatform service") + else: + logging.error("WayDroid container is {}".format( + session_cfg["session"]["state"])) + else: + logging.error("WayDroid session is stopped") + +def set(args, prop, value): + if os.path.exists(tools.config.session_defaults["config_path"]): + session_cfg = tools.config.load_session() + if session_cfg["session"]["state"] == "RUNNING": + platformService = IPlatform.get_service(args) + if platformService: + platformService.setprop(prop, value) + else: + logging.error("Failed to access IPlatform service") + else: + logging.error("WayDroid container is {}".format( + session_cfg["session"]["state"])) + else: + logging.error("WayDroid session is stopped") diff --git a/tools/helpers/run.py b/tools/helpers/run.py new file mode 100644 index 0000000..d3c7738 --- /dev/null +++ b/tools/helpers/run.py @@ -0,0 +1,78 @@ +# Copyright 2021 Oliver Smith +# SPDX-License-Identifier: GPL-3.0-or-later +import shlex +import tools.helpers.run_core + + +def flat_cmd(cmd, working_dir=None, env={}): + """ + Convert a shell command passed as list into a flat shell string with + proper escaping. + + :param cmd: command as list, e.g. ["echo", "string with spaces"] + :param working_dir: when set, prepend "cd ...;" to execute the command + in the given working directory + :param env: dict of environment variables to be passed to the command, e.g. + {"JOBS": "5"} + :returns: the flat string, e.g. + echo 'string with spaces' + cd /home/pmos;echo 'string with spaces' + """ + # Merge env and cmd into escaped list + escaped = [] + for key, value in env.items(): + escaped.append(key + "=" + shlex.quote(value)) + for i in range(len(cmd)): + escaped.append(shlex.quote(cmd[i])) + + # Prepend working dir + ret = " ".join(escaped) + if working_dir: + ret = "cd " + shlex.quote(working_dir) + ";" + ret + + return ret + + +def user(args, cmd, working_dir=None, output="log", output_return=False, + check=None, env={}, sudo=False): + """ + Run a command on the host system as user. + + :param env: dict of environment variables to be passed to the command, e.g. + {"JOBS": "5"} + + See tools.helpers.run_core.core() for a detailed description of all other + arguments and the return value. + """ + # Readable log message (without all the escaping) + msg = "% " + for key, value in env.items(): + msg += key + "=" + value + " " + if working_dir: + msg += "cd " + working_dir + "; " + msg += " ".join(cmd) + + # Add environment variables and run + if env: + cmd = ["sh", "-c", flat_cmd(cmd, env=env)] + return tools.helpers.run_core.core(args, msg, cmd, working_dir, output, + output_return, check, sudo) + + +def root(args, cmd, working_dir=None, output="log", output_return=False, + check=None, env={}): + """ + Run a command on the host system as root, with sudo. + + :param env: dict of environment variables to be passed to the command, e.g. + {"JOBS": "5"} + + See tools.helpers.run_core.core() for a detailed description of all other + arguments and the return value. + """ + if env: + cmd = ["sh", "-c", flat_cmd(cmd, env=env)] + cmd = ["sudo"] + cmd + + return user(args, cmd, working_dir, output, output_return, check, env, + True) diff --git a/tools/helpers/run_core.py b/tools/helpers/run_core.py new file mode 100644 index 0000000..dcc4c53 --- /dev/null +++ b/tools/helpers/run_core.py @@ -0,0 +1,346 @@ +# Copyright 2021 Oliver Smith +# SPDX-License-Identifier: GPL-3.0-or-later +import fcntl +import logging +import selectors +import subprocess +import sys +import threading +import time +import os +import tools.helpers.run + +""" For a detailed description of all output modes, read the description of + core() at the bottom. All other functions in this file get (indirectly) + called by core(). """ + + +def sanity_checks(output="log", output_return=False, check=None): + """ + Raise an exception if the parameters passed to core() don't make sense + (all parameters are described in core() below). + """ + vals = ["log", "stdout", "interactive", "tui", "background", "pipe"] + if output not in vals: + raise RuntimeError("Invalid output value: " + str(output)) + + # Prevent setting the check parameter with output="background". + # The exit code won't be checked when running in background, so it would + # always by check=False. But we prevent it from getting set to check=False + # as well, so it does not look like you could change it to check=True. + if check is not None and output == "background": + raise RuntimeError("Can't use check with output: background") + + if output_return and output in ["tui", "background"]: + raise RuntimeError("Can't use output_return with output: " + output) + + +def background(args, cmd, working_dir=None): + """ Run a subprocess in background and redirect its output to the log. """ + ret = subprocess.Popen(cmd, stdout=args.logfd, stderr=args.logfd, + cwd=working_dir) + logging.debug("New background process: pid={}, output=background".format(ret.pid)) + return ret + + +def pipe(args, cmd, working_dir=None): + """ Run a subprocess in background and redirect its output to a pipe. """ + ret = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=args.logfd, + cwd=working_dir) + logging.verbose("New background process: pid={}, output=pipe".format(ret.pid)) + return ret + + +def pipe_read(args, process, output_to_stdout=False, output_return=False, + output_return_buffer=False): + """ + Read all available output from a subprocess and copy it to the log and + optionally stdout and a buffer variable. This is only meant to be called by + foreground_pipe() below. + + :param process: subprocess.Popen instance + :param output_to_stdout: copy all output to waydroid's stdout + :param output_return: when set to True, output_return_buffer will be + extended + :param output_return_buffer: list of bytes that gets extended with the + current output in case output_return is True. + """ + while True: + # Copy available output + out = process.stdout.readline() + if len(out): + args.logfd.buffer.write(out) + if output_to_stdout: + sys.stdout.buffer.write(out) + if output_return: + output_return_buffer.append(out) + continue + + # No more output (flush buffers) + args.logfd.flush() + if output_to_stdout: + sys.stdout.flush() + return + + +def kill_process_tree(args, pid, ppids, sudo): + """ + Recursively kill a pid and its child processes + + :param pid: process id that will be killed + :param ppids: list of process id and parent process id tuples (pid, ppid) + :param sudo: use sudo to kill the process + """ + if sudo: + tools.helpers.run.root(args, ["kill", "-9", str(pid)], + check=False) + else: + tools.helpers.run.user(args, ["kill", "-9", str(pid)], + check=False) + + for (child_pid, child_ppid) in ppids: + if child_ppid == str(pid): + kill_process_tree(args, child_pid, ppids, sudo) + + +def kill_command(args, pid, sudo): + """ + Kill a command process and recursively kill its child processes + + :param pid: process id that will be killed + :param sudo: use sudo to kill the process + """ + cmd = ["ps", "-e", "-o", "pid,ppid"] + ret = subprocess.run(cmd, check=True, stdout=subprocess.PIPE) + ppids = [] + proc_entries = ret.stdout.decode("utf-8").rstrip().split('\n')[1:] + for row in proc_entries: + items = row.split() + if len(items) != 2: + raise RuntimeError("Unexpected ps output: " + row) + ppids.append(items) + + kill_process_tree(args, pid, ppids, sudo) + + +def foreground_pipe(args, cmd, working_dir=None, output_to_stdout=False, + output_return=False, output_timeout=True, + sudo=False): + """ + Run a subprocess in foreground with redirected output and optionally kill + it after being silent for too long. + + :param cmd: command as list, e.g. ["echo", "string with spaces"] + :param working_dir: path in host system where the command should run + :param output_to_stdout: copy all output to waydroid's stdout + :param output_return: return the output of the whole program + :param output_timeout: kill the process when it doesn't print any output + after a certain time (configured with --timeout) + and raise a RuntimeError exception + :param sudo: use sudo to kill the process when it hits the timeout + :returns: (code, output) + * code: return code of the program + * output: "" + * output: full program output string (output_return is True) + """ + # Start process in background (stdout and stderr combined) + process = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, cwd=working_dir) + + # Make process.stdout non-blocking + handle = process.stdout.fileno() + flags = fcntl.fcntl(handle, fcntl.F_GETFL) + fcntl.fcntl(handle, fcntl.F_SETFL, flags | os.O_NONBLOCK) + + # While process exists wait for output (with timeout) + output_buffer = [] + sel = selectors.DefaultSelector() + sel.register(process.stdout, selectors.EVENT_READ) + timeout = args.timeout if output_timeout else None + while process.poll() is None: + wait_start = time.perf_counter() if output_timeout else None + sel.select(timeout) + + # On timeout raise error (we need to measure time on our own, because + # select() may exit early even if there is no data to read and the + # timeout was not reached.) + if output_timeout: + wait_end = time.perf_counter() + if wait_end - wait_start >= args.timeout: + logging.info("Process did not write any output for " + + str(args.timeout) + " seconds. Killing it.") + logging.info("NOTE: The timeout can be increased with" + " 'waydroid -t'.") + kill_command(args, process.pid, sudo) + continue + + # Read all currently available output + pipe_read(args, process, output_to_stdout, output_return, + output_buffer) + + # There may still be output after the process quit + pipe_read(args, process, output_to_stdout, output_return, output_buffer) + + # Return the return code and output (the output gets built as list of + # output chunks and combined at the end, this is faster than extending the + # combined string with each new chunk) + return (process.returncode, b"".join(output_buffer).decode("utf-8")) + + +def foreground_tui(cmd, working_dir=None): + """ + Run a subprocess in foreground without redirecting any of its output. + + This is the only way text-based user interfaces (ncurses programs like + vim, nano or the kernel's menuconfig) work properly. + """ + + logging.debug("*** output passed to waydroid stdout, not to this log" + " ***") + process = subprocess.Popen(cmd, cwd=working_dir) + return process.wait() + + +def check_return_code(args, code, log_message): + """ + Check the return code of a command. + + :param code: exit code to check + :param log_message: simplified and more readable form of the command, e.g. + "(native) % echo test" instead of the full command with + entering the chroot and more escaping + :raises RuntimeError: when the code indicates that the command failed + """ + + if code: + logging.debug("^" * 70) + logging.info("NOTE: The failed command's output is above the ^^^ line" + " in the log file: " + args.log) + raise RuntimeError("Command failed: " + log_message) + + +def sudo_timer_iterate(): + """ + Run sudo -v and schedule a new timer to repeat the same. + """ + + subprocess.Popen(["sudo", "-v"]).wait() + + timer = threading.Timer(interval=60, function=sudo_timer_iterate) + timer.daemon = True + timer.start() + + +def sudo_timer_start(args): + """ + Start a timer to call sudo -v periodically, so that the password is only + needed once. + """ + + if "sudo_timer_active" in args.cache: + return + args.cache["sudo_timer_active"] = True + + sudo_timer_iterate() + + +def core(args, log_message, cmd, working_dir=None, output="log", + output_return=False, check=None, sudo=False, disable_timeout=False): + """ + Run a command and create a log entry. + + This is a low level function not meant to be used directly. Use one of the + following instead: tools.helpers.run.user(), tools.helpers.run.root(), + tools.chroot.user(), tools.chroot.root() + + :param log_message: simplified and more readable form of the command, e.g. + "(native) % echo test" instead of the full command with + entering the chroot and more escaping + :param cmd: command as list, e.g. ["echo", "string with spaces"] + :param working_dir: path in host system where the command should run + :param output: where to write the output (stdout and stderr) of the + process. We almost always write to the log file, which can + be read with "waydroid log" (output values: "log", + "stdout", "interactive", "background"), so it's easy to + trace what waydroid does. + + The exceptions are "tui" (text-based user interface), where + it does not make sense to write to the log file (think of + ncurses UIs, such as "menuconfig") and "pipe" where the + output is written to a pipe for manual asynchronous + consumption by the caller. + + When the output is not set to "interactive", "tui", + "background" or "pipe", we kill the process if it does not + output anything for 5 minutes (time can be set with + "waydroid --timeout"). + + The table below shows all possible values along with + their properties. "wait" indicates that we wait for the + process to complete. + + output value | timeout | out to log | out to stdout | wait + ----------------------------------------------------------- + "log" | x | x | | x + "stdout" | x | x | x | x + "interactive" | | x | x | x + "tui" | | | x | x + "background" | | x | | + "pipe" | | | | + + :param output_return: in addition to writing the program's output to the + destinations above in real time, write to a buffer + and return it as string when the command has + completed. This is not possible when output is + "background", "pipe" or "tui". + :param check: an exception will be raised when the command's return code + is not 0. Set this to False to disable the check. This + parameter can not be used when the output is "background" or + "pipe". + :param sudo: use sudo to kill the process when it hits the timeout. + :returns: * program's return code (default) + * subprocess.Popen instance (output is "background" or "pipe") + * the program's entire output (output_return is True) + """ + sanity_checks(output, output_return, check) + + if args.sudo_timer and sudo: + sudo_timer_start(args) + + # Log simplified and full command (waydroid -v) + logging.debug(log_message) + logging.verbose("run: " + str(cmd)) + + # Background + if output == "background": + return background(args, cmd, working_dir) + + # Pipe + if output == "pipe": + return pipe(args, cmd, working_dir) + + # Foreground + output_after_run = "" + if output == "tui": + # Foreground TUI + code = foreground_tui(cmd, working_dir) + else: + # Foreground pipe (always redirects to the error log file) + output_to_stdout = False + if not args.details_to_stdout and output in ["stdout", "interactive"]: + output_to_stdout = True + + output_timeout = output in ["log", "stdout"] and not disable_timeout + + (code, output_after_run) = foreground_pipe(args, cmd, working_dir, + output_to_stdout, + output_return, + output_timeout, + sudo) + + # Check the return code + if check is not False: + check_return_code(args, code, log_message) + + # Return (code or output string) + return output_after_run if output_return else code diff --git a/tools/interfaces/IClipboard.py b/tools/interfaces/IClipboard.py new file mode 100644 index 0000000..7d09d92 --- /dev/null +++ b/tools/interfaces/IClipboard.py @@ -0,0 +1,48 @@ +import gbinder +import logging +from tools import helpers +from gi.repository import GLib + + +INTERFACE = "lineageos.waydroid.IClipboard" +SERVICE_NAME = "waydroidclipboard" + +TRANSACTION_sendClipboardData = 1 +TRANSACTION_getClipboardData = 2 + +def add_service(args, sendClipboardData, getClipboardData): + helpers.drivers.loadBinderNodes(args) + serviceManager = gbinder.ServiceManager("/dev/" + args.BINDER_DRIVER) + + def response_handler(req, code, flags): + logging.debug( + "{}: Received transaction: {}".format(SERVICE_NAME, code)) + reader = req.init_reader() + local_response = response.new_reply() + if code == TRANSACTION_sendClipboardData: + arg1 = reader.read_string16() + sendClipboardData(arg1) + local_response.append_int32(0) + if code == TRANSACTION_getClipboardData: + ret = getClipboardData() + local_response.append_int32(0) + local_response.append_string16(ret) + + return local_response, 0 + + def binder_presence(): + if serviceManager.is_present(): + status = serviceManager.add_service_sync(SERVICE_NAME, response) + + if status: + logging.error("Failed to add service " + SERVICE_NAME) + args.clipboardLoop.quit() + + response = serviceManager.new_local_object(INTERFACE, response_handler) + args.clipboardLoop = GLib.MainLoop() + binder_presence() + status = serviceManager.add_presence_handler(binder_presence) + if status: + args.clipboardLoop.run() + else: + logging.error("Failed to add presence handler: {}".format(status)) diff --git a/tools/interfaces/IHardware.py b/tools/interfaces/IHardware.py new file mode 100644 index 0000000..347a89c --- /dev/null +++ b/tools/interfaces/IHardware.py @@ -0,0 +1,66 @@ +import gbinder +import logging +from tools import helpers +from gi.repository import GLib + + +INTERFACE = "lineageos.waydroid.IHardware" +SERVICE_NAME = "waydroidhardware" + +TRANSACTION_enableNFC = 1 +TRANSACTION_enableBluetooth = 2 +TRANSACTION_suspend = 3 +TRANSACTION_reboot = 4 +TRANSACTION_upgrade = 5 + +def add_service(args, enableNFC, enableBluetooth, suspend, reboot, upgrade): + helpers.drivers.loadBinderNodes(args) + serviceManager = gbinder.ServiceManager("/dev/" + args.BINDER_DRIVER) + + def response_handler(req, code, flags): + logging.debug( + "{}: Received transaction: {}".format(SERVICE_NAME, code)) + reader = req.init_reader() + local_response = response.new_reply() + if code == TRANSACTION_enableNFC: + status, arg1 = reader.read_int32() + ret = enableNFC(arg1 != 0) + local_response.append_int32(0) + local_response.append_int32(ret) + if code == TRANSACTION_enableBluetooth: + status, arg1 = reader.read_int32() + ret = enableBluetooth(arg1 != 0) + local_response.append_int32(0) + local_response.append_int32(ret) + if code == TRANSACTION_suspend: + suspend() + local_response.append_int32(0) + if code == TRANSACTION_reboot: + reboot() + local_response.append_int32(0) + if code == TRANSACTION_upgrade: + arg1 = reader.read_string16() + status, arg2 = reader.read_int32() + arg3 = reader.read_string16() + status, arg4 = reader.read_int32() + upgrade(arg1, arg2, arg3, arg4) + local_response.append_int32(0) + + return local_response, 0 + + def binder_presence(): + if serviceManager.is_present(): + status = serviceManager.add_service_sync(SERVICE_NAME, response) + + if status: + logging.error("Failed to add service " + SERVICE_NAME) + args.hardwareLoop.quit() + + response = serviceManager.new_local_object(INTERFACE, response_handler) + args.hardwareLoop = GLib.MainLoop() + binder_presence() + status = serviceManager.add_presence_handler(binder_presence) + if status: + args.hardwareLoop.run() + else: + logging.error("Failed to add presence handler: {}".format(status)) diff --git a/tools/interfaces/IPlatform.py b/tools/interfaces/IPlatform.py new file mode 100644 index 0000000..9887d13 --- /dev/null +++ b/tools/interfaces/IPlatform.py @@ -0,0 +1,292 @@ +import gbinder +import logging +import time +from tools import helpers + + +INTERFACE = "lineageos.waydroid.IPlatform" +SERVICE_NAME = "waydroidplatform" + +TRANSACTION_getprop = 1 +TRANSACTION_setprop = 2 +TRANSACTION_getAppsInfo = 3 +TRANSACTION_getAppInfo = 4 +TRANSACTION_installApp = 5 +TRANSACTION_removeApp = 6 +TRANSACTION_launchApp = 7 +TRANSACTION_getAppName = 8 +TRANSACTION_settingsPutString = 9 +TRANSACTION_settingsGetString = 10 +TRANSACTION_settingsPutInt = 11 +TRANSACTION_getAppName = 12 + +class IPlatform: + def __init__(self, remote): + self.client = gbinder.Client(remote, INTERFACE) + + def getprop(self, arg1, arg2): + request = self.client.new_request() + request.append_string16(arg1) + request.append_string16(arg2) + reply, status = self.client.transact_sync_reply( + TRANSACTION_getprop, request) + + if status: + logging.error("Sending reply failed") + else: + reader = reply.init_reader() + status, exception = reader.read_int32() + if exception == 0: + rep1 = reader.read_string16() + return rep1 + else: + logging.error("Failed with code: {}".format(exception)) + + return None + + def setprop(self, arg1, arg2): + request = self.client.new_request() + request.append_string16(arg1) + request.append_string16(arg2) + reply, status = self.client.transact_sync_reply( + TRANSACTION_setprop, request) + + if status: + logging.error("Sending reply failed") + else: + reader = reply.init_reader() + status, exception = reader.read_int32() + if exception == 0: + return + else: + logging.error("Failed with code: {}".format(exception)) + + return + + def getAppsInfo(self): + request = self.client.new_request() + reply, status = self.client.transact_sync_reply( + TRANSACTION_getAppsInfo, request) + + if status: + logging.error("Sending reply failed") + else: + reader = reply.init_reader() + apps_list = [] + status, exception = reader.read_int32() + if exception == 0: + status, apps = reader.read_int32() + for j in range(apps): + status, has_value = reader.read_int32() + if has_value == 1: + appinfo = { + "name": reader.read_string16(), + "packageName": reader.read_string16(), + "action": reader.read_string16(), + "launchIntent": reader.read_string16(), + "componentPackageName": reader.read_string16(), + "componentClassName": reader.read_string16(), + "categories": [] + } + status, categories = reader.read_int32() + for i in range(categories): + appinfo["categories"].append(reader.read_string16()) + apps_list.append(appinfo) + else: + logging.error("Failed with code: {}".format(exception)) + + return apps_list + + def getAppInfo(self, arg1): + request = self.client.new_request() + request.append_string16(arg1) + reply, status = self.client.transact_sync_reply( + TRANSACTION_getAppInfo, request) + + if status: + logging.error("Sending reply failed") + else: + reader = reply.init_reader() + status, exception = reader.read_int32() + if exception == 0: + status, has_value = reader.read_int32() + if has_value == 1: + appinfo = { + "name": reader.read_string16(), + "packageName": reader.read_string16(), + "action": reader.read_string16(), + "launchIntent": reader.read_string16(), + "componentPackageName": reader.read_string16(), + "componentClassName": reader.read_string16(), + "categories": [] + } + status, categories = reader.read_int32() + for i in range(categories): + appinfo["categories"].append(reader.read_string16()) + + return appinfo + else: + logging.error("Failed with code: {}".format(exception)) + + return None + + def installApp(self, arg1): + request = self.client.new_request() + request.append_string16(arg1) + reply, status = self.client.transact_sync_reply( + TRANSACTION_installApp, request) + + if status: + logging.error("Sending reply failed") + else: + reader = reply.init_reader() + status, exception = reader.read_int32() + if exception == 0: + status, ret = reader.read_int32() + return ret + else: + logging.error("Failed with code: {}".format(exception)) + + return None + + def removeApp(self, arg1): + request = self.client.new_request() + request.append_string16(arg1) + reply, status = self.client.transact_sync_reply( + TRANSACTION_removeApp, request) + + if status: + logging.error("Sending reply failed") + else: + reader = reply.init_reader() + status, exception = reader.read_int32() + if exception == 0: + status, ret = reader.read_int32() + return ret + else: + logging.error("Failed with code: {}".format(exception)) + + return None + + def launchApp(self, arg1): + request = self.client.new_request() + request.append_string16(arg1) + reply, status = self.client.transact_sync_reply( + TRANSACTION_launchApp, request) + + if status: + logging.error("Sending reply failed") + else: + reader = reply.init_reader() + status, exception = reader.read_int32() + if exception != 0: + logging.error("Failed with code: {}".format(exception)) + + def getAppName(self, arg1): + request = self.client.new_request() + request.append_string16(arg1) + reply, status = self.client.transact_sync_reply( + TRANSACTION_getAppName, request) + + if status: + logging.error("Sending reply failed") + else: + reader = reply.init_reader() + status, exception = reader.read_int32() + if exception == 0: + rep1 = reader.read_string16() + return rep1 + else: + logging.error("Failed with code: {}".format(exception)) + + return None + + def settingsPutString(self, arg1, arg2, arg3): + request = self.client.new_request() + request.append_int32(arg1) + request.append_string16(arg2) + request.append_string16(arg3) + reply, status = self.client.transact_sync_reply( + TRANSACTION_settingsPutString, request) + + if status: + logging.error("Sending reply failed") + else: + reader = reply.init_reader() + status, exception = reader.read_int32() + if exception != 0: + logging.error("Failed with code: {}".format(exception)) + + def settingsGetString(self, arg1, arg2): + request = self.client.new_request() + request.append_int32(arg1) + request.append_string16(arg2) + reply, status = self.client.transact_sync_reply( + TRANSACTION_settingsGetString, request) + + if status: + logging.error("Sending reply failed") + else: + reader = reply.init_reader() + status, exception = reader.read_int32() + if exception == 0: + rep1 = reader.read_string16() + return rep1 + else: + logging.error("Failed with code: {}".format(exception)) + + return None + + def settingsPutInt(self, arg1, arg2, arg3): + request = self.client.new_request() + request.append_int32(arg1) + request.append_string16(arg2) + request.append_int32(arg3) + reply, status = self.client.transact_sync_reply( + TRANSACTION_settingsPutInt, request) + + if status: + logging.error("Sending reply failed") + else: + reader = reply.init_reader() + status, exception = reader.read_int32() + if exception != 0: + logging.error("Failed with code: {}".format(exception)) + + def settingsGetInt(self, arg1, arg2): + request = self.client.new_request() + request.append_int32(arg1) + request.append_string16(arg2) + reply, status = self.client.transact_sync_reply( + TRANSACTION_settingsGetString, request) + + if status: + logging.error("Sending reply failed") + else: + reader = reply.init_reader() + status, exception = reader.read_int32() + if exception == 0: + status, rep1 = reader.read_int32() + return rep1 + else: + logging.error("Failed with code: {}".format(exception)) + + return None + +def get_service(args): + helpers.drivers.loadBinderNodes(args) + serviceManager = gbinder.ServiceManager("/dev/" + args.BINDER_DRIVER) + tries = 1000 + + remote, status = serviceManager.get_service_sync(SERVICE_NAME) + while(not remote): + if tries > 0: + logging.warning( + "Failed to get service {}, trying again...".format(SERVICE_NAME)) + time.sleep(1) + remote, status = serviceManager.get_service_sync(SERVICE_NAME) + tries = tries - 1 + else: + return None + + return IPlatform(remote) diff --git a/tools/interfaces/IStatusBarService.py b/tools/interfaces/IStatusBarService.py new file mode 100644 index 0000000..0fe30a7 --- /dev/null +++ b/tools/interfaces/IStatusBarService.py @@ -0,0 +1,59 @@ +import gbinder +import logging +import time +from tools import helpers + + +INTERFACE = "com.android.internal.statusbar.IStatusBarService" +SERVICE_NAME = "statusbar" + +TRANSACTION_expand = 1 +TRANSACTION_collapse = 2 + +class IStatusBarService: + def __init__(self, remote): + self.client = gbinder.Client(remote, INTERFACE) + + def expand(self): + request = self.client.new_request() + reply, status = self.client.transact_sync_reply( + TRANSACTION_expand, request) + + if status: + logging.error("Sending reply failed") + else: + reader = reply.init_reader() + status, exception = reader.read_int32() + if exception != 0: + logging.error("Failed with code: {}".format(exception)) + + def collapse(self): + request = self.client.new_request() + reply, status = self.client.transact_sync_reply( + TRANSACTION_collapse, request) + + if status: + logging.error("Sending reply failed") + else: + reader = reply.init_reader() + status, exception = reader.read_int32() + if exception != 0: + logging.error("Failed with code: {}".format(exception)) + +def get_service(args): + helpers.drivers.loadBinderNodes(args) + serviceManager = gbinder.ServiceManager("/dev/" + args.BINDER_DRIVER) + tries = 1000 + + remote, status = serviceManager.get_service_sync(SERVICE_NAME) + while(not remote): + if tries > 0: + logging.warning( + "Failed to get service {}, trying again...".format(SERVICE_NAME)) + time.sleep(1) + remote, status = serviceManager.get_service_sync(SERVICE_NAME) + tries = tries - 1 + else: + return None + + return IStatusBarService(remote) diff --git a/tools/interfaces/IUserMonitor.py b/tools/interfaces/IUserMonitor.py new file mode 100644 index 0000000..6652778 --- /dev/null +++ b/tools/interfaces/IUserMonitor.py @@ -0,0 +1,51 @@ +import gbinder +import logging +from tools import helpers +from gi.repository import GLib + + +INTERFACE = "lineageos.waydroid.IUserMonitor" +SERVICE_NAME = "waydroidusermonitor" + +TRANSACTION_userUnlocked = 1 +TRANSACTION_packageStateChanged = 2 + +def add_service(args, userUnlocked, packageStateChanged): + helpers.drivers.loadBinderNodes(args) + serviceManager = gbinder.ServiceManager("/dev/" + args.BINDER_DRIVER) + + def response_handler(req, code, flags): + logging.debug( + "{}: Received transaction: {}".format(SERVICE_NAME, code)) + reader = req.init_reader() + local_response = response.new_reply() + if code == TRANSACTION_userUnlocked: + status, arg1 = reader.read_int32() + userUnlocked(arg1) + local_response.append_int32(0) + if code == TRANSACTION_packageStateChanged: + status, arg1 = reader.read_int32() + arg2 = reader.read_string16() + status, arg3 = reader.read_int32() + packageStateChanged(arg1, arg2, arg3) + local_response.append_int32(0) + + return local_response, 0 + + def binder_presence(): + if serviceManager.is_present(): + status = serviceManager.add_service_sync(SERVICE_NAME, response) + + if status: + logging.error("Failed to add service " + SERVICE_NAME) + args.userMonitorLoop.quit() + + response = serviceManager.new_local_object(INTERFACE, response_handler) + args.userMonitorLoop = GLib.MainLoop() + binder_presence() + status = serviceManager.add_presence_handler(binder_presence) + if status: + args.userMonitorLoop.run() + else: + logging.error("Failed to add presence handler: {}".format(status)) + diff --git a/tools/services/__init__.py b/tools/services/__init__.py new file mode 100644 index 0000000..8510c33 --- /dev/null +++ b/tools/services/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2021 Erfan Abdi +# SPDX-License-Identifier: GPL-3.0-or-later +from tools.services.user_manager import start, stop +from tools.services.clipboard_manager import start, stop +from tools.services.hardware_manager import start, stop diff --git a/tools/services/clipboard_manager.py b/tools/services/clipboard_manager.py new file mode 100644 index 0000000..72fe62a --- /dev/null +++ b/tools/services/clipboard_manager.py @@ -0,0 +1,41 @@ +# Copyright 2021 Erfan Abdi +# SPDX-License-Identifier: GPL-3.0-or-later +import logging +import threading +from tools.interfaces import IClipboard + +try: + import pyclip + canClip = True +except Exception as e: + logging.debug(str(e)) + canClip = False + +def start(args): + def sendClipboardData(value): + try: + pyclip.copy(value) + except Exception as e: + logging.debug(str(e)) + + def getClipboardData(): + try: + return pyclip.paste() + except Exception as e: + logging.debug(str(e)) + + def service_thread(): + IClipboard.add_service(args, sendClipboardData, getClipboardData) + + if canClip: + args.clipboard_manager = threading.Thread(target=service_thread) + args.clipboard_manager.start() + else: + logging.warning("Failed to start Clipboard manager service, check logs") + +def stop(args): + try: + if args.clipboardLoop: + args.clipboardLoop.quit() + except AttributeError: + logging.debug("Clipboard service is not even started") diff --git a/tools/services/hardware_manager.py b/tools/services/hardware_manager.py new file mode 100644 index 0000000..529348d --- /dev/null +++ b/tools/services/hardware_manager.py @@ -0,0 +1,44 @@ +# Copyright 2021 Erfan Abdi +# SPDX-License-Identifier: GPL-3.0-or-later +import logging +import threading +import tools.actions.container_manager +from tools import helpers +from tools.interfaces import IHardware + + +def start(args): + def enableNFC(enable): + logging.debug("Function enableNFC not implemented") + + def enableBluetooth(enable): + logging.debug("Function enableBluetooth not implemented") + + def suspend(): + tools.actions.container_manager.freeze(args) + + def reboot(): + helpers.lxc.stop(args) + helpers.lxc.start(args) + + def upgrade(system_zip, system_time, vendor_zip, vendor_time): + helpers.lxc.stop(args) + helpers.images.umount_rootfs(args) + helpers.images.replace(args, system_zip, system_time, + vendor_zip, vendor_time) + helpers.images.mount_rootfs(args, args.images_path) + helpers.lxc.start(args) + + def service_thread(): + IHardware.add_service( + args, enableNFC, enableBluetooth, suspend, reboot, upgrade) + + args.hardware_manager = threading.Thread(target=service_thread) + args.hardware_manager.start() + +def stop(args): + try: + if args.hardwareLoop: + args.hardwareLoop.quit() + except AttributeError: + logging.debug("Hardware service is not even started") diff --git a/tools/services/user_manager.py b/tools/services/user_manager.py new file mode 100644 index 0000000..41fea83 --- /dev/null +++ b/tools/services/user_manager.py @@ -0,0 +1,98 @@ +# Copyright 2021 Erfan Abdi +# SPDX-License-Identifier: GPL-3.0-or-later +import logging +import os +import threading +import tools.config +from tools.interfaces import IUserMonitor +from tools.interfaces import IPlatform + + +def start(args): + def makeDesktopFile(appInfo): + showApp = False + for cat in appInfo["categories"]: + if cat.strip() == "android.intent.category.LAUNCHER": + showApp = True + if not showApp: + return -1 + + packageName = appInfo["packageName"] + + desktop_file_path = args.host_user + \ + "/.local/share/applications/" + packageName + ".desktop" + if not os.path.exists(desktop_file_path): + lines = ["[Desktop Entry]", "Type=Application"] + lines.append("Name=" + appInfo["name"]) + lines.append("Exec=waydroid app launch " + packageName) + lines.append("Icon=" + args.waydroid_data + "/icons/" + packageName + ".png") + desktop_file = open(desktop_file_path, "w") + for line in lines: + desktop_file.write(line + "\n") + desktop_file.close() + os.chmod(desktop_file_path, 0o755) + return 0 + + def makeWaydroidDesktopFile(): + desktop_file_path = args.host_user + \ + "/.local/share/applications/Waydroid.desktop" + if not os.path.exists(desktop_file_path): + lines = ["[Desktop Entry]", "Type=Application"] + lines.append("Name=Waydroid") + lines.append("Exec=waydroid show-full-ui") + lines.append("Icon=" + tools.config.tools_src + "/data/AppIcon.png") + desktop_file = open(desktop_file_path, "w") + for line in lines: + desktop_file.write(line + "\n") + desktop_file.close() + os.chmod(desktop_file_path, 0o755) + + def userUnlocked(uid): + logging.info("Android with user {} is ready".format(uid)) + session_cfg = tools.config.load_session() + args.waydroid_data = session_cfg["session"]["waydroid_data"] + args.host_user = session_cfg["session"]["host_user"] + + platformService = IPlatform.get_service(args) + if platformService: + appsList = platformService.getAppsInfo() + for app in appsList: + makeDesktopFile(app) + multiwin = platformService.getprop("persist.waydroid.multi_windows", "false") + if multiwin == "false": + makeWaydroidDesktopFile() + else: + desktop_file_path = args.host_user + \ + "/.local/share/applications/Waydroid.desktop" + if os.path.isfile(desktop_file_path): + os.remove(desktop_file_path) + + def packageStateChanged(mode, packageName, uid): + platformService = IPlatform.get_service(args) + if platformService: + appInfo = platformService.getAppInfo(packageName) + desktop_file_path = args.host_user + \ + "/.local/share/applications/" + packageName + ".desktop" + if mode == 0: + # Package added + makeDesktopFile(appInfo) + elif mode == 1: + if os.path.isfile(desktop_file_path): + os.remove(desktop_file_path) + else: + if os.path.isfile(desktop_file_path): + if makeDesktopFile(appInfo) == -1: + os.remove(desktop_file_path) + + def service_thread(): + IUserMonitor.add_service(args, userUnlocked, packageStateChanged) + + args.user_manager = threading.Thread(target=service_thread) + args.user_manager.start() + +def stop(args): + try: + if args.userMonitorLoop: + args.userMonitorLoop.quit() + except AttributeError: + logging.debug("UserMonitor service is not even started") diff --git a/waydroid.py b/waydroid.py new file mode 100755 index 0000000..a79b015 --- /dev/null +++ b/waydroid.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python3 +# Copyright 2021 Oliver Smith +# SPDX-License-Identifier: GPL-3.0-or-later +# PYTHON_ARGCOMPLETE_OK +import sys +import tools + +if __name__ == "__main__": + sys.exit(tools.main()) -- 2.47.3