├── .github ├── FUNDING.yml └── workflows │ └── magisk.yml ├── LICENSE ├── README.md ├── arm64 ├── gapps │ ├── priv-app │ │ └── SetupWizard │ │ │ └── SetupWizard.apk │ └── product │ │ └── overlay │ │ └── GoogleWebViewOverlay.apk └── system │ └── system │ └── priv-app │ └── ExternalStorageProvider │ └── ExternalStorageProvider.apk ├── wine ├── .cache │ └── winetricks │ │ └── msxml6 │ │ └── msxml6-KB973686-enu-amd64.exe └── makepri.exe └── x64 ├── gapps ├── priv-app │ └── SetupWizard │ │ └── SetupWizard.apk └── product │ └── overlay │ └── GoogleWebViewOverlay.apk └── system └── system └── priv-app └── ExternalStorageProvider └── ExternalStorageProvider.apk /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [yujincheng08] 2 | -------------------------------------------------------------------------------- /.github/workflows/magisk.yml: -------------------------------------------------------------------------------- 1 | name: Build WSA 2 | on: 3 | push: 4 | pull_request: 5 | workflow_dispatch: 6 | inputs: 7 | magisk_apk: 8 | description: "Download link to magisk apk." 9 | required: true 10 | default: "https://raw.githubusercontent.com/topjohnwu/magisk-files/canary/app-debug.apk" 11 | gapps_variant: 12 | description: "Variants of gapps. Should be: [none, aroma, super, stock, full, mini, micro, nano, pico, tvstock, tvmini]" 13 | required: true 14 | default: "none" 15 | root_sol: 16 | description: "Root soluction. Should be: [magisk, none]" 17 | required: true 18 | default: "magisk" 19 | 20 | jobs: 21 | build: 22 | runs-on: ubuntu-20.04 23 | strategy: 24 | matrix: 25 | arch: [x64, arm64] 26 | steps: 27 | - name: Dependencies 28 | run: | 29 | pip3 install beautifulsoup4 lxml 30 | sudo apt-get update && sudo apt-get install setools lzip qemu-utils wine winetricks 31 | wget -qO- "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/archive/$GITHUB_REF.tar.gz" | sudo tar --wildcards -zxvf- -C ~ --strip-component=2 '*/wine/*' 32 | winetricks msxml6 33 | - name: Download WSA 34 | shell: python 35 | run: | 36 | import requests 37 | from bs4 import BeautifulSoup 38 | import re 39 | import zipfile 40 | import os 41 | import urllib.request 42 | 43 | arch = "${{ matrix.arch }}" 44 | 45 | res = requests.post("https://store.rg-adguard.net/api/GetFiles", "type=CategoryId&url=858014f3-3934-4abe-8078-4aa193e74ca8&ring=WIS&lang=en-US", headers={ 46 | "content-type": "application/x-www-form-urlencoded" 47 | }) 48 | html = BeautifulSoup(res.content, "lxml") 49 | 50 | a = html.find("a", string=re.compile(f"Microsoft\.UI\.Xaml\..*_{arch}_.*\.appx")) 51 | link = a["href"] 52 | print(f"downloading link: {link}", flush=True) 53 | out_file = "xaml.appx" 54 | if not os.path.isfile(out_file): 55 | urllib.request.urlretrieve(link, out_file) 56 | 57 | a = html.find("a", string=re.compile(f"Microsoft\.VCLibs\..*_{arch}_.*\.appx")) 58 | link = a["href"] 59 | print(f"downloading link: {link}", flush=True) 60 | out_file = "vclibs.appx" 61 | if not os.path.isfile(out_file): 62 | urllib.request.urlretrieve(link, out_file) 63 | 64 | a = html.find("a", string=re.compile("MicrosoftCorporationII\.WindowsSubsystemForAndroid_.*\.msixbundle")) 65 | link = a["href"] 66 | print(f"downloading link: {link}", flush=True) 67 | out_file = "wsa.zip" 68 | if not os.path.isfile(out_file): 69 | urllib.request.urlretrieve(link, out_file) 70 | 71 | zip_name = "" 72 | with zipfile.ZipFile(out_file) as zip: 73 | for f in zip.filelist: 74 | if arch in f.filename.lower(): 75 | zip_name = f.filename 76 | if not os.path.isfile(zip_name): 77 | print(f"unzipping to {zip_name}", flush=True) 78 | zip.extract(f) 79 | ver_no = zip_name.split("_") 80 | ver = ver_no[1] 81 | with open(os.environ['GITHUB_ENV'], 'a') as g: 82 | g.write(f'WSA_VER={ver}\n') 83 | rel = ver_no[3].split(".") 84 | rell = str(rel[0]) 85 | with open(os.environ['GITHUB_ENV'], 'a') as g: 86 | g.write(f'WSA_REL={rell}\n') 87 | if 'language' in f.filename.lower() or 'scale' in f.filename.lower(): 88 | name = f.filename.split("-", 1)[1].split(".")[0] 89 | zip.extract(f) 90 | with zipfile.ZipFile(f.filename) as l: 91 | for g in l.filelist: 92 | if g.filename == 'resources.pri': 93 | g.filename = f'{name}.pri' 94 | l.extract(g, 'pri') 95 | print(f"extract resource pack {g.filename}") 96 | elif g.filename == 'AppxManifest.xml': 97 | g.filename = f'{name}.xml' 98 | l.extract(g, 'xml') 99 | 100 | with zipfile.ZipFile(zip_name) as zip: 101 | if not os.path.isdir(arch): 102 | print(f"unzipping from {zip_name}", flush=True) 103 | zip.extractall(arch) 104 | 105 | print("done", flush=True) 106 | - name: Download Magisk 107 | shell: python 108 | run: | 109 | import urllib.request 110 | import zipfile 111 | import os 112 | 113 | magisk_apk = """${{ github.event.inputs.magisk_apk }}""" 114 | 115 | if not magisk_apk: 116 | magisk_apk = """https://raw.githubusercontent.com/topjohnwu/magisk-files/canary/app-debug.apk""" 117 | 118 | out_file = "magisk.zip" 119 | 120 | arch = "${{ matrix.arch }}" 121 | 122 | abi_map={"x64" : ["x86_64", "x86"], "arm64" : ["arm64-v8a", "armeabi-v7a"]} 123 | 124 | if not os.path.isfile(out_file): 125 | urllib.request.urlretrieve(magisk_apk, out_file) 126 | 127 | def extract_as(zip, name, as_name, dir): 128 | info = zip.getinfo(name) 129 | info.filename = as_name 130 | zip.extract(info, dir) 131 | 132 | with zipfile.ZipFile(out_file) as zip: 133 | extract_as(zip, f"lib/{ abi_map[arch][0] }/libmagisk64.so", "magisk64", "magisk") 134 | extract_as(zip, f"lib/{ abi_map[arch][1] }/libmagisk32.so", "magisk32", "magisk") 135 | extract_as(zip, f"lib/{ abi_map[arch][0] }/libmagiskinit.so", "magiskinit", "magisk") 136 | extract_as(zip, f"lib/{ abi_map[arch][0] }/libmagiskinit.so", "magiskpolicy", "magisk") 137 | extract_as(zip, f"lib/{ abi_map[arch][0] }/libmagiskboot.so", "magiskboot", "magisk") 138 | extract_as(zip, f"lib/{ abi_map[arch][0] }/libbusybox.so", "busybox", "magisk") 139 | extract_as(zip, f"lib/{ abi_map['x64'][0] }/libmagiskinit.so", "magiskpolicy", ".") 140 | extract_as(zip, f"assets/boot_patch.sh", "boot_patch.sh", "magisk") 141 | extract_as(zip, f"assets/util_functions.sh", "util_functions.sh", "magisk") 142 | - name: Download OpenGApps 143 | if: ${{ github.event.inputs.gapps_variant != 'none' && github.event.inputs.gapps_variant != '' }} 144 | shell: python 145 | run: | 146 | import requests 147 | import zipfile 148 | import os 149 | import urllib.request 150 | import json 151 | 152 | arch = "${{ matrix.arch }}" 153 | variant = "${{ github.event.inputs.gapps_variant }}" 154 | abi_map = {"x64" : "x86_64", "arm64": "arm64"} 155 | 156 | res = requests.get(f"https://api.opengapps.org/list") 157 | 158 | j = json.loads(res.content) 159 | 160 | link = {i["name"]: i for i in j["archs"][abi_map[arch]]["apis"]["11.0"]["variants"]}[variant]["zip"] 161 | 162 | print(f"downloading link: {link}", flush=True) 163 | 164 | out_file = "gapps.zip" 165 | 166 | if not os.path.isfile(out_file): 167 | urllib.request.urlretrieve(link, out_file) 168 | print("done", flush=True) 169 | 170 | - name: Extract GApps and expand images 171 | if: ${{ github.event.inputs.gapps_variant != 'none' && github.event.inputs.gapps_variant != '' }} 172 | run: | 173 | mkdir gapps 174 | unzip -p gapps.zip {Core,GApps}/'*.lz' | tar --lzip -C gapps -xvf - -i --strip-components=2 --exclude='setupwizardtablet-x86_64' --exclude='packageinstallergoogle-all' --exclude='speech-common' --exclude='markup-lib-arm' --exclude='markup-lib-arm64' --exclude='markup-all' --exclude='setupwizarddefault-x86_64' --exclude='pixellauncher-all' --exclude='pixellauncher-common' 175 | 176 | e2fsck -yf ${{ matrix.arch }}/system.img 177 | resize2fs ${{ matrix.arch }}/system.img $(( $(du -sB512 gapps | cut -f1) + $(du -sB512 ${{ matrix.arch }}/system.img | cut -f1) ))s 178 | e2fsck -yf ${{ matrix.arch }}/product.img 179 | resize2fs ${{ matrix.arch }}/product.img 1024M 180 | e2fsck -yf ${{ matrix.arch }}/system_ext.img 181 | resize2fs ${{ matrix.arch }}/system_ext.img 108M 182 | - name: Expand vendor 183 | run: | 184 | e2fsck -yf ${{ matrix.arch }}/vendor.img 185 | resize2fs ${{ matrix.arch }}/vendor.img 320M 186 | - name: Mount images 187 | run: | 188 | sudo mkdir system 189 | sudo mkdir userdata 190 | sudo mount -o loop ${{ matrix.arch }}/system.img system 191 | sudo mount -o loop ${{ matrix.arch }}/vendor.img system/vendor 192 | sudo mount -o loop ${{ matrix.arch }}/product.img system/product 193 | sudo mount -o loop ${{ matrix.arch }}/system_ext.img system/system_ext 194 | qemu-img convert -O raw ${{ matrix.arch }}/userdata.vhdx userdata.img 195 | sudo mount -o loop userdata.img userdata 196 | - name: Integrate Magisk 197 | if: ${{ github.event.inputs.root_sol == 'magisk' || github.event.inputs.root_sol == '' }} 198 | run: | 199 | sudo mkdir system/sbin 200 | sudo chcon --reference system/init.environ.rc system/sbin 201 | sudo chown root:root system/sbin 202 | sudo chmod 0700 system/sbin 203 | sudo cp magisk/* system/sbin/ 204 | sudo mkdir -p userdata/adb/magisk 205 | sudo chmod -R 700 userdata/adb 206 | sudo cp magisk/* userdata/adb/magisk/ 207 | sudo find userdata/adb/magisk -type f -exec chmod 0755 {} \; 208 | sudo cp magisk.zip userdata/adb/magisk/magisk.apk 209 | sudo tee -a system/sbin/loadpolicy.sh < 281 | 282 | 283 | 284 | 285 | 286 | 287 | EOF 288 | wine64 ~/makepri.exe new /pr pri /in MicrosoftCorporationII.WindowsSubsystemForAndroid /cf priconfig.xml /of ${{ matrix.arch }}/resources.pri /o 289 | sed -i -zE "s//\n$(cat xml/* | grep -Po ']*/>' | sed ':a;N;$!ba;s/\n/\\n/g' | sed 's/\$/\\$/g' | sed 's/\//\\\//g')\n<\/Resources>/g" ${{ matrix.arch }}/AppxManifest.xml 290 | - name: Fix External Storage 291 | run: | 292 | wget -qO- "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/archive/$GITHUB_REF.tar.gz" | sudo tar --wildcards -zxvf- --strip-component=2 '*/${{ matrix.arch }}/system/*' 293 | sudo find system/system/priv-app -type d -exec chmod 0755 {} \; 294 | sudo find system/system/priv-app -type f -exec chmod 0644 {} \; 295 | sudo find system/system/priv-app -exec chcon --reference=system/system/priv-app {} \; 296 | - name: Integrate GApps 297 | if: ${{ github.event.inputs.gapps_variant != 'none' && github.event.inputs.gapps_variant != '' }} 298 | run: | 299 | wget -qO- "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/archive/$GITHUB_REF.tar.gz" | sudo tar --wildcards -zxvf- --strip-component=2 '*/${{ matrix.arch }}/gapps/*' 300 | shopt -s extglob 301 | sudo cp -vr gapps/!(product) system/system 302 | sudo cp -vr gapps/product/* system/product/ 303 | 304 | sudo find system/system/{app,etc,framework,priv-app} -exec chown root:root {} \; 305 | sudo find system/product/{app,etc,overlay,priv-app} -exec chown root:root {} \; 306 | 307 | sudo find system/system/{app,etc,framework,priv-app} -type d -exec chmod 0755 {} \; 308 | sudo find system/product/{app,etc,overlay,priv-app} -type d -exec chmod 0755 {} \; 309 | 310 | sudo find system/system/{app,framework,priv-app} -type f -exec chmod 0644 {} \; 311 | ls gapps/etc/ | xargs -n 1 -I dir sudo find system/system/etc/dir -type f -exec chmod 0644 {} \; 312 | sudo find system/product/{app,etc,overlay,priv-app} -type f -exec chmod 0644 {} \; 313 | 314 | sudo find system/system/{app,framework,priv-app} -type d -exec chcon --reference=system/system/app {} \; 315 | sudo find system/product/{app,etc,overlay,priv-app} -type d -exec chcon --reference=system/product/app {} \; 316 | ls gapps/etc/ | xargs -n 1 -I dir sudo find system/system/etc/dir -type d -exec chcon --reference=system/system/etc/permissions {} \; 317 | 318 | sudo find system/system/{app,framework,priv-app} -type f -exec chcon --reference=system/system/framework/ext.jar {} \; 319 | ls gapps/etc/ | xargs -n 1 -I dir sudo find system/system/etc/dir -type f -exec chcon --reference=system/system/etc/permissions {} \; 320 | sudo find system/product/{app,etc,overlay,priv-app} -type f -exec chcon --reference=system/product/etc/permissions/privapp-permissions-venezia.xml {} \; 321 | chmod +x ./magiskpolicy 322 | sudo ./magiskpolicy --load system/vendor/etc/selinux/precompiled_sepolicy --save system/vendor/etc/selinux/precompiled_sepolicy "allow gmscore_app gmscore_app vsock_socket { create connect write read }" "allow gmscore_app device_config_runtime_native_boot_prop file read" "allow gmscore_app system_server_tmpfs dir search" "allow gmscore_app system_server_tmpfs file open" 323 | - name: Fix GApps prop 324 | if: ${{ github.event.inputs.gapps_variant != 'none' && github.event.inputs.gapps_variant != '' }} 325 | shell: sudo python {0} 326 | run: | 327 | from __future__ import annotations 328 | from io import TextIOWrapper 329 | from os import system 330 | from typing import OrderedDict 331 | 332 | 333 | class Prop(OrderedDict): 334 | def __init__(self, file: TextIOWrapper) -> None: 335 | super().__init__() 336 | for i, line in enumerate(file.read().splitlines(False)): 337 | if '=' in line: 338 | k, v = line.split('=', 2) 339 | self[k] = v 340 | else: 341 | self[f".{i}"] = line 342 | 343 | def __str__(self) -> str: 344 | return '\n'.join([v if k.startswith('.') else f"{k}={v}" for k, v in self.items()]) 345 | 346 | def __iadd__(self, other: str) -> Prop: 347 | self[f".{len(self)}"] = other 348 | return self 349 | 350 | 351 | new_props = { 352 | ("product", "brand"): "google", 353 | ("product", "manufacturer"): "Google", 354 | ("build", "product"): "redfin", 355 | ("product", "name"): "redfin", 356 | ("product", "device"): "redfin", 357 | ("product", "model"): "Pixel 5", 358 | ("build", "flavor"): "redfin-user" 359 | } 360 | 361 | 362 | def description(sec: str, p: Prop) -> str: 363 | return f"{p[f'ro.{sec}.build.flavor']} {p[f'ro.{sec}.build.version.release_or_codename']} {p[f'ro.{sec}.build.id']} {p[f'ro.{sec}.build.version.incremental']} {p[f'ro.{sec}.build.tags']}" 364 | 365 | 366 | def fingerprint(sec: str, p: Prop) -> str: 367 | return f"""{p[f"ro.product.{sec}.brand"]}/{p[f"ro.product.{sec}.name"]}/{p[f"ro.product.{sec}.device"]}:{p[f"ro.{sec}.build.version.release"]}/{p[f"ro.{sec}.build.id"]}/{p[f"ro.{sec}.build.version.incremental"]}:{p[f"ro.{sec}.build.type"]}/{p[f"ro.{sec}.build.tags"]}""" 368 | 369 | 370 | def fix_prop(sec, prop): 371 | print(f"fixing {prop}", flush=True) 372 | with open(prop, 'r') as f: 373 | p = Prop(f) 374 | 375 | p += "# extra prop added by MagiskOnWSA" 376 | 377 | for k, v in new_props.items(): 378 | p[f"ro.{k[0]}.{k[1]}"] = v 379 | 380 | if k[0] == "build": 381 | p[f"ro.{sec}.{k[0]}.{k[1]}"] = v 382 | elif k[0] == "product": 383 | p[f"ro.{k[0]}.{sec}.{k[1]}"] = v 384 | 385 | p["ro.build.description"] = description(sec, p) 386 | p[f"ro.build.fingerprint"] = fingerprint(sec, p) 387 | p[f"ro.{sec}.build.description"] = description(sec, p) 388 | p[f"ro.{sec}.build.fingerprint"] = fingerprint(sec, p) 389 | p[f"ro.bootimage.build.fingerprint"] = fingerprint(sec, p) 390 | 391 | with open(prop, 'w') as f: 392 | f.write(str(p)) 393 | 394 | for sec, prop in {"system": "system/system/build.prop", "product": "system/product/build.prop", "system_ext": "system/system_ext/build.prop", "vendor": "system/vendor/build.prop", "odm": "system/vendor/odm/etc/build.prop"}.items(): 395 | fix_prop(sec, prop) 396 | - name: Umount images 397 | run: | 398 | sudo umount system/vendor 399 | sudo umount system/product 400 | sudo umount system/system_ext 401 | sudo umount system 402 | sudo umount userdata 403 | qemu-img convert -o subformat=dynamic -f raw -O vhdx userdata.img ${{ matrix.arch }}/userdata.vhdx 404 | - name: Shrink images 405 | run: | 406 | e2fsck -yf ${{ matrix.arch }}/system.img 407 | resize2fs -M ${{ matrix.arch }}/system.img 408 | e2fsck -yf ${{ matrix.arch }}/vendor.img 409 | resize2fs -M ${{ matrix.arch }}/vendor.img 410 | e2fsck -yf ${{ matrix.arch }}/product.img 411 | resize2fs -M ${{ matrix.arch }}/product.img 412 | e2fsck -yf ${{ matrix.arch }}/system_ext.img 413 | resize2fs -M ${{ matrix.arch }}/system_ext.img 414 | - name: Remove signature and add scripts 415 | run: | 416 | rm -rf ${{ matrix.arch }}/\[Content_Types\].xml ${{ matrix.arch }}/AppxBlockMap.xml ${{ matrix.arch }}/AppxSignature.p7x ${{ matrix.arch }}/AppxMetadata 417 | cp vclibs.appx xaml.appx ${{ matrix.arch }} 418 | tee ${{ matrix.arch }}/Install.ps1 <