Merge pull request #246 from chenxiaolong/remove_modules

Remove oemunlockonboot module
This commit is contained in:
Andrew Gunnerson
2023-12-30 19:24:06 -05:00
committed by GitHub
13 changed files with 5 additions and 572 deletions
-35
View File
@@ -1,35 +0,0 @@
---
name: Modules
on:
push:
branches:
- master
pull_request:
jobs:
build:
name: Build modules
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v3
with:
# For git describe
fetch-depth: 0
- name: Get version
id: get_version
shell: bash
run: |
echo -n 'version=' >> "${GITHUB_OUTPUT}"
git describe --always \
| sed -E "s/^v//g;s/([^-]*-g)/r\1/;s/-/./g" \
>> "${GITHUB_OUTPUT}"
- name: Build modules
run: cargo xtask modules -a
- name: Archive artifacts
uses: actions/upload-artifact@v3
with:
name: avbroot-modules-${{ steps.get_version.outputs.version }}
path: modules/dist/
Generated
-60
View File
@@ -728,12 +728,6 @@ dependencies = [
"rustc_version",
]
[[package]]
name = "human-sort"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "140a09c9305e6d5e557e2ed7cbc68e05765a7d4213975b87cb04920689cc6219"
[[package]]
name = "ident_case"
version = "1.0.1"
@@ -1433,15 +1427,6 @@ dependencies = [
"cipher",
]
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "scrypt"
version = "0.11.0"
@@ -1731,16 +1716,6 @@ version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "walkdir"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
@@ -1759,37 +1734,6 @@ dependencies = [
"rustix",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.48.0"
@@ -1961,12 +1905,8 @@ version = "3.0.0"
dependencies = [
"anyhow",
"clap",
"human-sort",
"regex",
"tempfile",
"toml_edit",
"walkdir",
"zip",
]
[[package]]
+2 -26
View File
@@ -33,11 +33,7 @@ avbroot applies the following patches to the partition images:
Repeat: **_ALWAYS leave `OEM unlocking` enabled if rooted._**
* Any operation that causes an improperly-signed boot image to be flashed will result in the device being unbootable and unrecoverable without unlocking the bootloader again (and thus, triggering a data wipe). This includes:
* Performing a unpatched A/B OTA update while booted into Android via the OS' default updater. This can be blocked via a Magisk/KernelSU module (see: [Blocking A/B OTA Updates](#blocking-ab-ota-updates)).
* The `Direct install` method for updating Magisk. Magisk updates **must** be done by repatching the OTA, not via the app.
* Any operation that causes an improperly-signed boot image to be flashed will result in the device being unbootable and unrecoverable without unlocking the bootloader again (and thus, triggering a data wipe). This includes the `Direct install` method for updating Magisk. Magisk updates **must** be done by repatching the OTA, not via the app.
If the boot image is ever modified, **do not reboot**. [Open an issue](https://github.com/chenxiaolong/avbroot/issues/new) for support and be very clear about what steps were done that lead to the situation. If Android is still running and root access works, it might be possible to recover without wiping and starting over.
@@ -184,12 +180,10 @@ If you lose your AVB or OTA signing key, you will no longer be able to sign new
Remember: **Do not uncheck `OEM unlocking`!**
**WARNING**: If you are flashing CalyxOS, the setup wizard will [automatically turn off the `OEM unlocking` switch](https://github.com/CalyxOS/platform_packages_apps_SetupWizard/blob/7d2df25cedcbff83ddb608e628f9d97b38259c26/src/org/lineageos/setupwizard/SetupWizardApp.java#L135-L140). Make sure to manually reenable it again from Android's developer settings. Consider using [avbroot's `oemunlockonboot` Magisk module](#oemunlockonboot-enable-oem-unlocking-on-every-boot) to automatically ensure OEM unlocking is enabled on every boot.
**WARNING**: If you are flashing CalyxOS, the setup wizard will [automatically turn off the `OEM unlocking` switch](https://github.com/CalyxOS/platform_packages_apps_SetupWizard/blob/7d2df25cedcbff83ddb608e628f9d97b38259c26/src/org/lineageos/setupwizard/SetupWizardApp.java#L135-L140). Make sure to manually reenable it again from Android's developer settings. Consider using the [`OEMUnlockOnBoot` module](https://github.com/chenxiaolong/OEMUnlockOnBoot) to automatically ensure OEM unlocking is enabled on every boot.
8. That's it! To install future OS, Magisk, or KernelSU updates, see the [next section](#updates).
For extra safety, consider flashing [avbroot's Magisk/KernelSU modules](#avbroot-modules).
## Updates
Updates to Android, Magisk, and KernelSU are all done the same way by patching (or repatching) the OTA.
@@ -251,16 +245,6 @@ To safely use Repair Mode:
Because the unrooting and rooting are done by flashing OTAs, the device's data will not be wiped.
## avbroot modules
avbroot's Magisk/KernelSU modules can be downloaded from the [releases page](https://github.com/chenxiaolong/avbroot/releases).
### `oemunlockonboot`: Enable OEM unlocking on every boot
To help reduce the risk of OEM unlocking being accidentally disabled (or intentionally disabled as part of some OS' initial setup wizard), this module will attempt to enable the OEM unlocking option on every boot.
The logs for this module can be found at `/data/local/tmp/avbroot_oem_unlock.log`.
## Magisk preinit device
Magisk versions 25211 and newer require a writable partition for storing custom SELinux rules that need to be accessed during early boot stages. This can only be determined on a real device, so avbroot requires the partition to be explicitly specified via `--magisk-preinit-device <name>`. To find the partition name:
@@ -437,14 +421,6 @@ Debug builds work too, but they will run significantly slower (in the sha256 com
By default, the executable links to the system's bzip2 and liblzma libraries, which are the only external libraries avbroot depends on. To compile and statically link these two libraries, pass in `--features static`.
To build avbroot's modules from source, run:
```bash
cargo xtask modules -a
```
This requires Java and the Android SDK to be installed. The `ANDROID_HOME` environment variable must be set to the Android SDK path.
## Verifying digital signatures
First, save the public key to a file listing the keys to be trusted. This is the same key listed in [the author's profile](https://github.com/chenxiaolong/).
-33
View File
@@ -1,33 +0,0 @@
#!/sbin/sh
#################
# Initialization
#################
umask 022
# echo before loading util_functions
ui_print() { echo "$1"; }
require_new_magisk() {
ui_print "*******************************"
ui_print " Please install Magisk v20.4+! "
ui_print "*******************************"
exit 1
}
#########################
# Load util_functions.sh
#########################
OUTFD=$2
ZIPFILE=$3
mount /data 2>/dev/null
[ -f /data/adb/magisk/util_functions.sh ] || require_new_magisk
. /data/adb/magisk/util_functions.sh
[ $MAGISK_VER_CODE -lt 20400 ] && require_new_magisk
install_module
exit 0
-1
View File
@@ -1 +0,0 @@
#MAGISK
-83
View File
@@ -1,83 +0,0 @@
import android.annotation.SuppressLint;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Process;
import android.system.ErrnoException;
import java.lang.reflect.Method;
@SuppressLint({"DiscouragedPrivateApi", "PrivateApi", "SoonBlockedPrivateApi"})
public class Main {
private static final int GET_SERVICE_ATTEMPTS = 30;
@SuppressWarnings("SameParameterValue")
private static IInterface getService(Class<?> interfaceClass, String serviceName) throws Exception {
Class<?> serviceManager = Class.forName("android.os.ServiceManager");
Method getService = serviceManager.getDeclaredMethod("getService", String.class);
Class<?> stub = Class.forName(interfaceClass.getCanonicalName() + "$Stub");
Method asInterface = stub.getDeclaredMethod("asInterface", IBinder.class);
// ServiceManager.waitForService() tries to start the service, which we want to avoid to be
// 100% sure we're not disrupting the boot flow.
for (int attempt = 1; attempt <= GET_SERVICE_ATTEMPTS; ++attempt) {
IBinder iBinder = (IBinder) getService.invoke(null, serviceName);
if (iBinder != null) {
return (IInterface) asInterface.invoke(null, iBinder);
}
if (attempt < GET_SERVICE_ATTEMPTS) {
Thread.sleep(1000);
}
}
throw new IllegalStateException(
"Service " + serviceName + " not found after " + GET_SERVICE_ATTEMPTS + " attempts");
}
@SuppressWarnings("ConstantConditions")
private static void unlock() throws Exception {
Class<?> iOemLockService = Class.forName("android.service.oemlock.IOemLockService");
IInterface iFace = getService(iOemLockService, "oem_lock");
Method setOemUnlockAllowedByUser = iOemLockService.getDeclaredMethod("setOemUnlockAllowedByUser", boolean.class);
Method isOemUnlockAllowedByUser = iOemLockService.getDeclaredMethod("isOemUnlockAllowedByUser");
Boolean unlockAllowed = (Boolean) isOemUnlockAllowedByUser.invoke(iFace);
if (unlockAllowed) {
System.out.println("OEM unlocking already enabled");
return;
}
System.out.println("Enabling OEM unlocking");
setOemUnlockAllowedByUser.invoke(iFace, true);
}
@SuppressWarnings({"ConstantConditions", "JavaReflectionMemberAccess"})
private static void switchToSystemUid() throws Exception {
if (Process.myUid() != Process.SYSTEM_UID) {
Method setUid = Process.class.getDeclaredMethod("setUid", int.class);
int errno = (int) setUid.invoke(null, Process.SYSTEM_UID);
if (errno != 0) {
throw new Exception("Failed to switch to SYSTEM (" + Process.SYSTEM_UID + ") user",
new ErrnoException("setuid", errno));
}
if (Process.myUid() != Process.SYSTEM_UID) {
throw new IllegalStateException("UID didn't actually change: " +
Process.myUid() + " != " + Process.SYSTEM_UID);
}
}
}
public static void main(String[] args) {
try {
switchToSystemUid();
unlock();
} catch (Exception e) {
System.err.println("Failed to enable OEM unlocking");
e.printStackTrace();
System.exit(1);
}
}
}
-6
View File
@@ -1,6 +0,0 @@
id=com.chiller3.avbroot.oemunlockonboot
name=oemunlockonboot
version=v3.0.0
versionCode=196608
author=chenxiaolong
description=Enable OEM unlocking on every boot
-20
View File
@@ -1,20 +0,0 @@
exec >/data/local/tmp/avbroot_oem_unlock.log 2>&1
mod_dir=${0%/*}
header() {
echo "----- ${*} -----"
}
header Environment
echo "Timestamp: $(date)"
echo "Script: ${0}"
echo "UID/GID/Context: $(id)"
header Enable OEM unlocking
CLASSPATH="${mod_dir}/classes.dex" app_process / Main &
pid=${!}
wait "${pid}"
echo "Exit status: ${?}"
echo "Logcat:"
logcat -d --pid "${pid}"
-9
View File
@@ -11,14 +11,5 @@ publish = false
[dependencies]
anyhow = "1.0.75"
clap = { version = "4.4.1", features = ["derive"] }
human-sort = "0.2.2"
regex = { version = "1.9.4", default-features = false, features = ["perf", "std"] }
tempfile = "3.8.0"
toml_edit = "0.21.0"
walkdir = "2.3.3"
# https://github.com/zip-rs/zip/pull/383
[dependencies.zip]
git = "https://github.com/chenxiaolong/zip"
rev = "989101f9384b9e94e36e6e9e0f51908fdf98bde6"
default-features = false
+1 -12
View File
@@ -8,7 +8,7 @@ To update the project version, run:
cargo xtask set-version -V <version>
```
This will update the main [`Cargo.toml`](../Cargo.toml) and each module's `module.prop` file.
This will update the main [`Cargo.toml`](../Cargo.toml) file.
## Updating changelog links
@@ -19,14 +19,3 @@ Then, run the following command to generate the appropriate link references:
```bash
cargo xtask update-changelog
```
## Build modules
To build avbroot's companion modules, run:
```bash
cargo xtask modules -a
# or -m <module1> -m <module2> ...
```
See the main [`README.md`](../README.md#avbroot-magisk-modules) for more details.
+1 -4
View File
@@ -4,13 +4,12 @@
*/
mod changelog;
mod module;
mod version;
use anyhow::Result;
use clap::{Parser, Subcommand};
use crate::{module::ModulesCli, version::SetVersionCli};
use crate::version::SetVersionCli;
const WORKSPACE_DIR: &str = env!("CARGO_WORKSPACE_DIR");
@@ -19,7 +18,6 @@ fn main() -> Result<()> {
match cli.command {
Command::SetVersion(c) => version::set_version_subcommand(&c),
Command::Modules(c) => module::modules_subcommand(&c),
Command::UpdateChangelog => changelog::update_changelog_subcommand(),
}
}
@@ -27,7 +25,6 @@ fn main() -> Result<()> {
#[derive(Debug, Subcommand)]
pub enum Command {
SetVersion(SetVersionCli),
Modules(ModulesCli),
/// Update links in CHANGELOG.md.
UpdateChangelog,
}
-231
View File
@@ -1,231 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Andrew Gunnerson
* SPDX-License-Identifier: GPL-3.0-only
*/
use std::{
collections::HashMap,
env,
ffi::OsStr,
fmt,
fs::{self, File},
io::{self, BufWriter, Write},
path::{Path, PathBuf},
process::Command,
};
use anyhow::{anyhow, bail, Result};
use clap::{Args, Parser, ValueEnum};
use tempfile::TempDir;
use walkdir::WalkDir;
use zip::{write::FileOptions, ZipWriter};
use crate::WORKSPACE_DIR;
#[cfg(unix)]
const D8: &str = "d8";
#[cfg(windows)]
const D8: &str = "d8.bat";
fn newest_child_by_name(directory: &Path) -> Result<PathBuf> {
let mut children = vec![];
for entry in fs::read_dir(directory)? {
let entry = entry?;
let path = entry
.path()
.into_os_string()
.into_string()
.map_err(|e| anyhow!("Non-UTF-8 path: {e:?}"))?;
children.push(path);
}
children.sort_by(|a, b| human_sort::compare(a, b));
children
.pop()
.map(PathBuf::from)
.ok_or_else(|| anyhow!("{directory:?} has no children"))
}
fn build_dex(writer: &mut dyn Write, sources: &[&Path]) -> Result<()> {
let sdk = env::var_os("ANDROID_HOME")
.map(PathBuf::from)
.ok_or_else(|| anyhow!("ANDROID_HOME must be set to the Android SDK path"))?;
let build_tools = newest_child_by_name(&sdk.join("build-tools"))?;
let platform = newest_child_by_name(&sdk.join("platforms"))?;
let d8 = build_tools.join(D8);
let android_jar = platform.join("android.jar");
let temp_dir = TempDir::new()?;
let mut process = Command::new("javac")
.args(["-source", "1.8"])
.args(["-target", "1.8"])
.arg("-cp")
.arg(android_jar)
.arg("-d")
.arg(temp_dir.path())
.args(sources)
.spawn()?;
let status = process.wait()?;
if !status.success() {
bail!("javac failed with {status}");
}
let mut class_files = vec![];
for entry in WalkDir::new(temp_dir.path()) {
let entry = entry?;
if entry.path().extension() == Some(OsStr::new("class")) {
class_files.push(entry.into_path());
}
}
let mut process = Command::new(d8)
.arg("--output")
.arg(temp_dir.path())
.args(class_files)
.spawn()?;
let status = process.wait()?;
if !status.success() {
bail!("d8 failed with {status}");
}
let mut reader = File::open(temp_dir.path().join("classes.dex"))?;
io::copy(&mut reader, writer)?;
Ok(())
}
fn parse_props(data: &str) -> Result<HashMap<String, String>> {
let mut result = HashMap::new();
for line in data.split('\n') {
if line.is_empty() {
continue;
}
let Some((k, v)) = line.split_once('=') else {
bail!("Malformed line: {line:?}");
};
result.insert(k.trim().to_owned(), v.trim().to_owned());
}
Ok(result)
}
fn start_module(
dist_dir: &Path,
common_dir: &Path,
module_dir: &Path,
) -> Result<(PathBuf, ZipWriter<BufWriter<File>>)> {
let module_prop_raw = fs::read_to_string(module_dir.join("module.prop"))?;
let module_prop = parse_props(&module_prop_raw)?;
let name = module_prop["name"].as_str();
let version = module_prop["version"].as_str();
let version = version.strip_prefix('v').unwrap_or(version);
let zip_path = dist_dir.join(format!("{name}-{version}.zip"));
let raw_writer = File::create(&zip_path)?;
let mut zip_writer = ZipWriter::new(BufWriter::new(raw_writer));
zip_writer.start_file(
"META-INF/com/google/android/update-binary",
FileOptions::default(),
)?;
io::copy(
&mut File::open(common_dir.join("update-binary"))?,
&mut zip_writer,
)?;
zip_writer.start_file(
"META-INF/com/google/android/updater-script",
FileOptions::default(),
)?;
io::copy(
&mut File::open(common_dir.join("updater-script"))?,
&mut zip_writer,
)?;
zip_writer.start_file("module.prop", FileOptions::default())?;
zip_writer.write_all(module_prop_raw.as_bytes())?;
Ok((zip_path, zip_writer))
}
pub fn modules_subcommand(cli: &ModulesCli) -> Result<()> {
let modules_dir = Path::new(WORKSPACE_DIR).join("modules");
let common_dir = modules_dir.join("common");
let dist_dir = modules_dir.join("dist");
fs::create_dir_all(&dist_dir)?;
let modules = if cli.module.all {
Module::value_variants()
} else {
&cli.module.module
};
for module in modules {
let module_dir = modules_dir.join(module.to_string());
let (path, mut writer) = start_module(&dist_dir, &common_dir, &module_dir)?;
match module {
Module::OemUnlockOnBoot => {
writer.start_file("classes.dex", FileOptions::default())?;
build_dex(&mut writer, &[&module_dir.join("Main.java")])?;
writer.start_file("service.sh", FileOptions::default())?;
let mut reader = File::open(module_dir.join("service.sh"))?;
io::copy(&mut reader, &mut writer)?;
}
}
writer.finish()?;
let path = path.canonicalize()?;
println!("Built module: {path:?}");
}
Ok(())
}
#[derive(Clone, Copy, Debug, ValueEnum)]
#[value(rename_all = "lower")]
enum Module {
OemUnlockOnBoot,
}
impl fmt::Display for Module {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.to_possible_value()
.expect("no values are skipped")
.get_name()
.fmt(f)
}
}
#[derive(Debug, Args)]
#[group(required = true, multiple = false)]
pub struct ModuleGroup {
/// Name of module.
#[arg(short, long)]
module: Vec<Module>,
/// Build all modules.
#[arg(short, long, conflicts_with = "module")]
all: bool,
}
/// Build companion modules.
#[derive(Debug, Parser)]
pub struct ModulesCli {
#[command(flatten)]
module: ModuleGroup,
}
+1 -52
View File
@@ -27,43 +27,6 @@ fn update_cargo_version(version: &str) -> Result<()> {
Ok(())
}
fn update_module_version(path: &Path, version: &str) -> Result<()> {
let mut version_code = 0;
// 8 bits per version component.
for piece in version.split('.') {
let piece: u32 = piece.parse()?;
version_code <<= 8;
version_code |= piece;
}
let raw_reader = File::open(path)?;
let mut reader = BufReader::new(raw_reader);
let mut result = String::new();
let mut line = String::new();
loop {
line.clear();
let n = reader.read_line(&mut line)?;
if n == 0 {
break;
}
if line.starts_with("version=") {
result.push_str(&format!("version=v{version}\n"));
} else if line.starts_with("versionCode=") {
result.push_str(&format!("versionCode={version_code}\n"));
} else {
result.push_str(&line);
}
}
fs::write(path, &result)?;
Ok(())
}
fn update_changelog_version(version: &str) -> Result<()> {
let path = Path::new(WORKSPACE_DIR).join("CHANGELOG.md");
let raw_reader = File::open(&path)?;
@@ -109,26 +72,12 @@ fn update_changelog_version(version: &str) -> Result<()> {
pub fn set_version_subcommand(cli: &SetVersionCli) -> Result<()> {
update_cargo_version(&cli.version)?;
let modules_dir = Path::new(WORKSPACE_DIR).join("modules");
for entry in fs::read_dir(modules_dir)? {
let entry = entry?;
if entry.file_type()?.is_dir() {
let module_prop = entry.path().join("module.prop");
if module_prop.exists() {
update_module_version(&module_prop, &cli.version)?;
}
}
}
update_changelog_version(&cli.version)?;
Ok(())
}
/// Set the version number in all Cargo.toml files and in the module metadata.
/// Set the version number in all Cargo.toml files.
#[derive(Debug, Parser)]
pub struct SetVersionCli {
/// Version number.