Skip to content

Commit

Permalink
Add dtbo, vbmeta, super_empty flashing
Browse files Browse the repository at this point in the history
  • Loading branch information
anon1892 committed Aug 1, 2023
1 parent f656346 commit 0604ff1
Show file tree
Hide file tree
Showing 6 changed files with 239 additions and 2 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ Every config file should have `metadata` with the following fields:
- `device_code`: str; The official device code.
- `supported_device_codes`: List[str]; A list of supported device codes for the config. The config will be loaded based on this field.
- `twrp-link`: [OPTIONAL] str; name of the corresponding twrp page.
- `additional_steps` : [OPTIONAL] List[str]; A list of additional steps. Could be `dtbo`, `vbmeta`, `super_empty`.

In addition to these metadata, every config can have optional `requirements`. If these are set, the user is asked to check if they are meet.
- `android`: [OPTIONAL] int|str; Android version to install prior to installing a custom ROM.
Expand All @@ -231,7 +232,7 @@ Every step in the config file corresponds to one view in the application. These
- `img`: [OPTIONAL] Display an image on the left pane of the step view. Images are loaded from `openandroidinstaller/assets/imgs/`.
- `content`: str; The content text displayed alongside the action of the step. Used to inform the user about what's going on. For consistency and better readability the text should be moved into the next line using `>`.
- `link`: [OPTIONAL] Link to use for the link button if type is `link_button_with_confirm`.
- `command`: [ONLY for call_button* steps] str; The command to run. One of `adb_reboot`, `adb_reboot_bootloader`, `adb_reboot_download`, `adb_sideload`, `adb_twrp_wipe_and_install`, `adb_twrp_copy_partitions`, `fastboot_boot_recovery`, `fastboot_unlock_with_code`, `fastboot_unlock`, `fastboot_oem_unlock`, `fastboot_get_unlock_data`, `fastboot_reboot`, `heimdall_flash_recovery`.
- `command`: [ONLY for call_button* steps] str; The command to run. One of `adb_reboot`, `adb_reboot_bootloader`, `adb_reboot_download`, `adb_sideload`, `adb_twrp_wipe_and_install`, `adb_twrp_copy_partitions`, `fastboot_boot_recovery`, `fastboot_flash_additional_partitions`, `fastboot_unlock_with_code`, `fastboot_unlock`, `fastboot_oem_unlock`, `fastboot_get_unlock_data`, `fastboot_reboot`, `heimdall_flash_recovery`.
- `allow_skip`: [OPTIONAL] boolean; If a skip button should be displayed to allow skipping this step. Can be useful when the bootloader is already unlocked.

**Please try to retain this order of these fields in your config to ensure consistency.**
Expand Down
3 changes: 3 additions & 0 deletions openandroidinstaller/app_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ def __init__(
self.config = None
self.image_path = None
self.recovery_path = None
self.dtbo_path = None
self.vbmeta_path = None
self.super_empty_path = None

# store views
self.default_views: List = []
Expand Down
6 changes: 5 additions & 1 deletion openandroidinstaller/installer_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ def __init__(
self.requirements = requirements
self.device_code = metadata.get("device_code")
self.is_ab = metadata.get("is_ab_device", False)
self.additional_steps = metadata.get("additional_steps")
self.supported_device_codes = metadata.get("supported_device_codes")
self.twrp_link = metadata.get("twrp-link")

Expand Down Expand Up @@ -134,6 +135,8 @@ def _load_config(device_code: str, config_path: Path) -> Optional[InstallerConfi
config = InstallerConfig.from_file(path)
logger.info(f"Loaded device config from {path}.")
if config:
if "additional_steps" not in config.metadata:
config.metadata.update({"additional_steps": "[]"})
logger.info(f"Config metadata: {config.metadata}.")
return config
else:
Expand All @@ -150,7 +153,7 @@ def validate_config(config: str) -> bool:
),
"content": str,
schema.Optional("command"): Regex(
r"adb_reboot|adb_reboot_bootloader|adb_reboot_download|adb_sideload|adb_twrp_wipe_and_install|adb_twrp_copy_partitions|fastboot_boot_recovery|fastboot_flash_boot|fastboot_unlock_with_code|fastboot_get_unlock_data|fastboot_unlock|fastboot_oem_unlock|fastboot_reboot|heimdall_flash_recovery"
r"adb_reboot|adb_reboot_bootloader|adb_reboot_download|adb_sideload|adb_twrp_wipe_and_install|adb_twrp_copy_partitions|fastboot_boot_recovery|fastboot_flash_boot|fastboot_unlock_with_code|fastboot_get_unlock_data|fastboot_unlock|fastboot_oem_unlock|fastboot_reboot|heimdall_flash_recovery|fastboot_flash_additional_partitions"
),
schema.Optional("allow_skip"): bool,
schema.Optional("img"): str,
Expand All @@ -166,6 +169,7 @@ def validate_config(config: str) -> bool:
"device_code": str,
"supported_device_codes": [str],
schema.Optional("twrp-link"): str,
schema.Optional("additional_steps"): [str],
},
schema.Optional("requirements"): {
schema.Optional("android"): schema.Or(str, int),
Expand Down
51 changes: 51 additions & 0 deletions openandroidinstaller/tooling.py
Original file line number Diff line number Diff line change
Expand Up @@ -497,3 +497,54 @@ def search_device(platform: str, bin_path: Path) -> Optional[str]:
except CalledProcessError:
logger.error("Failed to detect a device.")
return None


@add_logging("Flash additional partitions with fastboot")
def fastboot_flash_additional_partitions(
bin_path: Path, dtbo: str, vbmeta: str, super_empty: str, is_ab: bool = True
) -> TerminalResponse:
"""Flash additional partitions (dtbo, vbmeta, super_empty) with fastboot."""
if dtbo:
for line in run_command(
"fastboot flash dtbo ", target=f"{dtbo}", bin_path=bin_path
):
yield line
if not is_ab:
if (type(line) == bool) and not line:
logger.error("Flashing dtbo failed.")
yield False
else:
yield True
else:
logger.info("No dtbo selected. Skipping")
yield True

if vbmeta:
for line in run_command(
"fastboot flash vbmeta ", target=f"{vbmeta}", bin_path=bin_path
):
yield line
if not is_ab:
if (type(line) == bool) and not line:
logger.error("Flashing vbmeta failed.")
yield False
else:
yield True
else:
logger.info("No vbmeta selected. Skipping")
yield True

if super_empty:
for line in run_command(
"fastboot wipe-super ", target=f"{super_empty}", bin_path=bin_path
):
yield line
if not is_ab:
if (type(line) == bool) and not line:
logger.error("Wiping super failed.")
yield False
else:
yield True
else:
logger.info("No super_empty selected. Skipping")
yield True
170 changes: 170 additions & 0 deletions openandroidinstaller/views/select_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,26 @@ def init_visuals(
# initialize file pickers
self.pick_image_dialog = FilePicker(on_result=self.pick_image_result)
self.pick_recovery_dialog = FilePicker(on_result=self.pick_recovery_result)
self.pick_dtbo_dialog = FilePicker(on_result=self.pick_dtbo_result)
self.pick_vbmeta_dialog = FilePicker(on_result=self.pick_vbmeta_result)
self.pick_super_empty_dialog = FilePicker(
on_result=self.pick_super_empty_result
)

self.selected_image = Text("Selected image: ")
self.selected_recovery = Text("Selected recovery: ")
self.selected_dtbo = Text("Selected dtbo: ")
self.selected_vbmeta = Text("Selected vbmeta: ")
self.selected_super_empty = Text("Selected super_empty: ")

# initialize and manage button state.
self.confirm_button = confirm_button(self.on_confirm)
self.confirm_button.disabled = True
self.pick_recovery_dialog.on_result = self.enable_button_if_ready
self.pick_image_dialog.on_result = self.enable_button_if_ready
self.pick_dtbo_dialog.on_result = self.enable_button_if_ready
self.pick_vbmeta_dialog.on_result = self.enable_button_if_ready
self.pick_super_empty_dialog.on_result = self.enable_button_if_ready
# back button
self.back_button = ElevatedButton(
"Back",
Expand All @@ -122,6 +134,9 @@ def build(self):
# attach hidden dialogues
self.right_view.controls.append(self.pick_image_dialog)
self.right_view.controls.append(self.pick_recovery_dialog)
self.right_view.controls.append(self.pick_dtbo_dialog)
self.right_view.controls.append(self.pick_vbmeta_dialog)
self.right_view.controls.append(self.pick_super_empty_dialog)

# create help/info button to show the help dialog
info_button = OutlinedButton(
Expand Down Expand Up @@ -226,6 +241,83 @@ def build(self):
),
self.selected_recovery,
Divider(),
]
)

# attach the controls for uploading others partitions, like dtbo, vbmeta & super_empty
if "dtbo" in self.state.config.metadata["additional_steps"]:
self.right_view.controls.extend(
[
Text("Select other specific images:", style="titleSmall"),
Markdown(
"""
Depending of the ROM, OpenAndroidInstaller may have to install additional images.
These images are usually needed for Android 13 ROM.
Make sure the file is for **your exact phone model!**
"""
),
Row(
[
FilledButton(
"Pick `dtbo.img` image",
icon=icons.UPLOAD_FILE,
on_click=lambda _: self.pick_dtbo_dialog.pick_files(
allow_multiple=False,
file_type="custom",
allowed_extensions=["img"],
),
expand=True,
),
]
),
self.selected_dtbo,
]
)
if "vbmeta" in self.state.config.metadata["additional_steps"]:
self.right_view.controls.extend(
[
Row(
[
FilledButton(
"Pick `vbmeta.img` image",
icon=icons.UPLOAD_FILE,
on_click=lambda _: self.pick_vbmeta_dialog.pick_files(
allow_multiple=False,
file_type="custom",
allowed_extensions=["img"],
),
expand=True,
),
]
),
self.selected_vbmeta,
]
)
if "super_empty" in self.state.config.metadata["additional_steps"]:
self.right_view.controls.extend(
[
Row(
[
FilledButton(
"Pick `super_empty.img` image",
icon=icons.UPLOAD_FILE,
on_click=lambda _: self.pick_super_empty_dialog.pick_files(
allow_multiple=False,
file_type="custom",
allowed_extensions=["img"],
),
expand=True,
),
]
),
self.selected_super_empty,
Divider(),
]
)

# attach the bottom buttons
self.right_view.controls.extend(
[
self.info_field,
Row([self.back_button, self.confirm_button]),
]
Expand Down Expand Up @@ -291,6 +383,61 @@ def pick_recovery_result(self, e: FilePickerResultEvent):
# update
self.selected_recovery.update()

def pick_dtbo_result(self, e: FilePickerResultEvent):
path = ", ".join(map(lambda f: f.name, e.files)) if e.files else "Cancelled!"
# update the textfield with the name of the file
self.selected_dtbo.value = self.selected_dtbo.value.split(":")[0] + f": {path}"
if e.files:
# check if the dtbo works with the device and show the filename in different colors accordingly
if path == "dtbo.img":
self.selected_dtbo.color = colors.GREEN
self.state.dtbo_path = e.files[0].path
logger.info(f"Selected dtbo from {self.state.dtbo_path}")
else:
self.selected_dtbo.color = colors.RED
else:
logger.info("No image selected.")
# update
self.selected_dtbo.update()

def pick_vbmeta_result(self, e: FilePickerResultEvent):
path = ", ".join(map(lambda f: f.name, e.files)) if e.files else "Cancelled!"
# update the textfield with the name of the file
self.selected_vbmeta.value = (
self.selected_vbmeta.value.split(":")[0] + f": {path}"
)
if e.files:
# check if the vbmeta works with the device and show the filename in different colors accordingly
if path == "vbmeta.img":
self.selected_vbmeta.color = colors.GREEN
self.state.vbmeta_path = e.files[0].path
logger.info(f"Selected vbmeta from {self.state.vbmeta_path}")
else:
self.selected_vbmeta.color = colors.RED
else:
logger.info("No image selected.")
# update
self.selected_vbmeta.update()

def pick_super_empty_result(self, e: FilePickerResultEvent):
path = ", ".join(map(lambda f: f.name, e.files)) if e.files else "Cancelled!"
# update the textfield with the name of the file
self.selected_super_empty.value = (
self.selected_super_empty.value.split(":")[0] + f": {path}"
)
if e.files:
# check if the super_empty works with the device and show the filename in different colors accordingly
if path == "super_empty.img":
self.selected_super_empty.color = colors.GREEN
self.state.super_empty_path = e.files[0].path
logger.info(f"Selected super_empty from {self.state.super_empty_path}")
else:
self.selected_super_empty.color = colors.RED
else:
logger.info("No image selected.")
# update
self.selected_super_empty.update()

def enable_button_if_ready(self, e):
"""Enable the confirm button if both files have been selected."""
if (".zip" in self.selected_image.value) and (
Expand Down Expand Up @@ -320,6 +467,29 @@ def enable_button_if_ready(self, e):
self.confirm_button.disabled = True
self.right_view.update()
return

if (
(self.selected_dtbo.color and self.selected_dtbo.color == "red")
or (self.selected_vbmeta.color and self.selected_vbmeta.color == "red")
or (
self.selected_super_empty.color
and self.selected_super_empty.color == "red"
)
):
logger.error(
"Some additional images don't match. Please select different ones."
)
self.info_field.controls = [
Text(
"Some additional images don't match. Select right ones or unselect them.",
color=colors.RED,
weight="bold",
)
]
self.confirm_button.disabled = True
self.right_view.update()
return

logger.info("Image and recovery work with the device. You can continue.")
self.info_field.controls = []
self.confirm_button.disabled = False
Expand Down
8 changes: 8 additions & 0 deletions openandroidinstaller/views/step_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
adb_twrp_copy_partitions,
fastboot_boot_recovery,
fastboot_flash_boot,
fastboot_flash_additional_partitions,
fastboot_oem_unlock,
fastboot_reboot,
fastboot_unlock,
Expand Down Expand Up @@ -231,6 +232,13 @@ def call_to_phone(self, e, command: str):
fastboot_flash_boot,
recovery=self.state.recovery_path,
),
"fastboot_flash_additional_partitions": partial(
fastboot_flash_additional_partitions,
dtbo=self.state.dtbo_path,
vbmeta=self.state.vbmeta_path,
super_empty=self.state.super_empty_path,
is_ab=self.state.config.is_ab,
),
"fastboot_reboot": fastboot_reboot,
"heimdall_flash_recovery": partial(
heimdall_flash_recovery, recovery=self.state.recovery_path
Expand Down

0 comments on commit 0604ff1

Please sign in to comment.