diff --git a/waydroid_extras.py b/waydroid_extras.py index fabd1aa..2360328 100644 --- a/waydroid_extras.py +++ b/waydroid_extras.py @@ -18,6 +18,24 @@ def stop_waydroid(): os.system("umount /var/lib/waydroid/rootfs/vendor &> /dev/null") os.system("umount /var/lib/waydroid/rootfs &> /dev/null") +def download_file(url, f_name): + md5 = "" + response = requests.get(url, stream=True) + total_size_in_bytes = int(response.headers.get('content-length', 0)) + block_size = 1024 # 1 Kibibyte + progress_bar = tqdm(total=total_size_in_bytes, unit='iB', unit_scale=True) + with open(f_name, 'wb') as file: + for data in response.iter_content(block_size): + progress_bar.update(len(data)) + file.write(data) + progress_bar.close() + with open(f_name, "rb") as f: + bytes = f.read() + md5 = hashlib.md5(bytes).hexdigest() + if total_size_in_bytes != 0 and progress_bar.n != total_size_in_bytes: + print("==> Something went wrong while downloading") + sys.exit(1) + return md5 def get_image_dir(): # Read waydroid config to get image location @@ -33,6 +51,20 @@ def get_image_dir(): sys.exit(1) return cfg["waydroid"]["images_path"] +def mount_image(image, mount_point): + print("==> Unmounting .. ") + try: + subprocess.check_output(["losetup", "-D"], stderr=subprocess.STDOUT) + subprocess.check_output(["umount", mount_point], stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + print("==> Warning: umount failed.. {} ".format(str(e.output.decode()))) + if not os.path.exists(mount_point): + os.makedirs(mount_point) + try: + subprocess.check_output(["mount", "-o", "rw", image, mount_point], stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + print("==> Failed to mount system image... ! {}".format(str(e.output.decode()))) + sys.exit(1) def resize_img(img_file, size): # Resize the system image @@ -58,8 +90,8 @@ def install_gapps(): if platform.machine() not in dl_links.keys(): print("==> Unsupported architecture '{}' .. ".format(platform.machine())) sys.exit(1) - opengapps_dl_link = dl_links[platform.machine()][0] - dl_fname = "open_gapps.zip" + google_apps_dl_link = dl_links[platform.machine()][0] + dl_file_name = "open_gapps.zip" act_md5 = dl_links[platform.machine()][1] loc_md5 = "" sys_image_mount = "/tmp/waydroidimage" @@ -74,14 +106,13 @@ def install_gapps(): "setupwizardtablet-x86_64.tar.lz" ] - if not os.path.exists(extract_to): os.makedirs(extract_to) if not os.path.exists(os.path.join(extract_to, "appunpack")): os.makedirs(os.path.join(extract_to, "appunpack")) - if os.path.isfile("/tmp/"+dl_fname): - with open("/tmp/"+dl_fname,"rb") as f: + if os.path.isfile("/tmp/"+dl_file_name): + with open("/tmp/"+dl_file_name,"rb") as f: bytes = f.read() loc_md5 = hashlib.md5(bytes).hexdigest() print("==> Excepted hash: {} | File hash: {}".format(act_md5, loc_md5)) @@ -93,54 +124,22 @@ def install_gapps(): sys.exit(1) print("==> Found system image: "+system_img) - - # Clear mount point - print("==> Unmounting .. ") - try: - subprocess.check_output(["losetup", "-D"], stderr=subprocess.STDOUT) - subprocess.check_output(["umount", sys_image_mount], stderr=subprocess.STDOUT) - except subprocess.CalledProcessError as e: - print("==> Warning: umount failed.. {} ".format(str(e.output.decode()))) - - # Resize image to get some free space resize_img(system_img, "5G") - # Mount the system image - if not os.path.exists(sys_image_mount): - os.makedirs(sys_image_mount) - try: - mount = subprocess.check_output(["mount", "-o", "rw", system_img, sys_image_mount], stderr=subprocess.STDOUT) - except subprocess.CalledProcessError as e: - print("==> Failed to mount system image... ! {}".format(str(e.output.decode()))) - sys.exit(1) + mount_image(system_img, sys_image_mount) # Download the file if hash mismatches or if file does not exist - while not os.path.isfile("/tmp/"+dl_fname) or loc_md5 != act_md5: - if os.path.isfile("/tmp/"+dl_fname): - os.remove("/tmp/"+dl_fname) + while not os.path.isfile("/tmp/"+dl_file_name) or loc_md5 != act_md5: + if os.path.isfile("/tmp/"+dl_file_name): + os.remove("/tmp/"+dl_file_name) print("==> OpenGapps zip not downloaded or hash mismatches, downloading now .....") - response = requests.get(opengapps_dl_link, stream=True) - total_size_in_bytes= int(response.headers.get('content-length', 0)) - block_size = 1024 #1 Kibibyte - progress_bar = tqdm(total=total_size_in_bytes, unit='iB', unit_scale=True) - with open('/tmp/'+dl_fname, 'wb') as file: - for data in response.iter_content(block_size): - progress_bar.update(len(data)) - file.write(data) - progress_bar.close() - with open("/tmp/"+dl_fname,"rb") as f: - bytes = f.read() - loc_md5 = hashlib.md5(bytes).hexdigest() - if total_size_in_bytes != 0 and progress_bar.n != total_size_in_bytes: - print("==> Something went wrong while downloading") - sys.exit(1) - + loc_md5 = download_file(google_apps_dl_link, '/tmp/'+dl_file_name) # Extract opengapps print("==> Extracting opengapps...") - with zipfile.ZipFile("/tmp/"+dl_fname) as z: + with zipfile.ZipFile("/tmp/"+dl_file_name) as z: z.extractall(extract_to) # Now copy the files @@ -169,7 +168,7 @@ def install_gapps(): subprocess.check_output(["umount", sys_image_mount], stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: print("==> Warning: umount failed.. {} ".format(str(e.output.decode()))) - print("==> OpenGapps installation complete try reiniting/restarting waydroid") + print("==> OpenGapps installation complete try re init /restarting waydroid") print("==> Please note, google apps wont be usable without device registration !, Use --get-android-id for registration instructions") @@ -191,7 +190,7 @@ def get_android_id(): def install_ndk(): sys_image_mount = "/tmp/waydroidimage" ndk_zip_url = "https://github.com/newbit1/libndk_translation_Module/archive/c6077f3398172c64f55aad7aab0e55fad9110cf3.zip" - dl_fname = "libndktranslation.zip" + dl_file_name = "libndktranslation.zip" extract_to = "/tmp/libndkunpack" act_md5 = "5e8e0cbde0e672fdc2b47f20a87472fd" loc_md5 = "" @@ -215,65 +214,33 @@ on property:ro.enable.native.bridge.exec=1 cat /system/etc/binfmt_misc/arm64_exe >> /proc/sys/fs/binfmt_misc/register cat /system/etc/binfmt_misc/arm64_dyn >> /proc/sys/fs/binfmt_misc/register """ - - system_img = os.path.join(get_image_dir(), "system.img") - if os.path.isfile("/tmp/"+dl_fname): - with open("/tmp/"+dl_fname,"rb") as f: + if os.path.isfile("/tmp/"+dl_file_name): + with open("/tmp/"+dl_file_name,"rb") as f: bytes = f.read() loc_md5 = hashlib.md5(bytes).hexdigest() + + system_img = os.path.join(get_image_dir(), "system.img") if not os.path.isfile(system_img): print("The system image path '{}' from waydroid config is not valid !".format(system_img)) sys.exit(1) print("==> Found system image: "+system_img) - - # Clear mount point - print("==> Unmounting .. ") - try: - subprocess.check_output(["losetup", "-D"], stderr=subprocess.STDOUT) - subprocess.check_output(["umount", sys_image_mount], stderr=subprocess.STDOUT) - except subprocess.CalledProcessError as e: - print("==> Warning: umount failed.. {} ".format(str(e.output.decode()))) - - system_img = os.path.join(get_image_dir(), "system.img") - # Resize rootfs resize_img(system_img, "6G") - # Mount the system image - if not os.path.exists(sys_image_mount): - os.makedirs(sys_image_mount) - try: - mount = subprocess.check_output(["mount", "-o", "rw", system_img, sys_image_mount], stderr=subprocess.STDOUT) - except subprocess.CalledProcessError as e: - print("==> Failed to mount system image... ! {}".format(str(e.output.decode()))) - sys.exit(1) + mount_image(system_img, sys_image_mount) # Download the file if hash mismatches or if file does not exist - while not os.path.isfile("/tmp/"+dl_fname) or loc_md5 != act_md5: - if os.path.isfile("/tmp/"+dl_fname): - os.remove("/tmp/"+dl_fname) + while not os.path.isfile("/tmp/"+dl_file_name) or loc_md5 != act_md5: + if os.path.isfile("/tmp/"+dl_file_name): + os.remove("/tmp/"+dl_file_name) print("==> NDK Translation zip not downloaded or hash mismatches, downloading now .....") - response = requests.get(ndk_zip_url, stream=True) - total_size_in_bytes= int(response.headers.get('content-length', 0)) - block_size = 1024 #1 Kibibyte - progress_bar = tqdm(total=total_size_in_bytes, unit='iB', unit_scale=True) - with open('/tmp/'+dl_fname, 'wb') as file: - for data in response.iter_content(block_size): - progress_bar.update(len(data)) - file.write(data) - progress_bar.close() - with open("/tmp/"+dl_fname,"rb") as f: - bytes = f.read() - loc_md5 = hashlib.md5(bytes).hexdigest() - if total_size_in_bytes != 0 and progress_bar.n != total_size_in_bytes: - print("==> Something went wrong while downloading") - sys.exit(1) + loc_md5 = download_file(ndk_zip_url, '/tmp/'+dl_file_name) - # Extract opengapps + # Extract ndk files print("==> Extracting archive...") - with zipfile.ZipFile("/tmp/"+dl_fname) as z: + with zipfile.ZipFile("/tmp/"+dl_file_name) as z: z.extractall(extract_to) # Copy library file @@ -283,19 +250,19 @@ on property:ro.enable.native.bridge.exec=1 # Add entries to build.prop print("==> Adding arch in build.prop") with open(os.path.join(sys_image_mount, "system", "build.prop"), "r") as propfile: - propcontent = propfile.read() + prop_content = propfile.read() for key in apply_props: - if key not in propcontent: - propcontent = propcontent+"\n{key}={value}".format(key=key, value=apply_props[key]) + if key not in prop_content: + prop_content = prop_content+"\n{key}={value}".format(key=key, value=apply_props[key]) else: p = re.compile(r"^{key}=.*$".format(key=key), re.M) - propcontent = re.sub(p, "{key}={value}".format(key=key, value=apply_props[key]), propcontent) + prop_content = re.sub(p, "{key}={value}".format(key=key, value=apply_props[key]), prop_content) with open(os.path.join(sys_image_mount, "system", "build.prop"), "w") as propfile: - propfile.write(propcontent) + propfile.write(prop_content) # Add entry to init.rc - print("==> Addig entry to init.rc") + print("==> Adding entry to init.rc") with open(os.path.join(sys_image_mount, "init.rc"), "r") as initfile: initcontent = initfile.read() if init_rc_component not in initcontent: @@ -303,22 +270,143 @@ on property:ro.enable.native.bridge.exec=1 with open(os.path.join(sys_image_mount, "init.rc"), "w") as initfile: initfile.write(initcontent) - # Apply permissions - #print("==> Setting permissions") - #for dirname in ["bin", "etc", "lib", "lib64"]: - # os.system() - # Unmount and exit print("==> Unmounting .. ") try: - pass - #subprocess.check_output(["umount", sys_image_mount], stderr=subprocess.STDOUT) + subprocess.check_output(["umount", sys_image_mount], stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: print("==> Warning: umount failed.. {} ".format(str(e.output.decode()))) - print("==> libndk translation installed ! anbox service to apply changes !") + print("==> libndk translation installed ! Restart waydroid service to apply changes !") + +def install_magisk(): + dl_link = "https://github.com/topjohnwu/Magisk/releases/download/v20.4/Magisk-v20.4.zip" + busybox_dl_link = "https://github.com/Gnurou/busybox-android/raw/master/busybox-android" + busybox_dl_file_name = "busybox-android" + dl_file_name = "magisk.zip" + extract_to = "/tmp/magisk_unpack" + act_md5 = "9503fc692e03d60cb8897ff2753c193f" + busybox_act_md5 = "2e43cc2e8f44b83f9029a6561ce5d8b9" + sys_image_mount = "/tmp/waydroidimage" + loc_md5 = "" + busybox_loc_md5 = "" + magisk_init = """#!/system/bin/sh +mkdir -p /data/adb/magisk +cp /busybox /data/adb/magisk/busybox +cp /util_functions.sh /data/adb/magisk/util_functions.sh +cp /boot_patch.sh /data/adb/magisk/boot_patch.sh +cp /addon.d.sh /data/adb/magisk/addon.d.sh +magisk -c >&2 +ln -sf /data /sbin/.magisk/mirror/data +ln -sf /vendor /sbin/.magisk/mirror/vendor +magisk --post-fs-data +sleep 1 +magisk --service +magisk --boot-complete + """ + init_rc_component = """on property:dev.bootcomplete=1 + start magisk + +service magisk /system/bin/init-magisk.sh + class main + user root + group root + oneshot + """ + if os.path.isfile("/tmp/"+dl_file_name): + with open("/tmp/"+dl_file_name,"rb") as f: + bytes = f.read() + loc_md5 = hashlib.md5(bytes).hexdigest() + + if os.path.isfile("/tmp/"+busybox_dl_file_name): + with open("/tmp/"+busybox_dl_file_name,"rb") as f: + bytes = f.read() + busybox_loc_md5 = hashlib.md5(bytes).hexdigest() + + + system_img = os.path.join(get_image_dir(), "system.img") + if not os.path.isfile(system_img): + print("The system image path '{}' from waydroid config is not valid !".format(system_img)) + sys.exit(1) + print("==> Found system image: " + system_img) + + # Resize rootfs + resize_img(system_img, "6G") + # Mount the system image + mount_image(system_img, sys_image_mount) + + # Download magisk + while not os.path.isfile("/tmp/"+dl_file_name) or loc_md5 != act_md5: + if os.path.isfile("/tmp/"+dl_file_name): + os.remove("/tmp/"+dl_file_name) + print("==> Magisk zip not downloaded or hash mismatches, downloading now .....") + loc_md5 = download_file(dl_link, '/tmp/'+dl_file_name) + + # Download busybox android binary + while not os.path.isfile("/tmp/"+busybox_dl_file_name) or busybox_loc_md5 != busybox_act_md5: + if os.path.isfile("/tmp/"+busybox_dl_file_name): + os.remove("/tmp/"+busybox_dl_file_name) + print("==> BusyBox binary not downloaded or hash mismatches, downloading now .....") + busybox_loc_md5 = download_file(busybox_dl_link, '/tmp/'+busybox_dl_file_name) + + # Extract magisk files + print("==> Extracting archive...") + with zipfile.ZipFile("/tmp/" + dl_file_name) as z: + z.extractall(extract_to) + + # Now setup and install magisk binary and app + print("==> Installing magisk now ...") + with open(os.path.join(sys_image_mount, "system", "bin", "init-magisk.sh"), "w") as imf: + imf.write(magisk_init) + os.system("chmod 755 {}".format(os.path.join(sys_image_mount, "system", "bin", "init-magisk.sh"))) + arch_dir = "x86" if platform.machine() == "x86" or "x86_64" else "arm" + arch = "" if platform.machine() == "x86" or "arm" else "64" + shutil.copyfile(os.path.join(extract_to, arch_dir, "magiskinit{arch}".format(arch=arch)), + os.path.join(sys_image_mount, "sbin", "magiskinit")) + os.system("chmod 755 {}".format(os.path.join(sys_image_mount, "sbin", "magiskinit"))) + + # Copy busybox + print("==> Installing BusyBox") + shutil.copyfile(os.path.join("/tmp", busybox_dl_file_name), os.path.join(sys_image_mount, "busybox")) + os.system("chmod 755 {}".format(os.path.join(sys_image_mount, "busybox"))) + + # Copy files from common directory + for file in ["util_functions.sh", "boot_patch.sh", "addon.d.sh"]: + shutil.copyfile(os.path.join(extract_to, "common", file), + os.path.join(sys_image_mount, file)) + os.system("chmod 755 {}".format(os.path.join(sys_image_mount, file))) + + # Create symlinks + print("==> Creating symlinks") + os.system("cd {root}/sbin && ln -s magiskinit magisk >> /dev/null 2>&1".format(root=sys_image_mount)) + print("==> magiskinit -> magisk") + os.system("cd {root}/sbin && ln -s magiskinit magiskpolicy >> /dev/null 2>&1".format(root=sys_image_mount)) + print("==> magiskinit -> magiskpolicy") + # Add entry to init.rc + print("==> Adding entry to init.rc") + with open(os.path.join(sys_image_mount, "init.rc"), "r") as initfile: + initcontent = initfile.read() + if init_rc_component not in initcontent: + initcontent=initcontent+init_rc_component + with open(os.path.join(sys_image_mount, "init.rc"), "w") as initfile: + initfile.write(initcontent) + + # Install Magisk apk + if not os.path.exists(os.path.join(sys_image_mount, "system", "priv-app", "Magisk")): + os.makedirs(os.path.join(sys_image_mount, "system", "priv-app", "Magisk")) + shutil.copyfile(os.path.join(extract_to, "common", "magisk.apk"), + os.path.join(sys_image_mount, "system", "priv-app", "Magisk", "magisk.apk")) + + # Unmount and exit + print("==> Unmounting .. ") + try: + subprocess.check_output(["umount", sys_image_mount], stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + print("==> Warning: umount failed.. {} ".format(str(e.output.decode()))) + + print("==> Magisk was installed ! Restart waydroid service to apply changes !") def main(): about = """s WayDroid Helper script v0.3 @@ -337,6 +425,9 @@ def main(): parser.add_argument('-i', '--get-android-id', dest='getid', help='Displays your android id for manual registration', action='store_true') + parser.add_argument('-m', '--install-magisk', dest='magisk', + help='Attempts to install Magisk ( Bootless )', + action='store_true') args = parser.parse_args() if args.install: @@ -347,7 +438,8 @@ def main(): install_ndk() elif args.getid: get_android_id() - + elif args.magisk: + install_magisk() if __name__ == "__main__": main()