mirror of
https://github.com/waydroid/waydroid.git
synced 2026-04-21 15:37:05 -04:00
297 lines
10 KiB
Python
297 lines
10 KiB
Python
# Copyright 2021 Erfan Abdi
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
import logging
|
|
import os
|
|
import glob
|
|
import signal
|
|
import tools.config
|
|
from contextlib import suppress
|
|
from shutil import which
|
|
from tools import helpers
|
|
from tools import services
|
|
from tools import actions
|
|
import dbus
|
|
import dbus.service
|
|
import dbus.exceptions
|
|
from gi.repository import GLib
|
|
|
|
class DbusContainerManager(dbus.service.Object):
|
|
def __init__(self, looper, bus, object_path, args):
|
|
self.args = args
|
|
self.looper = looper
|
|
dbus.service.Object.__init__(self, bus, object_path)
|
|
|
|
@helpers.logging.log_exceptions
|
|
@dbus.service.method("id.waydro.ContainerManager", in_signature='a{ss}', out_signature='', sender_keyword="sender", connection_keyword="conn")
|
|
def Start(self, session, sender, conn):
|
|
dbus_info = dbus.Interface(conn.get_object("org.freedesktop.DBus", "/org/freedesktop/DBus/Bus", False), "org.freedesktop.DBus")
|
|
uid = dbus_info.GetConnectionUnixUser(sender)
|
|
if str(uid) not in ["0", session["user_id"]]:
|
|
raise RuntimeError("Cannot start a session on behalf of another user")
|
|
pid = dbus_info.GetConnectionUnixProcessID(sender)
|
|
if str(uid) != "0" and str(pid) != session["pid"]:
|
|
raise RuntimeError("Invalid session pid")
|
|
do_start(self.args, session)
|
|
|
|
@helpers.logging.log_exceptions
|
|
@dbus.service.method("id.waydro.ContainerManager", in_signature='b', out_signature='')
|
|
def Stop(self, quit_session):
|
|
stop(self.args, quit_session)
|
|
|
|
@helpers.logging.log_exceptions
|
|
@dbus.service.method("id.waydro.ContainerManager", in_signature='', out_signature='')
|
|
def Freeze(self):
|
|
if not actions.initializer.is_initialized(self.args):
|
|
raise RuntimeError("Waydroid is not initialized")
|
|
freeze(self.args)
|
|
|
|
@helpers.logging.log_exceptions
|
|
@dbus.service.method("id.waydro.ContainerManager", in_signature='', out_signature='')
|
|
def Unfreeze(self):
|
|
if not actions.initializer.is_initialized(self.args):
|
|
raise RuntimeError("Waydroid is not initialized")
|
|
unfreeze(self.args)
|
|
|
|
@helpers.logging.log_exceptions
|
|
@dbus.service.method("id.waydro.ContainerManager", in_signature='', out_signature='a{ss}')
|
|
def GetSession(self):
|
|
if not actions.initializer.is_initialized(self.args):
|
|
raise RuntimeError("Waydroid is not initialized")
|
|
try:
|
|
session = self.args.session
|
|
session["state"] = helpers.lxc.status(self.args)
|
|
return session
|
|
except AttributeError:
|
|
return {}
|
|
|
|
def set_permissions(args, perm_list=None, mode="777"):
|
|
def chmod(path, mode):
|
|
if os.path.exists(path):
|
|
command = ["chmod", mode, "-R", path]
|
|
tools.helpers.run.user(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",
|
|
|
|
# Graphics
|
|
"/dev/graphics",
|
|
"/dev/pvr_sync",
|
|
"/dev/ion",
|
|
]
|
|
|
|
# DRM render nodes
|
|
perm_list.extend(glob.glob("/dev/dri/renderD*"))
|
|
# Framebuffers
|
|
perm_list.extend(glob.glob("/dev/fb*"))
|
|
# Videos
|
|
perm_list.extend(glob.glob("/dev/video*"))
|
|
# DMA-BUF Heaps
|
|
perm_list.extend(glob.glob("/dev/dma_heap/*"))
|
|
|
|
for path in perm_list:
|
|
chmod(path, mode)
|
|
|
|
def start(args):
|
|
mainloop = GLib.MainLoop()
|
|
|
|
def sigint_handler(data):
|
|
with suppress(Exception):
|
|
stop(args)
|
|
mainloop.quit()
|
|
|
|
GLib.unix_signal_add(GLib.PRIORITY_HIGH, signal.SIGINT, sigint_handler, None)
|
|
GLib.unix_signal_add(GLib.PRIORITY_HIGH, signal.SIGTERM, sigint_handler, None)
|
|
|
|
initializer = actions.initializer.DbusInitializer(mainloop, dbus.SystemBus(), '/Initializer', args)
|
|
_container_manager = DbusContainerManager(mainloop, dbus.SystemBus(), '/ContainerManager', args)
|
|
|
|
try:
|
|
_name = dbus.service.BusName("id.waydro.Container", dbus.SystemBus(), do_not_queue=True)
|
|
except dbus.exceptions.NameExistsException:
|
|
logging.error("Container service is already running")
|
|
return
|
|
|
|
mainloop.run()
|
|
|
|
if initializer.worker_thread is not None:
|
|
initializer.worker_thread.kill()
|
|
initializer.worker_thread.join()
|
|
|
|
prepared_drivers = False
|
|
def prepare_drivers_once(args):
|
|
global prepared_drivers
|
|
if prepared_drivers:
|
|
return
|
|
|
|
# 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")
|
|
helpers.drivers.probeAshmemDriver(args)
|
|
helpers.drivers.loadBinderNodes(args)
|
|
set_permissions(args, [
|
|
"/dev/" + args.BINDER_DRIVER,
|
|
"/dev/" + args.VNDBINDER_DRIVER,
|
|
"/dev/" + args.HWBINDER_DRIVER
|
|
], "666")
|
|
prepared_drivers = True
|
|
|
|
def do_start(args, session):
|
|
if not actions.initializer.is_initialized(args):
|
|
raise RuntimeError("Waydroid is not initialized")
|
|
|
|
if "session" in args:
|
|
raise RuntimeError("Already tracking a session")
|
|
|
|
prepare_drivers_once(args)
|
|
|
|
logging.info("Starting up container for a new session")
|
|
|
|
# Networking
|
|
command = [tools.config.tools_src +
|
|
"/data/scripts/waydroid-net.sh", "start"]
|
|
tools.helpers.run.user(args, command)
|
|
|
|
# Sensors
|
|
if which("waydroid-sensord"):
|
|
tools.helpers.run.user(
|
|
args, ["waydroid-sensord", "/dev/" + args.HWBINDER_DRIVER], output="background")
|
|
|
|
# Cgroup hacks
|
|
if which("start"):
|
|
command = ["start", "cgroup-lite"]
|
|
tools.helpers.run.user(args, command, check=False)
|
|
|
|
# Keep schedtune around in case nesting is supported
|
|
if os.path.ismount("/sys/fs/cgroup/schedtune"):
|
|
try:
|
|
os.mkdir("/sys/fs/cgroup/schedtune/probe0")
|
|
os.mkdir("/sys/fs/cgroup/schedtune/probe0/probe1")
|
|
except OSError:
|
|
command = ["umount", "-l", "/sys/fs/cgroup/schedtune"]
|
|
tools.helpers.run.user(args, command, check=False)
|
|
finally:
|
|
if os.path.exists("/sys/fs/cgroup/schedtune/probe0/probe1"):
|
|
os.rmdir("/sys/fs/cgroup/schedtune/probe0/probe1")
|
|
if os.path.exists("/sys/fs/cgroup/schedtune/probe0"):
|
|
os.rmdir("/sys/fs/cgroup/schedtune/probe0")
|
|
|
|
#TODO: remove NFC hacks
|
|
if which("stop"):
|
|
command = ["stop", "nfcd"]
|
|
tools.helpers.run.user(args, command, check=False)
|
|
elif which("systemctl") and (tools.helpers.run.user(args, ["systemctl", "is-active", "-q", "nfcd"], check=False) == 0):
|
|
command = ["systemctl", "stop", "nfcd"]
|
|
tools.helpers.run.user(args, command, check=False)
|
|
|
|
# Set permissions
|
|
set_permissions(args)
|
|
|
|
# Create session-specific LXC config file
|
|
helpers.lxc.generate_session_lxc_config(args, session)
|
|
# Backwards compatibility
|
|
with open(tools.config.defaults["lxc"] + "/waydroid/config") as f:
|
|
if "config_session" not in f.read():
|
|
helpers.mount.bind(args, session["waydroid_data"],
|
|
tools.config.defaults["data"])
|
|
|
|
# Mount rootfs
|
|
cfg = tools.config.load(args)
|
|
helpers.images.mount_rootfs(args, cfg["waydroid"]["images_path"], session)
|
|
|
|
helpers.protocol.set_aidl_version(args)
|
|
|
|
helpers.lxc.start(args)
|
|
services.hardware_manager.start(args)
|
|
|
|
args.session = session
|
|
|
|
def stop(args, quit_session=True):
|
|
if not actions.initializer.is_initialized(args):
|
|
raise RuntimeError("Waydroid is not initialized")
|
|
|
|
logging.info("Stopping container")
|
|
|
|
try:
|
|
services.hardware_manager.stop(args)
|
|
status = helpers.lxc.status(args)
|
|
if status != "STOPPED":
|
|
helpers.lxc.stop(args)
|
|
while helpers.lxc.status(args) != "STOPPED":
|
|
pass
|
|
|
|
# Networking
|
|
command = [tools.config.tools_src +
|
|
"/data/scripts/waydroid-net.sh", "stop"]
|
|
tools.helpers.run.user(args, command, check=False)
|
|
|
|
#TODO: remove NFC hacks
|
|
if which("start"):
|
|
command = ["start", "nfcd"]
|
|
tools.helpers.run.user(args, command, check=False)
|
|
elif which("systemctl") and (tools.helpers.run.user(args, ["systemctl", "is-enabled", "-q", "nfcd"], check=False) == 0):
|
|
command = ["systemctl", "start", "nfcd"]
|
|
tools.helpers.run.user(args, command, check=False)
|
|
|
|
# Sensors
|
|
if which("waydroid-sensord"):
|
|
command = ["pidof", "waydroid-sensord"]
|
|
pid = tools.helpers.run.user(args, command, check=False, output_return=True).strip()
|
|
if pid:
|
|
command = ["kill", "-9", pid]
|
|
tools.helpers.run.user(args, command, check=False)
|
|
|
|
# Umount rootfs
|
|
helpers.images.umount_rootfs(args)
|
|
|
|
# Backwards compatibility
|
|
with suppress(Exception):
|
|
helpers.mount.umount_all(args, tools.config.defaults["data"])
|
|
|
|
if "session" in args:
|
|
if quit_session:
|
|
logging.info("Terminating session because the container was stopped")
|
|
with suppress(OSError):
|
|
os.kill(int(args.session["pid"]), signal.SIGUSR1)
|
|
del args.session
|
|
except Exception as e:
|
|
logging.debug("Error while stopping container: %s", e)
|
|
|
|
def restart(args):
|
|
status = helpers.lxc.status(args)
|
|
if status == "RUNNING":
|
|
helpers.lxc.stop(args)
|
|
helpers.lxc.start(args)
|
|
else:
|
|
logging.error("WayDroid container is {}".format(status))
|
|
|
|
def freeze(args):
|
|
status = helpers.lxc.status(args)
|
|
if status == "RUNNING":
|
|
helpers.lxc.freeze(args)
|
|
while helpers.lxc.status(args) == "RUNNING":
|
|
pass
|
|
else:
|
|
logging.error("WayDroid container is {}".format(status))
|
|
|
|
def unfreeze(args):
|
|
status = helpers.lxc.status(args)
|
|
if status == "FROZEN":
|
|
helpers.lxc.unfreeze(args)
|
|
while helpers.lxc.status(args) == "FROZEN":
|
|
pass
|