Skip to content

Commit 5d34157

Browse files
committed
Set branch to anon1892:dtbo
2 parents 2af8e34 + f464396 commit 5d34157

8 files changed

Lines changed: 257 additions & 9 deletions

File tree

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,6 @@ Please have a look before opening an issue or starting to contribute.
195195
A detailed list can be found [here](https://openandroidinstaller.org/#contribute).
196196

197197

198-
199198
## Tools
200199

201200
- The [Android SDK Platform Tools](https://developer.android.com/studio/releases/platform-tools) (such as adb and fastboot) are [Apache](https://android.googlesource.com/platform/system/adb/+/refs/heads/master/NOTICE)-licensed universal Android utilities

openandroidinstaller/app_state.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ def __init__(
4444
self.config = None
4545
self.image_path = None
4646
self.recovery_path = None
47+
self.dtbo_path = None
48+
self.vbmeta_path = None
49+
self.super_empty_path = None
4750

4851
# store views
4952
self.default_views: List = []

openandroidinstaller/installer_config.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ def __init__(
6262
self.requirements = requirements
6363
self.device_code = metadata.get("device_code")
6464
self.is_ab = metadata.get("is_ab_device", False)
65+
self.additional_steps = metadata.get("additional_steps")
6566
self.supported_device_codes = metadata.get("supported_device_codes")
6667
self.twrp_link = metadata.get("twrp-link")
6768

@@ -134,6 +135,8 @@ def _load_config(device_code: str, config_path: Path) -> Optional[InstallerConfi
134135
config = InstallerConfig.from_file(path)
135136
logger.info(f"Loaded device config from {path}.")
136137
if config:
138+
if "additional_steps" not in config.metadata:
139+
config.metadata.update({"additional_steps": "[]"})
137140
logger.info(f"Config metadata: {config.metadata}.")
138141
return config
139142
else:
@@ -150,7 +153,8 @@ def validate_config(config: str) -> bool:
150153
),
151154
"content": str,
152155
schema.Optional("command"): Regex(
153-
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_critical|fastboot_unlock_with_code|fastboot_get_unlock_data|fastboot_unlock|fastboot_oem_unlock|fastboot_reboot|heimdall_flash_recovery"
156+
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|
157+
fastboot_unlock_critical|fastboot_unlock_with_code|fastboot_get_unlock_data|fastboot_unlock|fastboot_oem_unlock|fastboot_reboot|heimdall_flash_recovery|fastboot_flash_additional_partitions"""
154158
),
155159
schema.Optional("allow_skip"): bool,
156160
schema.Optional("img"): str,
@@ -166,6 +170,7 @@ def validate_config(config: str) -> bool:
166170
"device_code": str,
167171
"supported_device_codes": [str],
168172
schema.Optional("twrp-link"): str,
173+
schema.Optional("additional_steps"): [str],
169174
},
170175
schema.Optional("requirements"): {
171176
schema.Optional("android"): schema.Or(str, int),

openandroidinstaller/tooling.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,3 +497,54 @@ def search_device(platform: str, bin_path: Path) -> Optional[str]:
497497
except CalledProcessError:
498498
logger.error("Failed to detect a device.")
499499
return None
500+
501+
502+
@add_logging("Flash additional partitions with fastboot")
503+
def fastboot_flash_additional_partitions(
504+
bin_path: Path, dtbo: str, vbmeta: str, super_empty: str, is_ab: bool = True
505+
) -> TerminalResponse:
506+
"""Flash additional partitions (dtbo, vbmeta, super_empty) with fastboot."""
507+
if dtbo:
508+
for line in run_command(
509+
"fastboot flash dtbo ", target=f"{dtbo}", bin_path=bin_path
510+
):
511+
yield line
512+
if not is_ab:
513+
if (type(line) == bool) and not line:
514+
logger.error("Flashing dtbo failed.")
515+
yield False
516+
else:
517+
yield True
518+
else:
519+
logger.info("No dtbo selected. Skipping")
520+
yield True
521+
522+
if vbmeta:
523+
for line in run_command(
524+
"fastboot flash vbmeta ", target=f"{vbmeta}", bin_path=bin_path
525+
):
526+
yield line
527+
if not is_ab:
528+
if (type(line) == bool) and not line:
529+
logger.error("Flashing vbmeta failed.")
530+
yield False
531+
else:
532+
yield True
533+
else:
534+
logger.info("No vbmeta selected. Skipping")
535+
yield True
536+
537+
if super_empty:
538+
for line in run_command(
539+
"fastboot wipe-super ", target=f"{super_empty}", bin_path=bin_path
540+
):
541+
yield line
542+
if not is_ab:
543+
if (type(line) == bool) and not line:
544+
logger.error("Wiping super failed.")
545+
yield False
546+
else:
547+
yield True
548+
else:
549+
logger.info("No super_empty selected. Skipping")
550+
yield True

openandroidinstaller/utils.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,22 @@ def image_works_with_device(supported_device_codes: List[str], image_path: str)
6060
return False
6161

6262

63+
def image_sdk_level(image_path: str) -> int:
64+
"""
65+
Determine Android version of the selected image.
66+
Android 13 : 33
67+
"""
68+
with zipfile.ZipFile(image_path) as image_zip:
69+
with image_zip.open(
70+
"META-INF/com/android/metadata", mode="r"
71+
) as image_metadata:
72+
metadata = image_metadata.readlines()
73+
for line in metadata:
74+
if b"sdk-level" in line:
75+
return int(line[line.find(b"=") + 1 : -1].decode("utf-8"))
76+
return 0
77+
78+
6379
def recovery_works_with_device(device_code: str, recovery_path: str) -> bool:
6480
"""Determine if a recovery works for the given device.
6581

openandroidinstaller/views/select_view.py

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,26 @@ def init_visuals(
9595
# initialize file pickers
9696
self.pick_image_dialog = FilePicker(on_result=self.pick_image_result)
9797
self.pick_recovery_dialog = FilePicker(on_result=self.pick_recovery_result)
98+
self.pick_dtbo_dialog = FilePicker(on_result=self.pick_dtbo_result)
99+
self.pick_vbmeta_dialog = FilePicker(on_result=self.pick_vbmeta_result)
100+
self.pick_super_empty_dialog = FilePicker(
101+
on_result=self.pick_super_empty_result
102+
)
103+
98104
self.selected_image = Text("Selected image: ")
99105
self.selected_recovery = Text("Selected recovery: ")
106+
self.selected_dtbo = Text("Selected dtbo: ")
107+
self.selected_vbmeta = Text("Selected vbmeta: ")
108+
self.selected_super_empty = Text("Selected super_empty: ")
100109

101110
# initialize and manage button state.
102111
self.confirm_button = confirm_button(self.on_confirm)
103112
self.confirm_button.disabled = True
104113
self.pick_recovery_dialog.on_result = self.enable_button_if_ready
105114
self.pick_image_dialog.on_result = self.enable_button_if_ready
115+
self.pick_dtbo_dialog.on_result = self.enable_button_if_ready
116+
self.pick_vbmeta_dialog.on_result = self.enable_button_if_ready
117+
self.pick_super_empty_dialog.on_result = self.enable_button_if_ready
106118
# back button
107119
self.back_button = ElevatedButton(
108120
"Back",
@@ -122,6 +134,9 @@ def build(self):
122134
# attach hidden dialogues
123135
self.right_view.controls.append(self.pick_image_dialog)
124136
self.right_view.controls.append(self.pick_recovery_dialog)
137+
self.right_view.controls.append(self.pick_dtbo_dialog)
138+
self.right_view.controls.append(self.pick_vbmeta_dialog)
139+
self.right_view.controls.append(self.pick_super_empty_dialog)
125140

126141
# create help/info button to show the help dialog
127142
info_button = OutlinedButton(
@@ -226,6 +241,83 @@ def build(self):
226241
),
227242
self.selected_recovery,
228243
Divider(),
244+
]
245+
)
246+
247+
# attach the controls for uploading others partitions, like dtbo, vbmeta & super_empty
248+
if "dtbo" in self.state.config.metadata["additional_steps"]:
249+
self.right_view.controls.extend(
250+
[
251+
Text("Select other specific images:", style="titleSmall"),
252+
Markdown(
253+
"""
254+
Depending of the ROM, OpenAndroidInstaller may have to install additional images.
255+
These images are usually needed for Android 13 ROM.
256+
Make sure the file is for **your exact phone model!**
257+
"""
258+
),
259+
Row(
260+
[
261+
FilledButton(
262+
"Pick `dtbo.img` image",
263+
icon=icons.UPLOAD_FILE,
264+
on_click=lambda _: self.pick_dtbo_dialog.pick_files(
265+
allow_multiple=False,
266+
file_type="custom",
267+
allowed_extensions=["img"],
268+
),
269+
expand=True,
270+
),
271+
]
272+
),
273+
self.selected_dtbo,
274+
]
275+
)
276+
if "vbmeta" in self.state.config.metadata["additional_steps"]:
277+
self.right_view.controls.extend(
278+
[
279+
Row(
280+
[
281+
FilledButton(
282+
"Pick `vbmeta.img` image",
283+
icon=icons.UPLOAD_FILE,
284+
on_click=lambda _: self.pick_vbmeta_dialog.pick_files(
285+
allow_multiple=False,
286+
file_type="custom",
287+
allowed_extensions=["img"],
288+
),
289+
expand=True,
290+
),
291+
]
292+
),
293+
self.selected_vbmeta,
294+
]
295+
)
296+
if "super_empty" in self.state.config.metadata["additional_steps"]:
297+
self.right_view.controls.extend(
298+
[
299+
Row(
300+
[
301+
FilledButton(
302+
"Pick `super_empty.img` image",
303+
icon=icons.UPLOAD_FILE,
304+
on_click=lambda _: self.pick_super_empty_dialog.pick_files(
305+
allow_multiple=False,
306+
file_type="custom",
307+
allowed_extensions=["img"],
308+
),
309+
expand=True,
310+
),
311+
]
312+
),
313+
self.selected_super_empty,
314+
Divider(),
315+
]
316+
)
317+
318+
# attach the bottom buttons
319+
self.right_view.controls.extend(
320+
[
229321
self.info_field,
230322
Row([self.back_button, self.confirm_button]),
231323
]
@@ -291,6 +383,61 @@ def pick_recovery_result(self, e: FilePickerResultEvent):
291383
# update
292384
self.selected_recovery.update()
293385

386+
def pick_dtbo_result(self, e: FilePickerResultEvent):
387+
path = ", ".join(map(lambda f: f.name, e.files)) if e.files else "Cancelled!"
388+
# update the textfield with the name of the file
389+
self.selected_dtbo.value = self.selected_dtbo.value.split(":")[0] + f": {path}"
390+
if e.files:
391+
# check if the dtbo works with the device and show the filename in different colors accordingly
392+
if path == "dtbo.img":
393+
self.selected_dtbo.color = colors.GREEN
394+
self.state.dtbo_path = e.files[0].path
395+
logger.info(f"Selected dtbo from {self.state.dtbo_path}")
396+
else:
397+
self.selected_dtbo.color = colors.RED
398+
else:
399+
logger.info("No image selected.")
400+
# update
401+
self.selected_dtbo.update()
402+
403+
def pick_vbmeta_result(self, e: FilePickerResultEvent):
404+
path = ", ".join(map(lambda f: f.name, e.files)) if e.files else "Cancelled!"
405+
# update the textfield with the name of the file
406+
self.selected_vbmeta.value = (
407+
self.selected_vbmeta.value.split(":")[0] + f": {path}"
408+
)
409+
if e.files:
410+
# check if the vbmeta works with the device and show the filename in different colors accordingly
411+
if path == "vbmeta.img":
412+
self.selected_vbmeta.color = colors.GREEN
413+
self.state.vbmeta_path = e.files[0].path
414+
logger.info(f"Selected vbmeta from {self.state.vbmeta_path}")
415+
else:
416+
self.selected_vbmeta.color = colors.RED
417+
else:
418+
logger.info("No image selected.")
419+
# update
420+
self.selected_vbmeta.update()
421+
422+
def pick_super_empty_result(self, e: FilePickerResultEvent):
423+
path = ", ".join(map(lambda f: f.name, e.files)) if e.files else "Cancelled!"
424+
# update the textfield with the name of the file
425+
self.selected_super_empty.value = (
426+
self.selected_super_empty.value.split(":")[0] + f": {path}"
427+
)
428+
if e.files:
429+
# check if the super_empty works with the device and show the filename in different colors accordingly
430+
if path == "super_empty.img":
431+
self.selected_super_empty.color = colors.GREEN
432+
self.state.super_empty_path = e.files[0].path
433+
logger.info(f"Selected super_empty from {self.state.super_empty_path}")
434+
else:
435+
self.selected_super_empty.color = colors.RED
436+
else:
437+
logger.info("No image selected.")
438+
# update
439+
self.selected_super_empty.update()
440+
294441
def enable_button_if_ready(self, e):
295442
"""Enable the confirm button if both files have been selected."""
296443
if (".zip" in self.selected_image.value) and (
@@ -320,6 +467,29 @@ def enable_button_if_ready(self, e):
320467
self.confirm_button.disabled = True
321468
self.right_view.update()
322469
return
470+
471+
if (
472+
(self.selected_dtbo.color and self.selected_dtbo.color == "red")
473+
or (self.selected_vbmeta.color and self.selected_vbmeta.color == "red")
474+
or (
475+
self.selected_super_empty.color
476+
and self.selected_super_empty.color == "red"
477+
)
478+
):
479+
logger.error(
480+
"Some additional images don't match. Please select different ones."
481+
)
482+
self.info_field.controls = [
483+
Text(
484+
"Some additional images don't match. Select right ones or unselect them.",
485+
color=colors.RED,
486+
weight="bold",
487+
)
488+
]
489+
self.confirm_button.disabled = True
490+
self.right_view.update()
491+
return
492+
323493
logger.info("Image and recovery work with the device. You can continue.")
324494
self.info_field.controls = []
325495
self.confirm_button.disabled = False

openandroidinstaller/views/start_view.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,7 @@ def check_bootloader_unlocked(e):
104104
self.state.steps = []
105105
else:
106106
logger.info("Enabled unlocking the bootloader again.")
107-
self.state.steps = copy.deepcopy(
108-
self.state.config.unlock_bootloader
109-
)
107+
self.state.steps = copy.deepcopy(self.state.config.unlock_bootloader)
110108
# if the recovery is already flashed, skip flashing it again
111109
if self.recovery_switch.value == False:
112110
self.state.steps += copy.deepcopy(self.state.config.boot_recovery)
@@ -122,12 +120,10 @@ def check_bootloader_unlocked(e):
122120
# toggleswitch to allow skipping flashing recovery
123121
def check_recovery_already_flashed(e):
124122
"""Enable skipping flashing recovery if selected."""
125-
123+
126124
# manage the bootloader unlocking switch
127125
if self.bootloader_switch.value == False:
128-
self.state.steps = copy.deepcopy(
129-
self.state.config.unlock_bootloader
130-
)
126+
self.state.steps = copy.deepcopy(self.state.config.unlock_bootloader)
131127
else:
132128
self.state.steps = []
133129

openandroidinstaller/views/step_view.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
adb_twrp_copy_partitions,
4646
fastboot_boot_recovery,
4747
fastboot_flash_boot,
48+
fastboot_flash_additional_partitions,
4849
fastboot_oem_unlock,
4950
fastboot_reboot,
5051
fastboot_unlock,
@@ -233,6 +234,13 @@ def call_to_phone(self, e, command: str):
233234
fastboot_flash_boot,
234235
recovery=self.state.recovery_path,
235236
),
237+
"fastboot_flash_additional_partitions": partial(
238+
fastboot_flash_additional_partitions,
239+
dtbo=self.state.dtbo_path,
240+
vbmeta=self.state.vbmeta_path,
241+
super_empty=self.state.super_empty_path,
242+
is_ab=self.state.config.is_ab,
243+
),
236244
"fastboot_reboot": fastboot_reboot,
237245
"heimdall_flash_recovery": partial(
238246
heimdall_flash_recovery, recovery=self.state.recovery_path

0 commit comments

Comments
 (0)