Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
M
Magisk
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
Magisk
Commits
7d93ca5c
Commit
7d93ca5c
authored
Jul 29, 2019
by
topjohnwu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Modernize MagiskInstaller
parent
ac20063e
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
357 additions
and
359 deletions
+357
-359
App.kt
app/src/main/java/com/topjohnwu/magisk/App.kt
+1
-8
MagiskInstaller.java
...main/java/com/topjohnwu/magisk/tasks/MagiskInstaller.java
+0
-351
MagiskInstaller.kt
...c/main/java/com/topjohnwu/magisk/tasks/MagiskInstaller.kt
+356
-0
No files found.
app/src/main/java/com/topjohnwu/magisk/App.kt
View file @
7d93ca5c
...
@@ -41,12 +41,10 @@ open class App : Application() {
...
@@ -41,12 +41,10 @@ open class App : Application() {
protectedContext
=
baseContext
protectedContext
=
baseContext
self
=
this
self
=
this
deContext
=
base
if
(
Build
.
VERSION
.
SDK_INT
>=
24
)
{
if
(
Build
.
VERSION
.
SDK_INT
>=
24
)
{
protectedContext
=
base
.
createDeviceProtectedStorageContext
()
protectedContext
=
base
.
createDeviceProtectedStorageContext
()
deContext
=
protectedContext
protectedContext
.
moveSharedPreferencesFrom
(
base
,
base
.
defaultPrefsName
)
deContext
.
moveSharedPreferencesFrom
(
base
,
base
.
defaultPrefsName
)
}
}
registerActivityLifecycleCallbacks
(
get
())
registerActivityLifecycleCallbacks
(
get
())
...
@@ -69,11 +67,6 @@ open class App : Application() {
...
@@ -69,11 +67,6 @@ open class App : Application() {
@JvmStatic
@JvmStatic
lateinit
var
self
:
App
lateinit
var
self
:
App
@SuppressLint
(
"StaticFieldLeak"
)
@Deprecated
(
"Use dependency injection; replace with protectedContext"
)
@JvmStatic
lateinit
var
deContext
:
Context
@Deprecated
(
"Use Rx or similar"
)
@Deprecated
(
"Use Rx or similar"
)
@JvmField
@JvmField
var
THREAD_POOL
:
ThreadPoolExecutor
var
THREAD_POOL
:
ThreadPoolExecutor
...
...
app/src/main/java/com/topjohnwu/magisk/tasks/MagiskInstaller.java
deleted
100644 → 0
View file @
ac20063e
package
com
.
topjohnwu
.
magisk
.
tasks
;
import
android.net.Uri
;
import
android.os.Build
;
import
android.text.TextUtils
;
import
androidx.annotation.MainThread
;
import
androidx.annotation.WorkerThread
;
import
com.topjohnwu.magisk.App
;
import
com.topjohnwu.magisk.Const
;
import
com.topjohnwu.magisk.Info
;
import
com.topjohnwu.magisk.utils.Utils
;
import
com.topjohnwu.net.Networking
;
import
com.topjohnwu.signing.SignBoot
;
import
com.topjohnwu.superuser.Shell
;
import
com.topjohnwu.superuser.ShellUtils
;
import
com.topjohnwu.superuser.internal.NOPList
;
import
com.topjohnwu.superuser.internal.UiThreadHandler
;
import
com.topjohnwu.superuser.io.SuFile
;
import
com.topjohnwu.superuser.io.SuFileInputStream
;
import
com.topjohnwu.superuser.io.SuFileOutputStream
;
import
org.kamranzafar.jtar.TarEntry
;
import
org.kamranzafar.jtar.TarHeader
;
import
org.kamranzafar.jtar.TarInputStream
;
import
org.kamranzafar.jtar.TarOutputStream
;
import
java.io.BufferedInputStream
;
import
java.io.BufferedOutputStream
;
import
java.io.File
;
import
java.io.FileOutputStream
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.OutputStream
;
import
java.nio.ByteBuffer
;
import
java.util.Arrays
;
import
java.util.List
;
import
java.util.zip.ZipEntry
;
import
java.util.zip.ZipInputStream
;
public
abstract
class
MagiskInstaller
{
protected
String
srcBoot
;
protected
File
destFile
;
protected
File
installDir
;
protected
Uri
zipUri
;
private
final
List
<
String
>
console
;
private
final
List
<
String
>
logs
;
private
boolean
isTar
=
false
;
protected
MagiskInstaller
()
{
console
=
NOPList
.
getInstance
();
logs
=
NOPList
.
getInstance
();
}
public
MagiskInstaller
(
Uri
zip
,
List
<
String
>
out
,
List
<
String
>
err
)
{
console
=
out
;
logs
=
err
;
zipUri
=
zip
;
installDir
=
new
File
(
App
.
deContext
.
getFilesDir
().
getParent
(),
"install"
);
Shell
.
sh
(
"rm -rf "
+
installDir
).
exec
();
installDir
.
mkdirs
();
}
protected
boolean
findImage
()
{
srcBoot
=
ShellUtils
.
fastCmd
(
"find_boot_image"
,
"echo \"$BOOTIMAGE\""
);
if
(
srcBoot
.
isEmpty
())
{
console
.
add
(
"! Unable to detect target image"
);
return
false
;
}
console
.
add
(
"- Target image: "
+
srcBoot
);
return
true
;
}
protected
boolean
findSecondaryImage
()
{
String
slot
=
ShellUtils
.
fastCmd
(
"echo $SLOT"
);
String
target
=
TextUtils
.
equals
(
slot
,
"_a"
)
?
"_b"
:
"_a"
;
console
.
add
(
"- Target slot: "
+
target
);
srcBoot
=
ShellUtils
.
fastCmd
(
"SLOT="
+
target
,
"find_boot_image"
,
"SLOT="
+
slot
,
"echo \"$BOOTIMAGE\""
);
if
(
srcBoot
.
isEmpty
())
{
console
.
add
(
"! Unable to detect target image"
);
return
false
;
}
console
.
add
(
"- Target image: "
+
srcBoot
);
return
true
;
}
protected
boolean
extractZip
()
{
String
arch
;
if
(
Build
.
VERSION
.
SDK_INT
>=
21
)
{
List
<
String
>
abis
=
Arrays
.
asList
(
Build
.
SUPPORTED_ABIS
);
arch
=
abis
.
contains
(
"x86"
)
?
"x86"
:
"arm"
;
}
else
{
arch
=
TextUtils
.
equals
(
Build
.
CPU_ABI
,
"x86"
)
?
"x86"
:
"arm"
;
}
console
.
add
(
"- Device platform: "
+
Build
.
CPU_ABI
);
try
{
ZipInputStream
zi
=
new
ZipInputStream
(
new
BufferedInputStream
(
App
.
self
.
getContentResolver
().
openInputStream
(
zipUri
)));
ZipEntry
ze
;
while
((
ze
=
zi
.
getNextEntry
())
!=
null
)
{
if
(
ze
.
isDirectory
())
continue
;
String
name
=
null
;
String
[]
names
=
{
arch
+
"/"
,
"common/"
,
"META-INF/com/google/android/update-binary"
};
for
(
String
n
:
names
)
{
if
(
ze
.
getName
().
startsWith
(
n
))
{
name
=
ze
.
getName
().
substring
(
ze
.
getName
().
lastIndexOf
(
'/'
)
+
1
);
break
;
}
}
if
(
name
==
null
&&
ze
.
getName
().
startsWith
(
"chromeos/"
))
name
=
ze
.
getName
();
if
(
name
==
null
)
continue
;
File
dest
=
installDir
instanceof
SuFile
?
new
SuFile
(
installDir
,
name
)
:
new
File
(
installDir
,
name
);
dest
.
getParentFile
().
mkdirs
();
try
(
OutputStream
out
=
new
SuFileOutputStream
(
dest
))
{
ShellUtils
.
pump
(
zi
,
out
);
}
}
}
catch
(
IOException
e
)
{
console
.
add
(
"! Cannot unzip zip"
);
return
false
;
}
File
init64
=
SuFile
.
open
(
installDir
,
"magiskinit64"
);
if
(
Build
.
VERSION
.
SDK_INT
>=
21
&&
Build
.
SUPPORTED_64_BIT_ABIS
.
length
!=
0
)
{
init64
.
renameTo
(
SuFile
.
open
(
installDir
,
"magiskinit"
));
}
else
{
init64
.
delete
();
}
Shell
.
sh
(
"cd "
+
installDir
,
"chmod 755 *"
).
exec
();
return
true
;
}
private
TarEntry
newEntry
(
String
name
,
long
size
)
{
console
.
add
(
"-- Writing: "
+
name
);
return
new
TarEntry
(
TarHeader
.
createHeader
(
name
,
size
,
0
,
false
,
0644
));
}
private
void
handleTar
(
InputStream
in
)
throws
IOException
{
console
.
add
(
"- Processing tar file"
);
boolean
vbmeta
=
false
;
try
(
TarInputStream
tarIn
=
new
TarInputStream
(
in
);
TarOutputStream
tarOut
=
new
TarOutputStream
(
destFile
))
{
TarEntry
entry
;
while
((
entry
=
tarIn
.
getNextEntry
())
!=
null
)
{
if
(
entry
.
getName
().
contains
(
"boot.img"
)
||
entry
.
getName
().
contains
(
"recovery.img"
))
{
String
name
=
entry
.
getName
();
console
.
add
(
"-- Extracting: "
+
name
);
File
extract
=
new
File
(
installDir
,
name
);
try
(
FileOutputStream
fout
=
new
FileOutputStream
(
extract
))
{
ShellUtils
.
pump
(
tarIn
,
fout
);
}
if
(
name
.
contains
(
".lz4"
))
{
console
.
add
(
"-- Decompressing: "
+
name
);
Shell
.
sh
(
"./magiskboot --decompress "
+
extract
).
to
(
console
).
exec
();
}
}
else
if
(
entry
.
getName
().
contains
(
"vbmeta.img"
))
{
vbmeta
=
true
;
ByteBuffer
buf
=
ByteBuffer
.
allocate
(
256
);
buf
.
put
(
"AVB0"
.
getBytes
());
// magic
buf
.
putInt
(
1
);
// required_libavb_version_major
buf
.
putInt
(
120
,
2
);
// flags
buf
.
position
(
128
);
// release_string
buf
.
put
(
"avbtool 1.1.0"
.
getBytes
());
tarOut
.
putNextEntry
(
newEntry
(
"vbmeta.img"
,
256
));
tarOut
.
write
(
buf
.
array
());
}
else
{
console
.
add
(
"-- Writing: "
+
entry
.
getName
());
tarOut
.
putNextEntry
(
entry
);
ShellUtils
.
pump
(
tarIn
,
tarOut
);
}
}
File
boot
=
SuFile
.
open
(
installDir
,
"boot.img"
);
File
recovery
=
SuFile
.
open
(
installDir
,
"recovery.img"
);
if
(
vbmeta
&&
recovery
.
exists
()
&&
boot
.
exists
())
{
// Install Magisk to recovery
srcBoot
=
recovery
.
getPath
();
// Repack boot image to prevent restore
Shell
.
sh
(
"./magiskboot --unpack boot.img"
,
"./magiskboot --repack boot.img"
,
"./magiskboot --cleanup"
,
"mv new-boot.img boot.img"
).
exec
();
try
(
InputStream
sin
=
new
SuFileInputStream
(
boot
))
{
tarOut
.
putNextEntry
(
newEntry
(
"boot.img"
,
boot
.
length
()));
ShellUtils
.
pump
(
sin
,
tarOut
);
}
boot
.
delete
();
}
else
{
if
(!
boot
.
exists
())
{
console
.
add
(
"! No boot image found"
);
throw
new
IOException
();
}
srcBoot
=
boot
.
getPath
();
}
}
}
protected
boolean
handleFile
(
Uri
uri
)
{
try
(
InputStream
in
=
new
BufferedInputStream
(
App
.
self
.
getContentResolver
().
openInputStream
(
uri
)))
{
in
.
mark
(
500
);
byte
[]
magic
=
new
byte
[
5
];
if
(
in
.
skip
(
257
)
!=
257
||
in
.
read
(
magic
)
!=
magic
.
length
)
{
console
.
add
(
"! Invalid file"
);
return
false
;
}
in
.
reset
();
if
(
Arrays
.
equals
(
magic
,
"ustar"
.
getBytes
()))
{
isTar
=
true
;
destFile
=
new
File
(
Const
.
EXTERNAL_PATH
,
"magisk_patched.tar"
);
handleTar
(
in
);
}
else
{
// Raw image
srcBoot
=
new
File
(
installDir
,
"boot.img"
).
getPath
();
destFile
=
new
File
(
Const
.
EXTERNAL_PATH
,
"magisk_patched.img"
);
console
.
add
(
"- Copying image to cache"
);
try
(
OutputStream
out
=
new
FileOutputStream
(
srcBoot
))
{
ShellUtils
.
pump
(
in
,
out
);
}
}
}
catch
(
IOException
e
)
{
console
.
add
(
"! Process error"
);
e
.
printStackTrace
();
return
false
;
}
return
true
;
}
protected
boolean
patchBoot
()
{
boolean
isSigned
;
try
(
InputStream
in
=
new
SuFileInputStream
(
srcBoot
))
{
isSigned
=
SignBoot
.
verifySignature
(
in
,
null
);
if
(
isSigned
)
{
console
.
add
(
"- Boot image is signed with AVB 1.0"
);
}
}
catch
(
IOException
e
)
{
console
.
add
(
"! Unable to check signature"
);
return
false
;
}
if
(!
Shell
.
sh
(
Utils
.
INSTANCE
.
fmt
(
"KEEPFORCEENCRYPT=%b KEEPVERITY=%b RECOVERYMODE=%b "
+
"sh update-binary sh boot_patch.sh %s"
,
Info
.
keepEnc
,
Info
.
keepVerity
,
Info
.
recovery
,
srcBoot
))
.
to
(
console
,
logs
).
exec
().
isSuccess
())
return
false
;
Shell
.
Job
job
=
Shell
.
sh
(
"./magiskboot --cleanup"
,
"mv bin/busybox busybox"
,
"rm -rf magisk.apk bin boot.img update-binary"
,
"cd /"
);
File
patched
=
new
File
(
installDir
,
"new-boot.img"
);
if
(
isSigned
)
{
console
.
add
(
"- Signing boot image with test keys"
);
File
signed
=
new
File
(
installDir
,
"signed.img"
);
try
(
InputStream
in
=
new
SuFileInputStream
(
patched
);
OutputStream
out
=
new
BufferedOutputStream
(
new
FileOutputStream
(
signed
)))
{
SignBoot
.
doSignature
(
"/boot"
,
in
,
out
,
null
,
null
);
}
catch
(
IOException
e
)
{
return
false
;
}
job
.
add
(
"mv -f "
+
signed
+
" "
+
patched
);
}
job
.
exec
();
return
true
;
}
protected
boolean
flashBoot
()
{
if
(!
Shell
.
su
(
Utils
.
INSTANCE
.
fmt
(
"direct_install %s %s"
,
installDir
,
srcBoot
))
.
to
(
console
,
logs
).
exec
().
isSuccess
())
return
false
;
if
(!
Info
.
keepVerity
)
Shell
.
su
(
"patch_dtbo_image"
).
to
(
console
,
logs
).
exec
();
return
true
;
}
protected
boolean
storeBoot
()
{
File
patched
=
SuFile
.
open
(
installDir
,
"new-boot.img"
);
try
{
OutputStream
os
;
if
(
isTar
)
{
os
=
new
TarOutputStream
(
destFile
,
true
);
((
TarOutputStream
)
os
).
putNextEntry
(
newEntry
(
srcBoot
.
contains
(
"recovery"
)
?
"recovery.img"
:
"boot.img"
,
patched
.
length
()));
}
else
{
os
=
new
BufferedOutputStream
(
new
FileOutputStream
(
destFile
));
}
try
(
InputStream
in
=
new
SuFileInputStream
(
patched
);
OutputStream
out
=
os
)
{
ShellUtils
.
pump
(
in
,
out
);
}
}
catch
(
IOException
e
)
{
console
.
add
(
"! Failed to output to "
+
destFile
);
e
.
printStackTrace
();
}
patched
.
delete
();
console
.
add
(
""
);
console
.
add
(
"****************************"
);
console
.
add
(
" Output file is placed in "
);
console
.
add
(
" "
+
destFile
+
" "
);
console
.
add
(
"****************************"
);
return
true
;
}
protected
boolean
postOTA
()
{
SuFile
bootctl
=
new
SuFile
(
"/data/adb/bootctl"
);
try
(
InputStream
in
=
Networking
.
get
(
Const
.
Url
.
BOOTCTL_URL
).
execForInputStream
().
getResult
();
OutputStream
out
=
new
SuFileOutputStream
(
bootctl
))
{
ShellUtils
.
pump
(
in
,
out
);
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
return
false
;
}
Shell
.
su
(
"post_ota "
+
bootctl
.
getParent
()).
exec
();
console
.
add
(
"***************************************"
);
console
.
add
(
" Next reboot will boot to second slot!"
);
console
.
add
(
"***************************************"
);
return
true
;
}
@WorkerThread
protected
abstract
boolean
operations
();
@MainThread
protected
abstract
void
onResult
(
boolean
success
);
public
void
exec
()
{
App
.
THREAD_POOL
.
execute
(()
->
{
boolean
b
=
operations
();
UiThreadHandler
.
run
(()
->
onResult
(
b
));
});
}
}
app/src/main/java/com/topjohnwu/magisk/tasks/MagiskInstaller.kt
0 → 100644
View file @
7d93ca5c
package
com.topjohnwu.magisk.tasks
import
android.content.Context
import
android.net.Uri
import
android.os.Build
import
android.text.TextUtils
import
androidx.annotation.MainThread
import
androidx.annotation.WorkerThread
import
com.skoumal.teanity.extensions.subscribeK
import
com.topjohnwu.magisk.Config
import
com.topjohnwu.magisk.Info
import
com.topjohnwu.magisk.data.network.GithubRawServices
import
com.topjohnwu.magisk.di.Protected
import
com.topjohnwu.magisk.extensions.*
import
com.topjohnwu.signing.SignBoot
import
com.topjohnwu.superuser.Shell
import
com.topjohnwu.superuser.ShellUtils
import
com.topjohnwu.superuser.internal.NOPList
import
com.topjohnwu.superuser.io.SuFile
import
com.topjohnwu.superuser.io.SuFileInputStream
import
com.topjohnwu.superuser.io.SuFileOutputStream
import
io.reactivex.Single
import
org.kamranzafar.jtar.TarEntry
import
org.kamranzafar.jtar.TarHeader
import
org.kamranzafar.jtar.TarInputStream
import
org.kamranzafar.jtar.TarOutputStream
import
timber.log.Timber
import
java.io.*
import
java.nio.ByteBuffer
import
java.util.*
import
java.util.zip.ZipEntry
import
java.util.zip.ZipInputStream
abstract
class
MagiskInstaller
{
protected
lateinit
var
srcBoot
:
String
protected
lateinit
var
destFile
:
File
protected
lateinit
var
installDir
:
File
protected
lateinit
var
zipUri
:
Uri
private
val
console
:
MutableList
<
String
>
private
val
logs
:
MutableList
<
String
>
private
var
isTar
=
false
private
val
service
:
GithubRawServices
by
inject
()
private
val
context
:
Context
by
inject
()
protected
constructor
()
{
console
=
NOPList
.
getInstance
()
logs
=
NOPList
.
getInstance
()
}
constructor
(
zip
:
Uri
,
out
:
MutableList
<
String
>,
err
:
MutableList
<
String
>)
{
console
=
out
logs
=
err
zipUri
=
zip
installDir
=
File
(
get
<
Context
>(
Protected
).
filesDir
.
parent
,
"install"
)
"rm -rf $installDir"
.
sh
()
installDir
.
mkdirs
()
}
protected
fun
findImage
():
Boolean
{
srcBoot
=
"find_boot_image; echo \"\$BOOTIMAGE\""
.
fsh
()
if
(
srcBoot
.
isEmpty
())
{
console
.
add
(
"! Unable to detect target image"
)
return
false
}
console
.
add
(
"- Target image: $srcBoot"
)
return
true
}
protected
fun
findSecondaryImage
():
Boolean
{
val
slot
=
"echo \$SLOT"
.
fsh
()
val
target
=
if
(
slot
==
"_a"
)
"_b"
else
"_a"
console
.
add
(
"- Target slot: $target"
)
srcBoot
=
arrayOf
(
"SLOT=$target"
,
"find_boot_image"
,
"SLOT=$slot"
,
"echo \"\$BOOTIMAGE\""
).
fsh
()
if
(
srcBoot
.
isEmpty
())
{
console
.
add
(
"! Unable to detect target image"
)
return
false
}
console
.
add
(
"- Target image: $srcBoot"
)
return
true
}
protected
fun
extractZip
():
Boolean
{
val
arch
:
String
arch
=
if
(
Build
.
VERSION
.
SDK_INT
>=
21
)
{
val
abis
=
listOf
(*
Build
.
SUPPORTED_ABIS
)
if
(
abis
.
contains
(
"x86"
))
"x86"
else
"arm"
}
else
{
if
(
TextUtils
.
equals
(
Build
.
CPU_ABI
,
"x86"
))
"x86"
else
"arm"
}
console
.
add
(
"- Device platform: "
+
Build
.
CPU_ABI
)
try
{
ZipInputStream
(
context
.
readUri
(
zipUri
).
buffered
()).
use
{
zi
->
lateinit
var
ze
:
ZipEntry
while
(
zi
.
nextEntry
?.
let
{
ze
=
it
}
!=
null
)
{
if
(
ze
.
isDirectory
)
continue
var
name
:
String
?
=
null
val
names
=
arrayOf
(
"$arch/"
,
"common/"
,
"META-INF/com/google/android/update-binary"
)
for
(
n
in
names
)
{
ze
.
name
.
run
{
if
(
startsWith
(
n
))
{
name
=
substring
(
lastIndexOf
(
'/'
)
+
1
)
}
}
name
?:
continue
break
}
if
(
name
==
null
&&
ze
.
name
.
startsWith
(
"chromeos/"
))
name
=
ze
.
name
if
(
name
==
null
)
continue
val
dest
=
if
(
installDir
is
SuFile
)
SuFile
(
installDir
,
name
)
else
File
(
installDir
,
name
)
dest
.
parentFile
!!
.
mkdirs
()
SuFileOutputStream
(
dest
).
use
{
zi
.
copyTo
(
it
)
}
}
}
}
catch
(
e
:
IOException
)
{
console
.
add
(
"! Cannot unzip zip"
)
Timber
.
e
(
e
)
return
false
}
val
init64
=
SuFile
.
open
(
installDir
,
"magiskinit64"
)
if
(
Build
.
VERSION
.
SDK_INT
>=
21
&&
Build
.
SUPPORTED_64_BIT_ABIS
.
isNotEmpty
())
{
init64
.
renameTo
(
SuFile
.
open
(
installDir
,
"magiskinit"
))
}
else
{
init64
.
delete
()
}
"cd $installDir; chmod 755 *"
.
sh
()
return
true
}
private
fun
newEntry
(
name
:
String
,
size
:
Long
):
TarEntry
{
console
.
add
(
"-- Writing: $name"
)
return
TarEntry
(
TarHeader
.
createHeader
(
name
,
size
,
0
,
false
,
420
/* 0644 */
))
}
@Throws
(
IOException
::
class
)
private
fun
handleTar
(
input
:
InputStream
)
{
console
.
add
(
"- Processing tar file"
)
var
vbmeta
=
false
withStreams
(
TarInputStream
(
input
),
TarOutputStream
(
destFile
))
{
tarIn
,
tarOut
->
lateinit
var
entry
:
TarEntry
while
(
tarIn
.
nextEntry
?.
let
{
entry
=
it
}
!=
null
)
{
if
(
entry
.
name
.
contains
(
"boot.img"
)
||
entry
.
name
.
contains
(
"recovery.img"
))
{
val
name
=
entry
.
name
console
.
add
(
"-- Extracting: $name"
)
val
extract
=
File
(
installDir
,
name
)
FileOutputStream
(
extract
).
use
{
tarIn
.
copyTo
(
it
)
}
if
(
name
.
contains
(
".lz4"
))
{
console
.
add
(
"-- Decompressing: $name"
)
"./magiskboot --decompress $extract"
.
sh
()
}
}
else
if
(
entry
.
name
.
contains
(
"vbmeta.img"
))
{
vbmeta
=
true
val
buf
=
ByteBuffer
.
allocate
(
256
)
buf
.
put
(
"AVB0"
.
toByteArray
())
// magic
buf
.
putInt
(
1
)
// required_libavb_version_major
buf
.
putInt
(
120
,
2
)
// flags
buf
.
position
(
128
)
// release_string
buf
.
put
(
"avbtool 1.1.0"
.
toByteArray
())
tarOut
.
putNextEntry
(
newEntry
(
"vbmeta.img"
,
256
))
tarOut
.
write
(
buf
.
array
())
}
else
{
console
.
add
(
"-- Writing: "
+
entry
.
name
)
tarOut
.
putNextEntry
(
entry
)
tarIn
.
copyTo
(
tarOut
)
}
}
val
boot
=
SuFile
.
open
(
installDir
,
"boot.img"
)
val
recovery
=
SuFile
.
open
(
installDir
,
"recovery.img"
)
if
(
vbmeta
&&
recovery
.
exists
()
&&
boot
.
exists
())
{
// Install Magisk to recovery
srcBoot
=
recovery
.
path
// Repack boot image to prevent restore
arrayOf
(
"./magiskboot --unpack boot.img"
,
"./magiskboot --repack boot.img"
,
"./magiskboot --cleanup"
,
"mv new-boot.img boot.img"
).
sh
()
SuFileInputStream
(
boot
).
use
{
tarOut
.
putNextEntry
(
newEntry
(
"boot.img"
,
boot
.
length
()))
it
.
copyTo
(
tarOut
)
}
boot
.
delete
()
}
else
{
if
(!
boot
.
exists
())
{
console
.
add
(
"! No boot image found"
)
throw
IOException
()
}
srcBoot
=
boot
.
path
}
}
}
protected
fun
handleFile
(
uri
:
Uri
):
Boolean
{
try
{
context
.
readUri
(
uri
).
buffered
().
use
{
it
.
mark
(
500
)
val
magic
=
ByteArray
(
5
)
if
(
it
.
skip
(
257
)
!=
257L
||
it
.
read
(
magic
)
!=
magic
.
size
)
{
console
.
add
(
"! Invalid file"
)
return
false
}
it
.
reset
()
if
(
Arrays
.
equals
(
magic
,
"ustar"
.
toByteArray
()))
{
isTar
=
true
destFile
=
File
(
Config
.
downloadDirectory
,
"magisk_patched.tar"
)
handleTar
(
it
)
}
else
{
// Raw image
srcBoot
=
File
(
installDir
,
"boot.img"
).
path
destFile
=
File
(
Config
.
downloadDirectory
,
"magisk_patched.img"
)
console
.
add
(
"- Copying image to cache"
)
FileOutputStream
(
srcBoot
).
use
{
out
->
it
.
copyTo
(
out
)
}
}
}
}
catch
(
e
:
IOException
)
{
console
.
add
(
"! Process error"
)
Timber
.
e
(
e
)
return
false
}
return
true
}
protected
fun
patchBoot
():
Boolean
{
var
isSigned
=
false
try
{
SuFileInputStream
(
srcBoot
).
use
{
isSigned
=
SignBoot
.
verifySignature
(
it
,
null
)
if
(
isSigned
)
{
console
.
add
(
"- Boot image is signed with AVB 1.0"
)
}
}
}
catch
(
e
:
IOException
)
{
console
.
add
(
"! Unable to check signature"
)
return
false
}
if
(!(
"KEEPFORCEENCRYPT=${Info.keepEnc} KEEPVERITY=${Info.keepVerity} "
+
"RECOVERYMODE=${Info.recovery} sh update-binary "
+
"sh boot_patch.sh $srcBoot"
).
sh
().
isSuccess
)
{
return
false
}
val
job
=
Shell
.
sh
(
"./magiskboot --cleanup"
,
"mv bin/busybox busybox"
,
"rm -rf magisk.apk bin boot.img update-binary"
,
"cd /"
)
val
patched
=
File
(
installDir
,
"new-boot.img"
)
if
(
isSigned
)
{
console
.
add
(
"- Signing boot image with test keys"
)
val
signed
=
File
(
installDir
,
"signed.img"
)
try
{
withStreams
(
SuFileInputStream
(
patched
),
signed
.
outputStream
().
buffered
())
{
input
,
out
->
SignBoot
.
doSignature
(
"/boot"
,
input
,
out
,
null
,
null
)
}
}
catch
(
e
:
IOException
)
{
console
.
add
(
"! Unable to sign image"
)
Timber
.
e
(
e
)
return
false
}
job
.
add
(
"mv -f $signed $patched"
)
}
job
.
exec
()
return
true
}
protected
fun
flashBoot
():
Boolean
{
if
(!
"direct_install $installDir $srcBoot"
.
sh
().
isSuccess
)
return
false
if
(!
Info
.
keepVerity
)
"patch_dtbo_image"
.
sh
()
return
true
}
protected
fun
storeBoot
():
Boolean
{
val
patched
=
SuFile
.
open
(
installDir
,
"new-boot.img"
)
try
{
val
os
:
OutputStream
if
(
isTar
)
{
os
=
TarOutputStream
(
destFile
,
true
)
os
.
putNextEntry
(
newEntry
(
if
(
srcBoot
.
contains
(
"recovery"
))
"recovery.img"
else
"boot.img"
,
patched
.
length
()))
}
else
{
os
=
destFile
.
outputStream
()
}
patched
.
suInputStream
().
use
{
it
.
copyTo
(
os
);
os
.
close
()
}
}
catch
(
e
:
IOException
)
{
console
.
add
(
"! Failed to output to $destFile"
)
Timber
.
e
(
e
)
return
false
}
patched
.
delete
()
console
.
add
(
""
)
console
.
add
(
"****************************"
)
console
.
add
(
" Output file is placed in "
)
console
.
add
(
" $destFile "
)
console
.
add
(
"****************************"
)
return
true
}
protected
fun
postOTA
():
Boolean
{
val
bootctl
=
SuFile
(
"/data/adb/bootctl"
)
try
{
withStreams
(
service
.
fetchBootctl
().
blockingGet
().
byteStream
(),
bootctl
.
suOutputStream
())
{
input
,
out
->
input
.
copyTo
(
out
)
}
}
catch
(
e
:
IOException
)
{
console
.
add
(
"! Unable to download bootctl"
)
Timber
.
e
(
e
)
return
false
}
"post_ota ${bootctl.parent}"
.
sh
()
console
.
add
(
"***************************************"
)
console
.
add
(
" Next reboot will boot to second slot!"
)
console
.
add
(
"***************************************"
)
return
true
}
private
fun
String
.
sh
()
=
Shell
.
sh
(
this
).
to
(
console
,
logs
).
exec
()
private
fun
Array
<
String
>.
sh
()
=
Shell
.
sh
(*
this
).
to
(
console
,
logs
).
exec
()
private
fun
String
.
fsh
()
=
ShellUtils
.
fastCmd
(
this
)
private
fun
Array
<
String
>.
fsh
()
=
ShellUtils
.
fastCmd
(*
this
)
@WorkerThread
protected
abstract
fun
operations
():
Boolean
@MainThread
protected
abstract
fun
onResult
(
success
:
Boolean
)
fun
exec
()
{
Single
.
fromCallable
{
operations
()
}.
subscribeK
{
onResult
(
it
)
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment