Skip to content

Commit 7e34275

Browse files
authored
Dev: flash additional partitions (#246)
This PR is based on #220 (by @anon1892) and aims to integrate the code in the Installer. - Include the commands `fastboot_flash_recovery` and `fastboot_reboot_recovery` (thanks to @anon1892) - Add support for Mi439 (Redmi 7A & co.) (thanks to @anon1892) - Display device specific notes at the image selection step (thanks to @anon1892) Other fixes & additions: - Display the scroll bar by default if scrolling is necessary - An info text box for additional images Todo: - [x] When the "custom recovery already flashed" button is selected, user should not have to select recovery file, as in this PR for now. - [x] User should be able not to select additional partitions, even on Android 13 (maybe add a button like "custom recovery already flashed". - [x] When recovery flashing is disabled, `adb reboot recovery` is not performed. Fix that.
2 parents 5e31e38 + aab4785 commit 7e34275

15 files changed

+702
-70
lines changed

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,13 @@ OnePlus | Nord N200 | [dre](https://wiki.lineageos.org/devices/dre) | | tested
171171
OnePlus | 9 | lemonade | | under development
172172
</details>
173173

174+
<details><summary><b>Xiaomi</b></summary>
175+
176+
Vendor | Device Name | CodeName | Models | Status
177+
---|---|---|---|---
178+
Xiaomi | Redmi 7A / 8 / 8A / 8A Dual | [Mi439](https://wiki.lineageos.org/devices/Mi439) : pine / olive / olivelite / olivewood | | tested
179+
</details>
180+
174181
And more to come!
175182

176183

@@ -197,7 +204,6 @@ Please have a look before opening an issue or starting to contribute.
197204
A detailed list can be found [here](https://openandroidinstaller.org/#contribute).
198205

199206

200-
201207
## Tools
202208

203209
- 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

docs/how_to_contribute_your_own_installation_configurations.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,14 @@ A config file consists of two parts. The first part are some metadata about the
1212
Every config file should have `metadata` with the following fields:
1313
- `maintainer`: str; Maintainer and author of the config file.
1414
- `device_name`: str; Name of the device.
15+
- `brand`: [OPTIONAL] str; Name of the brand. Can be used to make brand specific actions.
1516
- `is_ab_device`: bool; A boolean to determine if the device is a/b-partitioned or not.
1617
- `device_code`: str; The official device code.
1718
- `supported_device_codes`: List[str]; A list of supported device codes for the config. The config will be loaded based on this field.
1819
- `twrp-link`: [OPTIONAL] str; name of the corresponding twrp page.
20+
- `additional_steps` : [OPTIONAL] List[str]; A list of additional steps. Can be `dtbo`, `vbmeta`, `vendor_boot` or `super_empty`.
21+
- `notes`: [OPTIONAL] List[str]; specific phone information, showed before choosing ROM / recovery
22+
- `untested`: [OPTIONAL] bool; If `true`, a warning message is showed during installation process.
1923

2024
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.
2125
- `android`: [OPTIONAL] int|str; Android version to install prior to installing a custom ROM.
@@ -32,7 +36,7 @@ Every step in the config file corresponds to one view in the application. These
3236
- `img`: [OPTIONAL] Display an image on the left pane of the step view. Images are loaded from `openandroidinstaller/assets/imgs/`.
3337
- `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 `>`.
3438
- `link`: [OPTIONAL] Link to use for the link button if type is `link_button_with_confirm`.
35-
- `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`.
39+
- `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_recovery`, `fastboot_reboot_recovery`, `fastboot_flash_additional_partitions`, `fastboot_unlock_with_code`, `fastboot_unlock`, `fastboot_unlock_critical`, `fastboot_oem_unlock`, `fastboot_get_unlock_data`, `fastboot_reboot`, `heimdall_flash_recovery`.
3640
- `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.
3741

3842
**Please try to retain this order of these fields in your config to ensure consistency.**

openandroidinstaller/app_state.py

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@
1616
import copy
1717
from pathlib import Path
1818
from typing import List, Optional
19+
from loguru import logger
1920

20-
from installer_config import _load_config
21+
from installer_config import _load_config, Step
2122

2223

2324
class AppState:
@@ -37,13 +38,20 @@ def __init__(
3738
self.test = test
3839
self.test_config = test_config
3940

41+
# store state
42+
self.unlock_bootloader = True
43+
self.flash_recovery = True
44+
4045
# placeholders
4146
self.advanced = False
4247
self.install_addons = False
4348
self.addon_paths = []
4449
self.config = None
4550
self.image_path = None
4651
self.recovery_path = None
52+
self.dtbo_path = None
53+
self.vbmeta_path = None
54+
self.super_empty_path = None
4755

4856
# store views
4957
self.default_views: List = []
@@ -69,3 +77,48 @@ def load_config(self, device_code: str):
6977
self.steps = copy.deepcopy(self.config.unlock_bootloader) + copy.deepcopy(
7078
self.config.boot_recovery
7179
)
80+
81+
def toggle_flash_unlock_bootloader(self):
82+
"""Toggle flashing of unlock bootloader."""
83+
self.unlock_bootloader = not self.unlock_bootloader
84+
if self.unlock_bootloader:
85+
logger.info("Enabled unlocking the bootloader again.")
86+
self.steps = copy.deepcopy(self.config.unlock_bootloader)
87+
else:
88+
logger.info("Skipping bootloader unlocking.")
89+
self.steps = []
90+
# if the recovery is already flashed, skip flashing it again
91+
if self.flash_recovery:
92+
self.steps += copy.deepcopy(self.config.boot_recovery)
93+
else:
94+
self.steps = [
95+
Step(
96+
title="Boot custom recovery",
97+
type="confirm_button",
98+
content="If you already flashed TWRP, boot into it by pressing 'Confirm and run'. Otherwise restart the process. Once your phone screen looks like the picture on the left, continue.",
99+
command="adb_reboot_recovery",
100+
img="twrp-start.jpeg",
101+
)
102+
]
103+
104+
def toggle_flash_recovery(self):
105+
"""Toggle flashing of recovery."""
106+
self.flash_recovery = not self.flash_recovery
107+
if self.unlock_bootloader:
108+
self.steps = copy.deepcopy(self.config.unlock_bootloader)
109+
else:
110+
self.steps = []
111+
if self.flash_recovery:
112+
logger.info("Enabled flashing recovery again.")
113+
self.steps += copy.deepcopy(self.config.boot_recovery)
114+
else:
115+
logger.info("Skipping flashing recovery.")
116+
self.steps = [
117+
Step(
118+
title="Boot custom recovery",
119+
type="call_button",
120+
content="If you already flashed TWRP, boot into it by pressing 'Confirm and run'. Otherwise restart the process. Once your phone screen looks like the picture on the left, continue.",
121+
command="adb_reboot_recovery",
122+
img="twrp-start.jpeg",
123+
)
124+
]
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
metadata:
2+
maintainer: A non (anon)
3+
brand: xiaomi
4+
device_name: Xiaomi Redmi 7A / 8 / 8A / 8A Dual
5+
is_ab_device: false
6+
device_code: Mi439
7+
additional_steps:
8+
- dtbo
9+
- vbmeta
10+
- super_empty
11+
supported_device_codes:
12+
- Mi439
13+
- mi439
14+
- pine
15+
- olive
16+
- olivelite
17+
- olivewood
18+
notes:
19+
- Be careful when choosing OrangeFox version, Android 12 & 13 ROM needs OrangeFox version code with `A12`, for example `R11.1_5_A12`. Android 10 & 11 ROM needs OrangeFox version code without `A12` (bellow on the page)
20+
requirements:
21+
firmware: MiUI 12.5 (Q)
22+
steps:
23+
unlock_bootloader:
24+
- type: confirm_button
25+
content: >
26+
As a first step, you need to unlock the bootloader. A bootloader is the piece of software, that tells your phone
27+
how to start and run an operating system (like Android). Your device should be turned on. This will reset your phone.
28+
- type: link_button_with_confirm
29+
content: >
30+
- Create a Mi account on Xiaomi’s website. Beware that one account is only allowed to unlock one unique device every 30 days.
31+
32+
- Add a phone number to your Mi account, insert a SIM into your phone.
33+
34+
- Enable developer options in `Settings` > `About Phone` by repeatedly tapping MIUI Version.
35+
36+
- Link the device to your Mi account in `Settings` > `Additional settings` > `Developer options` > `Mi Unlock status`.
37+
38+
- Download the Mi Unlock app with the link bellow (Windows is required to run the app), and follow the instructions provided by the app. It may tell you that you have to wait, usually 7 days. If it does so, please wait the quoted amount of time before continuing to the next step!
39+
40+
- After device and Mi account are successfully verified, the bootloader should be unlocked.
41+
42+
- Since the device resets completely, you will need to re-enable USB debugging to continue : `Settings` > `Additional settings` > `Developer options` > `USB debugging`
43+
link: https://en.miui.com/unlock/download_en.html
44+
boot_recovery:
45+
- type: call_button
46+
content: >
47+
Now you need to install a custom recovery system on the phone. A recovery is a small subsystem on your phone,
48+
that manages updating, adapting and repairing of the operating system.
49+
50+
Once the device is fully booted, you need to reboot into the bootloader again by pressing 'Confirm and run' here. Then continue.
51+
command: adb_reboot_bootloader
52+
- type: call_button
53+
content: >
54+
Install additional partitions selected before by pressing 'Confirm and run'. Once it's done continue.
55+
56+
Note : If you have not selected this partition, it will do nothing.
57+
command: fastboot_flash_additional_partitions
58+
- type: call_button
59+
content: >
60+
Install the recovery you chosen before by pressing 'Confirm and run'. Once it's done continue.
61+
command: fastboot_flash_recovery
62+
- type: call_button
63+
img: ofox.png
64+
content: >
65+
Reboot to recovery by pressing 'Confirm and run', and hold the Vol+ button of your phone UNTIL you see the recovery.
66+
If MiUI starts, you have to start the process again, since MiUI delete the recovery you just flashed.
67+
Once it's done continue.
68+
command: fastboot_reboot_recovery

openandroidinstaller/installer_config.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
from pathlib import Path
1717
from typing import List, Optional
18+
from typing_extensions import Self
1819

1920
import schema
2021
import yaml
@@ -62,11 +63,12 @@ def __init__(
6263
self.requirements = requirements
6364
self.device_code = metadata.get("device_code")
6465
self.is_ab = metadata.get("is_ab_device", False)
66+
self.additional_steps = metadata.get("additional_steps", [])
6567
self.supported_device_codes = metadata.get("supported_device_codes")
6668
self.twrp_link = metadata.get("twrp-link")
6769

6870
@classmethod
69-
def from_file(cls, path):
71+
def from_file(cls, path) -> Self:
7072
with open(path, "r", encoding="utf-8") as stream:
7173
try:
7274
raw_config = yaml.safe_load(stream)
@@ -150,7 +152,8 @@ def validate_config(config: str) -> bool:
150152
),
151153
"content": str,
152154
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"
155+
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_flash_recovery|
156+
fastboot_unlock_critical|fastboot_unlock_with_code|fastboot_get_unlock_data|fastboot_unlock|fastboot_oem_unlock|fastboot_reboot|fastboot_reboot_recovery|heimdall_flash_recovery|fastboot_flash_additional_partitions"""
154157
),
155158
schema.Optional("allow_skip"): bool,
156159
schema.Optional("img"): str,
@@ -166,6 +169,11 @@ def validate_config(config: str) -> bool:
166169
"device_code": str,
167170
"supported_device_codes": [str],
168171
schema.Optional("twrp-link"): str,
172+
schema.Optional("additional_steps"): [
173+
Regex(r"dtbo|vbmeta|vendor_boot|super_empty")
174+
],
175+
schema.Optional("notes"): [str],
176+
schema.Optional("brand"): str,
169177
},
170178
schema.Optional("requirements"): {
171179
schema.Optional("android"): schema.Or(str, int),

openandroidinstaller/tooling.py

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,15 @@ def adb_wait_for_sideload(bin_path: Path) -> TerminalResponse:
161161
yield line
162162

163163

164+
@add_logging("Reboot to recovery with adb")
165+
def adb_reboot_recovery(bin_path: Path) -> TerminalResponse:
166+
"""Reboot to recovery with adb."""
167+
for line in run_command("adb reboot recovery", bin_path):
168+
yield line
169+
for line in adb_wait_for_recovery(bin_path=bin_path):
170+
yield line
171+
172+
164173
def adb_twrp_copy_partitions(bin_path: Path, config_path: Path) -> TerminalResponse:
165174
# some devices like one plus 6t or motorola moto g7 power need the partitions copied to prevent a hard brick
166175
logger.info("Sideload copy_partitions script with adb.")
@@ -416,7 +425,7 @@ def fastboot_boot_recovery(
416425

417426

418427
def fastboot_flash_boot(bin_path: Path, recovery: str) -> TerminalResponse:
419-
"""Temporarily, flash custom recovery with fastboot to boot partition."""
428+
"""Flash custom recovery with fastboot to boot partition."""
420429
logger.info("Flash custom recovery with fastboot.")
421430
for line in run_command(
422431
"fastboot flash boot", target=f"{recovery}", bin_path=bin_path
@@ -440,6 +449,85 @@ def fastboot_flash_boot(bin_path: Path, recovery: str) -> TerminalResponse:
440449
yield True
441450

442451

452+
@add_logging("Flash custom recovery with fastboot.")
453+
def fastboot_flash_recovery(
454+
bin_path: Path, recovery: str, is_ab: bool = True
455+
) -> TerminalResponse:
456+
"""Flash custom recovery with fastboot."""
457+
for line in run_command(
458+
"fastboot flash recovery ", target=f"{recovery}", bin_path=bin_path
459+
):
460+
yield line
461+
if not is_ab:
462+
if (type(line) == bool) and not line:
463+
logger.error("Flashing recovery failed.")
464+
yield False
465+
else:
466+
yield True
467+
468+
469+
@add_logging("Rebooting device to recovery.")
470+
def fastboot_reboot_recovery(bin_path: Path) -> TerminalResponse:
471+
"""Reboot to recovery with fastboot.
472+
473+
WARNING: On some devices, users need to press a specific key combo to make it work.
474+
"""
475+
for line in run_command("fastboot reboot recovery", bin_path):
476+
yield line
477+
478+
479+
@add_logging("Flash additional partitions with fastboot")
480+
def fastboot_flash_additional_partitions(
481+
bin_path: Path,
482+
dtbo: Optional[str],
483+
vbmeta: Optional[str],
484+
super_empty: Optional[str],
485+
is_ab: bool = True,
486+
) -> TerminalResponse:
487+
"""Flash additional partitions (dtbo, vbmeta, super_empty) with fastboot."""
488+
logger.info("Flash additional partitions with fastboot.")
489+
if dtbo:
490+
logger.info("dtbo selected. Flashing dtbo partition.")
491+
for line in run_command(
492+
"fastboot flash dtbo ", target=f"{dtbo}", bin_path=bin_path
493+
):
494+
yield line
495+
if not is_ab:
496+
if (type(line) == bool) and not line:
497+
logger.error("Flashing dtbo failed.")
498+
yield False
499+
else:
500+
yield True
501+
else:
502+
yield True
503+
504+
if vbmeta:
505+
logger.info("vbmeta selected. Flashing vbmeta partition.")
506+
for line in run_command(
507+
"fastboot flash vbmeta ", target=f"{vbmeta}", bin_path=bin_path
508+
):
509+
yield line
510+
if not is_ab:
511+
if (type(line) == bool) and not line:
512+
logger.error("Flashing vbmeta failed.")
513+
yield False
514+
else:
515+
yield True
516+
517+
if super_empty:
518+
logger.info("super_empty selected. Wiping super partition.")
519+
for line in run_command(
520+
"fastboot wipe-super ", target=f"{super_empty}", bin_path=bin_path
521+
):
522+
yield line
523+
if not is_ab:
524+
if (type(line) == bool) and not line:
525+
logger.error("Wiping super failed.")
526+
yield False
527+
else:
528+
yield True
529+
530+
443531
def heimdall_wait_for_download_available(bin_path: Path) -> bool:
444532
"""Use heimdall detect to wait for download mode to become available on the device."""
445533
logger.info("Wait for download mode to become available.")

0 commit comments

Comments
 (0)