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
fc05f377
Commit
fc05f377
authored
Feb 29, 2020
by
topjohnwu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update env fix handling logic
parent
5c0e8638
Show whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
539 additions
and
535 deletions
+539
-535
DownloadService.kt
...ava/com/topjohnwu/magisk/core/download/DownloadService.kt
+12
-4
ManagerUpgrade.kt
...java/com/topjohnwu/magisk/core/download/ManagerUpgrade.kt
+9
-9
NotificationService.kt
...com/topjohnwu/magisk/core/download/NotificationService.kt
+20
-19
RemoteFileService.kt
...a/com/topjohnwu/magisk/core/download/RemoteFileService.kt
+14
-17
FlashResultListener.kt
...va/com/topjohnwu/magisk/core/tasks/FlashResultListener.kt
+10
-0
FlashZip.kt
...src/main/java/com/topjohnwu/magisk/core/tasks/FlashZip.kt
+1
-3
Flashing.kt
...src/main/java/com/topjohnwu/magisk/core/tasks/Flashing.kt
+2
-12
MagiskInstallImpl.kt
...java/com/topjohnwu/magisk/core/tasks/MagiskInstallImpl.kt
+370
-0
MagiskInstaller.kt
...n/java/com/topjohnwu/magisk/core/tasks/MagiskInstaller.kt
+57
-362
FlashViewModel.kt
.../java/com/topjohnwu/magisk/legacy/flash/FlashViewModel.kt
+11
-11
Configuration.kt
...m/topjohnwu/magisk/model/entity/internal/Configuration.kt
+4
-1
DownloadSubject.kt
...topjohnwu/magisk/model/entity/internal/DownloadSubject.kt
+10
-9
EnvFixDialog.kt
.../com/topjohnwu/magisk/model/events/dialog/EnvFixDialog.kt
+19
-29
FlashResultListener.kt
...a/com/topjohnwu/magisk/model/flash/FlashResultListener.kt
+0
-7
Patching.kt
...rc/main/java/com/topjohnwu/magisk/model/flash/Patching.kt
+0
-52
No files found.
app/src/main/java/com/topjohnwu/magisk/core/download/DownloadService.kt
View file @
fc05f377
...
@@ -9,15 +9,18 @@ import android.os.Build
...
@@ -9,15 +9,18 @@ import android.os.Build
import
android.webkit.MimeTypeMap
import
android.webkit.MimeTypeMap
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.core.intent
import
com.topjohnwu.magisk.core.intent
import
com.topjohnwu.magisk.core.tasks.EnvFixTask
import
com.topjohnwu.magisk.extensions.chooser
import
com.topjohnwu.magisk.extensions.chooser
import
com.topjohnwu.magisk.extensions.exists
import
com.topjohnwu.magisk.extensions.exists
import
com.topjohnwu.magisk.extensions.provide
import
com.topjohnwu.magisk.extensions.provide
import
com.topjohnwu.magisk.extensions.subscribeK
import
com.topjohnwu.magisk.legacy.flash.FlashActivity
import
com.topjohnwu.magisk.legacy.flash.FlashActivity
import
com.topjohnwu.magisk.model.entity.internal.Configuration.*
import
com.topjohnwu.magisk.model.entity.internal.Configuration.*
import
com.topjohnwu.magisk.model.entity.internal.Configuration.Flash.Secondary
import
com.topjohnwu.magisk.model.entity.internal.Configuration.Flash.Secondary
import
com.topjohnwu.magisk.model.entity.internal.DownloadSubject
import
com.topjohnwu.magisk.model.entity.internal.DownloadSubject
import
com.topjohnwu.magisk.model.entity.internal.DownloadSubject.*
import
com.topjohnwu.magisk.model.entity.internal.DownloadSubject.*
import
com.topjohnwu.magisk.utils.APKInstall
import
com.topjohnwu.magisk.utils.APKInstall
import
io.reactivex.Completable
import
org.koin.core.get
import
org.koin.core.get
import
java.io.File
import
java.io.File
import
kotlin.random.Random.Default.nextInt
import
kotlin.random.Random.Default.nextInt
...
@@ -43,6 +46,7 @@ open class DownloadService : RemoteFileService() {
...
@@ -43,6 +46,7 @@ open class DownloadService : RemoteFileService() {
id
:
Int
id
:
Int
)
=
when
(
val
conf
=
subject
.
configuration
)
{
)
=
when
(
val
conf
=
subject
.
configuration
)
{
Uninstall
->
FlashActivity
.
uninstall
(
this
,
subject
.
file
,
id
)
Uninstall
->
FlashActivity
.
uninstall
(
this
,
subject
.
file
,
id
)
EnvFix
->
{
remove
(
id
);
EnvFixTask
(
subject
.
file
).
exec
()
}
is
Patch
->
FlashActivity
.
patch
(
this
,
subject
.
file
,
conf
.
fileUri
,
id
)
is
Patch
->
FlashActivity
.
patch
(
this
,
subject
.
file
,
conf
.
fileUri
,
id
)
is
Flash
->
FlashActivity
.
flash
(
this
,
subject
.
file
,
conf
is
Secondary
,
id
)
is
Flash
->
FlashActivity
.
flash
(
this
,
subject
.
file
,
conf
is
Secondary
,
id
)
else
->
Unit
else
->
Unit
...
@@ -60,12 +64,16 @@ open class DownloadService : RemoteFileService() {
...
@@ -60,12 +64,16 @@ open class DownloadService : RemoteFileService() {
subject
:
Manager
,
subject
:
Manager
,
id
:
Int
id
:
Int
)
{
)
{
Completable
.
fromAction
{
handleAPK
(
subject
)
}.
subscribeK
{
remove
(
id
)
remove
(
id
)
when
(
subject
.
configuration
)
{
when
(
subject
.
configuration
)
{
is
APK
.
Upgrade
->
APKInstall
.
install
(
this
,
subject
.
file
)
is
APK
.
Upgrade
->
APKInstall
.
install
(
this
,
subject
.
file
)
is
APK
.
Restore
->
Unit
is
APK
.
Restore
->
Unit
}
}
}
}
}
// ---
// ---
...
...
app/src/main/java/com/topjohnwu/magisk/core/download/ManagerUpgrade.kt
View file @
fc05f377
...
@@ -20,8 +20,8 @@ private fun RemoteFileService.patch(apk: File, id: Int) {
...
@@ -20,8 +20,8 @@ private fun RemoteFileService.patch(apk: File, id: Int) {
if
(
packageName
==
BuildConfig
.
APPLICATION_ID
)
if
(
packageName
==
BuildConfig
.
APPLICATION_ID
)
return
return
update
(
id
)
{
notification
->
update
(
id
)
{
notification
.
setProgress
(
0
,
0
,
true
)
it
.
setProgress
(
0
,
0
,
true
)
.
setProgress
(
0
,
0
,
true
)
.
setProgress
(
0
,
0
,
true
)
.
setContentTitle
(
getString
(
R
.
string
.
hide_manager_title
))
.
setContentTitle
(
getString
(
R
.
string
.
hide_manager_title
))
.
setContentText
(
""
)
.
setContentText
(
""
)
...
@@ -53,8 +53,8 @@ private fun RemoteFileService.upgrade(apk: File, id: Int) {
...
@@ -53,8 +53,8 @@ private fun RemoteFileService.upgrade(apk: File, id: Int) {
}
}
private
fun
RemoteFileService
.
restore
(
apk
:
File
,
id
:
Int
)
{
private
fun
RemoteFileService
.
restore
(
apk
:
File
,
id
:
Int
)
{
update
(
id
)
{
notification
->
update
(
id
)
{
notification
.
setProgress
(
0
,
0
,
true
)
it
.
setProgress
(
0
,
0
,
true
)
.
setProgress
(
0
,
0
,
true
)
.
setProgress
(
0
,
0
,
true
)
.
setContentTitle
(
getString
(
R
.
string
.
restore_img_msg
))
.
setContentTitle
(
getString
(
R
.
string
.
restore_img_msg
))
.
setContentText
(
""
)
.
setContentText
(
""
)
...
@@ -65,8 +65,8 @@ private fun RemoteFileService.restore(apk: File, id: Int) {
...
@@ -65,8 +65,8 @@ private fun RemoteFileService.restore(apk: File, id: Int) {
Shell
.
su
(
"pm install $apk && pm uninstall $packageName"
).
exec
()
Shell
.
su
(
"pm install $apk && pm uninstall $packageName"
).
exec
()
}
}
fun
RemoteFileService
.
handleAPK
(
subject
:
DownloadSubject
.
Manager
)
fun
RemoteFileService
.
handleAPK
(
subject
:
DownloadSubject
.
Manager
)
=
=
when
(
subject
.
configuration
)
{
when
(
subject
.
configuration
)
{
is
Upgrade
->
upgrade
(
subject
.
file
,
subject
.
hashCode
())
is
Upgrade
->
upgrade
(
subject
.
file
,
subject
.
hashCode
())
is
Restore
->
restore
(
subject
.
file
,
subject
.
hashCode
())
is
Restore
->
restore
(
subject
.
file
,
subject
.
hashCode
())
}
}
app/src/main/java/com/topjohnwu/magisk/core/download/NotificationService.kt
View file @
fc05f377
...
@@ -7,16 +7,14 @@ import com.topjohnwu.magisk.core.base.BaseService
...
@@ -7,16 +7,14 @@ import com.topjohnwu.magisk.core.base.BaseService
import
com.topjohnwu.magisk.core.view.Notifications
import
com.topjohnwu.magisk.core.view.Notifications
import
org.koin.core.KoinComponent
import
org.koin.core.KoinComponent
import
java.util.*
import
java.util.*
import
kotlin.collections.HashMap
import
kotlin.random.Random.Default.nextInt
import
kotlin.random.Random.Default.nextInt
abstract
class
NotificationService
:
BaseService
(),
KoinComponent
{
abstract
class
NotificationService
:
BaseService
(),
KoinComponent
{
abstract
val
defaultNotification
:
Notification
.
Builder
private
val
hasNotifications
get
()
=
notifications
.
isNotEmpty
()
private
val
hasNotifications
get
()
=
notifications
.
isNotEmpty
()
private
val
notifications
=
private
val
notifications
=
Collections
.
synchronizedMap
(
HashMap
<
Int
,
Notification
.
Builder
>())
Collections
.
synchronizedMap
(
mutableMapOf
<
Int
,
Notification
.
Builder
>())
override
fun
onTaskRemoved
(
rootIntent
:
Intent
?)
{
override
fun
onTaskRemoved
(
rootIntent
:
Intent
?)
{
super
.
onTaskRemoved
(
rootIntent
)
super
.
onTaskRemoved
(
rootIntent
)
...
@@ -24,22 +22,23 @@ abstract class NotificationService : BaseService(), KoinComponent {
...
@@ -24,22 +22,23 @@ abstract class NotificationService : BaseService(), KoinComponent {
notifications
.
clear
()
notifications
.
clear
()
}
}
abstract
fun
createNotification
():
Notification
.
Builder
// --
// --
fun
update
(
fun
update
(
id
:
Int
,
id
:
Int
,
body
:
(
Notification
.
Builder
)
->
Unit
=
{}
body
:
(
Notification
.
Builder
)
->
Unit
=
{}
)
{
)
{
val
notification
=
notifications
.
getOrPut
(
id
)
{
defaultNotification
}
val
wasEmpty
=
notifications
.
isEmpty
()
val
notification
=
notifications
.
getOrPut
(
id
,
::
createNotification
).
also
(
body
)
notify
(
id
,
notification
.
also
(
body
).
build
())
if
(
wasEmpty
)
if
(
notifications
.
size
==
1
)
{
updateForeground
()
updateForeground
()
}
else
notify
(
id
,
notification
.
build
())
}
}
protected
fun
finish
Notify
(
protected
fun
last
Notify
(
id
:
Int
,
id
:
Int
,
editBody
:
(
Notification
.
Builder
)
->
Notification
.
Builder
?
=
{
null
}
editBody
:
(
Notification
.
Builder
)
->
Notification
.
Builder
?
=
{
null
}
)
:
Int
{
)
:
Int
{
...
@@ -57,6 +56,11 @@ abstract class NotificationService : BaseService(), KoinComponent {
...
@@ -57,6 +56,11 @@ abstract class NotificationService : BaseService(), KoinComponent {
return
newId
return
newId
}
}
protected
fun
remove
(
id
:
Int
)
=
notifications
.
remove
(
id
).
also
{
cancel
(
id
)
updateForeground
()
}
// ---
// ---
private
fun
notify
(
id
:
Int
,
notification
:
Notification
)
{
private
fun
notify
(
id
:
Int
,
notification
:
Notification
)
{
...
@@ -67,17 +71,14 @@ abstract class NotificationService : BaseService(), KoinComponent {
...
@@ -67,17 +71,14 @@ abstract class NotificationService : BaseService(), KoinComponent {
Notifications
.
mgr
.
cancel
(
id
)
Notifications
.
mgr
.
cancel
(
id
)
}
}
protected
fun
remove
(
id
:
Int
)
=
notifications
.
remove
(
id
).
also
{
cancel
(
id
)
updateForeground
()
}
private
fun
updateForeground
()
{
private
fun
updateForeground
()
{
if
(
hasNotifications
)
if
(
hasNotifications
)
{
startForeground
(
notifications
.
keys
.
first
(),
notifications
.
values
.
first
().
build
())
val
first
=
notifications
.
entries
.
first
()
else
startForeground
(
first
.
key
,
first
.
value
.
build
())
}
else
{
stopForeground
(
true
)
stopForeground
(
true
)
}
}
}
// --
// --
...
...
app/src/main/java/com/topjohnwu/magisk/core/download/RemoteFileService.kt
View file @
fc05f377
...
@@ -14,7 +14,8 @@ import com.topjohnwu.magisk.extensions.get
...
@@ -14,7 +14,8 @@ import com.topjohnwu.magisk.extensions.get
import
com.topjohnwu.magisk.extensions.subscribeK
import
com.topjohnwu.magisk.extensions.subscribeK
import
com.topjohnwu.magisk.extensions.writeTo
import
com.topjohnwu.magisk.extensions.writeTo
import
com.topjohnwu.magisk.model.entity.internal.DownloadSubject
import
com.topjohnwu.magisk.model.entity.internal.DownloadSubject
import
com.topjohnwu.magisk.model.entity.internal.DownloadSubject.*
import
com.topjohnwu.magisk.model.entity.internal.DownloadSubject.Magisk
import
com.topjohnwu.magisk.model.entity.internal.DownloadSubject.Module
import
com.topjohnwu.superuser.ShellUtils
import
com.topjohnwu.superuser.ShellUtils
import
io.reactivex.Completable
import
io.reactivex.Completable
import
okhttp3.ResponseBody
import
okhttp3.ResponseBody
...
@@ -27,19 +28,17 @@ abstract class RemoteFileService : NotificationService() {
...
@@ -27,19 +28,17 @@ abstract class RemoteFileService : NotificationService() {
val
service
:
GithubRawServices
by
inject
()
val
service
:
GithubRawServices
by
inject
()
override
val
defaultNotification
get
()
=
Notifications
.
progress
(
this
,
""
)
override
fun
onStartCommand
(
intent
:
Intent
?,
flags
:
Int
,
startId
:
Int
):
Int
{
override
fun
onStartCommand
(
intent
:
Intent
?,
flags
:
Int
,
startId
:
Int
):
Int
{
intent
?.
getParcelableExtra
<
DownloadSubject
>(
ARG_URL
)
?.
let
{
start
(
it
)
}
intent
?.
getParcelableExtra
<
DownloadSubject
>(
ARG_URL
)
?.
let
{
start
(
it
)
}
return
START_REDELIVER_INTENT
return
START_REDELIVER_INTENT
}
}
override
fun
createNotification
()
=
Notifications
.
progress
(
this
,
""
)
// ---
// ---
private
fun
start
(
subject
:
DownloadSubject
)
=
checkExisting
(
subject
)
private
fun
start
(
subject
:
DownloadSubject
)
=
checkExisting
(
subject
)
.
onErrorResumeNext
{
download
(
subject
)
}
.
onErrorResumeNext
{
download
(
subject
)
}
.
doOnSubscribe
{
update
(
subject
.
hashCode
())
{
it
.
setContentTitle
(
subject
.
title
)
}
}
.
subscribeK
(
onError
=
{
.
subscribeK
(
onError
=
{
Timber
.
e
(
it
)
Timber
.
e
(
it
)
failNotify
(
subject
)
failNotify
(
subject
)
...
@@ -52,16 +51,14 @@ abstract class RemoteFileService : NotificationService() {
...
@@ -52,16 +51,14 @@ abstract class RemoteFileService : NotificationService() {
private
fun
checkExisting
(
subject
:
DownloadSubject
)
=
Completable
.
fromAction
{
private
fun
checkExisting
(
subject
:
DownloadSubject
)
=
Completable
.
fromAction
{
check
(
subject
is
Magisk
)
{
"Download cache is disabled"
}
check
(
subject
is
Magisk
)
{
"Download cache is disabled"
}
check
(
subject
.
file
.
exists
()
&&
subject
.
file
.
also
{
ShellUtils
.
checkSum
(
"MD5"
,
subject
.
file
,
subject
.
magisk
.
md5
))
{
check
(
it
.
exists
()
&&
ShellUtils
.
checkSum
(
"MD5"
,
it
,
subject
.
magisk
.
md5
))
{
"The given file does not match checksum"
"The given file does not match checksum"
}
}
}
}
}
private
fun
download
(
subject
:
DownloadSubject
)
=
service
.
fetchFile
(
subject
.
url
)
private
fun
download
(
subject
:
DownloadSubject
)
=
service
.
fetchFile
(
subject
.
url
)
.
map
{
it
.
to
Stream
(
subject
.
hashCode
(),
subject
)
}
.
map
{
it
.
to
ProgressStream
(
subject
)
}
.
flatMapCompletable
{
stream
->
.
flatMapCompletable
{
stream
->
when
(
subject
)
{
when
(
subject
)
{
is
Module
->
service
.
fetchInstaller
()
is
Module
->
service
.
fetchInstaller
()
...
@@ -69,14 +66,14 @@ abstract class RemoteFileService : NotificationService() {
...
@@ -69,14 +66,14 @@ abstract class RemoteFileService : NotificationService() {
.
ignoreElement
()
.
ignoreElement
()
else
->
Completable
.
fromAction
{
stream
.
writeTo
(
subject
.
file
)
}
else
->
Completable
.
fromAction
{
stream
.
writeTo
(
subject
.
file
)
}
}
}
}.
doOnComplete
{
if
(
subject
is
Manager
)
handleAPK
(
subject
)
}
}
private
fun
ResponseBody
.
to
Stream
(
id
:
Int
,
subject
:
DownloadSubject
):
InputStream
{
private
fun
ResponseBody
.
to
ProgressStream
(
subject
:
DownloadSubject
):
InputStream
{
val
maxRaw
=
contentLength
()
val
maxRaw
=
contentLength
()
val
max
=
maxRaw
/
1
_000_000f
val
max
=
maxRaw
/
1
_000_000f
val
id
=
subject
.
hashCode
()
update
(
id
)
{
it
.
setContentTitle
(
subject
.
title
)
}
return
ProgressInputStream
(
byteStream
())
{
return
ProgressInputStream
(
byteStream
())
{
val
progress
=
it
/
1
_000_000f
val
progress
=
it
/
1
_000_000f
...
@@ -94,14 +91,14 @@ abstract class RemoteFileService : NotificationService() {
...
@@ -94,14 +91,14 @@ abstract class RemoteFileService : NotificationService() {
}
}
}
}
private
fun
failNotify
(
subject
:
DownloadSubject
)
=
finish
Notify
(
subject
.
hashCode
())
{
private
fun
failNotify
(
subject
:
DownloadSubject
)
=
last
Notify
(
subject
.
hashCode
())
{
send
(
0f
,
subject
)
send
(
0f
,
subject
)
it
.
setContentText
(
getString
(
R
.
string
.
download_file_error
))
it
.
setContentText
(
getString
(
R
.
string
.
download_file_error
))
.
setSmallIcon
(
android
.
R
.
drawable
.
stat_notify_error
)
.
setSmallIcon
(
android
.
R
.
drawable
.
stat_notify_error
)
.
setOngoing
(
false
)
.
setOngoing
(
false
)
}
}
private
fun
finishNotify
(
subject
:
DownloadSubject
)
=
finish
Notify
(
subject
.
hashCode
())
{
private
fun
finishNotify
(
subject
:
DownloadSubject
)
=
last
Notify
(
subject
.
hashCode
())
{
send
(
1f
,
subject
)
send
(
1f
,
subject
)
it
.
addActions
(
subject
)
it
.
addActions
(
subject
)
.
setContentText
(
getString
(
R
.
string
.
download_complete
))
.
setContentText
(
getString
(
R
.
string
.
download_complete
))
...
...
app/src/main/java/com/topjohnwu/magisk/core/tasks/FlashResultListener.kt
0 → 100644
View file @
fc05f377
package
com.topjohnwu.magisk.core.tasks
import
androidx.annotation.MainThread
interface
FlashResultListener
{
@MainThread
fun
onResult
(
success
:
Boolean
)
}
app/src/main/java/com/topjohnwu/magisk/core/tasks/FlashZip.kt
View file @
fc05f377
...
@@ -18,7 +18,7 @@ abstract class FlashZip(
...
@@ -18,7 +18,7 @@ abstract class FlashZip(
private
val
mUri
:
Uri
,
private
val
mUri
:
Uri
,
private
val
console
:
MutableList
<
String
>,
private
val
console
:
MutableList
<
String
>,
private
val
logs
:
MutableList
<
String
>
private
val
logs
:
MutableList
<
String
>
)
{
)
:
FlashResultListener
{
private
val
context
:
Context
by
inject
()
private
val
context
:
Context
by
inject
()
private
val
installFolder
=
File
(
context
.
cacheDir
,
"flash"
).
apply
{
private
val
installFolder
=
File
(
context
.
cacheDir
,
"flash"
).
apply
{
...
@@ -94,6 +94,4 @@ abstract class FlashZip(
...
@@ -94,6 +94,4 @@ abstract class FlashZip(
.
subscribeK
(
onError
=
{
onResult
(
false
)
})
{
onResult
(
it
)
}
.
subscribeK
(
onError
=
{
onResult
(
false
)
})
{
onResult
(
it
)
}
.
let
{
Unit
}
// ignores result disposable
.
let
{
Unit
}
// ignores result disposable
protected
abstract
fun
onResult
(
success
:
Boolean
)
}
}
app/src/main/java/com/topjohnwu/magisk/
model/flash
/Flashing.kt
→
app/src/main/java/com/topjohnwu/magisk/
core/tasks
/Flashing.kt
View file @
fc05f377
package
com.topjohnwu.magisk.
model.flash
package
com.topjohnwu.magisk.
core.tasks
import
android.content.Context
import
android.content.Context
import
android.net.Uri
import
android.net.Uri
import
androidx.core.os.postDelayed
import
androidx.core.os.postDelayed
import
com.topjohnwu.magisk.core.tasks.FlashZip
import
com.topjohnwu.magisk.extensions.inject
import
com.topjohnwu.magisk.extensions.inject
import
com.topjohnwu.superuser.Shell
import
com.topjohnwu.superuser.Shell
import
com.topjohnwu.superuser.internal.UiThreadHandler
import
com.topjohnwu.superuser.internal.UiThreadHandler
...
@@ -28,16 +27,7 @@ sealed class Flashing(
...
@@ -28,16 +27,7 @@ sealed class Flashing(
console
:
MutableList
<
String
>,
console
:
MutableList
<
String
>,
log
:
MutableList
<
String
>,
log
:
MutableList
<
String
>,
resultListener
:
FlashResultListener
resultListener
:
FlashResultListener
)
:
Flashing
(
uri
,
console
,
log
,
resultListener
)
{
)
:
Flashing
(
uri
,
console
,
log
,
resultListener
)
override
fun
onResult
(
success
:
Boolean
)
{
if
(
success
)
{
//Utils.loadModules()
}
super
.
onResult
(
success
)
}
}
class
Uninstall
(
class
Uninstall
(
uri
:
Uri
,
uri
:
Uri
,
...
...
app/src/main/java/com/topjohnwu/magisk/core/tasks/MagiskInstallImpl.kt
0 → 100644
View file @
fc05f377
package
com.topjohnwu.magisk.core.tasks
import
android.content.Context
import
android.net.Uri
import
android.os.Build
import
android.text.TextUtils
import
androidx.annotation.WorkerThread
import
androidx.core.net.toUri
import
com.topjohnwu.magisk.core.Config
import
com.topjohnwu.magisk.core.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.File
import
java.io.FileOutputStream
import
java.io.IOException
import
java.io.InputStream
import
java.nio.ByteBuffer
import
java.util.zip.ZipEntry
import
java.util.zip.ZipInputStream
abstract
class
MagiskInstallImpl
:
FlashResultListener
{
protected
lateinit
var
installDir
:
File
private
lateinit
var
srcBoot
:
String
private
lateinit
var
destFile
:
File
private
lateinit
var
zipUri
:
Uri
private
val
console
:
MutableList
<
String
>
private
val
logs
:
MutableList
<
String
>
private
var
tarOut
:
TarOutputStream
?
=
null
private
val
service
:
GithubRawServices
by
inject
()
protected
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
()
}
private
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
}
private
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
}
private
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
val
tarOut
=
TarOutputStream
(
destFile
)
this
.
tarOut
=
tarOut
TarInputStream
(
input
).
use
{
tarIn
->
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
}
}
}
private
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
(
magic
.
contentEquals
(
"ustar"
.
toByteArray
()))
{
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
}
private
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 verity 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
}
private
fun
flashBoot
():
Boolean
{
if
(!
"direct_install $installDir $srcBoot"
.
sh
().
isSuccess
)
return
false
arrayOf
(
"(KEEPVERITY=${Info.keepVerity} patch_dtb_partitions)"
,
"run_migrations"
).
sh
()
return
true
}
private
fun
storeBoot
():
Boolean
{
val
patched
=
SuFile
.
open
(
installDir
,
"new-boot.img"
)
try
{
val
os
=
tarOut
?.
let
{
it
.
putNextEntry
(
newEntry
(
if
(
srcBoot
.
contains
(
"recovery"
))
"recovery.img"
else
"boot.img"
,
patched
.
length
()))
tarOut
=
null
it
}
?:
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
}
private
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
)
protected
fun
doPatchFile
(
patchFile
:
Uri
)
=
extractZip
()
&&
handleFile
(
patchFile
)
&&
patchBoot
()
&&
storeBoot
()
protected
fun
direct
()
=
findImage
()
&&
extractZip
()
&&
patchBoot
()
&&
flashBoot
()
protected
fun
secondSlot
()
=
findSecondaryImage
()
&&
extractZip
()
&&
patchBoot
()
&&
flashBoot
()
&&
postOTA
()
protected
fun
fixEnv
(
zip
:
File
):
Boolean
{
installDir
=
SuFile
(
"/data/adb/magisk"
)
Shell
.
su
(
"rm -rf /data/adb/magisk/*"
).
exec
()
zipUri
=
zip
.
toUri
()
return
extractZip
()
&&
Shell
.
su
(
"fix_env"
).
exec
().
isSuccess
}
@WorkerThread
protected
abstract
fun
operations
():
Boolean
fun
exec
()
{
Single
.
fromCallable
{
operations
()
}.
subscribeK
{
onResult
(
it
)
}
}
}
app/src/main/java/com/topjohnwu/magisk/core/tasks/MagiskInstaller.kt
View file @
fc05f377
package
com.topjohnwu.magisk.core.tasks
package
com.topjohnwu.magisk.core.tasks
import
android.content.
Contex
t
import
android.content.
Inten
t
import
android.net.Uri
import
android.net.Uri
import
android.os.Build
import
android.widget.Toast
import
android.text.TextUtils
import
androidx.core.os.postDelayed
import
androidx.annotation.MainThread
import
androidx.localbroadcastmanager.content.LocalBroadcastManager
import
androidx.annotation.WorkerThread
import
com.topjohnwu.magisk.R
import
androidx.core.net.toUri
import
com.topjohnwu.magisk.core.utils.Utils
import
com.topjohnwu.magisk.core.Config
import
com.topjohnwu.magisk.extensions.reboot
import
com.topjohnwu.magisk.core.Info
import
com.topjohnwu.magisk.model.events.dialog.EnvFixDialog
import
com.topjohnwu.magisk.data.network.GithubRawServices
import
com.topjohnwu.magisk.di.Protected
import
com.topjohnwu.magisk.extensions.*
import
com.topjohnwu.magisk.net.Networking
import
com.topjohnwu.signing.SignBoot
import
com.topjohnwu.superuser.Shell
import
com.topjohnwu.superuser.Shell
import
com.topjohnwu.superuser.ShellUtils
import
com.topjohnwu.superuser.internal.UiThreadHandler
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.File
import
java.io.File
import
java.io.FileOutputStream
import
java.io.IOException
import
java.io.InputStream
import
java.nio.ByteBuffer
import
java.util.zip.ZipEntry
import
java.util.zip.ZipInputStream
abstract
class
MagiskInstaller
{
sealed
class
MagiskInstaller
(
file
:
Uri
,
private
val
console
:
MutableList
<
String
>,
logs
:
MutableList
<
String
>,
private
val
resultListener
:
FlashResultListener
)
:
MagiskInstallImpl
(
file
,
console
,
logs
)
{
protected
lateinit
var
installDir
:
File
override
fun
onResult
(
success
:
Boolean
)
{
private
lateinit
var
srcBoot
:
String
if
(
success
)
{
private
lateinit
var
destFile
:
File
console
.
add
(
"- All done!"
)
private
lateinit
var
zipUri
:
Uri
private
val
console
:
MutableList
<
String
>
private
val
logs
:
MutableList
<
String
>
private
var
tarOut
:
TarOutputStream
?
=
null
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
()
}
private
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
}
private
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
}
private
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
{
}
else
{
init64
.
delete
()
Shell
.
sh
(
"rm -rf $installDir"
).
submit
()
console
.
add
(
"! Installation failed"
)
}
}
"cd $installDir; chmod 755 *"
.
sh
()
resultListener
.
onResult
(
success
)
return
true
}
}
private
fun
newEntry
(
name
:
String
,
size
:
Long
):
TarEntry
{
class
Patch
(
console
.
add
(
"-- Writing: $name"
)
file
:
Uri
,
return
TarEntry
(
TarHeader
.
createHeader
(
name
,
size
,
0
,
false
,
420
/* 0644 */
))
private
val
uri
:
Uri
,
console
:
MutableList
<
String
>,
logs
:
MutableList
<
String
>,
resultListener
:
FlashResultListener
)
:
MagiskInstaller
(
file
,
console
,
logs
,
resultListener
)
{
override
fun
operations
()
=
doPatchFile
(
uri
)
}
}
@Throws
(
IOException
::
class
)
class
SecondSlot
(
private
fun
handleTar
(
input
:
InputStream
)
{
file
:
Uri
,
console
.
add
(
"- Processing tar file"
)
console
:
MutableList
<
String
>,
var
vbmeta
=
false
logs
:
MutableList
<
String
>,
val
tarOut
=
TarOutputStream
(
destFile
)
resultListener
:
FlashResultListener
this
.
tarOut
=
tarOut
)
:
MagiskInstaller
(
file
,
console
,
logs
,
resultListener
)
{
TarInputStream
(
input
).
use
{
tarIn
->
override
fun
operations
()
=
secondSlot
()
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
}
}
}
}
private
fun
handleFile
(
uri
:
Uri
):
Boolean
{
class
Direct
(
try
{
file
:
Uri
,
context
.
readUri
(
uri
).
buffered
().
use
{
console
:
MutableList
<
String
>,
it
.
mark
(
500
)
logs
:
MutableList
<
String
>,
val
magic
=
ByteArray
(
5
)
resultListener
:
FlashResultListener
if
(
it
.
skip
(
257
)
!=
257L
||
it
.
read
(
magic
)
!=
magic
.
size
)
{
)
:
MagiskInstaller
(
file
,
console
,
logs
,
resultListener
)
{
console
.
add
(
"! Invalid file"
)
override
fun
operations
()
=
direct
()
return
false
}
it
.
reset
()
if
(
magic
.
contentEquals
(
"ustar"
.
toByteArray
()))
{
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
}
}
private
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 verity 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
}
private
fun
flashBoot
():
Boolean
{
if
(!
"direct_install $installDir $srcBoot"
.
sh
().
isSuccess
)
return
false
arrayOf
(
"(KEEPVERITY=${Info.keepVerity} patch_dtb_partitions)"
,
"run_migrations"
).
sh
()
return
true
}
private
fun
storeBoot
():
Boolean
{
val
patched
=
SuFile
.
open
(
installDir
,
"new-boot.img"
)
try
{
val
os
=
tarOut
?.
let
{
it
.
putNextEntry
(
newEntry
(
if
(
srcBoot
.
contains
(
"recovery"
))
"recovery.img"
else
"boot.img"
,
patched
.
length
()))
tarOut
=
null
it
}
?:
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
}
private
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
)
protected
fun
doPatchFile
(
patchFile
:
Uri
)
=
extractZip
()
&&
handleFile
(
patchFile
)
&&
patchBoot
()
&&
storeBoot
()
protected
fun
direct
()
=
findImage
()
&&
extractZip
()
&&
patchBoot
()
&&
flashBoot
()
protected
fun
secondSlot
()
=
findSecondaryImage
()
&&
extractZip
()
&&
patchBoot
()
&&
flashBoot
()
&&
postOTA
()
protected
fun
fixEnv
():
Boolean
{
val
context
=
get
<
Context
>()
val
zip
:
File
=
context
.
cachedFile
(
"magisk.zip"
)
installDir
=
SuFile
(
"/data/adb/magisk"
)
Shell
.
su
(
"rm -rf /data/adb/magisk/*"
).
exec
()
if
(!
ShellUtils
.
checkSum
(
"MD5"
,
zip
,
Info
.
remote
.
magisk
.
md5
))
Networking
.
get
(
Info
.
remote
.
magisk
.
link
).
execForFile
(
zip
)
zipUri
=
zip
.
toUri
()
return
extractZip
()
&&
Shell
.
su
(
"fix_env"
).
exec
().
isSuccess
}
@WorkerThread
protected
abstract
fun
operations
():
Boolean
@MainThread
class
EnvFixTask
(
protected
abstract
fun
onResult
(
success
:
Boolean
)
private
val
zip
:
File
)
:
MagiskInstallImpl
()
{
override
fun
operations
()
=
fixEnv
(
zip
)
fun
exec
()
{
override
fun
onResult
(
success
:
Boolean
)
{
Single
.
fromCallable
{
operations
()
}.
subscribeK
{
onResult
(
it
)
}
LocalBroadcastManager
.
getInstance
(
context
).
sendBroadcast
(
Intent
(
EnvFixDialog
.
DISMISS
))
Utils
.
toast
(
if
(
success
)
R
.
string
.
reboot_delay_toast
else
R
.
string
.
setup_fail
,
Toast
.
LENGTH_LONG
)
if
(
success
)
UiThreadHandler
.
handler
.
postDelayed
(
5000
)
{
reboot
()
}
}
}
}
}
app/src/main/java/com/topjohnwu/magisk/legacy/flash/FlashViewModel.kt
View file @
fc05f377
...
@@ -12,13 +12,13 @@ import androidx.databinding.ObservableArrayList
...
@@ -12,13 +12,13 @@ import androidx.databinding.ObservableArrayList
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.core.Config
import
com.topjohnwu.magisk.core.Config
import
com.topjohnwu.magisk.core.Const
import
com.topjohnwu.magisk.core.Const
import
com.topjohnwu.magisk.core.tasks.FlashResultListener
import
com.topjohnwu.magisk.core.tasks.Flashing
import
com.topjohnwu.magisk.core.tasks.MagiskInstaller
import
com.topjohnwu.magisk.extensions.*
import
com.topjohnwu.magisk.extensions.*
import
com.topjohnwu.magisk.model.binding.BindingAdapter
import
com.topjohnwu.magisk.model.binding.BindingAdapter
import
com.topjohnwu.magisk.model.entity.recycler.ConsoleItem
import
com.topjohnwu.magisk.model.entity.recycler.ConsoleItem
import
com.topjohnwu.magisk.model.events.SnackbarEvent
import
com.topjohnwu.magisk.model.events.SnackbarEvent
import
com.topjohnwu.magisk.model.flash.FlashResultListener
import
com.topjohnwu.magisk.model.flash.Flashing
import
com.topjohnwu.magisk.model.flash.Patching
import
com.topjohnwu.magisk.ui.base.BaseViewModel
import
com.topjohnwu.magisk.ui.base.BaseViewModel
import
com.topjohnwu.magisk.ui.base.diffListOf
import
com.topjohnwu.magisk.ui.base.diffListOf
import
com.topjohnwu.magisk.ui.base.itemBindingOf
import
com.topjohnwu.magisk.ui.base.itemBindingOf
...
@@ -60,26 +60,26 @@ class FlashViewModel(
...
@@ -60,26 +60,26 @@ class FlashViewModel(
Const
.
Value
.
UNINSTALL
->
Flashing
Const
.
Value
.
UNINSTALL
->
Flashing
.
Uninstall
(
installer
,
outItems
,
logItems
,
this
)
.
Uninstall
(
installer
,
outItems
,
logItems
,
this
)
.
exec
()
.
exec
()
Const
.
Value
.
FLASH_MAGISK
->
Patching
Const
.
Value
.
FLASH_MAGISK
->
MagiskInstaller
.
Direct
(
installer
,
outItems
,
logItems
,
this
)
.
Direct
(
installer
,
outItems
,
logItems
,
this
)
.
exec
()
.
exec
()
Const
.
Value
.
FLASH_INACTIVE_SLOT
->
Patching
Const
.
Value
.
FLASH_INACTIVE_SLOT
->
MagiskInstaller
.
SecondSlot
(
installer
,
outItems
,
logItems
,
this
)
.
SecondSlot
(
installer
,
outItems
,
logItems
,
this
)
.
exec
()
.
exec
()
Const
.
Value
.
PATCH_FILE
->
Patching
Const
.
Value
.
PATCH_FILE
->
MagiskInstaller
.
File
(
installer
,
uri
?:
return
,
outItems
,
logItems
,
this
)
.
Patch
(
installer
,
uri
?:
return
,
outItems
,
logItems
,
this
)
.
exec
()
.
exec
()
}
}
}
}
override
fun
onResult
(
isS
uccess
:
Boolean
)
{
override
fun
onResult
(
s
uccess
:
Boolean
)
{
state
=
if
(
isS
uccess
)
State
.
LOADED
else
State
.
LOADING_FAILED
state
=
if
(
s
uccess
)
State
.
LOADED
else
State
.
LOADING_FAILED
behaviorText
.
value
=
when
{
behaviorText
.
value
=
when
{
isS
uccess
->
resources
.
getString
(
R
.
string
.
done
)
s
uccess
->
resources
.
getString
(
R
.
string
.
done
)
else
->
resources
.
getString
(
R
.
string
.
failure
)
else
->
resources
.
getString
(
R
.
string
.
failure
)
}
}
if
(
isS
uccess
)
{
if
(
s
uccess
)
{
Handler
().
postDelayed
(
500
)
{
Handler
().
postDelayed
(
500
)
{
showRestartTitle
.
value
=
true
showRestartTitle
.
value
=
true
}
}
...
...
app/src/main/java/com/topjohnwu/magisk/model/entity/internal/Configuration.kt
View file @
fc05f377
...
@@ -31,6 +31,9 @@ sealed class Configuration : Parcelable {
...
@@ -31,6 +31,9 @@ sealed class Configuration : Parcelable {
@Parcelize
@Parcelize
object
Uninstall
:
Configuration
()
object
Uninstall
:
Configuration
()
@Parcelize
object
EnvFix
:
Configuration
()
@Parcelize
@Parcelize
data class
Patch
(
val
fileUri
:
Uri
)
:
Configuration
()
data class
Patch
(
val
fileUri
:
Uri
)
:
Configuration
()
...
...
app/src/main/java/com/topjohnwu/magisk/model/entity/internal/DownloadSubject.kt
View file @
fc05f377
...
@@ -20,7 +20,7 @@ sealed class DownloadSubject : Parcelable {
...
@@ -20,7 +20,7 @@ sealed class DownloadSubject : Parcelable {
open
val
title
:
String
get
()
=
file
.
name
open
val
title
:
String
get
()
=
file
.
name
@Parcelize
@Parcelize
data
class
Module
(
class
Module
(
val
module
:
Repo
,
val
module
:
Repo
,
val
configuration
:
Configuration
val
configuration
:
Configuration
)
:
DownloadSubject
()
{
)
:
DownloadSubject
()
{
...
@@ -33,7 +33,7 @@ sealed class DownloadSubject : Parcelable {
...
@@ -33,7 +33,7 @@ sealed class DownloadSubject : Parcelable {
}
}
@Parcelize
@Parcelize
data
class
Manager
(
class
Manager
(
val
configuration
:
Configuration
.
APK
val
configuration
:
Configuration
.
APK
)
:
DownloadSubject
()
{
)
:
DownloadSubject
()
{
...
@@ -53,13 +53,13 @@ sealed class DownloadSubject : Parcelable {
...
@@ -53,13 +53,13 @@ sealed class DownloadSubject : Parcelable {
}
}
sealed
class
Magisk
:
DownloadSubject
()
{
abstract
class
Magisk
:
DownloadSubject
()
{
abstract
val
configuration
:
Configuration
abstract
val
configuration
:
Configuration
val
magisk
:
MagiskJson
=
Info
.
remote
.
magisk
val
magisk
:
MagiskJson
=
Info
.
remote
.
magisk
@Parcelize
@Parcelize
data class
Flash
(
private
class
DownloadInternal
(
override
val
configuration
:
Configuration
override
val
configuration
:
Configuration
)
:
Magisk
()
{
)
:
Magisk
()
{
override
val
url
:
String
get
()
=
magisk
.
link
override
val
url
:
String
get
()
=
magisk
.
link
...
@@ -72,8 +72,8 @@ sealed class DownloadSubject : Parcelable {
...
@@ -72,8 +72,8 @@ sealed class DownloadSubject : Parcelable {
}
}
@Parcelize
@Parcelize
class
Uninstall
:
Magisk
()
{
private
class
Uninstall
:
Magisk
()
{
override
val
configuration
:
Configuration
get
()
=
Configuration
.
Uninstall
override
val
configuration
get
()
=
Configuration
.
Uninstall
override
val
url
:
String
get
()
=
Info
.
remote
.
uninstaller
.
link
override
val
url
:
String
get
()
=
Info
.
remote
.
uninstaller
.
link
@IgnoredOnParcel
@IgnoredOnParcel
...
@@ -83,8 +83,8 @@ sealed class DownloadSubject : Parcelable {
...
@@ -83,8 +83,8 @@ sealed class DownloadSubject : Parcelable {
}
}
@Parcelize
@Parcelize
class
Download
:
Magisk
()
{
private
class
Download
:
Magisk
()
{
override
val
configuration
:
Configuration
get
()
=
Configuration
.
Download
override
val
configuration
get
()
=
Configuration
.
Download
override
val
url
:
String
get
()
=
magisk
.
link
override
val
url
:
String
get
()
=
magisk
.
link
@IgnoredOnParcel
@IgnoredOnParcel
...
@@ -97,7 +97,8 @@ sealed class DownloadSubject : Parcelable {
...
@@ -97,7 +97,8 @@ sealed class DownloadSubject : Parcelable {
operator
fun
invoke
(
configuration
:
Configuration
)
=
when
(
configuration
)
{
operator
fun
invoke
(
configuration
:
Configuration
)
=
when
(
configuration
)
{
Configuration
.
Download
->
Download
()
Configuration
.
Download
->
Download
()
Configuration
.
Uninstall
->
Uninstall
()
Configuration
.
Uninstall
->
Uninstall
()
else
->
Flash
(
configuration
)
Configuration
.
EnvFix
,
is
Configuration
.
Flash
->
DownloadInternal
(
configuration
)
else
->
throw
IllegalArgumentException
()
}
}
}
}
...
...
app/src/main/java/com/topjohnwu/magisk/model/events/dialog/EnvFixDialog.kt
View file @
fc05f377
package
com.topjohnwu.magisk.model.events.dialog
package
com.topjohnwu.magisk.model.events.dialog
import
android.content.DialogInterface
import
android.content.BroadcastReceiver
import
android.widget.Toast
import
android.content.Context
import
android.content.Intent
import
android.content.IntentFilter
import
androidx.localbroadcastmanager.content.LocalBroadcastManager
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.core.
tasks.MagiskInstaller
import
com.topjohnwu.magisk.core.
download.DownloadService
import
com.topjohnwu.magisk.
core.utils.Utils
import
com.topjohnwu.magisk.
model.entity.internal.Configuration.EnvFix
import
com.topjohnwu.magisk.
extensions.reboot
import
com.topjohnwu.magisk.
model.entity.internal.DownloadSubject.Magisk
import
com.topjohnwu.magisk.view.MagiskDialog
import
com.topjohnwu.magisk.view.MagiskDialog
import
com.topjohnwu.superuser.internal.UiThreadHandler
import
org.koin.core.KoinComponent
class
EnvFixDialog
:
DialogEvent
()
{
class
EnvFixDialog
:
DialogEvent
()
{
...
@@ -21,14 +22,16 @@ class EnvFixDialog : DialogEvent() {
...
@@ -21,14 +22,16 @@ class EnvFixDialog : DialogEvent() {
onClick
{
onClick
{
dialog
.
applyTitle
(
R
.
string
.
setup_title
)
dialog
.
applyTitle
(
R
.
string
.
setup_title
)
.
applyMessage
(
R
.
string
.
setup_msg
)
.
applyMessage
(
R
.
string
.
setup_msg
)
.
applyButton
(
MagiskDialog
.
ButtonType
.
POSITIVE
)
{
.
resetButtons
()
title
=
""
}
.
applyButton
(
MagiskDialog
.
ButtonType
.
NEGATIVE
)
{
title
=
""
}
.
cancellable
(
false
)
.
cancellable
(
false
)
fixEnv
(
it
)
val
lbm
=
LocalBroadcastManager
.
getInstance
(
dialog
.
context
)
lbm
.
registerReceiver
(
object
:
BroadcastReceiver
()
{
override
fun
onReceive
(
context
:
Context
,
intent
:
Intent
?)
{
dialog
.
dismiss
()
lbm
.
unregisterReceiver
(
this
)
}
},
IntentFilter
(
DISMISS
))
DownloadService
(
dialog
.
context
)
{
subject
=
Magisk
(
EnvFix
)
}
}
}
}
}
.
applyButton
(
MagiskDialog
.
ButtonType
.
NEGATIVE
)
{
.
applyButton
(
MagiskDialog
.
ButtonType
.
NEGATIVE
)
{
...
@@ -36,20 +39,7 @@ class EnvFixDialog : DialogEvent() {
...
@@ -36,20 +39,7 @@ class EnvFixDialog : DialogEvent() {
}
}
.
let
{
Unit
}
.
let
{
Unit
}
private
fun
fixEnv
(
dialog
:
DialogInterface
)
{
companion
object
{
object
:
MagiskInstaller
(),
KoinComponent
{
const
val
DISMISS
=
"com.topjohnwu.magisk.ENV_DONE"
override
fun
operations
()
=
fixEnv
()
override
fun
onResult
(
success
:
Boolean
)
{
dialog
.
dismiss
()
Utils
.
toast
(
if
(
success
)
R
.
string
.
reboot_delay_toast
else
R
.
string
.
setup_fail
,
Toast
.
LENGTH_LONG
)
if
(
success
)
UiThreadHandler
.
handler
.
postDelayed
({
reboot
()
},
5000
)
}
}
}.
exec
()
}
}
}
app/src/main/java/com/topjohnwu/magisk/model/flash/FlashResultListener.kt
deleted
100644 → 0
View file @
5c0e8638
package
com.topjohnwu.magisk.model.flash
interface
FlashResultListener
{
fun
onResult
(
isSuccess
:
Boolean
)
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/model/flash/Patching.kt
deleted
100644 → 0
View file @
5c0e8638
package
com.topjohnwu.magisk.model.flash
import
android.net.Uri
import
com.topjohnwu.magisk.core.tasks.MagiskInstaller
import
com.topjohnwu.superuser.Shell
sealed
class
Patching
(
file
:
Uri
,
private
val
console
:
MutableList
<
String
>,
logs
:
MutableList
<
String
>,
private
val
resultListener
:
FlashResultListener
)
:
MagiskInstaller
(
file
,
console
,
logs
)
{
override
fun
onResult
(
success
:
Boolean
)
{
if
(
success
)
{
console
.
add
(
"- All done!"
)
}
else
{
Shell
.
sh
(
"rm -rf $installDir"
).
submit
()
console
.
add
(
"! Installation failed"
)
}
resultListener
.
onResult
(
success
)
}
class
File
(
file
:
Uri
,
private
val
uri
:
Uri
,
console
:
MutableList
<
String
>,
logs
:
MutableList
<
String
>,
resultListener
:
FlashResultListener
)
:
Patching
(
file
,
console
,
logs
,
resultListener
)
{
override
fun
operations
()
=
doPatchFile
(
uri
)
}
class
SecondSlot
(
file
:
Uri
,
console
:
MutableList
<
String
>,
logs
:
MutableList
<
String
>,
resultListener
:
FlashResultListener
)
:
Patching
(
file
,
console
,
logs
,
resultListener
)
{
override
fun
operations
()
=
secondSlot
()
}
class
Direct
(
file
:
Uri
,
console
:
MutableList
<
String
>,
logs
:
MutableList
<
String
>,
resultListener
:
FlashResultListener
)
:
Patching
(
file
,
console
,
logs
,
resultListener
)
{
override
fun
operations
()
=
direct
()
}
}
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