from shutil import which
import logging
import os
-import time
import glob
import signal
-import sys
-import uuid
import tools.config
from tools import helpers
from tools import services
+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)
+
+ @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)
+
+ @dbus.service.method("id.waydro.ContainerManager", in_signature='b', out_signature='')
+ def Stop(self, quit_session):
+ stop(self.args, quit_session)
+
+ @dbus.service.method("id.waydro.ContainerManager", in_signature='', out_signature='')
+ def Freeze(self):
+ freeze(self.args)
+
+ @dbus.service.method("id.waydro.ContainerManager", in_signature='', out_signature='')
+ def Unfreeze(self):
+ unfreeze(self.args)
+
+ @dbus.service.method("id.waydro.ContainerManager", in_signature='', out_signature='a{ss}')
+ def GetSession(self):
+ try:
+ session = self.args.session
+ session["state"] = helpers.lxc.status(self.args)
+ return session
+ except AttributeError:
+ return {}
+
+def service(args, looper):
+ dbus_obj = DbusContainerManager(looper, dbus.SystemBus(), '/ContainerManager', args)
+ looper.run()
+
+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):
- def make_prop(full_props_path):
- def add_prop(key, cfg_key):
- value = session_cfg["session"][cfg_key]
- if value != "None":
- value = value.replace("/mnt/", "/mnt_extra/")
- props.append(key + "=" + value)
-
- 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.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/dri",
- "/dev/graphics",
- "/dev/pvr_sync",
- "/dev/ion",
- ]
-
- # Framebuffers
- perm_list.extend(glob.glob("/dev/fb*"))
- # Videos
- perm_list.extend(glob.glob("/dev/video*"))
-
- for path in perm_list:
- chmod(path, mode)
-
- def set_aidl_version():
- cfg = tools.config.load(args)
- android_api = 0
- try:
- android_api = int(helpers.props.file_get(args,
- tools.config.defaults["rootfs"] + "/system/build.prop",
- "ro.build.version.sdk"))
- except:
- logging.error("Failed to parse android version from system.img")
-
- if android_api < 28:
- binder_protocol = "aidl"
- sm_protocol = "aidl"
- elif android_api < 30:
- binder_protocol = "aidl2"
- sm_protocol = "aidl2"
- elif android_api < 31:
- binder_protocol = "aidl3"
- sm_protocol = "aidl3"
- else:
- binder_protocol = "aidl3"
- sm_protocol = "aidl4"
-
- cfg["waydroid"]["binder_protocol"] = binder_protocol
- cfg["waydroid"]["service_manager_protocol"] = sm_protocol
- tools.config.save(args, cfg)
-
- def signal_handler(sig, frame):
- services.hardware_manager.stop(args)
- stop(args)
- sys.exit(0)
+ 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
status = helpers.lxc.status(args)
if status == "STOPPED":
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.probeAshmemDriver(args)
helpers.drivers.loadBinderNodes(args)
- set_permissions([
+ set_permissions(args, [
"/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")
+ mainloop = GLib.MainLoop()
- # Networking
- command = [tools.config.tools_src +
- "/data/scripts/waydroid-net.sh", "start"]
- tools.helpers.run.user(args, command, check=False)
+ def sigint_handler(data):
+ stop(args)
+ mainloop.quit()
- # Sensors
- if which("waydroid-sensord"):
- tools.helpers.run.user(
- args, ["waydroid-sensord", "/dev/" + args.HWBINDER_DRIVER], output="background")
+ GLib.unix_signal_add(GLib.PRIORITY_HIGH, signal.SIGINT, sigint_handler, None)
+ GLib.unix_signal_add(GLib.PRIORITY_HIGH, signal.SIGTERM, sigint_handler, None)
+ service(args, mainloop)
+ else:
+ logging.error("WayDroid container is {}".format(status))
- # Mount rootfs
- helpers.images.mount_rootfs(args, cfg["waydroid"]["images_path"])
+def do_start(args, session):
+ if "session" in args:
+ raise RuntimeError("Already tracking a session")
- set_aidl_version()
+ logging.info("Starting up container for a new session")
- # Mount data
- helpers.mount.bind(args, session_cfg["session"]["waydroid_data"],
- tools.config.defaults["data"])
+ # Networking
+ command = [tools.config.tools_src +
+ "/data/scripts/waydroid-net.sh", "start"]
+ tools.helpers.run.user(args, command)
- # Cgroup hacks
- if which("start"):
- command = ["start", "cgroup-lite"]
- tools.helpers.run.user(args, command, check=False)
- command = ["umount", "-l", "/sys/fs/cgroup/schedtune"]
+ # 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)
- #TODO: remove NFC hacks
- if which("stop"):
- command = ["stop", "nfcd"]
+ # 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:
+ 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()
-
- helpers.lxc.start(args)
- session_cfg["session"]["state"] = helpers.lxc.status(args)
- timeout = 10
- while session_cfg["session"]["state"] != "RUNNING" and timeout > 0:
- session_cfg["session"]["state"] = helpers.lxc.status(args)
- logging.info(
- "waiting {} seconds for container to start...".format(timeout))
- timeout = timeout - 1
- time.sleep(1)
- if session_cfg["session"]["state"] != "RUNNING":
- raise OSError("container failed to start")
- tools.config.save_session(session_cfg)
-
- 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)
- services.hardware_manager.stop(args)
- start(args)
- else:
- logging.error("WayDroid container is {}".format(status))
+ # Set permissions
+ set_permissions(args)
-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)
+ # 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):
+ 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 +
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"):
# Umount rootfs
helpers.images.umount_rootfs(args)
- # Umount data
- helpers.mount.umount_all(args, tools.config.defaults["data"])
-
- else:
- logging.error("WayDroid container is {}".format(status))
+ # Backwards compatibility
+ try:
+ helpers.mount.umount_all(args, tools.config.defaults["data"])
+ except:
+ pass
+
+ if "session" in args:
+ if quit_session:
+ logging.info("Terminating session because the container was stopped")
+ try:
+ os.kill(int(args.session["pid"]), signal.SIGUSR1)
+ except:
+ pass
+ del args.session
+ except:
+ pass
def restart(args):
status = helpers.lxc.status(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)
+ while helpers.lxc.status(args) == "RUNNING":
+ pass
else:
logging.error("WayDroid container is {}".format(status))
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))
+ while helpers.lxc.status(args) == "FROZEN":
+ pass