1 # Copyright 2021 Erfan Abdi
2 # SPDX-License-Identifier: GPL-3.0-or-later
12 import tools
.helpers
.run
15 def get_lxc_version(args
):
16 if shutil
.which("lxc-info") is not None:
17 command
= ["lxc-info", "--version"]
18 version_str
= tools
.helpers
.run
.user(args
, command
, output_return
=True)
19 return int(version_str
[0])
24 def generate_nodes_lxc_config(args
):
25 def make_entry(src
, dist
=None, mnt_type
="none", options
="bind,create=file,optional 0 0", check
=True):
26 if check
and not os
.path
.exists(src
):
28 entry
= "lxc.mount.entry = "
33 entry
+= mnt_type
+ " "
40 make_entry("tmpfs", "dev", "tmpfs", "nosuid 0 0", False)
41 make_entry("/dev/zero")
42 make_entry("/dev/null")
43 make_entry("/dev/full")
44 make_entry("/dev/ashmem", check
=False)
45 make_entry("/dev/fuse")
46 make_entry("/dev/ion")
47 make_entry("/dev/char", options
="bind,create=dir,optional 0 0")
50 make_entry("/dev/kgsl-3d0")
51 make_entry("/dev/mali0")
52 make_entry("/dev/pvr_sync")
53 make_entry("/dev/pmsg0")
54 make_entry("/dev/dxg")
55 make_entry(tools
.helpers
.gpu
.getDriNode(args
), "dev/dri/renderD128")
57 for n
in glob
.glob("/dev/fb*"):
59 for n
in glob
.glob("/dev/graphics/fb*"):
61 for n
in glob
.glob("/dev/video*"):
65 make_entry("/dev/" + args
.BINDER_DRIVER
, "dev/binder", check
=False)
66 make_entry("/dev/" + args
.VNDBINDER_DRIVER
, "dev/vndbinder", check
=False)
67 make_entry("/dev/" + args
.HWBINDER_DRIVER
, "dev/hwbinder", check
=False)
69 if args
.vendor_type
!= "MAINLINE":
70 if not make_entry("/dev/hwbinder", "dev/host_hwbinder"):
71 raise OSError('Binder node "hwbinder" of host not found')
72 make_entry("/vendor", "vendor_extra", options
="bind,optional 0 0")
74 # Necessary device nodes for adb
75 make_entry("none", "dev/pts", "devpts", "defaults,mode=644,ptmxmode=666,create=dir 0 0", False)
76 make_entry("/dev/uhid")
78 # TUN/TAP device node for VPN
79 make_entry("/dev/net/tun", "dev/tun")
81 # Low memory killer sys node
82 make_entry("/sys/module/lowmemorykiller", options
="bind,create=dir,optional 0 0")
85 make_entry("tmpfs", "mnt", "tmpfs", "mode=0755,uid=0,gid=1000", False)
86 make_entry(tools
.config
.defaults
["data"], "data", options
="bind 0 0", check
=False)
88 # Mount host permissions
89 make_entry(tools
.config
.defaults
["host_perms"],
90 "vendor/etc/host-permissions", options
="bind,optional 0 0")
92 # Recursive mount /run to provide necessary host sockets
93 make_entry("/run", options
="rbind,create=dir 0 0")
95 # Necessary sw_sync node for HWC
96 make_entry("/dev/sw_sync")
97 make_entry("/sys/kernel/debug", options
="rbind,create=dir,optional 0 0")
100 make_entry("/sys/class/leds/vibrator",
101 options
="bind,create=dir,optional 0 0")
102 make_entry("/sys/devices/virtual/timed_output/vibrator",
103 options
="bind,create=dir,optional 0 0")
105 # Media dev nodes (for Mediatek)
106 make_entry("/dev/Vcodec")
107 make_entry("/dev/MTK_SMI")
108 make_entry("/dev/mdp_sync")
109 make_entry("/dev/mtk_cmdq")
112 make_entry("tmpfs", "mnt_extra", "tmpfs", "nodev 0 0", False)
113 make_entry("/mnt/wslg", "mnt_extra/wslg",
114 options
="rbind,create=dir,optional 0 0")
117 make_entry("tmpfs", "var", "tmpfs", "nodev 0 0", False)
118 make_entry("/var/run", options
="rbind,create=dir,optional 0 0")
121 make_entry("tmpfs", "tmp", "tmpfs", "nodev 0 0", False)
122 for n
in glob
.glob("/tmp/run-*"):
123 make_entry(n
, options
="rbind,create=dir,optional 0 0")
126 make_entry("/system/etc/libnfc-nci.conf", options
="bind,optional 0 0")
131 def set_lxc_config(args
):
132 lxc_path
= tools
.config
.defaults
["lxc"] + "/waydroid"
133 lxc_ver
= get_lxc_version(args
)
135 raise OSError("LXC is not installed")
136 config_paths
= tools
.config
.tools_src
+ "/data/configs/config_"
137 seccomp_profile
= tools
.config
.tools_src
+ "/data/configs/waydroid.seccomp"
138 apparmor_profiles
= [tools
.config
.tools_src
+ "/data/configs/" + "lxc-waydroid",tools
.config
.tools_src
+ "/data/configs/" + "android_app",tools
.config
.tools_src
+ "/data/configs/" + "adbd"]
139 apparmor_profile_dir
= "/etc/apparmor.d/"
141 config_snippets
= [ config_paths
+ "base" ]
142 # lxc v1 is a bit special because some options got renamed later
144 config_snippets
.append(config_paths
+ "1")
146 for ver
in range(2, 5):
147 snippet
= config_paths
+ str(ver
)
148 if lxc_ver
>= ver
and os
.path
.exists(snippet
):
149 config_snippets
.append(snippet
)
151 command
= ["mkdir", "-p", lxc_path
]
152 tools
.helpers
.run
.user(args
, command
)
153 command
= ["sh", "-c", "cat {} > \"{}\"".format(' '.join('"{0}"'.format(w
) for w
in config_snippets
), lxc_path
+ "/config")]
154 tools
.helpers
.run
.user(args
, command
)
155 command
= ["sed", "-i", "s/LXCARCH/{}/".format(platform
.machine()), lxc_path
+ "/config"]
156 tools
.helpers
.run
.user(args
, command
)
157 command
= ["cp", "-fpr", seccomp_profile
, lxc_path
+ "/waydroid.seccomp"]
158 tools
.helpers
.run
.user(args
, command
)
161 command
= ["cp", "-i", apparmor_profiles
[0], apparmor_profile_dir
+ "lxc/lxc-waydroid"]
162 tools
.helpers
.run
.user(args
, command
)
163 command
= ["apparmor_parser", "-r", apparmor_profile_dir
+ "lxc/lxc-waydroid"]
164 tools
.helpers
.run
.user(args
, command
)
165 command
= ["cp", "-i", apparmor_profiles
[1], apparmor_profile_dir
+ "android_app"]
166 tools
.helpers
.run
.user(args
, command
)
167 command
= ["apparmor_parser", "-r", apparmor_profile_dir
+ "android_app"]
168 tools
.helpers
.run
.user(args
, command
)
169 command
= ["cp", "-i", apparmor_profiles
[2], apparmor_profile_dir
+ "adbd"]
170 tools
.helpers
.run
.user(args
, command
)
171 command
= ["apparmor_parser", "-r", apparmor_profile_dir
+ "adbd"]
172 tools
.helpers
.run
.user(args
, command
)
174 logging
.warning("An error has occurred while installing AppArmor profiles. If profiles are not installed, or AppArmor is disabled or not supported on your system, then the container will run without AppArmor protection.")
176 nodes
= generate_nodes_lxc_config(args
)
177 config_nodes_tmp_path
= args
.work
+ "/config_nodes"
178 config_nodes
= open(config_nodes_tmp_path
, "w")
180 config_nodes
.write(node
+ "\n")
182 command
= ["mv", config_nodes_tmp_path
, lxc_path
]
183 tools
.helpers
.run
.user(args
, command
)
186 def make_base_props(args
):
187 def find_hal(hardware
):
189 "ro.hardware." + hardware
,
194 for p
in hardware_props
:
195 prop
= tools
.helpers
.props
.host_get(args
, p
)
197 for lib
in ["/odm/lib", "/odm/lib64", "/vendor/lib", "/vendor/lib64", "/system/lib", "/system/lib64"]:
198 hal_file
= lib
+ "/hw/" + hardware
+ "." + prop
+ ".so"
199 if os
.path
.isfile(hal_file
):
204 if args
.vendor_type
== "MAINLINE":
208 sm
= gbinder
.ServiceManager("/dev/hwbinder")
209 return intf
in sm
.list_sync()
215 if not os
.path
.exists("/dev/ashmem"):
216 props
.append("sys.use_memfd=true")
218 egl
= tools
.helpers
.props
.host_get(args
, "ro.hardware.egl")
219 dri
= tools
.helpers
.gpu
.getDriNode(args
)
221 gralloc
= find_hal("gralloc")
223 if find_hidl("android.hardware.graphics.allocator@4.0::IAllocator/default"):
232 props
.append("debug.stagefright.ccodec=0")
233 props
.append("ro.hardware.gralloc=" + gralloc
)
236 props
.append("ro.hardware.egl=" + egl
)
238 media_profiles
= tools
.helpers
.props
.host_get(args
, "media.settings.xml")
239 if media_profiles
!= "":
240 media_profiles
= media_profiles
.replace("vendor/", "vendor_extra/")
241 media_profiles
= media_profiles
.replace("odm/", "odm_extra/")
242 props
.append("media.settings.xml=" + media_profiles
)
244 ccodec
= tools
.helpers
.props
.host_get(args
, "debug.stagefright.ccodec")
246 props
.append("debug.stagefright.ccodec=" + ccodec
)
248 ext_library
= tools
.helpers
.props
.host_get(args
, "ro.vendor.extension_library")
249 if ext_library
!= "":
250 ext_library
= ext_library
.replace("vendor/", "vendor_extra/")
251 ext_library
= ext_library
.replace("odm/", "odm_extra/")
252 props
.append("ro.vendor.extension_library=" + ext_library
)
254 vulkan
= find_hal("vulkan")
255 if not vulkan
and dri
:
256 vulkan
= tools
.helpers
.gpu
.getVulkanDriver(args
, os
.path
.basename(dri
))
258 props
.append("ro.hardware.vulkan=" + vulkan
)
260 treble
= tools
.helpers
.props
.host_get(args
, "ro.treble.enabled")
262 camera
= find_hal("camera")
264 props
.append("ro.hardware.camera=" + camera
)
266 if args
.vendor_type
== "MAINLINE":
267 props
.append("ro.hardware.camera=v4l2")
269 opengles
= tools
.helpers
.props
.host_get(args
, "ro.opengles.version")
272 props
.append("ro.opengles.version=" + opengles
)
274 if args
.images_path
not in tools
.config
.defaults
["preinstalled_images_paths"]:
275 props
.append("waydroid.system_ota=" + args
.system_ota
)
276 props
.append("waydroid.vendor_ota=" + args
.vendor_ota
)
278 props
.append("waydroid.updater.disabled=true")
280 props
.append("waydroid.tools_version=" + tools
.config
.version
)
282 if args
.vendor_type
== "MAINLINE":
283 props
.append("ro.vndk.lite=true")
285 for product
in ["brand", "device", "manufacturer", "model", "name"]:
286 prop_product
= tools
.helpers
.props
.host_get(
287 args
, "ro.product.vendor." + product
)
288 if prop_product
!= "":
289 props
.append("ro.product.waydroid." + product
+ "=" + prop_product
)
291 if os
.path
.isfile("/proc/device-tree/" + product
):
292 with open("/proc/device-tree/" + product
) as f
:
293 f_value
= f
.read().strip().rstrip('\x00')
295 props
.append("ro.product.waydroid." +
296 product
+ "=" + f_value
)
298 prop_fp
= tools
.helpers
.props
.host_get(args
, "ro.vendor.build.fingerprint")
300 props
.append("ro.build.fingerprint=" + prop_fp
)
302 # now append/override with values in [properties] section of waydroid.cfg
303 cfg
= tools
.config
.load(args
)
304 for k
, v
in cfg
["properties"].items():
305 for idx
, elem
in enumerate(props
):
308 props
.append(k
+"="+v
)
310 base_props
= open(args
.work
+ "/waydroid_base.prop", "w")
312 base_props
.write(prop
+ "\n")
316 def setup_host_perms(args
):
317 if not os
.path
.exists(tools
.config
.defaults
["host_perms"]):
318 os
.mkdir(tools
.config
.defaults
["host_perms"])
320 treble
= tools
.helpers
.props
.host_get(args
, "ro.treble.enabled")
324 sku
= tools
.helpers
.props
.host_get(args
, "ro.boot.product.hardware.sku")
327 glob
.glob("/vendor/etc/permissions/android.hardware.nfc.*"))
328 if os
.path
.exists("/vendor/etc/permissions/android.hardware.consumerir.xml"):
329 copy_list
.append("/vendor/etc/permissions/android.hardware.consumerir.xml")
331 glob
.glob("/odm/etc/permissions/android.hardware.nfc.*"))
332 if os
.path
.exists("/odm/etc/permissions/android.hardware.consumerir.xml"):
333 copy_list
.append("/odm/etc/permissions/android.hardware.consumerir.xml")
336 glob
.glob("/odm/etc/permissions/sku_{}/android.hardware.nfc.*".format(sku
)))
337 if os
.path
.exists("/odm/etc/permissions/sku_{}/android.hardware.consumerir.xml".format(sku
)):
339 "/odm/etc/permissions/sku_{}/android.hardware.consumerir.xml".format(sku
))
341 for filename
in copy_list
:
342 shutil
.copy(filename
, tools
.config
.defaults
["host_perms"])
345 command
= ["lxc-info", "-P", tools
.config
.defaults
["lxc"], "-n", "waydroid", "-sH"]
346 out
= subprocess
.run(command
, stdout
=subprocess
.PIPE
).stdout
.decode('utf-8').strip()
347 os
.chmod(args
.log
, 0o666)
351 command
= ["lxc-start", "-P", tools
.config
.defaults
["lxc"],
352 "-F", "-n", "waydroid", "--", "/init"]
353 tools
.helpers
.run
.user(args
, command
, output
="background")
356 command
= ["lxc-stop", "-P",
357 tools
.config
.defaults
["lxc"], "-n", "waydroid", "-k"]
358 tools
.helpers
.run
.user(args
, command
)
361 command
= ["lxc-freeze", "-P", tools
.config
.defaults
["lxc"], "-n", "waydroid"]
362 tools
.helpers
.run
.user(args
, command
)
365 command
= ["lxc-unfreeze", "-P",
366 tools
.config
.defaults
["lxc"], "-n", "waydroid"]
367 tools
.helpers
.run
.user(args
, command
)
370 if status(args
) != "RUNNING":
371 logging
.error("WayDroid container is {}".format(status(args
)))
373 command
= ["lxc-attach", "-P", tools
.config
.defaults
["lxc"],
374 "-n", "waydroid", "--"]
376 command
.append(args
.COMMAND
)
378 command
.append("/system/bin/sh")
379 subprocess
.run(command
, env
={"PATH": os.environ['PATH'] + ":/system/bin:/vendor/bin"}
)
382 if status(args
) != "RUNNING":
383 logging
.error("WayDroid container is {}".format(status(args
)))
385 command
= ["lxc-attach", "-P", tools
.config
.defaults
["lxc"],
386 "-n", "waydroid", "--", "/system/bin/logcat"]
387 subprocess
.run(command
)