Skip to content

Commit 0a838f7

Browse files
committed
lazy initial sdless impl (missing parttool, create & update flows)
1 parent 1ce0334 commit 0a838f7

File tree

12 files changed

+158
-46
lines changed

12 files changed

+158
-46
lines changed

app/src/main/java/org/andbootmgr/app/BackupRestoreFlow.kt

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ import org.andbootmgr.app.util.SDUtils
2222
import java.io.File
2323
import java.io.IOException
2424

25-
class BackupRestoreFlow(private val partitionId: Int): WizardFlow() {
25+
class BackupRestoreFlow(private val partitionId: Int, private val partFile: File?): WizardFlow() {
2626
override fun get(vm: WizardState): List<IWizardPage> {
27-
val c = CreateBackupDataHolder(vm, partitionId)
27+
val c = CreateBackupDataHolder(vm, partitionId, partFile)
2828
return listOf(WizardPage("start",
2929
NavButton(vm.activity.getString(R.string.cancel)) { it.finish() },
3030
NavButton("") {})
@@ -44,22 +44,26 @@ class BackupRestoreFlow(private val partitionId: Int): WizardFlow() {
4444
}
4545
}
4646

47-
private class CreateBackupDataHolder(val vm: WizardState, val pi: Int) {
47+
private class CreateBackupDataHolder(val vm: WizardState, val pi: Int?, val partFile: File?) {
4848
var action: Int = 0
4949
var path: Uri? = null
5050
var meta: SDUtils.SDPartitionMeta? = null
5151
}
5252

5353
@Composable
5454
private fun ChooseAction(c: CreateBackupDataHolder) {
55-
LaunchedEffect(Unit) {
56-
c.meta = SDUtils.generateMeta(c.vm.deviceInfo)
55+
if (c.vm.deviceInfo.metaonsd) {
56+
LaunchedEffect(Unit) {
57+
c.meta = SDUtils.generateMeta(c.vm.deviceInfo)
58+
}
5759
}
5860

5961
Column(horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center,
6062
modifier = Modifier.fillMaxSize()
6163
) {
62-
Text(stringResource(id = R.string.backup_msg, c.meta!!.dumpKernelPartition(c.pi).name), textAlign = TextAlign.Center)
64+
val name = if (c.vm.deviceInfo.metaonsd)
65+
c.meta!!.dumpKernelPartition(c.pi!!).name else c.partFile!!.name
66+
Text(stringResource(id = R.string.backup_msg, name), textAlign = TextAlign.Center)
6367
Button(onClick = { c.action=1; c.vm.navigate("select") }) {
6468
Text(stringResource(R.string.backup))
6569
}
@@ -93,6 +97,8 @@ private fun SelectDroidBoot(c: CreateBackupDataHolder) {
9397
}
9498
)
9599
Button(onClick = {
100+
val name = if (c.vm.deviceInfo.metaonsd)
101+
c.meta!!.dumpKernelPartition(c.pi!!).name else c.partFile!!.nameWithoutExtension
96102
if (c.action != 1) {
97103
c.vm.activity.chooseFile("*/*") {
98104
c.vm.chosen["file"] = WizardState.DownloadedFile(it, null)
@@ -101,7 +107,7 @@ private fun SelectDroidBoot(c: CreateBackupDataHolder) {
101107
c.vm.onNext = { i -> i.navigate("go") }
102108
}
103109
} else {
104-
c.vm.activity.createFile("${c.meta!!.dumpKernelPartition(c.pi).name}.img") {
110+
c.vm.activity.createFile("${name}.img") {
105111
c.path = it
106112
nextButtonAvailable = true
107113
c.vm.nextText = c.vm.activity.getString(R.string.next)
@@ -120,25 +126,28 @@ private fun Flash(c: CreateBackupDataHolder) {
120126
WizardTerminalWork(c.vm, logFile = "flash_${System.currentTimeMillis()}.txt") { terminal ->
121127
c.vm.logic.extractToolkit(terminal)
122128
terminal.add(c.vm.activity.getString(R.string.term_starting))
123-
val p = c.meta!!.dumpKernelPartition(c.pi)
124-
if (!c.vm.logic.unmount(p).to(terminal).exec().isSuccess)
125-
throw IOException(c.vm.activity.getString(R.string.term_cant_umount))
129+
val path = if (c.vm.deviceInfo.metaonsd) {
130+
val p = c.meta!!.dumpKernelPartition(c.pi!!)
131+
if (!c.vm.logic.unmount(p).to(terminal).exec().isSuccess)
132+
throw IOException(c.vm.activity.getString(R.string.term_cant_umount))
133+
p.path
134+
} else c.partFile!!.absolutePath
126135
if (c.action == 1) {
127136
c.vm.copy(
128-
SuFileInputStream.open(File(p.path)),
137+
SuFileInputStream.open(path),
129138
c.vm.activity.contentResolver.openOutputStream(c.path!!)!!
130139
)
131140
} else if (c.action == 2) {
132141
c.vm.copyPriv(
133142
c.vm.chosen["file"]!!.openInputStream(c.vm),
134-
File(p.path)
143+
File(path)
135144
)
136145
} else if (c.action == 3) {
137146
val result2 = Shell.cmd(
138147
File(
139148
c.vm.logic.toolkitDir,
140149
"simg2img"
141-
).absolutePath + " ${c.vm.chosen["file"]!!.toFile(c.vm).absolutePath} ${p.path}"
150+
).absolutePath + " ${c.vm.chosen["file"]!!.toFile(c.vm).absolutePath} $path"
142151
).to(terminal).exec()
143152
if (!result2.isSuccess) {
144153
terminal.add(c.vm.activity.getString(R.string.term_failure))

app/src/main/java/org/andbootmgr/app/CreatePartFlow.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ private fun Start(c: CreatePartDataHolder) {
137137
LaunchedEffect(Unit) {
138138
if (c.meta == null) {
139139
withContext(Dispatchers.IO) {
140-
val meta = SDUtils.generateMeta(c.vm.deviceInfo)!!
140+
val meta = SDUtils.generateMeta(c.vm.deviceInfo)!! // TODO !metaonsd
141141
c.p =
142142
meta.s.find { c.desiredStartSector == it.startSector } as SDUtils.Partition.FreeSpace
143143
c.meta = meta
@@ -663,12 +663,12 @@ private fun Flash(c: CreatePartDataHolder) {
663663
tmpFile.setExecutable(true)
664664
terminal.add(vm.activity.getString(R.string.term_creating_pt))
665665

666-
vm.logic.unmountBootset()
666+
vm.logic.unmountBootset(vm.deviceInfo)
667667
val startSectorAbsolute = c.p.startSector + c.startSectorRelative
668668
val endSectorAbsolute = c.p.startSector + c.endSectorRelative
669669
if (endSectorAbsolute > c.p.endSector)
670670
throw IllegalArgumentException("$endSectorAbsolute can't be bigger than ${c.p.endSector}")
671-
c.parts.forEachIndexed { index, part ->
671+
c.parts.forEachIndexed { index, part -> // TODO !metaonsd
672672
terminal.add(vm.activity.getString(R.string.term_create_part))
673673
val start = c.p.startSector.coerceAtLeast(startSectorAbsolute)
674674
val end = c.p.endSector.coerceAtMost(endSectorAbsolute)
@@ -765,7 +765,7 @@ private fun Flash(c: CreatePartDataHolder) {
765765
terminal.add(vm.activity.getString(R.string.term_success))
766766
} else { // Portable partition
767767
terminal.add(vm.activity.getString(R.string.term_create_part))
768-
vm.logic.unmountBootset()
768+
vm.logic.unmountBootset(vm.deviceInfo)
769769
val r = vm.logic.create(c.p,
770770
c.startSectorRelative,
771771
c.endSectorRelative,

app/src/main/java/org/andbootmgr/app/DeviceInfo.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,19 @@ abstract class MetaOnSdDeviceInfo : DeviceInfo {
5858
override fun isCorrupt(logic: DeviceLogic): Boolean {
5959
return !SuFile.open(logic.abmDb, "db.conf").exists()
6060
}
61+
override fun getAbmSettings(logic: DeviceLogic): String? {
62+
return logic.dmPath.absolutePath
63+
}
64+
}
65+
66+
abstract class SdLessDeviceInfo : DeviceInfo {
67+
override val metaonsd = false
68+
override fun isInstalled(logic: DeviceLogic): Boolean {
69+
return SuFile.open(logic.abmSdLessBootsetImg.toURI()).exists()
70+
}
71+
override fun isCorrupt(logic: DeviceLogic): Boolean {
72+
return !SuFile.open(logic.abmDb, "db.conf").exists()
73+
}
6174
override fun getAbmSettings(logic: DeviceLogic): String? {
6275
if (SuFile.open(bdev).exists())
6376
SDUtils.generateMeta(this)?.let { meta ->

app/src/main/java/org/andbootmgr/app/DeviceLogic.kt

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ class DeviceLogic(private val ctx: Context) {
1616
val toolkitDir = File(toolkit.targetPath, "Toolkit") // will occasionally be pruned by OS, but it's fine
1717
private val rootTmpDir = File("/data/local/tmp")
1818
val abmBootset = File(rootTmpDir, ".abm_bootset")
19+
val abmSdLessBootset = File("/data/abm")
20+
val abmSdLessBootsetImg = File(abmSdLessBootset, "bootset.img")
21+
private val metadata = File("/metadata")
22+
val metadataMap = File(metadata, "bootset.map")
23+
val dmBase = File("/dev/block/mapper")
24+
val dmName = "abmbootset"
25+
val dmPath = File(dmBase, dmName)
1926
val abmDb = File(abmBootset, "db")
2027
val abmEntries = File(abmDb, "entries")
2128
val abmDbConf = File(abmDb, "db.conf")
@@ -25,6 +32,7 @@ class DeviceLogic(private val ctx: Context) {
2532
val ast = d.getAbmSettings(this) ?: return false
2633
val bootsetSu = SuFile.open(abmBootset.toURI())
2734
if (!bootsetSu.exists()) bootsetSu.mkdir()
35+
if (!d.metaonsd && !mapBootset()) return false
2836
val result = Shell
2937
.cmd("mount $ast ${abmBootset.absolutePath}")
3038
.exec()
@@ -42,7 +50,7 @@ class DeviceLogic(private val ctx: Context) {
4250
mounted = true
4351
return true
4452
}
45-
fun unmountBootset(): Boolean {
53+
fun unmountBootset(d: DeviceInfo): Boolean {
4654
if (!checkMounted()) return true
4755
val result = Shell.cmd("umount ${abmBootset.absolutePath}").exec()
4856
if (!result.isSuccess) {
@@ -56,6 +64,7 @@ class DeviceLogic(private val ctx: Context) {
5664
Log.e("ABM_UMOUNT", out)
5765
return !mounted
5866
}
67+
if (!d.metaonsd) unmapBootset()
5968
mounted = false
6069
return true
6170
}
@@ -67,6 +76,21 @@ class DeviceLogic(private val ctx: Context) {
6776
}
6877
return mounted
6978
}
79+
private fun mapBootset(): Boolean {
80+
if (SuFile.open(dmPath.toURI()).exists())
81+
return true
82+
val tempFile = File(cacheDir, "${System.currentTimeMillis()}.txt")
83+
if (!Shell.cmd(File(toolkitDir, "droidboot_map_to_dm")
84+
.absolutePath + " " + metadataMap.absolutePath + " " + tempFile.absolutePath
85+
).exec().isSuccess) {
86+
return false
87+
}
88+
return Shell.cmd("dmsetup create $dmName ${tempFile.absolutePath}").exec().isSuccess
89+
}
90+
private fun unmapBootset() {
91+
if (SuFile.open(dmPath.toURI()).exists())
92+
Shell.cmd("dmsetup remove -f --retry $dmName").exec()
93+
}
7094
fun mount(p: SDUtils.Partition): Shell.Job {
7195
return Shell.cmd(p.mount())
7296
}

app/src/main/java/org/andbootmgr/app/DroidBootFlow.kt

Lines changed: 56 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,8 @@ private fun Start(vm: WizardState) {
8989

9090
// shared across DroidBootFlow, UpdateDroidBootFlow, FixDroidBootFlow
9191
@Composable
92-
fun LoadDroidBootJson(vm: WizardState, content: @Composable () -> Unit) {
93-
var loading by remember { mutableStateOf(!vm.deviceInfo.isBooted(vm.logic) || vm.deviceInfo.postInstallScript) }
92+
fun LoadDroidBootJson(vm: WizardState, update: Boolean, content: @Composable () -> Unit) {
93+
var loading by remember { mutableStateOf(!vm.deviceInfo.isBooted(vm.logic) || vm.deviceInfo.postInstallScript || update) }
9494
var error by remember { mutableStateOf(false) }
9595
LaunchedEffect(Unit) {
9696
if (!loading) return@LaunchedEffect
@@ -101,14 +101,16 @@ fun LoadDroidBootJson(vm: WizardState, content: @Composable () -> Unit) {
101101
val json = JSONTokener(jsonText).nextValue() as JSONObject
102102
if (BuildConfig.VERSION_CODE < json.getInt("minAppVersion"))
103103
throw IllegalStateException("please upgrade app")
104-
if (!vm.deviceInfo.isBooted(vm.logic)) {
104+
if ((!vm.deviceInfo.isBooted(vm.logic) || update) && json.has("bootloader")) {
105105
val bl = json.getJSONObject("bootloader")
106-
val url = bl.getString("url")
107-
val sha = bl.getStringOrNull("sha256")
108-
vm.inetAvailable["droidboot"] = WizardState.Downloadable(
109-
url, sha, vm.activity.getString(R.string.droidboot_online)
110-
)
111-
vm.idNeeded.add("droidboot")
106+
if (!bl.optBoolean("updateOnly", false) || update) {
107+
val url = bl.getString("url")
108+
val sha = bl.getStringOrNull("sha256")
109+
vm.inetAvailable["droidboot"] = WizardState.Downloadable(
110+
url, sha, vm.activity.getString(R.string.droidboot_online)
111+
)
112+
vm.idNeeded.add("droidboot")
113+
}
112114
}
113115
if (vm.deviceInfo.postInstallScript) {
114116
val i = json.getJSONObject("installScript")
@@ -140,7 +142,11 @@ fun LoadDroidBootJson(vm: WizardState, content: @Composable () -> Unit) {
140142

141143
@Composable
142144
private fun Input(d: DroidBootFlowDataHolder) {
143-
LoadDroidBootJson(d.vm) {
145+
LoadDroidBootJson(d.vm, false) {
146+
if (!d.vm.deviceInfo.isBooted(d.vm.logic) && !d.vm.idNeeded.contains("droidboot")) {
147+
Text(stringResource(R.string.install_bl_first))
148+
return@LoadDroidBootJson
149+
}
144150
Column(
145151
horizontalAlignment = Alignment.CenterHorizontally,
146152
verticalArrangement = Arrangement.Center,
@@ -241,7 +247,43 @@ private fun Flash(d: DroidBootFlowDataHolder) {
241247
return@WizardTerminalWork
242248
}
243249
} else {
244-
// TODO provision for sdless
250+
if (!SuFile.open(vm.logic.abmSdLessBootset.toURI()).exists()) {
251+
if (!SuFile.open(vm.logic.abmSdLessBootset.toURI()).mkdir()) {
252+
terminal.add(vm.activity.getString(R.string.term_cant_create_bootset))
253+
return@WizardTerminalWork
254+
}
255+
}
256+
val bytes = 4L * 1024L * 1024L * 1024L // 4 GB for now
257+
if (!Shell.cmd("fallocate -l $bytes" +
258+
vm.logic.abmSdLessBootsetImg.absolutePath).to(terminal).exec().isSuccess) {
259+
terminal.add(vm.activity.getString(R.string.term_failed_fallocate))
260+
return@WizardTerminalWork
261+
}
262+
if (!Shell.cmd("uncrypt ${vm.logic.abmSdLessBootsetImg.absolutePath} " +
263+
vm.logic.metadataMap.absolutePath).to(terminal).exec().isSuccess) {
264+
terminal.add(vm.activity.getString(R.string.term_failed_uncrypt))
265+
return@WizardTerminalWork
266+
}
267+
val tempFile = File(vm.logic.cacheDir, "${System.currentTimeMillis()}.txt")
268+
if (!Shell.cmd(File(vm.logic.toolkitDir, "droidboot_map_to_dm")
269+
.absolutePath + " " + vm.logic.metadataMap.absolutePath + " " + tempFile.absolutePath
270+
).to(terminal).exec().isSuccess) {
271+
terminal.add(vm.activity.getString(R.string.term_failed_mapconv))
272+
return@WizardTerminalWork
273+
}
274+
if (SuFile.open(vm.logic.dmPath.toURI()).exists()) {
275+
if (!Shell.cmd("dmsetup remove --retry ${vm.logic.dmName}")
276+
.to(terminal).exec().isSuccess
277+
) {
278+
terminal.add(vm.activity.getString(R.string.term_failed_unmap))
279+
return@WizardTerminalWork
280+
}
281+
}
282+
if (!Shell.cmd("dmsetup create ${vm.logic.dmName} ${tempFile.absolutePath}")
283+
.to(terminal).exec().isSuccess) {
284+
terminal.add(vm.activity.getString(R.string.term_failed_map))
285+
return@WizardTerminalWork
286+
}
245287
}
246288

247289
if (!vm.logic.mountBootset(vm.deviceInfo)) {
@@ -256,14 +298,14 @@ private fun Flash(d: DroidBootFlowDataHolder) {
256298
if (!SuFile.open(vm.logic.abmDb.toURI()).exists()) {
257299
if (!SuFile.open(vm.logic.abmDb.toURI()).mkdir()) {
258300
terminal.add(vm.activity.getString(R.string.term_failed_create_db_dir))
259-
vm.logic.unmountBootset()
301+
vm.logic.unmountBootset(vm.deviceInfo)
260302
return@WizardTerminalWork
261303
}
262304
}
263305
if (!SuFile.open(vm.logic.abmEntries.toURI()).exists()) {
264306
if (!SuFile.open(vm.logic.abmEntries.toURI()).mkdir()) {
265307
terminal.add(vm.activity.getString(R.string.term_failed_create_entries_dir))
266-
vm.logic.unmountBootset()
308+
vm.logic.unmountBootset(vm.deviceInfo)
267309
return@WizardTerminalWork
268310
}
269311
}
@@ -314,7 +356,7 @@ private fun Flash(d: DroidBootFlowDataHolder) {
314356
tmpFile.delete()
315357
}
316358
terminal.add(vm.activity.getString(R.string.term_success))
317-
vm.logic.unmountBootset()
359+
vm.logic.unmountBootset(vm.deviceInfo)
318360
// TODO prompt user to reboot?
319361
}
320362
}

app/src/main/java/org/andbootmgr/app/FixDroidBootFlow.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ import androidx.compose.ui.Modifier
1111
import androidx.compose.ui.res.stringResource
1212
import com.topjohnwu.superuser.io.SuFile
1313
import com.topjohnwu.superuser.io.SuFileInputStream
14-
import kotlinx.coroutines.Dispatchers
15-
import kotlinx.coroutines.withContext
1614
import java.io.File
1715
import java.io.IOException
1816

@@ -39,7 +37,11 @@ class FixDroidBootFlow: WizardFlow() {
3937

4038
@Composable
4139
private fun Start(vm: WizardState) {
42-
LoadDroidBootJson(vm) {
40+
LoadDroidBootJson(vm, false) {
41+
if (!vm.deviceInfo.isBooted(vm.logic) && !vm.idNeeded.contains("droidboot")) {
42+
Text(stringResource(R.string.install_bl_first))
43+
return@LoadDroidBootJson
44+
}
4345
LaunchedEffect(Unit) {
4446
vm.nextText = vm.activity.getString(R.string.next)
4547
vm.onNext = { it.navigate(if (vm.idNeeded.isNotEmpty()) "dload" else "flash") }

app/src/main/java/org/andbootmgr/app/MainActivity.kt

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,11 @@ class MainActivityState(val activity: MainActivity?) {
137137

138138
fun unmountBootset() {
139139
defaultCfg.clear()
140-
logic!!.unmountBootset()
140+
logic!!.unmountBootset(deviceInfo!!)
141141
}
142142

143143
fun remountBootset() {
144-
logic!!.unmountBootset()
144+
logic!!.unmountBootset(deviceInfo!!)
145145
logic!!.mountBootset(deviceInfo!!)
146146
}
147147
}
@@ -233,10 +233,6 @@ class MainActivity : ComponentActivity() {
233233
if (Shell.cmd("mountpoint -q /data/abm/bootset").exec().isSuccess) {
234234
Shell.cmd("umount /data/abm/bootset").exec()
235235
}
236-
SuFile.open("/data/abm").let {
237-
if (it.exists())
238-
Shell.cmd("rm -rf /data/abm").exec()
239-
}
240236
SuFile.open(filesDir.parentFile!!, "assets").let {
241237
if (it.exists())
242238
Shell.cmd("rm -rf ${filesDir.parentFile!!.resolve("assets").absolutePath}")
@@ -246,6 +242,14 @@ class MainActivity : ComponentActivity() {
246242
// == temp migration code end ==
247243
}
248244
vm.deviceInfo = di.await() // blocking
245+
// == temp migration code 2 start ==
246+
if (vm.deviceInfo!!.metaonsd) launch {
247+
SuFile.open("/data/abm").let {
248+
if (it.exists())
249+
Shell.cmd("rm -rf /data/abm").exec()
250+
}
251+
}
252+
// == temp migration code 2 end ==
249253
if (StayAliveService.instance == null) {
250254
vm.init()
251255
}

0 commit comments

Comments
 (0)