mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-08-14 15:27:25 +00:00
Compare commits
39 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
bb64ba0ef6 | ||
![]() |
d89a568897 | ||
![]() |
9fd1f41e8b | ||
![]() |
c1ab348673 | ||
![]() |
00247c7901 | ||
![]() |
3c75f474c6 | ||
![]() |
db1f5b0397 | ||
![]() |
db277c3e55 | ||
![]() |
b9c93c66f6 | ||
![]() |
a250e2b56c | ||
![]() |
cd96454886 | ||
![]() |
741b679306 | ||
![]() |
90013e486d | ||
![]() |
4e2ecdb920 | ||
![]() |
6e5df1f06b | ||
![]() |
9469e79e3c | ||
![]() |
db78c20161 | ||
![]() |
1699da1754 | ||
![]() |
754e690274 | ||
![]() |
6f74ed6ceb | ||
![]() |
71205bc530 | ||
![]() |
10e236abdf | ||
![]() |
2248af00f3 | ||
![]() |
7e61716277 | ||
![]() |
50edb8d072 | ||
![]() |
515f81944c | ||
![]() |
46d4708386 | ||
![]() |
aabc36f86b | ||
![]() |
e0d5d90267 | ||
![]() |
482a5b991b | ||
![]() |
20124fe410 | ||
![]() |
f8dcec116a | ||
![]() |
343a339aae | ||
![]() |
42606efe56 | ||
![]() |
cae58c8790 | ||
![]() |
3a39dd4049 | ||
![]() |
89ff3c6572 | ||
![]() |
7bf9c74216 | ||
![]() |
e2f3753551 |
@@ -19,7 +19,7 @@ Some highlight features:
|
||||
[Github](https://github.com/topjohnwu/Magisk/) is the only source where you can get official Magisk information and downloads.
|
||||
|
||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v24.3)
|
||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v24.3)
|
||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v25.0)
|
||||
[](https://raw.githubusercontent.com/topjohnwu/magisk-files/canary/app-release.apk)
|
||||
[](https://raw.githubusercontent.com/topjohnwu/magisk-files/canary/app-debug.apk)
|
||||
|
||||
|
@@ -97,7 +97,7 @@ dependencies {
|
||||
implementation("com.squareup.moshi:moshi:${vMoshi}")
|
||||
kapt("com.squareup.moshi:moshi-kotlin-codegen:${vMoshi}")
|
||||
|
||||
val vRoom = "2.4.2"
|
||||
val vRoom = "2.5.0-alpha02"
|
||||
implementation("androidx.room:room-runtime:${vRoom}")
|
||||
implementation("androidx.room:room-ktx:${vRoom}")
|
||||
kapt("androidx.room:room-compiler:${vRoom}")
|
||||
@@ -109,12 +109,12 @@ dependencies {
|
||||
implementation("androidx.biometric:biometric:1.1.0")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||
implementation("androidx.appcompat:appcompat:1.4.1")
|
||||
implementation("androidx.appcompat:appcompat:1.4.2")
|
||||
implementation("androidx.preference:preference:1.2.0")
|
||||
implementation("androidx.recyclerview:recyclerview:1.2.1")
|
||||
implementation("androidx.fragment:fragment-ktx:1.4.1")
|
||||
implementation("androidx.transition:transition:1.4.1")
|
||||
implementation("androidx.core:core-ktx:1.7.0")
|
||||
implementation("androidx.core:core-ktx:1.8.0")
|
||||
implementation("androidx.core:core-splashscreen:1.0.0-rc01")
|
||||
implementation("com.google.android.material:material:1.6.0")
|
||||
implementation("com.google.android.material:material:1.6.1")
|
||||
}
|
||||
|
@@ -27,6 +27,7 @@
|
||||
android:name=".ui.surequest.SuRequestActivity"
|
||||
android:directBootAware="true"
|
||||
android:exported="false"
|
||||
android:taskAffinity=""
|
||||
tools:ignore="AppLinkUrlError">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
@@ -0,0 +1,22 @@
|
||||
package com.topjohnwu.magisk.arch
|
||||
|
||||
import androidx.annotation.MainThread
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
abstract class AsyncLoadViewModel : BaseViewModel() {
|
||||
|
||||
private var loadingJob: Job? = null
|
||||
|
||||
@MainThread
|
||||
fun startLoading() {
|
||||
if (loadingJob?.isActive == true) {
|
||||
// Prevent multiple jobs from running at the same time
|
||||
return
|
||||
}
|
||||
loadingJob = viewModelScope.launch { doLoadWork() }
|
||||
}
|
||||
|
||||
protected abstract suspend fun doLoadWork()
|
||||
}
|
@@ -76,7 +76,10 @@ abstract class BaseFragment<Binding : ViewDataBinding> : Fragment(), ViewModelHo
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
viewModel.requestRefresh()
|
||||
viewModel.let {
|
||||
if (it is AsyncLoadViewModel)
|
||||
it.startLoading()
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun onPreBind(binding: Binding) {
|
||||
|
@@ -4,61 +4,29 @@ import android.Manifest.permission.REQUEST_INSTALL_PACKAGES
|
||||
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import androidx.databinding.Bindable
|
||||
import androidx.databinding.PropertyChangeRegistry
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.navigation.NavDirections
|
||||
import com.topjohnwu.magisk.BR
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.databinding.ObservableHost
|
||||
import com.topjohnwu.magisk.databinding.set
|
||||
import com.topjohnwu.magisk.events.BackPressEvent
|
||||
import com.topjohnwu.magisk.events.NavigationEvent
|
||||
import com.topjohnwu.magisk.events.PermissionEvent
|
||||
import com.topjohnwu.magisk.events.SnackbarEvent
|
||||
import kotlinx.coroutines.Job
|
||||
|
||||
abstract class BaseViewModel(
|
||||
initialState: State = State.LOADING
|
||||
) : ViewModel(), ObservableHost {
|
||||
abstract class BaseViewModel : ViewModel(), ObservableHost {
|
||||
|
||||
override var callbacks: PropertyChangeRegistry? = null
|
||||
|
||||
enum class State {
|
||||
LOADED, LOADING, LOADING_FAILED
|
||||
}
|
||||
|
||||
@get:Bindable
|
||||
val loading get() = state == State.LOADING
|
||||
@get:Bindable
|
||||
val loaded get() = state == State.LOADED
|
||||
@get:Bindable
|
||||
val loadFailed get() = state == State.LOADING_FAILED
|
||||
|
||||
val viewEvents: LiveData<ViewEvent> get() = _viewEvents
|
||||
|
||||
var state= initialState
|
||||
set(value) = set(value, field, { field = it }, BR.loading, BR.loaded, BR.loadFailed)
|
||||
|
||||
private val _viewEvents = MutableLiveData<ViewEvent>()
|
||||
private var runningJob: Job? = null
|
||||
val viewEvents: LiveData<ViewEvent> get() = _viewEvents
|
||||
|
||||
open fun onSaveState(state: Bundle) {}
|
||||
open fun onRestoreState(state: Bundle) {}
|
||||
|
||||
/** This should probably never be called manually, it's called manually via delegate. */
|
||||
@Synchronized
|
||||
fun requestRefresh() {
|
||||
if (runningJob?.isActive == true) {
|
||||
return
|
||||
}
|
||||
runningJob = refresh()
|
||||
}
|
||||
|
||||
protected open fun refresh(): Job? = null
|
||||
open fun onNetworkChanged(network: Boolean) {}
|
||||
|
||||
fun withPermission(permission: String, callback: (Boolean) -> Unit) {
|
||||
PermissionEvent(permission, callback).publish()
|
||||
|
@@ -89,7 +89,10 @@ abstract class UIActivity<Binding : ViewDataBinding> : BaseActivity(), ViewModel
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
viewModel.requestRefresh()
|
||||
viewModel.let {
|
||||
if (it is AsyncLoadViewModel)
|
||||
it.startLoading()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onEventDispatched(event: ViewEvent) = when (event) {
|
||||
|
@@ -17,12 +17,8 @@ interface ViewModelHolder : LifecycleOwner, ViewModelStoreOwner {
|
||||
val viewModel: BaseViewModel
|
||||
|
||||
fun startObserveLiveData() {
|
||||
viewModel.viewEvents.observe(this) {
|
||||
onEventDispatched(it)
|
||||
}
|
||||
Info.isConnected.observe(this) {
|
||||
viewModel.requestRefresh()
|
||||
}
|
||||
viewModel.viewEvents.observe(this, this::onEventDispatched)
|
||||
Info.isConnected.observe(this, viewModel::onNetworkChanged)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -10,7 +10,6 @@ import com.topjohnwu.magisk.core.repository.NetworkService
|
||||
import com.topjohnwu.magisk.core.utils.net.NetworkObserver
|
||||
import com.topjohnwu.magisk.ktx.getProperty
|
||||
import com.topjohnwu.superuser.ShellUtils.fastCmd
|
||||
import com.topjohnwu.superuser.internal.UiThreadHandler
|
||||
|
||||
val isRunningAsStub get() = Info.stub != null
|
||||
|
||||
@@ -47,7 +46,8 @@ object Info {
|
||||
val isConnected: LiveData<Boolean> by lazy {
|
||||
MutableLiveData(false).also { field ->
|
||||
NetworkObserver.observe(AppContext) {
|
||||
UiThreadHandler.run { field.value = it }
|
||||
remote = EMPTY_REMOTE
|
||||
field.postValue(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -46,7 +46,10 @@ class NetworkService(
|
||||
|
||||
private inline fun <T> safe(factory: () -> T): T? {
|
||||
return try {
|
||||
factory()
|
||||
if (Info.isConnected.value == true)
|
||||
factory()
|
||||
else
|
||||
null
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
null
|
||||
|
@@ -37,6 +37,7 @@ import java.io.*
|
||||
import java.nio.ByteBuffer
|
||||
import java.security.SecureRandom
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipFile
|
||||
|
||||
@@ -57,7 +58,7 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
private val localFS get() = FileSystemManager.getLocal()
|
||||
|
||||
private fun findImage(): Boolean {
|
||||
val bootPath = "find_boot_image; echo \"\$BOOTIMAGE\"".fsh()
|
||||
val bootPath = "RECOVERYMODE=${Config.recovery} find_boot_image; echo \"\$BOOTIMAGE\"".fsh()
|
||||
if (bootPath.isEmpty()) {
|
||||
console.add("! Unable to detect target image")
|
||||
return false
|
||||
@@ -420,20 +421,15 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
protected abstract suspend fun operations(): Boolean
|
||||
|
||||
open suspend fun exec(): Boolean {
|
||||
synchronized(Companion) {
|
||||
if (haveActiveSession)
|
||||
return false
|
||||
haveActiveSession = true
|
||||
}
|
||||
if (haveActiveSession.getAndSet(true))
|
||||
return false
|
||||
val result = withContext(Dispatchers.IO) { operations() }
|
||||
synchronized(Companion) {
|
||||
haveActiveSession = false
|
||||
}
|
||||
haveActiveSession.set(false)
|
||||
return result
|
||||
}
|
||||
|
||||
companion object {
|
||||
private var haveActiveSession = false
|
||||
private var haveActiveSession = AtomicBoolean(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -9,6 +9,7 @@ import androidx.databinding.ViewDataBinding
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.topjohnwu.magisk.BuildConfig.APPLICATION_ID
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.StubApk
|
||||
import com.topjohnwu.magisk.arch.NavigationActivity
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.core.Const
|
||||
@@ -98,10 +99,10 @@ abstract class SplashActivity<Binding : ViewDataBinding> : NavigationActivity<Bi
|
||||
|
||||
Config.load(prevPkg)
|
||||
handleRepackage(prevPkg)
|
||||
if (prevPkg != null && !isRunningAsStub) {
|
||||
if (prevPkg != null) {
|
||||
runOnUiThread {
|
||||
// Might have new configuration loaded, relaunch the activity
|
||||
relaunch()
|
||||
// Relaunch the process after package migration
|
||||
StubApk.restartProcess(this)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@@ -2,23 +2,22 @@ package com.topjohnwu.magisk.ui.deny
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.databinding.Bindable
|
||||
import com.topjohnwu.magisk.BR
|
||||
import com.topjohnwu.magisk.arch.BaseViewModel
|
||||
import com.topjohnwu.magisk.arch.AsyncLoadViewModel
|
||||
import com.topjohnwu.magisk.core.di.AppContext
|
||||
import com.topjohnwu.magisk.databinding.bindExtra
|
||||
import com.topjohnwu.magisk.databinding.filterableListOf
|
||||
import com.topjohnwu.magisk.databinding.set
|
||||
import com.topjohnwu.magisk.ktx.concurrentMap
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.asFlow
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.toCollection
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class DenyListViewModel : BaseViewModel() {
|
||||
class DenyListViewModel : AsyncLoadViewModel() {
|
||||
|
||||
var isShowSystem = false
|
||||
set(value) {
|
||||
@@ -43,13 +42,13 @@ class DenyListViewModel : BaseViewModel() {
|
||||
it.put(BR.viewModel, this)
|
||||
}
|
||||
|
||||
@get:Bindable
|
||||
var loading = true
|
||||
private set(value) = set(value, field, { field = it }, BR.loading)
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
override fun refresh() = viewModelScope.launch {
|
||||
if (!Utils.showSuperUser()) {
|
||||
state = State.LOADING_FAILED
|
||||
return@launch
|
||||
}
|
||||
state = State.LOADING
|
||||
override suspend fun doLoadWork() {
|
||||
loading = true
|
||||
val (apps, diff) = withContext(Dispatchers.Default) {
|
||||
val pm = AppContext.packageManager
|
||||
val denyList = Shell.cmd("magisk --denylist ls").exec().out
|
||||
@@ -84,6 +83,6 @@ class DenyListViewModel : BaseViewModel() {
|
||||
|
||||
(it.isChecked || (filterSystem() && filterOS())) && filterQuery()
|
||||
}
|
||||
state = State.LOADED
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ import android.content.pm.ActivityInfo
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.*
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.navigation.NavDeepLinkBuilder
|
||||
import com.topjohnwu.magisk.MainDirections
|
||||
import com.topjohnwu.magisk.R
|
||||
@@ -21,6 +22,8 @@ class FlashFragment : BaseFragment<FragmentFlashMd2Binding>() {
|
||||
override val layoutRes = R.layout.fragment_flash_md2
|
||||
override val viewModel by viewModel<FlashViewModel>()
|
||||
override val snackbarView: View get() = binding.snackbarContainer
|
||||
override val snackbarAnchorView: View?
|
||||
get() = if (binding.restartBtn.isShown) binding.restartBtn else super.snackbarAnchorView
|
||||
|
||||
private var defaultOrientation = -1
|
||||
|
||||
@@ -34,8 +37,20 @@ class FlashFragment : BaseFragment<FragmentFlashMd2Binding>() {
|
||||
setHasOptionsMenu(true)
|
||||
activity?.setTitle(R.string.flash_screen_title)
|
||||
|
||||
viewModel.subtitle.observe(this) {
|
||||
activity?.supportActionBar?.setSubtitle(it)
|
||||
viewModel.state.observe(this) {
|
||||
activity?.supportActionBar?.setSubtitle(
|
||||
when (it) {
|
||||
FlashViewModel.State.FLASHING -> R.string.flashing
|
||||
FlashViewModel.State.SUCCESS -> R.string.done
|
||||
FlashViewModel.State.FAILED -> R.string.failure
|
||||
}
|
||||
)
|
||||
if (it == FlashViewModel.State.SUCCESS && viewModel.showReboot) {
|
||||
binding.restartBtn.apply {
|
||||
if (!this.isVisible) this.show()
|
||||
if (!this.isFocused) this.requestFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +81,7 @@ class FlashFragment : BaseFragment<FragmentFlashMd2Binding>() {
|
||||
}
|
||||
|
||||
override fun onKeyEvent(event: KeyEvent): Boolean {
|
||||
return when(event.keyCode) {
|
||||
return when (event.keyCode) {
|
||||
KeyEvent.KEYCODE_VOLUME_UP,
|
||||
KeyEvent.KEYCODE_VOLUME_DOWN -> true
|
||||
else -> false
|
||||
@@ -74,7 +89,8 @@ class FlashFragment : BaseFragment<FragmentFlashMd2Binding>() {
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
if (viewModel.loading) return true
|
||||
if (viewModel.flashing.value == true)
|
||||
return true
|
||||
return super.onBackPressed()
|
||||
}
|
||||
|
||||
|
@@ -4,6 +4,7 @@ import android.view.MenuItem
|
||||
import androidx.databinding.Bindable
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.Transformations
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.topjohnwu.magisk.BR
|
||||
import com.topjohnwu.magisk.R
|
||||
@@ -27,13 +28,18 @@ import kotlinx.coroutines.launch
|
||||
|
||||
class FlashViewModel : BaseViewModel() {
|
||||
|
||||
enum class State {
|
||||
FLASHING, SUCCESS, FAILED
|
||||
}
|
||||
|
||||
private val _state = MutableLiveData(State.FLASHING)
|
||||
val state: LiveData<State> get() = _state
|
||||
val flashing = Transformations.map(state) { it == State.FLASHING }
|
||||
|
||||
@get:Bindable
|
||||
var showReboot = Info.isRooted
|
||||
set(value) = set(value, field, { field = it }, BR.showReboot)
|
||||
|
||||
private val _subtitle = MutableLiveData(R.string.flashing)
|
||||
val subtitle get() = _subtitle as LiveData<Int>
|
||||
|
||||
val items = diffListOf<ConsoleItem>()
|
||||
lateinit var args: FlashFragmentArgs
|
||||
|
||||
@@ -83,11 +89,7 @@ class FlashViewModel : BaseViewModel() {
|
||||
}
|
||||
|
||||
private fun onResult(success: Boolean) {
|
||||
state = if (success) State.LOADED else State.LOADING_FAILED
|
||||
when {
|
||||
success -> _subtitle.postValue(R.string.done)
|
||||
else -> _subtitle.postValue(R.string.failure)
|
||||
}
|
||||
_state.value = if (success) State.SUCCESS else State.FAILED
|
||||
}
|
||||
|
||||
fun onMenuItemClicked(item: MenuItem): Boolean {
|
||||
@@ -100,7 +102,8 @@ class FlashViewModel : BaseViewModel() {
|
||||
private fun savePressed() = withExternalRW {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val name = "magisk_install_log_%s.log".format(
|
||||
System.currentTimeMillis().toTime(timeFormatStandard))
|
||||
System.currentTimeMillis().toTime(timeFormatStandard)
|
||||
)
|
||||
val file = MediaStoreUtils.getFile(name, true)
|
||||
file.uri.outputStream().bufferedWriter().use { writer ->
|
||||
synchronized(logItems) {
|
||||
|
@@ -3,7 +3,6 @@ package com.topjohnwu.magisk.ui.home
|
||||
import android.content.Context
|
||||
import androidx.core.net.toUri
|
||||
import androidx.databinding.Bindable
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.topjohnwu.magisk.BR
|
||||
import com.topjohnwu.magisk.BuildConfig
|
||||
import com.topjohnwu.magisk.R
|
||||
@@ -23,16 +22,15 @@ import com.topjohnwu.magisk.ktx.await
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
import com.topjohnwu.magisk.utils.asText
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
enum class MagiskState {
|
||||
NOT_INSTALLED, UP_TO_DATE, OBSOLETE, LOADING
|
||||
}
|
||||
|
||||
class HomeViewModel(
|
||||
private val svc: NetworkService
|
||||
) : BaseViewModel() {
|
||||
) : AsyncLoadViewModel() {
|
||||
|
||||
enum class State {
|
||||
LOADING, INVALID, OUTDATED, UP_TO_DATE
|
||||
}
|
||||
|
||||
val magiskTitleBarrierIds =
|
||||
intArrayOf(R.id.home_magisk_icon, R.id.home_magisk_title, R.id.home_magisk_button)
|
||||
@@ -43,16 +41,16 @@ class HomeViewModel(
|
||||
var isNoticeVisible = Config.safetyNotice
|
||||
set(value) = set(value, field, { field = it }, BR.noticeVisible)
|
||||
|
||||
val stateMagisk
|
||||
val magiskState
|
||||
get() = when {
|
||||
!Info.env.isActive -> MagiskState.NOT_INSTALLED
|
||||
Info.env.versionCode < BuildConfig.VERSION_CODE -> MagiskState.OBSOLETE
|
||||
else -> MagiskState.UP_TO_DATE
|
||||
!Info.env.isActive -> State.INVALID
|
||||
Info.env.versionCode < BuildConfig.VERSION_CODE -> State.OUTDATED
|
||||
else -> State.UP_TO_DATE
|
||||
}
|
||||
|
||||
@get:Bindable
|
||||
var stateManager = MagiskState.LOADING
|
||||
set(value) = set(value, field, { field = it }, BR.stateManager)
|
||||
var appState = State.LOADING
|
||||
set(value) = set(value, field, { field = it }, BR.appState)
|
||||
|
||||
val magiskInstalledVersion
|
||||
get() = Info.env.run {
|
||||
@@ -83,14 +81,12 @@ class HomeViewModel(
|
||||
private var checkedEnv = false
|
||||
}
|
||||
|
||||
override fun refresh() = viewModelScope.launch {
|
||||
state = State.LOADING
|
||||
override suspend fun doLoadWork() {
|
||||
appState = State.LOADING
|
||||
Info.getRemote(svc)?.apply {
|
||||
state = State.LOADED
|
||||
|
||||
stateManager = when {
|
||||
BuildConfig.VERSION_CODE < magisk.versionCode -> MagiskState.OBSOLETE
|
||||
else -> MagiskState.UP_TO_DATE
|
||||
appState = when {
|
||||
BuildConfig.VERSION_CODE < magisk.versionCode -> State.OUTDATED
|
||||
else -> State.UP_TO_DATE
|
||||
}
|
||||
|
||||
val isDebug = Config.updateChannel == Config.Value.DEBUG_CHANNEL
|
||||
@@ -98,19 +94,12 @@ class HomeViewModel(
|
||||
("${magisk.version} (${magisk.versionCode}) (${stub.versionCode})" +
|
||||
if (isDebug) " (D)" else "").asText()
|
||||
} ?: run {
|
||||
state = State.LOADING_FAILED
|
||||
managerRemoteVersion = R.string.not_available.asText()
|
||||
}
|
||||
ensureEnv()
|
||||
}
|
||||
|
||||
val showTest = false
|
||||
|
||||
fun onTestPressed() = object : ViewEvent(), ActivityExecutor {
|
||||
override fun invoke(activity: UIActivity<*>) {
|
||||
/* Entry point to trigger test events within the app */
|
||||
}
|
||||
}.publish()
|
||||
override fun onNetworkChanged(network: Boolean) = startLoading()
|
||||
|
||||
fun onProgressUpdate(progress: Float, subject: Subject) {
|
||||
if (subject is App)
|
||||
@@ -123,14 +112,14 @@ class HomeViewModel(
|
||||
|
||||
fun onDeletePressed() = UninstallDialog().publish()
|
||||
|
||||
fun onManagerPressed() = when (state) {
|
||||
State.LOADED -> withExternalRW {
|
||||
fun onManagerPressed() = when (magiskState) {
|
||||
State.LOADING -> SnackbarEvent(R.string.loading).publish()
|
||||
State.INVALID -> SnackbarEvent(R.string.no_connection).publish()
|
||||
else -> withExternalRW {
|
||||
withInstallPermission {
|
||||
ManagerInstallDialog().publish()
|
||||
}
|
||||
}
|
||||
State.LOADING -> SnackbarEvent(R.string.loading).publish()
|
||||
else -> SnackbarEvent(R.string.no_connection).publish()
|
||||
}
|
||||
|
||||
fun onMagiskPressed() = withExternalRW {
|
||||
@@ -143,7 +132,7 @@ class HomeViewModel(
|
||||
}
|
||||
|
||||
private suspend fun ensureEnv() {
|
||||
if (MagiskState.NOT_INSTALLED == stateMagisk || checkedEnv) return
|
||||
if (magiskState == State.INVALID || checkedEnv) return
|
||||
val cmd = "env_check ${Info.env.versionString} ${Info.env.versionCode}"
|
||||
if (!Shell.cmd(cmd).await().isSuccess) {
|
||||
EnvFixDialog(this).publish()
|
||||
@@ -151,4 +140,10 @@ class HomeViewModel(
|
||||
checkedEnv = true
|
||||
}
|
||||
|
||||
val showTest = false
|
||||
fun onTestPressed() = object : ViewEvent(), ActivityExecutor {
|
||||
override fun invoke(activity: UIActivity<*>) {
|
||||
/* Entry point to trigger test events within the app */
|
||||
}
|
||||
}.publish()
|
||||
}
|
||||
|
@@ -95,7 +95,6 @@ class InstallViewModel(
|
||||
R.id.method_inactive_slot -> FlashFragment.flash(true).navigate(true)
|
||||
else -> error("Unknown value")
|
||||
}
|
||||
state = State.LOADING
|
||||
}
|
||||
|
||||
override fun onSaveState(state: Bundle) {
|
||||
|
@@ -5,7 +5,7 @@ import androidx.lifecycle.viewModelScope
|
||||
import com.topjohnwu.magisk.BR
|
||||
import com.topjohnwu.magisk.BuildConfig
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.arch.BaseViewModel
|
||||
import com.topjohnwu.magisk.arch.AsyncLoadViewModel
|
||||
import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.repository.LogRepository
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils
|
||||
@@ -24,7 +24,7 @@ import java.io.FileInputStream
|
||||
|
||||
class LogViewModel(
|
||||
private val repo: LogRepository
|
||||
) : BaseViewModel() {
|
||||
) : AsyncLoadViewModel() {
|
||||
|
||||
// --- empty view
|
||||
|
||||
@@ -43,7 +43,7 @@ class LogViewModel(
|
||||
var consoleText = " "
|
||||
set(value) = set(value, field, { field = it }, BR.consoleText)
|
||||
|
||||
override fun refresh() = viewModelScope.launch {
|
||||
override suspend fun doLoadWork() {
|
||||
consoleText = repo.fetchMagiskLogs()
|
||||
val (suLogs, diff) = withContext(Dispatchers.Default) {
|
||||
val suLogs = repo.fetchSuLogs().map { LogRvItem(it) }
|
||||
@@ -89,12 +89,12 @@ class LogViewModel(
|
||||
|
||||
fun clearMagiskLog() = repo.clearMagiskLogs {
|
||||
SnackbarEvent(R.string.logs_cleared).publish()
|
||||
requestRefresh()
|
||||
startLoading()
|
||||
}
|
||||
|
||||
fun clearLog() = viewModelScope.launch {
|
||||
repo.clearLogs()
|
||||
SnackbarEvent(R.string.logs_cleared).publish()
|
||||
requestRefresh()
|
||||
startLoading()
|
||||
}
|
||||
}
|
||||
|
@@ -1,29 +1,24 @@
|
||||
package com.topjohnwu.magisk.ui.module
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.databinding.Bindable
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.topjohnwu.magisk.BR
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.arch.BaseViewModel
|
||||
import com.topjohnwu.magisk.arch.AsyncLoadViewModel
|
||||
import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.base.ContentResultCallback
|
||||
import com.topjohnwu.magisk.core.model.module.LocalModule
|
||||
import com.topjohnwu.magisk.core.model.module.OnlineModule
|
||||
import com.topjohnwu.magisk.databinding.MergeObservableList
|
||||
import com.topjohnwu.magisk.databinding.RvItem
|
||||
import com.topjohnwu.magisk.databinding.bindExtra
|
||||
import com.topjohnwu.magisk.databinding.diffListOf
|
||||
import com.topjohnwu.magisk.databinding.*
|
||||
import com.topjohnwu.magisk.events.GetContentEvent
|
||||
import com.topjohnwu.magisk.events.SnackbarEvent
|
||||
import com.topjohnwu.magisk.events.dialog.ModuleInstallDialog
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
class ModuleViewModel : BaseViewModel() {
|
||||
class ModuleViewModel : AsyncLoadViewModel() {
|
||||
|
||||
val bottomBarBarrierIds = intArrayOf(R.id.module_update, R.id.module_remove)
|
||||
|
||||
@@ -36,6 +31,10 @@ class ModuleViewModel : BaseViewModel() {
|
||||
|
||||
val data get() = uri
|
||||
|
||||
@get:Bindable
|
||||
var loading = true
|
||||
private set(value) = set(value, field, { field = it }, BR.loading)
|
||||
|
||||
init {
|
||||
if (Info.env.isActive && LocalModule.loaded()) {
|
||||
items.insertItem(InstallModule)
|
||||
@@ -43,15 +42,15 @@ class ModuleViewModel : BaseViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun refresh(): Job {
|
||||
return viewModelScope.launch {
|
||||
state = State.LOADING
|
||||
loadInstalled()
|
||||
state = State.LOADED
|
||||
loadUpdateInfo()
|
||||
}
|
||||
override suspend fun doLoadWork() {
|
||||
loading = true
|
||||
loadInstalled()
|
||||
loading = false
|
||||
loadUpdateInfo()
|
||||
}
|
||||
|
||||
override fun onNetworkChanged(network: Boolean) = startLoading()
|
||||
|
||||
private suspend fun loadInstalled() {
|
||||
val installed = LocalModule.installed().map { LocalModuleRvItem(it) }
|
||||
val diff = withContext(Dispatchers.Default) {
|
||||
|
@@ -4,20 +4,18 @@ import android.annotation.SuppressLint
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES
|
||||
import android.os.Process
|
||||
import androidx.databinding.Bindable
|
||||
import androidx.databinding.ObservableArrayList
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.topjohnwu.magisk.BR
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.arch.BaseViewModel
|
||||
import com.topjohnwu.magisk.arch.AsyncLoadViewModel
|
||||
import com.topjohnwu.magisk.core.data.magiskdb.PolicyDao
|
||||
import com.topjohnwu.magisk.core.di.AppContext
|
||||
import com.topjohnwu.magisk.core.model.su.SuPolicy
|
||||
import com.topjohnwu.magisk.core.utils.BiometricHelper
|
||||
import com.topjohnwu.magisk.core.utils.currentLocale
|
||||
import com.topjohnwu.magisk.databinding.AnyDiffRvItem
|
||||
import com.topjohnwu.magisk.databinding.MergeObservableList
|
||||
import com.topjohnwu.magisk.databinding.bindExtra
|
||||
import com.topjohnwu.magisk.databinding.diffListOf
|
||||
import com.topjohnwu.magisk.databinding.*
|
||||
import com.topjohnwu.magisk.events.SnackbarEvent
|
||||
import com.topjohnwu.magisk.events.dialog.BiometricEvent
|
||||
import com.topjohnwu.magisk.events.dialog.SuperuserRevokeDialog
|
||||
@@ -31,7 +29,7 @@ import kotlinx.coroutines.withContext
|
||||
|
||||
class SuperuserViewModel(
|
||||
private val db: PolicyDao
|
||||
) : BaseViewModel() {
|
||||
) : AsyncLoadViewModel() {
|
||||
|
||||
private val itemNoData = TextItem(R.string.superuser_policy_none)
|
||||
|
||||
@@ -45,15 +43,17 @@ class SuperuserViewModel(
|
||||
it.put(BR.listener, this)
|
||||
}
|
||||
|
||||
// ---
|
||||
@get:Bindable
|
||||
var loading = true
|
||||
private set(value) = set(value, field, { field = it }, BR.loading)
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
override fun refresh() = viewModelScope.launch {
|
||||
override suspend fun doLoadWork() {
|
||||
if (!Utils.showSuperUser()) {
|
||||
state = State.LOADING_FAILED
|
||||
return@launch
|
||||
loading = false
|
||||
return
|
||||
}
|
||||
state = State.LOADING
|
||||
loading = true
|
||||
val (policies, diff) = withContext(Dispatchers.IO) {
|
||||
db.deleteOutdated()
|
||||
db.delete(AppContext.applicationInfo.uid)
|
||||
@@ -98,7 +98,7 @@ class SuperuserViewModel(
|
||||
itemsHelpers.clear()
|
||||
else if (itemsHelpers.isEmpty())
|
||||
itemsHelpers.add(itemNoData)
|
||||
state = State.LOADED
|
||||
loading = false
|
||||
}
|
||||
|
||||
// ---
|
||||
|
@@ -24,13 +24,9 @@ open class SuRequestActivity : UIActivity<ActivityRequestBinding>() {
|
||||
override val layoutRes: Int = R.layout.activity_request
|
||||
override val viewModel: SuRequestViewModel by viewModel()
|
||||
|
||||
override fun onBackPressed() {
|
||||
viewModel.denyPressed()
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
supportRequestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||
lockOrientation()
|
||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LOCKED
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
window.addFlags(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
@@ -62,7 +58,11 @@ open class SuRequestActivity : UIActivity<ActivityRequestBinding>() {
|
||||
return theme
|
||||
}
|
||||
|
||||
private fun lockOrientation() {
|
||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LOCKED
|
||||
override fun onBackPressed() {
|
||||
viewModel.denyPressed()
|
||||
}
|
||||
|
||||
override fun finish() {
|
||||
super.finishAndRemoveTask()
|
||||
}
|
||||
}
|
||||
|
@@ -24,7 +24,7 @@
|
||||
android:paddingTop="@dimen/internal_action_bar_size"
|
||||
app:fitsSystemWindowsInsets="top|bottom"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:invisibleUnless="@{viewModel.loaded}"
|
||||
app:invisible="@{viewModel.loading}"
|
||||
app:items="@{viewModel.items}"
|
||||
app:extraBindings="@{viewModel.extraBindings}"
|
||||
tools:listitem="@layout/item_hide_md2"
|
||||
@@ -52,24 +52,6 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
goneUnless="@{viewModel.loadFailed}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
tools:visibility="gone">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/not_available"
|
||||
android:textAppearance="@style/AppearanceFoundation.Title"
|
||||
android:textStyle="bold" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</layout>
|
||||
|
@@ -24,13 +24,13 @@
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/flash_content"
|
||||
app:items="@{viewModel.items}"
|
||||
scrollToLast="@{true}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical"
|
||||
app:fitsSystemWindowsInsets="start|end|bottom"
|
||||
app:items="@{viewModel.items}"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
tools:listitem="@layout/item_console_md2" />
|
||||
@@ -38,27 +38,31 @@
|
||||
</HorizontalScrollView>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
gone="@{!viewModel.loaded || !viewModel.showReboot}"
|
||||
android:id="@+id/restart_btn"
|
||||
gone="@{viewModel.flashing || !viewModel.showReboot}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="@dimen/l1"
|
||||
android:layout_marginBottom="@dimen/l1"
|
||||
android:clickable="@{!viewModel.flashing}"
|
||||
android:enabled="@{!viewModel.flashing}"
|
||||
android:focusable="true"
|
||||
android:onClick="@{() -> viewModel.restartPressed()}"
|
||||
android:text="@string/reboot"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="?colorOnPrimary"
|
||||
android:textStyle="bold"
|
||||
app:layout_fitsSystemWindowsInsets="bottom"
|
||||
app:backgroundTint="?colorPrimary"
|
||||
app:icon="@drawable/ic_restart"
|
||||
app:iconTint="?colorOnPrimary" />
|
||||
app:iconTint="?colorOnPrimary"
|
||||
app:layout_fitsSystemWindowsInsets="bottom" />
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/snackbar_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/snackbar_container"
|
||||
app:fitsSystemWindowsInsets="top|bottom"/>
|
||||
app:fitsSystemWindowsInsets="top|bottom" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
|
@@ -7,8 +7,6 @@
|
||||
|
||||
<import type="com.topjohnwu.magisk.core.Info" />
|
||||
|
||||
<import type="com.topjohnwu.magisk.ui.home.MagiskState" />
|
||||
|
||||
<import type="com.topjohnwu.magisk.ui.home.DeveloperItem" />
|
||||
|
||||
<import type="com.topjohnwu.magisk.ui.home.IconLink" />
|
||||
|
@@ -19,7 +19,7 @@
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/superuser_list"
|
||||
goneUnless="@{viewModel.loaded || !viewModel.items.empty}"
|
||||
gone="@{viewModel.loading}"
|
||||
app:items="@{viewModel.items}"
|
||||
app:extraBindings="@{viewModel.extraBindings}"
|
||||
android:layout_width="match_parent"
|
||||
@@ -33,7 +33,7 @@
|
||||
tools:listitem="@layout/item_policy_md2" />
|
||||
|
||||
<LinearLayout
|
||||
goneUnless="@{viewModel.loading && viewModel.items.empty}"
|
||||
goneUnless="@{viewModel.loading}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
@@ -54,24 +54,6 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
goneUnless="@{viewModel.loadFailed}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
tools:visibility="gone">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/not_available"
|
||||
android:textAppearance="@style/AppearanceFoundation.Title"
|
||||
android:textStyle="bold" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</layout>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
|
||||
<import type="com.topjohnwu.magisk.core.Info" />
|
||||
|
||||
<import type="com.topjohnwu.magisk.ui.home.MagiskState" />
|
||||
<import type="com.topjohnwu.magisk.ui.home.HomeViewModel.State" />
|
||||
|
||||
<variable
|
||||
name="viewModel"
|
||||
@@ -63,7 +63,7 @@
|
||||
|
||||
<Button
|
||||
style="@style/WidgetFoundation.Button"
|
||||
gone="@{viewModel.stateMagisk != MagiskState.OBSOLETE}"
|
||||
gone="@{viewModel.magiskState != State.OUTDATED}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="@{() -> viewModel.onMagiskPressed()}"
|
||||
@@ -74,7 +74,7 @@
|
||||
|
||||
<Button
|
||||
style="@style/WidgetFoundation.Button.Text"
|
||||
gone="@{viewModel.stateMagisk == MagiskState.OBSOLETE}"
|
||||
gone="@{viewModel.magiskState == State.OUTDATED}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
|
@@ -7,7 +7,7 @@
|
||||
|
||||
<import type="com.topjohnwu.magisk.core.Info" />
|
||||
|
||||
<import type="com.topjohnwu.magisk.ui.home.MagiskState" />
|
||||
<import type="com.topjohnwu.magisk.ui.home.HomeViewModel.State" />
|
||||
|
||||
<variable
|
||||
name="viewModel"
|
||||
@@ -64,7 +64,7 @@
|
||||
|
||||
<Button
|
||||
style="@style/WidgetFoundation.Button"
|
||||
gone="@{viewModel.stateManager != MagiskState.OBSOLETE}"
|
||||
gone="@{viewModel.appState != State.OUTDATED}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="@{() -> viewModel.onManagerPressed()}"
|
||||
@@ -75,7 +75,7 @@
|
||||
|
||||
<Button
|
||||
style="@style/WidgetFoundation.Button.Text"
|
||||
gone="@{viewModel.stateManager != MagiskState.UP_TO_DATE}"
|
||||
gone="@{viewModel.appState != State.UP_TO_DATE}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
|
@@ -10,7 +10,7 @@ env_check() {
|
||||
for file in busybox magiskboot magiskinit util_functions.sh boot_patch.sh; do
|
||||
[ -f "$MAGISKBIN/$file" ] || return 1
|
||||
done
|
||||
if [ "$2" -ge 24302 ]; then
|
||||
if [ "$2" -ge 25000 ]; then
|
||||
[ -f "$MAGISKBIN/magiskpolicy" ] || return 1
|
||||
fi
|
||||
grep -xqF "MAGISK_VER='$1'" "$MAGISKBIN/util_functions.sh" || return 1
|
||||
@@ -99,7 +99,8 @@ post_ota() {
|
||||
rm -f $1
|
||||
chmod 755 bootctl
|
||||
./bootctl hal-info || return
|
||||
[ $(./bootctl get-current-slot) -eq 0 ] && SLOT_NUM=1 || SLOT_NUM=0
|
||||
SLOT_NUM=0
|
||||
[ $(./bootctl get-current-slot) -eq 0 ] && SLOT_NUM=1
|
||||
./bootctl set-active-boot-slot $SLOT_NUM
|
||||
cat << EOF > post-fs-data.d/post_ota.sh
|
||||
/data/adb/bootctl mark-boot-successful
|
||||
@@ -142,18 +143,16 @@ adb_pm_install() {
|
||||
|
||||
check_boot_ramdisk() {
|
||||
# Create boolean ISAB
|
||||
[ -z $SLOT ] && ISAB=false || ISAB=true
|
||||
|
||||
# If we are running as recovery mode, then we do not have ramdisk
|
||||
[ "$RECOVERYMODE" = "true" ] && return 1
|
||||
ISAB=true
|
||||
[ -z $SLOT ] && ISAB=false
|
||||
|
||||
# If we are A/B, then we must have ramdisk
|
||||
$ISAB && return 0
|
||||
|
||||
# If we are using legacy SAR, but not A/B, assume we do not have ramdisk
|
||||
if grep ' / ' /proc/mounts | grep -q '/dev/root'; then
|
||||
# Override recovery mode to true if not set
|
||||
[ -z $RECOVERYMODE ] && RECOVERYMODE=true
|
||||
# Override recovery mode to true
|
||||
RECOVERYMODE=true
|
||||
return 1
|
||||
fi
|
||||
|
||||
@@ -173,7 +172,8 @@ check_encryption() {
|
||||
CRYPTOTYPE="file"
|
||||
else
|
||||
# We are either FDE or metadata encryption (which is also FBE)
|
||||
grep -q ' /metadata ' /proc/mounts && CRYPTOTYPE="file" || CRYPTOTYPE="block"
|
||||
CRYPTOTYPE="block"
|
||||
grep -q ' /metadata ' /proc/mounts && CRYPTOTYPE="file"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@@ -189,12 +189,14 @@ check_encryption() {
|
||||
mount_partitions() {
|
||||
[ "$(getprop ro.build.ab_update)" = "true" ] && SLOT=$(getprop ro.boot.slot_suffix)
|
||||
# Check whether non rootfs root dir exists
|
||||
grep ' / ' /proc/mounts | grep -qv 'rootfs' && SYSTEM_ROOT=true || SYSTEM_ROOT=false
|
||||
SYSTEM_ROOT=false
|
||||
grep ' / ' /proc/mounts | grep -qv 'rootfs' && SYSTEM_ROOT=true
|
||||
}
|
||||
|
||||
get_flags() {
|
||||
KEEPVERITY=$SYSTEM_ROOT
|
||||
[ "$(getprop ro.crypto.state)" = "encrypted" ] && ISENCRYPTED=true || ISENCRYPTED=false
|
||||
ISENCRYPTED=false
|
||||
[ "$(getprop ro.crypto.state)" = "encrypted" ] && ISENCRYPTED=true
|
||||
KEEPFORCEENCRYPT=$ISENCRYPTED
|
||||
# Although this most certainly won't work without root, keep it just in case
|
||||
if [ -e /dev/block/by-name/vbmeta_a ] || [ -e /dev/block/by-name/vbmeta ]; then
|
||||
@@ -204,7 +206,8 @@ get_flags() {
|
||||
fi
|
||||
# Preset PATCHVBMETAFLAG to false in the non-root case
|
||||
PATCHVBMETAFLAG=false
|
||||
# Do NOT preset RECOVERYMODE here
|
||||
# Make sure RECOVERYMODE has value
|
||||
[ -z $RECOVERYMODE ] && RECOVERYMODE=false
|
||||
}
|
||||
|
||||
run_migrations() { return; }
|
||||
@@ -217,13 +220,12 @@ grep_prop() { return; }
|
||||
|
||||
app_init() {
|
||||
mount_partitions
|
||||
RAMDISKEXIST=false
|
||||
check_boot_ramdisk && RAMDISKEXIST=true
|
||||
get_flags
|
||||
run_migrations
|
||||
SHA1=$(grep_prop SHA1 $MAGISKTMP/config)
|
||||
check_boot_ramdisk && RAMDISKEXIST=true || RAMDISKEXIST=false
|
||||
check_encryption
|
||||
# Make sure RECOVERYMODE has value
|
||||
[ -z $RECOVERYMODE ] && RECOVERYMODE=false
|
||||
}
|
||||
|
||||
export BOOTMODE=true
|
||||
|
@@ -13,7 +13,7 @@
|
||||
<string name="no_connection">L\'accesu a internet nun ta disponible</string>
|
||||
<string name="app_changelog">Rexistru de cambeos</string>
|
||||
<string name="loading">Cargando…</string>
|
||||
<string name="update">Anovamienu</string>
|
||||
<string name="update">Anovar</string>
|
||||
<string name="not_available">N/D</string>
|
||||
<string name="hide">Anubrir</string>
|
||||
<string name="home_package">Paquete</string>
|
||||
@@ -23,8 +23,8 @@
|
||||
<string name="home_follow_title">Redes sociales</string>
|
||||
<string name="home_item_source">Códigu fonte</string>
|
||||
<string name="home_support_content">Magisk ye y va ser de códigu abiertu y gratuitu. Sicasí, pues ayudanos faciendo una donación o collaborando.</string>
|
||||
<string name="home_installed_version">V. instalada</string>
|
||||
<string name="home_latest_version">Última v.</string>
|
||||
<string name="home_installed_version">Versión instalada</string>
|
||||
<string name="home_latest_version">Última versión</string>
|
||||
<string name="invalid_update_channel">La canal d\'anovamientu nun ye válida</string>
|
||||
<string name="uninstall_magisk_title">Desinstalar Magisk</string>
|
||||
<string name="uninstall_magisk_msg">¡Van quitase y desactivase tolos módulos y l\'accesu root!\nCualesquier almacenamientu internu ensin cifrar pente l\'usu de Magisk va volver cifrase.</string>
|
||||
@@ -37,13 +37,13 @@
|
||||
<string name="install_method_title">Métodu</string>
|
||||
<string name="install_next">Siguiente</string>
|
||||
<string name="install_start">Siguir</string>
|
||||
<string name="manager_download_install">Primi pa baxar ya instalar</string>
|
||||
<string name="manager_download_install">Primi equí pa baxalu ya instalalu</string>
|
||||
<string name="direct_install">Instalación direuta (aconséyase)</string>
|
||||
<string name="install_inactive_slot">Instalar na ralura inactiva (darréu del OTA)</string>
|
||||
<string name="install_inactive_slot_msg">¡El preséu va arrincar OBLIGATORIAMENTE na ralura inactiva darréu de reaniciar!\nUsa esta opción namás dempués d\'acabar l\'anovamientu per OTA.\n¿Quies siguir?</string>
|
||||
<string name="setup_title">Configuración adicional</string>
|
||||
<string name="select_patch_file">Esbillar y parchiar un ficheru</string>
|
||||
<string name="patch_file_msg">Esbilla una imaxe en bruto (*.img) o un archivu d\'ODIN (*.tar)</string>
|
||||
<string name="select_patch_file">Seleicionar y parchiar un ficheru</string>
|
||||
<string name="patch_file_msg">Seleiciona una imaxe en bruto (*.img) o un archivu d\'ODIN (*.tar)</string>
|
||||
<string name="reboot_delay_toast">Reaniciando en 5 segundos…</string>
|
||||
<string name="flash_screen_title">Instalación</string>
|
||||
<!--Superuser-->
|
||||
@@ -106,7 +106,7 @@
|
||||
<string name="module_empty">Nun hai nengún módulu instaláu</string>
|
||||
<!--Settings-->
|
||||
<string name="settings_dark_mode_title">Mou del estilu</string>
|
||||
<string name="settings_dark_mode_message">¡Esbilla\'l mou que meyor s\'adaute al to estilu!</string>
|
||||
<string name="settings_dark_mode_message">¡Seleiciona\'l mou que meyor s\'adaute al to estilu!</string>
|
||||
<string name="settings_dark_mode_light">Claridá</string>
|
||||
<string name="settings_dark_mode_system">L\'estilu del sistema</string>
|
||||
<string name="settings_dark_mode_dark">Escuridá</string>
|
||||
@@ -130,7 +130,7 @@
|
||||
<string name="settings_denylist_summary">Los procesos de la llista d\'esclusión tienen toles modificaciones de Magisk anulaes</string>
|
||||
<string name="settings_denylist_error">Esta función rique l\'activación de: %1$s</string>
|
||||
<string name="settings_denylist_config_title">Configurar la llista d\'esclusión</string>
|
||||
<string name="settings_denylist_config_summary">Esbilla los procesos que s\'inclúin na llista d\'esclusión</string>
|
||||
<string name="settings_denylist_config_summary">Seleiciona los procesos que s\'inclúin na llista d\'esclusión</string>
|
||||
<string name="settings_hosts_title">Módulu «Systemless Hosts»</string>
|
||||
<string name="settings_hosts_summary">Un módulu pa les aplicaciones que bloquien anuncios</string>
|
||||
<string name="settings_hosts_toast">Amestóse\'l módulu «Systemless Hosts»</string>
|
||||
|
@@ -5,44 +5,49 @@
|
||||
<string name="superuser">Superuser</string>
|
||||
<string name="logs">Log</string>
|
||||
<string name="settings">Setelan</string>
|
||||
<string name="install">Instal</string>
|
||||
<string name="install">Pasang</string>
|
||||
<string name="section_home">Beranda</string>
|
||||
<string name="section_theme">Tema</string>
|
||||
<string name="denylist">DenyList</string>
|
||||
|
||||
<!--Home-->
|
||||
<string name="no_connection">Koneksi tidak tersedia</string>
|
||||
<string name="app_changelog">Catatan perubahan</string>
|
||||
<string name="loading">Memuat…</string>
|
||||
<string name="update">Update</string>
|
||||
<string name="update">Perbarui</string>
|
||||
<string name="not_available">N/A</string>
|
||||
<string name="hide">Tutup</string>
|
||||
<string name="home_package">Paket</string>
|
||||
<string name="home_app_title">Aplikasi</string>
|
||||
|
||||
<string name="home_notice_content">Unduh Magisk HANYA dari halaman GitHub resmi kami. File dari sumber yang tidak dikenal bisa berbahaya!</string>
|
||||
<string name="home_support_title">Dukung kami</string>
|
||||
<string name="home_follow_title">Ikuti Kami</string>
|
||||
<string name="home_item_source">Sumber</string>
|
||||
<string name="home_support_content">Magisk gratis dan bersumber terbuka, dan akan selalu seperti itu. Bagaimanapun juga Anda dapat menunjukan kepedulian Anda kepada kami dengan mengirimkan sedikit donasi.</string>
|
||||
<string name="home_installed_version">Terinstal</string>
|
||||
<string name="home_installed_version">Terpasang</string>
|
||||
<string name="home_latest_version">Terbaru</string>
|
||||
<string name="invalid_update_channel">Kanal update tidak valid</string>
|
||||
<string name="uninstall_magisk_title">Uninstal Magisk</string>
|
||||
<string name="invalid_update_channel">Saluran pembaruan tidak valid</string>
|
||||
<string name="uninstall_magisk_title">Copot Magisk</string>
|
||||
<string name="uninstall_magisk_msg">Semua modul akan dinonaktifkan/dihapus!\nRoot akan dihapus!\nData Anda berpotensi terenkripsi jika belum!</string>
|
||||
|
||||
<!--Install-->
|
||||
<string name="keep_force_encryption">Pertahankan enkripsi paksa</string>
|
||||
<string name="keep_dm_verity">Pertahankan AVB 2.0/dm-verity</string>
|
||||
<string name="patch_vbmeta">Tambal vbmeta dalam boot image</string>
|
||||
<string name="recovery_mode">Mode Recovery</string>
|
||||
<string name="install_options_title">Opsi</string>
|
||||
<string name="install_method_title">Metode</string>
|
||||
<string name="install_next">Berikutnya</string>
|
||||
<string name="install_start">Mulai</string>
|
||||
<string name="manager_download_install">Sentuh untuk download dan instal</string>
|
||||
<string name="direct_install">Langsung instal (Disarankan)</string>
|
||||
<string name="install_inactive_slot">Instal pada slot nonaktif (Setelah OTA)</string>
|
||||
<string name="manager_download_install">Sentuh untuk unduh dan pasang</string>
|
||||
<string name="direct_install">Langsung pasang (Disarankan)</string>
|
||||
<string name="install_inactive_slot">Pasang pada slot nonaktif (Setelah OTA)</string>
|
||||
<string name="install_inactive_slot_msg">Perangkat Anda akan DIPAKSA boot ke slot yang saat ini tidak aktif setelah perangkat dinyalakan ulang!\nGunakan opsi ini hanya setelah proses OTA selesai.\nLanjutkan?</string>
|
||||
<string name="setup_title">Penyiapan tambahan</string>
|
||||
<string name="select_patch_file">Pilih dan tambal file</string>
|
||||
<string name="patch_file_msg">Pilih mentahan image (*.img) atau file tar ODIN (*.tar)</string>
|
||||
<string name="reboot_delay_toast">Memulai kembali dalam 5 detik…</string>
|
||||
<string name="reboot_delay_toast">Memulai ulang dalam 5 detik…</string>
|
||||
<string name="flash_screen_title">Instalasi</string>
|
||||
|
||||
<!--Superuser-->
|
||||
@@ -94,16 +99,20 @@
|
||||
|
||||
<!--Module-->
|
||||
<string name="no_info_provided">(Info tidak tersedia)</string>
|
||||
<string name="reboot_userspace">Nyalakan ulang secara halus</string>
|
||||
<string name="reboot_recovery">Nyalakan ke mode Recovery</string>
|
||||
<string name="reboot_bootloader">Nyalakan ke mode Bootloader</string>
|
||||
<string name="reboot_download">Nyalakan ke mode Download</string>
|
||||
<string name="reboot_edl">Nyalakan ke mode EDL</string>
|
||||
<string name="reboot_userspace">Mulai ulang secara halus</string>
|
||||
<string name="reboot_recovery">Mulai ulang ke mode Recovery</string>
|
||||
<string name="reboot_bootloader">Mulai ulang ke mode Bootloader</string>
|
||||
<string name="reboot_download">Mulai ulang ke mode Download</string>
|
||||
<string name="reboot_edl">Mulai ulang ke mode EDL</string>
|
||||
<string name="module_version_author">%1$s oleh %2$s</string>
|
||||
<string name="module_state_remove">Hapus</string>
|
||||
<string name="module_state_restore">Pulihkan</string>
|
||||
<string name="module_action_install_external">Instal dari penyimpanan</string>
|
||||
<string name="update_available">Update tersedia</string>
|
||||
<string name="module_action_install_external">Pasang dari penyimpanan</string>
|
||||
<string name="update_available">Pembaruan tersedia</string>
|
||||
<string name="suspend_text_riru">Modul ditangguhkan karena %1$s diaktifkan</string>
|
||||
<string name="suspend_text_zygisk">Modul ditangguhkan karena %1$s tidak diaktifkan</string>
|
||||
<string name="zygisk_module_unloaded">Modul Zygisk tidak dimuat karena ketidakcocokan</string>
|
||||
<string name="module_empty">Tidak ada modul terpasang</string>
|
||||
|
||||
<!--Settings-->
|
||||
<string name="settings_dark_mode_title">Mode tema</string>
|
||||
@@ -111,17 +120,27 @@
|
||||
<string name="settings_dark_mode_light">Selalu terang</string>
|
||||
<string name="settings_dark_mode_system">Ikuti sistem</string>
|
||||
<string name="settings_dark_mode_dark">Selalu gelap</string>
|
||||
<string name="settings_download_path_title">Lokasi download</string>
|
||||
<string name="settings_download_path_title">Lokasi unduhan</string>
|
||||
<string name="settings_download_path_message">File akan disimpan ke %1$s</string>
|
||||
<string name="settings_hide_app_title">Sembunyikan aplikasi Magisk</string>
|
||||
<string name="settings_hide_app_summary">Pasang aplikasi proxy dengan ID paket acak dan label aplikasi khusus</string>
|
||||
<string name="settings_restore_app_title">Pulihkan aplikasi Magisk</string>
|
||||
<string name="settings_restore_app_summary">Tampilkan aplikasi and pulihkan APK asli</string>
|
||||
<string name="language">Bahasa</string>
|
||||
<string name="system_default">(Default sistem)</string>
|
||||
<string name="settings_check_update_title">Periksa update</string>
|
||||
<string name="settings_check_update_summary">Periksa update secara berkala di latar belakang</string>
|
||||
<string name="settings_update_channel_title">Kanal update</string>
|
||||
<string name="system_default">(Bawaan sistem)</string>
|
||||
<string name="settings_check_update_title">Periksa pembaruan</string>
|
||||
<string name="settings_check_update_summary">Periksa pembaruan secara berkala di latar belakang</string>
|
||||
<string name="settings_update_channel_title">Saluran pembaruan</string>
|
||||
<string name="settings_update_stable">Stabil</string>
|
||||
<string name="settings_update_beta">Beta</string>
|
||||
<string name="settings_update_custom">Kanal khusus</string>
|
||||
<string name="settings_update_custom">Saluran khusus</string>
|
||||
<string name="settings_update_custom_msg">Masukkan URL khusus</string>
|
||||
<string name="settings_zygisk_summary">Jalankan bagian-bagian Magisk dalam zygote daemon</string>
|
||||
<string name="settings_denylist_title">Paksa DenyList</string>
|
||||
<string name="settings_denylist_summary">Proses pada denylist akan mengembalikan semua modifikasi Magisk</string>
|
||||
<string name="settings_denylist_error">Fitur ini membutuhkan %1$s untuk diaktifkan</string>
|
||||
<string name="settings_denylist_config_title">Konfigurasi DenyList</string>
|
||||
<string name="settings_denylist_config_summary">Pilih proses yang akan disertakan pada denylist</string>
|
||||
<string name="settings_hosts_title">Host systemless</string>
|
||||
<string name="settings_hosts_summary">Dukungan host secara systemless untuk aplikasi pemblokir iklan</string>
|
||||
<string name="settings_hosts_toast">Menambahkan modul host systemless</string>
|
||||
@@ -142,8 +161,8 @@
|
||||
<string name="auto_response">Respons otomatis</string>
|
||||
<string name="request_timeout">Batas waktu permintaan</string>
|
||||
<string name="superuser_notification">Notifikasi superuser</string>
|
||||
<string name="settings_su_reauth_title">Autentikasi ulang setelah upgrade</string>
|
||||
<string name="settings_su_reauth_summary">Autentikasi ulang izin akses superuser setelah aplikasi diupgrade</string>
|
||||
<string name="settings_su_reauth_title">Autentikasi ulang setelah peningkatan</string>
|
||||
<string name="settings_su_reauth_summary">Autentikasi ulang izin akses superuser setelah aplikasi ditingkatkan</string>
|
||||
<string name="settings_su_tapjack_title">Aktifkan perlindungan tapjacking</string>
|
||||
<string name="settings_su_tapjack_summary">Dialog permintaan superuser tidak akan menanggapi masukan saat terhalangi oleh lapisan atau jendela lainnya</string>
|
||||
<string name="settings_su_biometric_title">Aktifkan autentikasi biometrik</string>
|
||||
@@ -171,35 +190,51 @@
|
||||
<string name="isolate_summary">Setiap sesi root akan memiliki ruang-nama tersendiri</string>
|
||||
|
||||
<!--Notifications-->
|
||||
<string name="update_channel">Update Magisk</string>
|
||||
<string name="update_channel">Pembaruan Magisk</string>
|
||||
<string name="progress_channel">Notifikasi Kemajuan</string>
|
||||
<string name="download_complete">Download selesai</string>
|
||||
<string name="download_file_error">Kesalahan saat mendownload file</string>
|
||||
<string name="magisk_update_title">Update Magisk tersedia!</string>
|
||||
<string name="updated_channel">Pembaruan Selesai</string>
|
||||
<string name="download_complete">Unduhan selesai</string>
|
||||
<string name="download_file_error">Kesalahan saat mengunduh file</string>
|
||||
<string name="magisk_update_title">Pembaruan Magisk tersedia!</string>
|
||||
<string name="updated_title">Magisk Diperbarui</string>
|
||||
<string name="updated_text">Ketuk untuk buka aplikasi</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="yes">Ya</string>
|
||||
<string name="no">Tidak</string>
|
||||
<string name="repo_install_title">Instal %1$s %2$s(%3$d)</string>
|
||||
<string name="download">Download</string>
|
||||
<string name="reboot">Nyalakan ulang</string>
|
||||
<string name="repo_install_title">Pasang %1$s %2$s(%3$d)</string>
|
||||
<string name="download">Unduh</string>
|
||||
<string name="reboot">Mulai ulang</string>
|
||||
<string name="release_notes">Catatan rilis</string>
|
||||
<string name="flashing">Memasang…</string>
|
||||
<string name="done">Selesai!</string>
|
||||
<string name="failure">Gagal!</string>
|
||||
<string name="hide_app_title">Menyembunyikan aplikasi Magisk…</string>
|
||||
<string name="open_link_failed_toast">Tidak ditemukan aplikasi untuk membuka link ini</string>
|
||||
<string name="complete_uninstall">Uninstal penuh</string>
|
||||
<string name="complete_uninstall">Pencopotan penuh</string>
|
||||
<string name="restore_img">Pulihkan image</string>
|
||||
<string name="restore_img_msg">Memulihkan…</string>
|
||||
<string name="restore_done">Pemulihan selesai!</string>
|
||||
<string name="restore_fail">Cadangan stock tidak ada!</string>
|
||||
<string name="setup_fail">Penyiapan gagal</string>
|
||||
<string name="env_fix_title">Perlu penyiapan tambahan</string>
|
||||
<string name="env_fix_msg">Perangkat Anda membutuhkan pengaturan tambahan untuk Magisk agar berfungsi dengan benar. Apakah Anda ingin melanjutkan dan Menyalakan ulang?</string>
|
||||
<string name="setup_msg">Memproses penyiapan lingkungan…</string>
|
||||
<string name="authenticate">Autentikasi</string>
|
||||
<string name="unsupport_magisk_title">Versi Magisk tidak didukung</string>
|
||||
<string name="unsupport_magisk_msg">Versi aplikasi ini tidak mendukung versi Magisk yang lebih rendah dari %1$s.\n\nAplikasi akan berperilaku seolah-olah tidak ada Magisk yang dipasang, harap tingkatkan Magisk sesegera mungkin.</string>
|
||||
<string name="unsupport_general_title">Keadaan tidak normal</string>
|
||||
<string name="unsupport_system_app_msg">Menjalankan aplikasi ini sebagai aplikasi sistem tidak didukung. Harap kembalikan aplikasi ke aplikasi pengguna.</string>
|
||||
<string name="unsupport_other_su_msg">Sebuah \"su\" biner bukan dari Magisk telah terdeteksi. Hapus semua solusi root yang bersaing dan/atau pasang ulang Magisk.</string>
|
||||
<string name="unsupport_external_storage_msg">Magisk dipasang ke penyimpanan eksternal. Harap pindahkan aplikasi ke penyimpanan internal.</string>
|
||||
<string name="unsupport_nonroot_stub_msg">Aplikasi Magisk yang tersembunyi tidak dapat terus berfungsi karena root hilang. Tolong pulihkan APK asli.</string>
|
||||
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
||||
<string name="external_rw_permission_denied">Berikan izin akses ke penyimpanan untuk mengaktifkan fungsi ini</string>
|
||||
<string name="install_unknown_denied">Izinkan "Sumber tidak dikenal" untuk mengaktifkan fungsi ini</string>
|
||||
<string name="add_shortcut_title">Tambahkan pintasan ke layar utama</string>
|
||||
<string name="add_shortcut_msg">Setelah menyembunyikan aplikasi ini, nama dan ikonnya mungkin sulit dikenali. Apakah Anda ingin menambahkan pintasan cantik ke layar utama?</string>
|
||||
<string name="app_not_found">Tidak ditemukan aplikasi untuk menangani tindakan ini</string>
|
||||
<string name="reboot_apply_change">Mulai ulang untuk menerapkan perubahan</string>
|
||||
<string name="restore_app_confirmation">Ini akan mengembalikan aplikasi tersembunyi kembali ke aplikasi asli. Apakah Anda benar-benar ingin melakukan ini?</string>
|
||||
|
||||
</resources>
|
||||
|
@@ -234,7 +234,7 @@
|
||||
<string name="add_shortcut_title">Добавление ярлыка</string>
|
||||
<string name="add_shortcut_msg">После скрытия приложения Magisk его название и иконка могут быть неудобны для восприятия. Хотите создать ярлык на рабочем столе?</string>
|
||||
<string name="app_not_found">Приложение для обработки этого действия не найдено</string>
|
||||
<string name="reboot_apply_change">Перезагрузить для применения изменений</string>
|
||||
<string name="reboot_apply_change">Перезагрузите устройство для применения изменений</string>
|
||||
<string name="restore_app_confirmation">Это действие восстановит пересобранное для скрытия приложение к исходному состоянию. Вы действительно хотите продолжить?</string>
|
||||
|
||||
</resources>
|
||||
|
@@ -130,7 +130,7 @@
|
||||
<string name="system_default">(Mặc định hệ thống)</string>
|
||||
<string name="settings_check_update_title">Kiểm tra cập nhật</string>
|
||||
<string name="settings_check_update_summary">Kiểm tra định kỳ các bản cập nhật trong nền</string>
|
||||
<string name="settings_update_channel_title">Cập nhật kênh</string>
|
||||
<string name="settings_update_channel_title">Kênh cập nhật</string>
|
||||
<string name="settings_update_stable">Ổn định</string>
|
||||
<string name="settings_update_beta">Beta</string>
|
||||
<string name="settings_update_custom">Kênh tùy chỉnh</string>
|
||||
|
@@ -3,7 +3,7 @@
|
||||
<!--Sections-->
|
||||
<string name="modules">模組</string>
|
||||
<string name="superuser">超級使用者</string>
|
||||
<string name="logs">記錄</string>
|
||||
<string name="logs">紀錄</string>
|
||||
<string name="settings">設定</string>
|
||||
<string name="install">安裝</string>
|
||||
<string name="section_home">首頁</string>
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
<!--Home-->
|
||||
<string name="no_connection">無法連線</string>
|
||||
<string name="app_changelog">變更記錄</string>
|
||||
<string name="app_changelog">變更紀錄</string>
|
||||
<string name="loading">載入中……</string>
|
||||
<string name="update">更新</string>
|
||||
<string name="not_available">無</string>
|
||||
@@ -41,9 +41,9 @@
|
||||
<string name="install_next">下一步</string>
|
||||
<string name="install_start">開始執行</string>
|
||||
<string name="manager_download_install">點選以下載並安裝</string>
|
||||
<string name="direct_install">直接安裝 (建議)</string>
|
||||
<string name="install_inactive_slot">安裝到非使用中的槽位 (在 OTA 更新後)</string>
|
||||
<string name="install_inactive_slot_msg">您的裝置將在下次重新啟動後強制切換到非使用中的槽位!\n這個選項僅在 OTA 更新完畢後使用。\n是否繼續?</string>
|
||||
<string name="direct_install">直接安裝(建議)</string>
|
||||
<string name="install_inactive_slot">安裝到非使用中的槽位(在 OTA 更新後)</string>
|
||||
<string name="install_inactive_slot_msg">您的裝置將在下次重新啟動後強制切換到非使用中的槽位!\n這個選項僅在 OTA 更新完畢後使用。\n請問是否繼續?</string>
|
||||
<string name="setup_title">修復安裝</string>
|
||||
<string name="select_patch_file">選擇並修補檔案</string>
|
||||
<string name="patch_file_msg">請選取未修改過的映像檔 (*.img) 或 Odin 的 TAR 檔案 (*.tar)</string>
|
||||
@@ -69,8 +69,8 @@
|
||||
<string name="su_snack_deny">已拒絕 %1$s 使用超級使用者的權限</string>
|
||||
<string name="su_snack_notif_on">已啟用 %1$s 通知</string>
|
||||
<string name="su_snack_notif_off">已停用 %1$s 通知</string>
|
||||
<string name="su_snack_log_on">已啟用 %1$s 寫入記錄</string>
|
||||
<string name="su_snack_log_off">已停用 %1$s 寫入記錄</string>
|
||||
<string name="su_snack_log_on">已啟用 %1$s 寫入紀錄</string>
|
||||
<string name="su_snack_log_off">已停用 %1$s 寫入紀錄</string>
|
||||
<string name="su_revoke_title">撤銷權限?</string>
|
||||
<string name="su_revoke_msg">確定撤銷 %1$s 的權限?</string>
|
||||
<string name="toast">快顯通知</string>
|
||||
@@ -81,13 +81,13 @@
|
||||
<string name="superuser_policy_none">目前沒有任何應用程式要求超級使用者的權限。</string>
|
||||
|
||||
<!--Logs-->
|
||||
<string name="log_data_none">您的記錄是空的,請嘗試使用具備需要超級使用者權限的應用程式。</string>
|
||||
<string name="log_data_magisk_none">Magisk 記錄是空的,很奇怪……</string>
|
||||
<string name="menuSaveLog">儲存記錄</string>
|
||||
<string name="menuClearLog">清除記錄</string>
|
||||
<string name="logs_cleared">已成功清除記錄</string>
|
||||
<string name="pid">PID:%1$d</string>
|
||||
<string name="target_uid">目標 UID:%1$d</string>
|
||||
<string name="log_data_none">您的紀錄是空的,請嘗試使用具備需要超級使用者權限的應用程式。</string>
|
||||
<string name="log_data_magisk_none">Magisk 的紀錄是空的,很奇怪……</string>
|
||||
<string name="menuSaveLog">儲存紀錄</string>
|
||||
<string name="menuClearLog">清除紀錄</string>
|
||||
<string name="logs_cleared">已成功清除紀錄</string>
|
||||
<string name="pid">PID: %1$d</string>
|
||||
<string name="target_uid">目標 UID: %1$d</string>
|
||||
|
||||
<!--SafetyNet-->
|
||||
|
||||
@@ -98,7 +98,7 @@
|
||||
<string name="hide_search">搜尋</string>
|
||||
|
||||
<!--Module -->
|
||||
<string name="no_info_provided">(未提供資訊)</string>
|
||||
<string name="no_info_provided">(未提供資訊)</string>
|
||||
<string name="reboot_userspace">快速重新啟動</string>
|
||||
<string name="reboot_recovery">重新啟動至 Recovery</string>
|
||||
<string name="reboot_bootloader">重新啟動至 Bootloader</string>
|
||||
@@ -111,7 +111,7 @@
|
||||
<string name="update_available">有可用的更新</string>
|
||||
<string name="suspend_text_riru">此模組因 %1$s 已啟用而暫停運作</string>
|
||||
<string name="suspend_text_zygisk">此模組因 %1$s 未啟用而暫停運作</string>
|
||||
<string name="zygisk_module_unloaded">此 Zygisk 模組因未相容而不載入</string>
|
||||
<string name="zygisk_module_unloaded">此模組因不相容 Zygisk 而未載入</string>
|
||||
<string name="module_empty">未安裝任何模組</string>
|
||||
|
||||
<!--Settings -->
|
||||
@@ -123,11 +123,11 @@
|
||||
<string name="settings_download_path_title">下載路徑</string>
|
||||
<string name="settings_download_path_message">檔案將被儲存在:%1$s</string>
|
||||
<string name="settings_hide_app_title">隱藏 Magisk</string>
|
||||
<string name="settings_hide_app_summary">安裝一個隨機套件 ID 和客製化應用標籤的 Proxy 應用程式</string>
|
||||
<string name="settings_hide_app_summary">安裝一個隨機套件名稱和客製化應用程式名稱的代理應用程式</string>
|
||||
<string name="settings_restore_app_title">還原 Magisk</string>
|
||||
<string name="settings_restore_app_summary">取消隱藏並還原為原始套件</string>
|
||||
<string name="language">語言</string>
|
||||
<string name="system_default">(系統預設)</string>
|
||||
<string name="language">語言</string>
|
||||
<string name="system_default">(系統預設值)</string>
|
||||
<string name="settings_check_update_title">檢查更新</string>
|
||||
<string name="settings_check_update_summary">定期於背景檢查更新</string>
|
||||
<string name="settings_update_channel_title">更新頻道</string>
|
||||
@@ -137,11 +137,11 @@
|
||||
<string name="settings_update_custom_msg">輸入一個自訂的網址</string>
|
||||
<string name="settings_zygisk_summary">在 Zygote 中執行 Magisk</string>
|
||||
<string name="settings_denylist_title">強制黑名單</string>
|
||||
<string name="settings_denylist_summary">在黑名單上的處理序將復原所有 Magisk 的修改</string>
|
||||
<string name="settings_denylist_summary">Magisk 黑名單上的處理程序將復原變更</string>
|
||||
<string name="settings_denylist_error">這個功能需要啟用 %1$s</string>
|
||||
<string name="settings_denylist_config_title">設定黑名單</string>
|
||||
<string name="settings_denylist_config_summary">選擇要包含在黑名單的處理序</string>
|
||||
<string name="settings_hosts_title">主機 (Hosts) 模組化</string>
|
||||
<string name="settings_denylist_config_summary">選擇要包含在黑名單的處理程序</string>
|
||||
<string name="settings_hosts_title">主機(hosts)模組化</string>
|
||||
<string name="settings_hosts_summary">為廣告阻擋程式提供主機模組</string>
|
||||
<string name="settings_hosts_toast">已安裝主機模組</string>
|
||||
<string name="settings_app_name_hint">新的名稱</string>
|
||||
@@ -164,13 +164,13 @@
|
||||
<string name="settings_su_reauth_title">更新後重新驗證</string>
|
||||
<string name="settings_su_reauth_summary">應用程式更新後,重新驗證超級使用者的要求</string>
|
||||
<string name="settings_su_tapjack_title">啟用點選攔截保護</string>
|
||||
<string name="settings_su_tapjack_summary">發現超級使用者視窗上有應用程式重疊在上層時,不回應允許操作</string>
|
||||
<string name="settings_su_tapjack_summary">發現有其他應用程式重疊在超級使用者視窗上面時,不回應允許操作</string>
|
||||
<string name="settings_su_biometric_title">生物特徵辨識驗證</string>
|
||||
<string name="settings_su_biometric_summary">使用生物特徵辨識驗證來允許超級使用者的要求</string>
|
||||
<string name="no_biometric">不支援的裝置或是未啟用生物特徵辨識設定</string>
|
||||
<string name="settings_customization">客製化</string>
|
||||
<string name="setting_add_shortcut_summary">在主螢幕中新增一個精緻的捷徑,以防隱藏應用程式後難以辨識其名稱和圖示</string>
|
||||
<string name="settings_doh_title">安全化的網域解析 (DoH)</string>
|
||||
<string name="setting_add_shortcut_summary">在主螢幕中新增一個精緻的捷徑。防止隱藏 Magisk 以後,其名稱與圖示將難以辨識</string>
|
||||
<string name="settings_doh_title">安全化的網域解析(DoH)</string>
|
||||
<string name="settings_doh_description">解決某些地區的 DNS 中毒問題</string>
|
||||
|
||||
<string name="multiuser_mode">多重使用者模式</string>
|
||||
@@ -192,12 +192,12 @@
|
||||
<!--Notifications-->
|
||||
<string name="update_channel">Magisk 更新</string>
|
||||
<string name="progress_channel">進度通知</string>
|
||||
<string name="updated_channel">更新完成</string>
|
||||
<string name="updated_channel">更新完成</string>
|
||||
<string name="download_complete">下載完成</string>
|
||||
<string name="download_file_error">下載錯誤</string>
|
||||
<string name="magisk_update_title">Magisk 有可用的更新!</string>
|
||||
<string name="updated_title">Magisk 已完成更新</string>
|
||||
<string name="updated_text">輕觸即可開啟應用</string>
|
||||
<string name="updated_title">Magisk 已完成更新</string>
|
||||
<string name="updated_text">點選即可開啟應用程式</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="yes">是</string>
|
||||
@@ -218,21 +218,21 @@
|
||||
<string name="restore_fail">不存在原始備份的映像檔!</string>
|
||||
<string name="setup_fail">安裝失敗</string>
|
||||
<string name="env_fix_title">需要修復執行環境</string>
|
||||
<string name="env_fix_msg">缺少讓 Magisk 正常執行所需的檔案。您同意讓 Magisk 額外下載安裝包進行修復安裝,修復完成後將自動重新啟動。是否繼續?</string>
|
||||
<string name="setup_msg">正在修復運作環境……</string>
|
||||
<string name="env_fix_msg">缺少讓 Magisk 正常執行所需的檔案。請您同意讓 Magisk 額外下載安裝包進行修復安裝,修復完成後將自動重新啟動。請問您是否繼續?</string>
|
||||
<string name="setup_msg">正在修復執行環境……</string>
|
||||
<string name="authenticate">驗證</string>
|
||||
<string name="unsupport_magisk_title">不支援的 Magisk 版本</string>
|
||||
<string name="unsupport_magisk_msg">此 Magisk 的版本不支援 Magisk %1$s 版或更低的版本。\n\nMagisk 將顯示為未安裝的狀態。不過您仍然可以升級功能,請盡快升級。</string>
|
||||
<string name="unsupport_magisk_msg">此應用程式的版本不支援 Magisk %1$s 版或更低的版本。\n\nMagisk 將顯示為未安裝的狀態。不過您仍然可以升級功能,請盡快升級。</string>
|
||||
<string name="unsupport_general_title">異常狀態</string>
|
||||
<string name="unsupport_system_app_msg">不支援讓本應用程式以系統應用程式的方式執行。請恢復為使用者應用程式。</string>
|
||||
<string name="unsupport_system_app_msg">本應用程式不支援以系統應用程式的方式執行。請恢復為使用者應用程式。</string>
|
||||
<string name="unsupport_other_su_msg">偵測到一個不是來自 Magisk 的「su」二進位檔案。請移除其他 Root 方案。</string>
|
||||
<string name="unsupport_external_storage_msg">Magisk 已被安裝到外部儲存空間。請移動應用程式至內部儲存空間。</string>
|
||||
<string name="unsupport_external_storage_msg">應用程式已被安裝到外部儲存空間。請移動應用程式至內部儲存空間。</string>
|
||||
<string name="unsupport_nonroot_stub_msg">應用程式無法在 Root 權限遺失的情況下以隱藏模式執行。請還原為原始套件。</string>
|
||||
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
||||
<string name="external_rw_permission_denied">授予儲存空間存取權以啟用此功能</string>
|
||||
<string name="install_unknown_denied">允許「安裝未知應用程式」以啟用此功能</string>
|
||||
<string name="install_unknown_denied">允許「安裝未知應用程式」以啟用此功能</string>
|
||||
<string name="add_shortcut_title">在主螢幕中新增捷徑</string>
|
||||
<string name="add_shortcut_msg">在隱藏應用後,其名稱與圖示將難以辨識。您想要在主螢幕中新增一個精緻的捷徑嗎?</string>
|
||||
<string name="add_shortcut_msg">在隱藏應用程式以後,其名稱與圖示將難以辨識。請問您想要在主螢幕中新增一個精緻的捷徑嗎?</string>
|
||||
<string name="app_not_found">沒有可以處理這個動作的應用程式</string>
|
||||
<string name="reboot_apply_change">重新啟動裝置以套用設定變更</string>
|
||||
<string name="restore_app_confirmation">這將會還原隱藏的應用程式至原始。請問您確定要執行?</string>
|
||||
|
@@ -24,7 +24,7 @@ tasks.withType<KotlinCompile> {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(kotlin("gradle-plugin", "1.6.21"))
|
||||
implementation(kotlin("gradle-plugin", "1.7.0"))
|
||||
implementation("com.android.tools.build:gradle:7.2.1")
|
||||
implementation("androidx.navigation:navigation-safe-args-gradle-plugin:2.5.0-rc01")
|
||||
implementation("io.michaelrocks:paranoid-gradle-plugin:0.3.7")
|
||||
|
@@ -64,8 +64,6 @@ fun genKeyData(keysDir: File, outSrc: File) {
|
||||
it.println("package com.topjohnwu.magisk.signing;")
|
||||
it.println("public final class KeyData {")
|
||||
|
||||
it.byteField("testCert", File(keysDir, "testkey.x509.pem").readBytes())
|
||||
it.byteField("testKey", File(keysDir, "testkey.pk8").readBytes())
|
||||
it.byteField("verityCert", File(keysDir, "verity.x509.pem").readBytes())
|
||||
it.byteField("verityKey", File(keysDir, "verity.pk8").readBytes())
|
||||
|
||||
@@ -127,6 +125,7 @@ fun genStubManifest(srcDir: File, outDir: File): String {
|
||||
| android:name="%s"
|
||||
| android:directBootAware="true"
|
||||
| android:exported="false"
|
||||
| android:taskAffinity=""
|
||||
| tools:ignore="AppLinkUrlError">
|
||||
| <intent-filter>
|
||||
| <action android:name="android.intent.action.VIEW"/>
|
||||
|
@@ -1,6 +1,11 @@
|
||||
|
||||
import com.android.build.gradle.BaseExtension
|
||||
import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
|
||||
import com.android.builder.internal.packaging.IncrementalPackager
|
||||
import com.android.builder.model.SigningConfig
|
||||
import com.android.tools.build.apkzlib.sign.SigningExtension
|
||||
import com.android.tools.build.apkzlib.sign.SigningOptions
|
||||
import com.android.tools.build.apkzlib.zfile.ZFiles
|
||||
import com.android.tools.build.apkzlib.zip.ZFileOptions
|
||||
import org.apache.tools.ant.filters.FixCrLfFilter
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.JavaVersion
|
||||
@@ -16,6 +21,8 @@ import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import java.io.PrintStream
|
||||
import java.security.KeyStore
|
||||
import java.security.cert.X509Certificate
|
||||
import java.util.*
|
||||
import java.util.zip.*
|
||||
|
||||
@@ -55,6 +62,37 @@ fun Project.setupCommon() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun SigningConfig.getPrivateKey(): KeyStore.PrivateKeyEntry {
|
||||
val keyStore = KeyStore.getInstance(storeType ?: KeyStore.getDefaultType())
|
||||
storeFile!!.inputStream().use {
|
||||
keyStore.load(it, storePassword!!.toCharArray())
|
||||
}
|
||||
val keyPwdArray = keyPassword!!.toCharArray()
|
||||
val entry = keyStore.getEntry(keyAlias!!, KeyStore.PasswordProtection(keyPwdArray))
|
||||
return entry as KeyStore.PrivateKeyEntry
|
||||
}
|
||||
|
||||
private fun addComment(apkPath: File, signConfig: SigningConfig, minSdk: Int, eocdComment: String) {
|
||||
val privateKey = signConfig.getPrivateKey()
|
||||
val signingOptions = SigningOptions.builder()
|
||||
.setMinSdkVersion(minSdk)
|
||||
.setV1SigningEnabled(true)
|
||||
.setV2SigningEnabled(true)
|
||||
.setKey(privateKey.privateKey)
|
||||
.setCertificates(privateKey.certificate as X509Certificate)
|
||||
.setValidation(SigningOptions.Validation.ASSUME_INVALID)
|
||||
.build()
|
||||
val options = ZFileOptions().apply {
|
||||
noTimestamps = true
|
||||
autoSortFiles = true
|
||||
}
|
||||
ZFiles.apk(apkPath, options).use {
|
||||
SigningExtension(signingOptions).register(it)
|
||||
it.eocdComment = eocdComment.toByteArray()
|
||||
it.get(IncrementalPackager.APP_METADATA_ENTRY_PATH)?.delete()
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.setupAppCommon() {
|
||||
setupCommon()
|
||||
|
||||
@@ -91,6 +129,19 @@ private fun Project.setupAppCommon() {
|
||||
includeInApk = false
|
||||
}
|
||||
}
|
||||
|
||||
android.applicationVariants.all {
|
||||
val projectName = project.name.toLowerCase(Locale.ROOT)
|
||||
val variantCapped = name.capitalize(Locale.ROOT)
|
||||
val variant = name.toLowerCase(Locale.ROOT)
|
||||
tasks.getByPath(":$projectName:package$variantCapped").doLast {
|
||||
val apkDir = if (properties["android.injected.invoked.from.ide"] == "true")
|
||||
"intermediates" else "outputs"
|
||||
val apk = File(buildDir, "${apkDir}/apk/${variant}/$projectName-${variant}.apk")
|
||||
val comment = "version=${Config.version}\nversionCode=${Config.versionCode}"
|
||||
addComment(apk, signingConfig, android.defaultConfig.minSdk!!, comment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.setupApp() {
|
||||
|
@@ -1,5 +1,14 @@
|
||||
# Magisk Changelog
|
||||
|
||||
### v25.1
|
||||
|
||||
- [MagiskBoot] Fix ramdisk backup being incorrectly skipped
|
||||
- [MagiskBoot] Add new feature to detect unsupported dtb and abort during installation
|
||||
- [Zygisk] Change binary hijack paths
|
||||
- [App] Fix incorrect recovery mode detection and installation
|
||||
- [MagiskInit] Fix config not properly exported in legacy SAR devices
|
||||
- [General] Enforce the Magisk app to always match or be newer than `magiskd`
|
||||
|
||||
### v25.0
|
||||
|
||||
- [MagiskInit] Update 2SI implementation, significantly increase device compatibility (e.g. Sony Xperia devices)
|
||||
|
25
docs/releases/25100.md
Normal file
25
docs/releases/25100.md
Normal file
@@ -0,0 +1,25 @@
|
||||
## 2022.6.19 Magisk v25.1
|
||||
|
||||
> v25.1 fixes some minor bugs over v25.0. The following are the same as v25.0 release notes.
|
||||
|
||||
Another major release! A lot of the changes aren't visible at the surface, but v25 is actually a really substantial upgrade!
|
||||
|
||||
### MagiskInit Rewrite
|
||||
|
||||
A significant portion of `magiskinit` (the critical software that runs before your device boots up) is completely rewritten from scratch. Ever since Android introduced [Project Treble](https://android-developers.googleblog.com/2017/05/here-comes-treble-modular-base-for.html) in Android 8.0, Magisk has been constantly fighting against the increasingly complex partitioning and early mount setups of all kinds of devices, sometimes with weird OEM specific implementations. It got to a point that `magiskinit` had become so complicated that few people (including myself!) were aware of every detail, and maintaining this piece of software like this was clearly not sustainable. After many months of planning (yes, this whole re-architecture has been in my head for a long time) and some help from external contributors, a whole new `sepolicy` injection mechanism is introduced into Magisk, solving the "SELinux Problem" once and for all.
|
||||
|
||||
Since this is a full paradigm shift on how Magisk hot-patch the device at boot, several behaviors that many developers implicitly relied on might not exist. For example, Magisk no longer patches fstabs in most scenarios, which means AVB will remain intact; some custom kernels rely on AVB being stripped out for them by Magisk.
|
||||
|
||||
### MagiskSU Security Enhancements
|
||||
|
||||
The superuser functionality of Magisk has not seen much changes ever since its introduction. v25 focuses on making root permission management more accurate and secure:
|
||||
|
||||
- Add a whole new package tracking system to ensure malicious UID reuse attack cannot be performed
|
||||
- Properly support and implement the UX in the Magisk app for packages using `sharedUserId`
|
||||
- Enforce root manager APK signature verification to combat the rampant unofficial Magisk app "mods"
|
||||
|
||||
Many might not realize, but using a trusted, unmodified Magisk app is really important. Magisk's root daemon treats the Magisk app differently and gives it blanket root access without any restrictions. A modded Magisk app can potentially backdoor your device.
|
||||
|
||||
And in case some of you are about to put on your tin foil hats, this is not designed to "vendor lock-in"; the goal is to make sure your root management app comes from the same developer of the underlying root implementation. Magisk's build system allows custom distributors to use its own signing keys, and in addition, I am also providing official debug builds which skips any signature verification for development.
|
||||
|
||||
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)
|
@@ -1,5 +1,6 @@
|
||||
# Release Notes
|
||||
|
||||
- [v25.1](25100.md)
|
||||
- [v25.0](25000.md)
|
||||
- [v24.3](24300.md)
|
||||
- [v24.2](24200.md)
|
||||
|
@@ -25,30 +25,38 @@ Usage: ./magiskboot <action> [args...]
|
||||
|
||||
Supported actions:
|
||||
unpack [-n] [-h] <bootimg>
|
||||
Unpack <bootimg> to, if available, kernel, kernel_dtb, ramdisk.cpio,
|
||||
second, dtb, extra, and recovery_dtbo into current directory.
|
||||
If '-n' is provided, it will not attempt to decompress kernel or
|
||||
ramdisk.cpio from their original formats.
|
||||
If '-h' is provided, it will dump header info to 'header',
|
||||
which will be parsed when repacking.
|
||||
Unpack <bootimg> to its individual components, each component to
|
||||
a file with its corresponding file name in the current directory.
|
||||
Supported components: kernel, kernel_dtb, ramdisk.cpio, second,
|
||||
dtb, extra, and recovery_dtbo.
|
||||
By default, each component will be automatically decompressed
|
||||
on-the-fly before writing to the output file.
|
||||
If '-n' is provided, all decompression operations will be skipped;
|
||||
each component will remain untouched, dumped in its original format.
|
||||
If '-h' is provided, the boot image header information will be
|
||||
dumped to the file 'header', which can be used to modify header
|
||||
configurations during repacking.
|
||||
Return values:
|
||||
0:valid 1:error 2:chromeos
|
||||
|
||||
repack [-n] <origbootimg> [outbootimg]
|
||||
Repack boot image components from current directory
|
||||
to [outbootimg], or new-boot.img if not specified.
|
||||
If '-n' is provided, it will not attempt to recompress ramdisk.cpio,
|
||||
otherwise it will compress ramdisk.cpio and kernel with the same format
|
||||
as in <origbootimg> if the file provided is not already compressed.
|
||||
If env variable PATCHVBMETAFLAG is set to true, all disable flags will
|
||||
be set in the vbmeta header.
|
||||
Repack boot image components using files from the current directory
|
||||
to [outbootimg], or 'new-boot.img' if not specified.
|
||||
<origbootimg> is the original boot image used to unpack the components.
|
||||
By default, each component will be automatically compressed using its
|
||||
corresponding format detected in <origbootimg>. If a component file
|
||||
in the current directory is already compressed, then no addition
|
||||
compression will be performed for that specific component.
|
||||
If '-n' is provided, all compression operations will be skipped.
|
||||
If env variable PATCHVBMETAFLAG is set to true, all disable flags in
|
||||
the boot image's vbmeta header will be set.
|
||||
|
||||
hexpatch <file> <hexpattern1> <hexpattern2>
|
||||
Search <hexpattern1> in <file>, and replace with <hexpattern2>
|
||||
Search <hexpattern1> in <file>, and replace it with <hexpattern2>
|
||||
|
||||
cpio <incpio> [commands...]
|
||||
Do cpio commands to <incpio> (modifications are done in-place)
|
||||
Each command is a single argument, add quotes for each command
|
||||
Each command is a single argument, add quotes for each command.
|
||||
Supported commands:
|
||||
exists ENTRY
|
||||
Return 0 if ENTRY exists, else return 1
|
||||
@@ -65,7 +73,7 @@ Supported actions:
|
||||
extract [ENTRY OUT]
|
||||
Extract ENTRY to OUT, or extract all entries to current directory
|
||||
test
|
||||
Test the current cpio's status
|
||||
Test the cpio's status
|
||||
Return value is 0 or bitwise or-ed of following values:
|
||||
0x1:Magisk 0x2:unsupported 0x4:Sony
|
||||
patch
|
||||
@@ -78,8 +86,8 @@ Supported actions:
|
||||
sha1
|
||||
Print stock boot SHA1 if previously backed up in ramdisk
|
||||
|
||||
dtb <input> <action> [args...]
|
||||
Do dtb related actions to <input>
|
||||
dtb <file> <action> [args...]
|
||||
Do dtb related actions to <file>
|
||||
Supported actions:
|
||||
print [-f]
|
||||
Print all contents of dtb for debugging
|
||||
@@ -88,8 +96,12 @@ Supported actions:
|
||||
Search for fstab and remove verity/avb
|
||||
Modifications are done directly to the file in-place
|
||||
Configure with env variables: KEEPVERITY
|
||||
test
|
||||
Test the fstab's status
|
||||
Return values:
|
||||
0:valid 1:error
|
||||
|
||||
split <input>
|
||||
split <file>
|
||||
Split image.*-dtb into kernel + kernel_dtb
|
||||
|
||||
sha1 <file>
|
||||
@@ -99,14 +111,19 @@ Supported actions:
|
||||
Cleanup the current working directory
|
||||
|
||||
compress[=format] <infile> [outfile]
|
||||
Compress <infile> with [format] (default: gzip), optionally to [outfile]
|
||||
<infile>/[outfile] can be '-' to be STDIN/STDOUT
|
||||
Supported formats: gzip zopfli xz lzma bzip2 lz4 lz4_legacy lz4_lg
|
||||
Compress <infile> with [format] to [outfile].
|
||||
<infile>/[outfile] can be '-' to be STDIN/STDOUT.
|
||||
If [format] is not specified, then gzip will be used.
|
||||
If [outfile] is not specified, then <infile> will be replaced
|
||||
with another file suffixed with a matching file extension.
|
||||
Supported formats: gzip zopfli xz lzma bzip2 lz4 lz4_legacy lz4_lg
|
||||
|
||||
decompress <infile> [outfile]
|
||||
Detect format and decompress <infile>, optionally to [outfile]
|
||||
<infile>/[outfile] can be '-' to be STDIN/STDOUT
|
||||
Supported formats: gzip zopfli xz lzma bzip2 lz4 lz4_legacy lz4_lg
|
||||
Detect format and decompress <infile> to [outfile].
|
||||
<infile>/[outfile] can be '-' to be STDIN/STDOUT.
|
||||
If [outfile] is not specified, then <infile> will be replaced
|
||||
with another file removing its archive format file extension.
|
||||
Supported formats: gzip zopfli xz lzma bzip2 lz4 lz4_legacy lz4_lg
|
||||
```
|
||||
|
||||
### magiskinit
|
||||
@@ -211,9 +228,8 @@ Options:
|
||||
Advanced Options (Internal APIs):
|
||||
--daemon manually start magisk daemon
|
||||
--stop remove all magisk changes and stop daemon
|
||||
--[init trigger] start service for init trigger
|
||||
Supported init triggers:
|
||||
post-fs-data, service, boot-complete
|
||||
--[init trigger] callback on init triggers. Valid triggers:
|
||||
post-fs-data, service, boot-complete, zygote-restart
|
||||
--unlock-blocks set BLKROSET flag to OFF for all block devices
|
||||
--restorecon restore selinux context on Magisk files
|
||||
--clone-attr SRC DEST clone permission, owner, and selinux context
|
||||
|
@@ -27,6 +27,6 @@ android.injected.testOnly=false
|
||||
android.nonTransitiveRClass=true
|
||||
|
||||
# Magisk
|
||||
magisk.stubVersion=31
|
||||
magisk.versionCode=25000
|
||||
magisk.stubVersion=32
|
||||
magisk.versionCode=25100
|
||||
magisk.ondkVersion=r24.1
|
||||
|
@@ -286,26 +286,7 @@ void fclone_attr(int src, int dest) {
|
||||
fsetattr(dest, &a);
|
||||
}
|
||||
|
||||
void fd_full_read(int fd, void **buf, size_t *size) {
|
||||
*size = lseek(fd, 0, SEEK_END);
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
*buf = xmalloc(*size + 1);
|
||||
xxread(fd, *buf, *size);
|
||||
((char *) *buf)[*size] = '\0';
|
||||
}
|
||||
|
||||
void full_read(const char *filename, void **buf, size_t *size) {
|
||||
int fd = xopen(filename, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
*buf = nullptr;
|
||||
*size = 0;
|
||||
return;
|
||||
}
|
||||
fd_full_read(fd, buf, size);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void fd_full_read(int fd, string &str) {
|
||||
void full_read(int fd, string &str) {
|
||||
char buf[4096];
|
||||
for (ssize_t len; (len = xread(fd, buf, sizeof(buf))) > 0;)
|
||||
str.insert(str.end(), buf, buf + len);
|
||||
@@ -313,14 +294,14 @@ void fd_full_read(int fd, string &str) {
|
||||
|
||||
void full_read(const char *filename, string &str) {
|
||||
if (int fd = xopen(filename, O_RDONLY | O_CLOEXEC); fd >= 0) {
|
||||
fd_full_read(fd, str);
|
||||
full_read(fd, str);
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
string fd_full_read(int fd) {
|
||||
string full_read(int fd) {
|
||||
string str;
|
||||
fd_full_read(fd, str);
|
||||
full_read(fd, str);
|
||||
return str;
|
||||
}
|
||||
|
||||
@@ -357,12 +338,19 @@ void file_readline(bool trim, FILE *fp, const function<bool(string_view)> &fn) {
|
||||
if (!fn(start))
|
||||
break;
|
||||
}
|
||||
fclose(fp);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
void parse_prop_file(const char *file, const function<bool(string_view, string_view)> &fn) {
|
||||
file_readline(true, file, [&](string_view line_view) -> bool {
|
||||
void file_readline(bool trim, const char *file, const function<bool(string_view)> &fn) {
|
||||
if (auto fp = open_file(file, "re"))
|
||||
file_readline(trim, fp.get(), fn);
|
||||
}
|
||||
void file_readline(const char *file, const function<bool(string_view)> &fn) {
|
||||
file_readline(false, file, fn);
|
||||
}
|
||||
|
||||
void parse_prop_file(FILE *fp, const function<bool(string_view, string_view)> &fn) {
|
||||
file_readline(true, fp, [&](string_view line_view) -> bool {
|
||||
char *line = (char *) line_view.data();
|
||||
if (line[0] == '#')
|
||||
return true;
|
||||
@@ -374,6 +362,11 @@ void parse_prop_file(const char *file, const function<bool(string_view, string_v
|
||||
});
|
||||
}
|
||||
|
||||
void parse_prop_file(const char *file, const function<bool(string_view, string_view)> &fn) {
|
||||
if (auto fp = open_file(file, "re"))
|
||||
parse_prop_file(fp.get(), fn);
|
||||
}
|
||||
|
||||
// Original source: https://android.googlesource.com/platform/bionic/+/master/libc/bionic/mntent.cpp
|
||||
// License: AOSP, full copyright notice please check original source
|
||||
static struct mntent *compat_getmntent_r(FILE *fp, struct mntent *e, char *buf, int buf_len) {
|
||||
@@ -429,12 +422,10 @@ void backup_folder(const char *dir, vector<raw_file> &files) {
|
||||
if (fgetattr(fd, &file.attr) < 0)
|
||||
return SKIP;
|
||||
if (entry->d_type == DT_REG) {
|
||||
fd_full_read(fd, file.buf, file.sz);
|
||||
file.content = full_read(fd);
|
||||
} else if (entry->d_type == DT_LNK) {
|
||||
xreadlinkat(dfd, entry->d_name, path, sizeof(path));
|
||||
file.sz = strlen(path) + 1;
|
||||
file.buf = (uint8_t *) xmalloc(file.sz);
|
||||
memcpy(file.buf, path, file.sz);
|
||||
file.content = path;
|
||||
}
|
||||
files.emplace_back(std::move(file));
|
||||
return CONTINUE;
|
||||
@@ -449,10 +440,10 @@ void restore_folder(const char *dir, vector<raw_file> &files) {
|
||||
if (S_ISDIR(file.attr.st.st_mode)) {
|
||||
mkdirs(path, 0);
|
||||
} else if (S_ISREG(file.attr.st.st_mode)) {
|
||||
auto fp = xopen_file(path.data(), "we");
|
||||
if (fp) fwrite(file.buf, 1, file.sz, fp.get());
|
||||
if (auto fp = xopen_file(path.data(), "we"))
|
||||
fwrite(file.content.data(), 1, file.content.size(), fp.get());
|
||||
} else if (S_ISLNK(file.attr.st.st_mode)) {
|
||||
symlink((char *)file.buf, path.data());
|
||||
symlink(file.content.data(), path.data());
|
||||
}
|
||||
setattr(path.data(), &file.attr);
|
||||
}
|
||||
|
@@ -39,14 +39,14 @@ protected:
|
||||
void swap(byte_data &o);
|
||||
};
|
||||
|
||||
struct raw_file : public byte_data {
|
||||
struct raw_file {
|
||||
std::string path;
|
||||
file_attr attr;
|
||||
std::string content;
|
||||
|
||||
raw_file() : attr{} {}
|
||||
raw_file(const raw_file&) = delete;
|
||||
raw_file(raw_file &&o) : path(std::move(o.path)), attr(o.attr) { swap(o); }
|
||||
~raw_file() { free(buf); }
|
||||
raw_file(raw_file &&o) : path(std::move(o.path)), attr(o.attr), content(std::move(o.content)) {}
|
||||
};
|
||||
|
||||
struct mmap_data : public byte_data {
|
||||
@@ -75,25 +75,15 @@ int setattrat(int dirfd, const char *name, file_attr *a);
|
||||
int fsetattr(int fd, file_attr *a);
|
||||
void fclone_attr(int src, int dest);
|
||||
void clone_attr(const char *src, const char *dest);
|
||||
void fd_full_read(int fd, void **buf, size_t *size);
|
||||
void full_read(const char *filename, void **buf, size_t *size);
|
||||
void fd_full_read(int fd, std::string &str);
|
||||
void full_read(int fd, std::string &str);
|
||||
void full_read(const char *filename, std::string &str);
|
||||
std::string fd_full_read(int fd);
|
||||
std::string full_read(int fd);
|
||||
std::string full_read(const char *filename);
|
||||
void write_zero(int fd, size_t size);
|
||||
void file_readline(bool trim, FILE *fp, const std::function<bool(std::string_view)> &fn);
|
||||
static inline void file_readline(
|
||||
bool trim, const char *file, const std::function<bool(std::string_view)> &fn) {
|
||||
FILE *fp = xfopen(file, "re");
|
||||
if (fp == nullptr)
|
||||
return;
|
||||
file_readline(trim, fp, fn);
|
||||
}
|
||||
static inline void file_readline(const char *file,
|
||||
const std::function<bool(std::string_view)> &fn) {
|
||||
file_readline(false, file, fn);
|
||||
}
|
||||
void file_readline(bool trim, const char *file, const std::function<bool(std::string_view)> &fn);
|
||||
void file_readline(const char *file, const std::function<bool(std::string_view)> &fn);
|
||||
void parse_prop_file(FILE *fp, const std::function<bool(std::string_view, std::string_view)> &fn);
|
||||
void parse_prop_file(const char *file,
|
||||
const std::function<bool(std::string_view, std::string_view)> &fn);
|
||||
void frm_rf(int dirfd);
|
||||
@@ -103,18 +93,6 @@ void backup_folder(const char *dir, std::vector<raw_file> &files);
|
||||
void restore_folder(const char *dir, std::vector<raw_file> &files);
|
||||
std::string find_apk_path(const char *pkg);
|
||||
|
||||
template <typename T>
|
||||
void full_read(const char *filename, T &buf, size_t &size) {
|
||||
static_assert(std::is_pointer<T>::value);
|
||||
full_read(filename, reinterpret_cast<void**>(&buf), &size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void fd_full_read(int fd, T &buf, size_t &size) {
|
||||
static_assert(std::is_pointer<T>::value);
|
||||
fd_full_read(fd, reinterpret_cast<void**>(&buf), &size);
|
||||
}
|
||||
|
||||
using sFILE = std::unique_ptr<FILE, decltype(&fclose)>;
|
||||
using sDIR = std::unique_ptr<DIR, decltype(&closedir)>;
|
||||
sDIR make_dir(DIR *dp);
|
||||
|
@@ -111,6 +111,14 @@ ssize_t xxread(int fd, void *buf, size_t count) {
|
||||
return read_sz;
|
||||
}
|
||||
|
||||
off_t xlseek(int fd, off_t offset, int whence) {
|
||||
off_t ret = lseek(fd, offset, whence);
|
||||
if (ret < 0) {
|
||||
PLOGE("lseek");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xpipe2(int pipefd[2], int flags) {
|
||||
int ret = pipe2(pipefd, flags);
|
||||
if (ret < 0) {
|
||||
|
@@ -14,6 +14,7 @@ int xopenat(int dirfd, const char *pathname, int flags, mode_t mode);
|
||||
ssize_t xwrite(int fd, const void *buf, size_t count);
|
||||
ssize_t xread(int fd, void *buf, size_t count);
|
||||
ssize_t xxread(int fd, void *buf, size_t count);
|
||||
off_t xlseek(int fd, off_t offset, int whence);
|
||||
int xpipe2(int pipefd[2], int flags);
|
||||
int xsetns(int fd, int nstype);
|
||||
int xunshare(int flags);
|
||||
|
@@ -577,7 +577,7 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) {
|
||||
}
|
||||
if (access(KERNEL_FILE, R_OK) == 0) {
|
||||
auto m = mmap_data(KERNEL_FILE);
|
||||
if (!COMPRESSED_ANY(check_fmt(m.buf, m.sz)) && COMPRESSED(boot.k_fmt)) {
|
||||
if (!skip_comp && !COMPRESSED_ANY(check_fmt(m.buf, m.sz)) && COMPRESSED(boot.k_fmt)) {
|
||||
// Always use zopfli for zImage compression
|
||||
auto fmt = (boot.flags[ZIMAGE_KERNEL] && boot.k_fmt == GZIP) ? ZOPFLI : boot.k_fmt;
|
||||
hdr->kernel_size() = compress(fmt, fd, m.buf, m.sz);
|
||||
|
@@ -53,7 +53,9 @@ void cpio::dump(const char *file) {
|
||||
dump(xfopen(file, "we"));
|
||||
}
|
||||
|
||||
void cpio::rm(entry_map::iterator &it) {
|
||||
void cpio::rm(entry_map::iterator it) {
|
||||
if (it == entries.end())
|
||||
return;
|
||||
fprintf(stderr, "Remove [%s]\n", it->first.data());
|
||||
entries.erase(it);
|
||||
}
|
||||
@@ -188,7 +190,7 @@ void cpio::ln(const char *target, const char *name) {
|
||||
fprintf(stderr, "Create symlink [%s] -> [%s]\n", name, target);
|
||||
}
|
||||
|
||||
void cpio::mv(entry_map::iterator &it, const char *name) {
|
||||
void cpio::mv(entry_map::iterator it, const char *name) {
|
||||
fprintf(stderr, "Move [%s] -> [%s]\n", it->first.data(), name);
|
||||
auto e = it->second.release();
|
||||
entries.erase(it);
|
||||
|
@@ -45,8 +45,8 @@ protected:
|
||||
entry_map entries;
|
||||
|
||||
static void extract_entry(const entry_map::value_type &e, const char *file);
|
||||
void rm(entry_map::iterator &it);
|
||||
void mv(entry_map::iterator &it, const char *name);
|
||||
void rm(entry_map::iterator it);
|
||||
void mv(entry_map::iterator it, const char *name);
|
||||
|
||||
private:
|
||||
void dump(FILE *out);
|
||||
|
@@ -93,47 +93,43 @@ static int find_fstab(const void *fdt, int node = 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void dtb_print(const char *file, bool fstab) {
|
||||
fprintf(stderr, "Loading dtbs from [%s]\n", file);
|
||||
auto m = mmap_data(file);
|
||||
// Loop through all the dtbs
|
||||
int dtb_num = 0;
|
||||
uint8_t * const end = m.buf + m.sz;
|
||||
template<typename Func>
|
||||
static void for_each_fdt(const char *file, bool rw, Func fn) {
|
||||
auto m = mmap_data(file, rw);
|
||||
uint8_t *end = m.buf + m.sz;
|
||||
for (uint8_t *fdt = m.buf; fdt < end;) {
|
||||
fdt = static_cast<uint8_t*>(memmem(fdt, end - fdt, DTB_MAGIC, sizeof(fdt32_t)));
|
||||
if (fdt == nullptr)
|
||||
break;
|
||||
fn(fdt);
|
||||
fdt += fdt_totalsize(fdt);
|
||||
}
|
||||
}
|
||||
|
||||
static void dtb_print(const char *file, bool fstab) {
|
||||
fprintf(stderr, "Loading dtbs from [%s]\n", file);
|
||||
int dtb_num = 0;
|
||||
for_each_fdt(file, false, [&](uint8_t *fdt) {
|
||||
if (fstab) {
|
||||
int node = find_fstab(fdt);
|
||||
if (node >= 0) {
|
||||
fprintf(stderr, "Found fstab in buf.%04d\n", dtb_num);
|
||||
if (int node = find_fstab(fdt); node >= 0) {
|
||||
fprintf(stderr, "Found fstab in dtb.%04d\n", dtb_num);
|
||||
print_node(fdt, node);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "Printing buf.%04d\n", dtb_num);
|
||||
fprintf(stderr, "Printing dtb.%04d\n", dtb_num);
|
||||
print_node(fdt);
|
||||
}
|
||||
++dtb_num;
|
||||
fdt += fdt_totalsize(fdt);
|
||||
}
|
||||
});
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
[[maybe_unused]]
|
||||
static bool dtb_patch_rebuild(uint8_t *dtb, size_t dtb_sz, const char *file);
|
||||
|
||||
static bool dtb_patch(const char *file) {
|
||||
bool keep_verity = check_env("KEEPVERITY");
|
||||
|
||||
fprintf(stderr, "Loading dtbs from [%s]\n", file);
|
||||
auto m = mmap_data(file, true);
|
||||
|
||||
bool keep_verity = check_env("KEEPVERITY");
|
||||
bool patched = false;
|
||||
uint8_t * const end = m.buf + m.sz;
|
||||
for (uint8_t *fdt = m.buf; fdt < end;) {
|
||||
fdt = static_cast<uint8_t*>(memmem(fdt, end - fdt, DTB_MAGIC, sizeof(fdt32_t)));
|
||||
if (fdt == nullptr)
|
||||
break;
|
||||
for_each_fdt(file, true, [&](uint8_t *fdt) {
|
||||
int node;
|
||||
// Patch the chosen node for bootargs
|
||||
fdt_for_each_subnode(node, fdt, 0) {
|
||||
@@ -149,20 +145,41 @@ static bool dtb_patch(const char *file) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (int fstab = find_fstab(fdt); fstab >= 0) {
|
||||
fdt_for_each_subnode(node, fdt, fstab) {
|
||||
if (!keep_verity) {
|
||||
if (!keep_verity) {
|
||||
if (int fstab = find_fstab(fdt); fstab >= 0) {
|
||||
fdt_for_each_subnode(node, fdt, fstab) {
|
||||
int len;
|
||||
char *value = (char *) fdt_getprop(fdt, node, "fsmgr_flags", &len);
|
||||
patched |= patch_verity(value, len) != len;
|
||||
}
|
||||
}
|
||||
}
|
||||
fdt += fdt_totalsize(fdt);
|
||||
}
|
||||
});
|
||||
return patched;
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
static void dtb_test(const char *file) {
|
||||
for_each_fdt(file, false, [&](uint8_t *fdt) {
|
||||
// Find the system node in fstab
|
||||
if (int fstab = find_fstab(fdt); fstab >= 0) {
|
||||
int node;
|
||||
fdt_for_each_subnode(node, fdt, fstab) {
|
||||
if (auto name = fdt_get_name(fdt, node, nullptr); !name || name != "system"sv)
|
||||
continue;
|
||||
int len;
|
||||
if (auto value = fdt_getprop(fdt, node, "mnt_point", &len)) {
|
||||
// If mnt_point is set to /system_root, abort!
|
||||
if (strncmp(static_cast<const char *>(value), "/system_root", len) == 0) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int dtb_commands(int argc, char *argv[]) {
|
||||
char *dtb = argv[0];
|
||||
++argv;
|
||||
@@ -175,11 +192,17 @@ int dtb_commands(int argc, char *argv[]) {
|
||||
if (!dtb_patch(dtb))
|
||||
exit(1);
|
||||
return 0;
|
||||
} else if (argv[0] == "test"sv) {
|
||||
dtb_test(dtb);
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// The following code is unused, left here for historical purpose. Since the code is
|
||||
// extremely complicated, I won't want to rewrite this whole thing if somehow we need
|
||||
// to use it in the future...
|
||||
|
||||
namespace {
|
||||
|
||||
struct fdt_blob {
|
||||
@@ -188,8 +211,6 @@ struct fdt_blob {
|
||||
uint32_t len;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
static bool fdt_patch(void *fdt) {
|
||||
int fstab = find_fstab(fdt);
|
||||
if (fstab < 0)
|
||||
@@ -361,6 +382,7 @@ static bool blob_patch(uint8_t *dtb, size_t dtb_sz, const char *out) {
|
||||
|
||||
#define DTB_MATCH(s) BUFFER_MATCH(dtb, s)
|
||||
|
||||
[[maybe_unused]]
|
||||
static bool dtb_patch_rebuild(uint8_t *dtb, size_t dtb_sz, const char *file) {
|
||||
if (DTB_MATCH(QCDT_MAGIC)) {
|
||||
auto hdr = reinterpret_cast<qcdt_hdr*>(dtb);
|
||||
@@ -426,3 +448,5 @@ static bool dtb_patch_rebuild(uint8_t *dtb, size_t dtb_sz, const char *file) {
|
||||
return blob_patch(dtb, dtb_sz, file);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@@ -20,30 +20,38 @@ Usage: %s <action> [args...]
|
||||
|
||||
Supported actions:
|
||||
unpack [-n] [-h] <bootimg>
|
||||
Unpack <bootimg> to, if available, kernel, kernel_dtb, ramdisk.cpio,
|
||||
second, dtb, extra, and recovery_dtbo into current directory.
|
||||
If '-n' is provided, it will not attempt to decompress kernel or
|
||||
ramdisk.cpio from their original formats.
|
||||
If '-h' is provided, it will dump header info to 'header',
|
||||
which will be parsed when repacking.
|
||||
Unpack <bootimg> to its individual components, each component to
|
||||
a file with its corresponding file name in the current directory.
|
||||
Supported components: kernel, kernel_dtb, ramdisk.cpio, second,
|
||||
dtb, extra, and recovery_dtbo.
|
||||
By default, each component will be automatically decompressed
|
||||
on-the-fly before writing to the output file.
|
||||
If '-n' is provided, all decompression operations will be skipped;
|
||||
each component will remain untouched, dumped in its original format.
|
||||
If '-h' is provided, the boot image header information will be
|
||||
dumped to the file 'header', which can be used to modify header
|
||||
configurations during repacking.
|
||||
Return values:
|
||||
0:valid 1:error 2:chromeos
|
||||
|
||||
repack [-n] <origbootimg> [outbootimg]
|
||||
Repack boot image components from current directory
|
||||
to [outbootimg], or new-boot.img if not specified.
|
||||
If '-n' is provided, it will not attempt to recompress ramdisk.cpio,
|
||||
otherwise it will compress ramdisk.cpio and kernel with the same format
|
||||
as in <origbootimg> if the file provided is not already compressed.
|
||||
If env variable PATCHVBMETAFLAG is set to true, all disable flags will
|
||||
be set in the vbmeta header.
|
||||
Repack boot image components using files from the current directory
|
||||
to [outbootimg], or 'new-boot.img' if not specified.
|
||||
<origbootimg> is the original boot image used to unpack the components.
|
||||
By default, each component will be automatically compressed using its
|
||||
corresponding format detected in <origbootimg>. If a component file
|
||||
in the current directory is already compressed, then no addition
|
||||
compression will be performed for that specific component.
|
||||
If '-n' is provided, all compression operations will be skipped.
|
||||
If env variable PATCHVBMETAFLAG is set to true, all disable flags in
|
||||
the boot image's vbmeta header will be set.
|
||||
|
||||
hexpatch <file> <hexpattern1> <hexpattern2>
|
||||
Search <hexpattern1> in <file>, and replace with <hexpattern2>
|
||||
Search <hexpattern1> in <file>, and replace it with <hexpattern2>
|
||||
|
||||
cpio <incpio> [commands...]
|
||||
Do cpio commands to <incpio> (modifications are done in-place)
|
||||
Each command is a single argument, add quotes for each command
|
||||
Each command is a single argument, add quotes for each command.
|
||||
Supported commands:
|
||||
exists ENTRY
|
||||
Return 0 if ENTRY exists, else return 1
|
||||
@@ -60,7 +68,7 @@ Supported actions:
|
||||
extract [ENTRY OUT]
|
||||
Extract ENTRY to OUT, or extract all entries to current directory
|
||||
test
|
||||
Test the current cpio's status
|
||||
Test the cpio's status
|
||||
Return value is 0 or bitwise or-ed of following values:
|
||||
0x1:Magisk 0x2:unsupported 0x4:Sony
|
||||
patch
|
||||
@@ -73,8 +81,8 @@ Supported actions:
|
||||
sha1
|
||||
Print stock boot SHA1 if previously backed up in ramdisk
|
||||
|
||||
dtb <input> <action> [args...]
|
||||
Do dtb related actions to <input>
|
||||
dtb <file> <action> [args...]
|
||||
Do dtb related actions to <file>
|
||||
Supported actions:
|
||||
print [-f]
|
||||
Print all contents of dtb for debugging
|
||||
@@ -83,8 +91,12 @@ Supported actions:
|
||||
Search for fstab and remove verity/avb
|
||||
Modifications are done directly to the file in-place
|
||||
Configure with env variables: KEEPVERITY
|
||||
test
|
||||
Test the fstab's status
|
||||
Return values:
|
||||
0:valid 1:error
|
||||
|
||||
split <input>
|
||||
split <file>
|
||||
Split image.*-dtb into kernel + kernel_dtb
|
||||
|
||||
sha1 <file>
|
||||
@@ -94,8 +106,11 @@ Supported actions:
|
||||
Cleanup the current working directory
|
||||
|
||||
compress[=format] <infile> [outfile]
|
||||
Compress <infile> with [format] (default: gzip), optionally to [outfile]
|
||||
<infile>/[outfile] can be '-' to be STDIN/STDOUT
|
||||
Compress <infile> with [format] to [outfile].
|
||||
<infile>/[outfile] can be '-' to be STDIN/STDOUT.
|
||||
If [format] is not specified, then gzip will be used.
|
||||
If [outfile] is not specified, then <infile> will be replaced
|
||||
with another file suffixed with a matching file extension.
|
||||
Supported formats: )EOF", arg0);
|
||||
|
||||
print_formats();
|
||||
@@ -103,8 +118,10 @@ Supported actions:
|
||||
fprintf(stderr, R"EOF(
|
||||
|
||||
decompress <infile> [outfile]
|
||||
Detect format and decompress <infile>, optionally to [outfile]
|
||||
<infile>/[outfile] can be '-' to be STDIN/STDOUT
|
||||
Detect format and decompress <infile> to [outfile].
|
||||
<infile>/[outfile] can be '-' to be STDIN/STDOUT.
|
||||
If [outfile] is not specified, then <infile> will be replaced
|
||||
with another file removing its archive format file extension.
|
||||
Supported formats: )EOF");
|
||||
|
||||
print_formats();
|
||||
|
@@ -82,11 +82,10 @@ int magisk_cpio::test() {
|
||||
}
|
||||
|
||||
#define for_each_line(line, buf, size) \
|
||||
for (line = (char *) buf; line < (char *) buf + size && line[0]; line = strchr(line + 1, '\n') + 1)
|
||||
for (char *line = (char *) buf; line < (char *) buf + size && line[0]; line = strchr(line + 1, '\n') + 1)
|
||||
|
||||
char *magisk_cpio::sha1() {
|
||||
char sha1[41];
|
||||
char *line;
|
||||
for (auto &e : entries) {
|
||||
if (e.first == "init.magisk.rc" || e.first == "overlay/init.magisk.rc") {
|
||||
for_each_line(line, e.second->data, e.second->filesize) {
|
||||
@@ -112,45 +111,59 @@ char *magisk_cpio::sha1() {
|
||||
}
|
||||
|
||||
#define for_each_str(str, buf, size) \
|
||||
for (str = (char *) buf; str < (char *) buf + size; str = str += strlen(str) + 1)
|
||||
for (char *str = (char *) buf; str < (char *) buf + size; str += strlen(str) + 1)
|
||||
|
||||
void magisk_cpio::restore() {
|
||||
if (auto it = entries.find(".backup/.rmlist"); it != entries.end()) {
|
||||
char *file;
|
||||
for_each_str(file, it->second->data, it->second->filesize) {
|
||||
rm(file);
|
||||
// Collect files
|
||||
auto bk = entries.end();
|
||||
auto rl = entries.end();
|
||||
auto mg = entries.end();
|
||||
vector<entry_map::iterator> backups;
|
||||
for (auto it = entries.begin(); it != entries.end(); ++it) {
|
||||
if (it->first == ".backup") {
|
||||
bk = it;
|
||||
} else if (it->first == ".backup/.rmlist") {
|
||||
rl = it;
|
||||
} else if (it->first == ".backup/.magisk") {
|
||||
mg = it;
|
||||
} else if (str_starts(it->first, ".backup/")) {
|
||||
backups.emplace_back(it);
|
||||
}
|
||||
rm(it);
|
||||
}
|
||||
|
||||
for (auto it = entries.begin(); it != entries.end();) {
|
||||
auto cur = it++;
|
||||
if (str_starts(cur->first, ".backup")) {
|
||||
if (cur->first.length() == 7 || &cur->first[8] == ".magisk"sv) {
|
||||
rm(cur);
|
||||
} else {
|
||||
mv(cur, &cur->first[8]);
|
||||
}
|
||||
} else if (str_starts(cur->first, "magisk") ||
|
||||
cur->first == "overlay/init.magisk.rc" ||
|
||||
cur->first == "sbin/magic_mask.sh" ||
|
||||
cur->first == "init.magisk.rc") {
|
||||
// Some known stuff we can remove
|
||||
rm(cur);
|
||||
// If the .backup folder is effectively empty, this means that the boot ramdisk was
|
||||
// created from scratch by an old broken magiskboot. This is just a hacky workaround.
|
||||
if (bk != entries.end() && mg != entries.end() && rl == entries.end() && backups.empty()) {
|
||||
fprintf(stderr, "Remove all in ramdisk\n");
|
||||
entries.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove files
|
||||
rm(bk);
|
||||
rm(mg);
|
||||
if (rl != entries.end()) {
|
||||
for_each_str(file, rl->second->data, rl->second->filesize) {
|
||||
rm(file);
|
||||
}
|
||||
rm(rl);
|
||||
}
|
||||
|
||||
// Restore files
|
||||
for (auto it : backups) {
|
||||
const char *name = &it->first[8];
|
||||
mv(it, name);
|
||||
}
|
||||
}
|
||||
|
||||
void magisk_cpio::backup(const char *orig) {
|
||||
if (access(orig, R_OK))
|
||||
return;
|
||||
|
||||
entry_map backups;
|
||||
string rm_list;
|
||||
backups.emplace(".backup", new cpio_entry(S_IFDIR));
|
||||
|
||||
magisk_cpio o;
|
||||
o.load_cpio(orig);
|
||||
if (access(orig, R_OK) == 0)
|
||||
o.load_cpio(orig);
|
||||
|
||||
// Remove existing backups in original ramdisk
|
||||
o.rm(".backup", true);
|
||||
|
@@ -95,27 +95,28 @@ struct EOCD {
|
||||
* This method extracts the first certificate of the first signer
|
||||
* within the APK v2 signature block.
|
||||
*/
|
||||
string read_certificate(int fd) {
|
||||
uint32_t size4;
|
||||
uint64_t size8;
|
||||
string read_certificate(int fd, int version) {
|
||||
uint32_t u32;
|
||||
uint64_t u64;
|
||||
|
||||
// Find EOCD
|
||||
for (int i = 0;; i++) {
|
||||
// i is the absolute offset to end of file
|
||||
uint16_t comment_sz = 0;
|
||||
lseek(fd, -((off_t) sizeof(comment_sz)) - i, SEEK_END);
|
||||
read(fd, &comment_sz, sizeof(comment_sz));
|
||||
xlseek(fd, -static_cast<off_t>(sizeof(comment_sz)) - i, SEEK_END);
|
||||
xxread(fd, &comment_sz, sizeof(comment_sz));
|
||||
if (comment_sz == i) {
|
||||
// Double check if we actually found the structure
|
||||
lseek(fd, -((off_t) sizeof(EOCD)), SEEK_CUR);
|
||||
xlseek(fd, -static_cast<off_t>(sizeof(EOCD)), SEEK_CUR);
|
||||
uint32_t magic = 0;
|
||||
read(fd, &magic, sizeof(magic));
|
||||
xxread(fd, &magic, sizeof(magic));
|
||||
if (magic == EOCD_MAGIC) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == 0xffff) {
|
||||
// Comments cannot be longer than 0xffff (overflow), abort
|
||||
LOGE("cert: invalid APK format\n");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -125,62 +126,83 @@ string read_certificate(int fd) {
|
||||
uint32_t central_dir_off = 0;
|
||||
{
|
||||
constexpr off_t off = offsetof(EOCD, central_dir_off) - sizeof(EOCD::magic);
|
||||
lseek(fd, off, SEEK_CUR);
|
||||
xlseek(fd, off, SEEK_CUR);
|
||||
}
|
||||
xxread(fd, ¢ral_dir_off, sizeof(central_dir_off));
|
||||
|
||||
// Parse APK comment to get version code
|
||||
if (version >= 0) {
|
||||
xlseek(fd, sizeof(EOCD::comment_sz), SEEK_CUR);
|
||||
FILE *fp = fdopen(fd, "r"); // DO NOT close this file pointer
|
||||
int apk_ver = -1;
|
||||
parse_prop_file(fp, [&](string_view key, string_view value) -> bool {
|
||||
if (key == "versionCode") {
|
||||
apk_ver = parse_int(value);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (version > apk_ver) {
|
||||
// Enforce the magisk app to always be newer than magiskd
|
||||
LOGE("cert: APK version too low\n");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
read(fd, ¢ral_dir_off, sizeof(central_dir_off));
|
||||
|
||||
// Next, find the start of the APK signing block
|
||||
{
|
||||
constexpr int off = sizeof(signing_block::block_sz_) + sizeof(signing_block::magic);
|
||||
lseek(fd, (off_t) (central_dir_off - off), SEEK_SET);
|
||||
xlseek(fd, (off_t) (central_dir_off - off), SEEK_SET);
|
||||
}
|
||||
read(fd, &size8, sizeof(size8)); // size8 = block_sz_
|
||||
xxread(fd, &u64, sizeof(u64)); // u64 = block_sz_
|
||||
char magic[sizeof(signing_block::magic)] = {0};
|
||||
read(fd, magic, sizeof(magic));
|
||||
xxread(fd, magic, sizeof(magic));
|
||||
if (memcmp(magic, APK_SIGNING_BLOCK_MAGIC, sizeof(magic)) != 0) {
|
||||
// Invalid signing block magic, abort
|
||||
LOGE("cert: invalid signing block magic\n");
|
||||
return {};
|
||||
}
|
||||
uint64_t signing_blk_sz = 0;
|
||||
lseek(fd, (off_t) (central_dir_off - size8 - sizeof(signing_blk_sz)), SEEK_SET);
|
||||
read(fd, &signing_blk_sz, sizeof(signing_blk_sz));
|
||||
if (signing_blk_sz != size8) {
|
||||
xlseek(fd, -static_cast<off_t>(u64 + sizeof(signing_blk_sz)), SEEK_CUR);
|
||||
xxread(fd, &signing_blk_sz, sizeof(signing_blk_sz));
|
||||
if (signing_blk_sz != u64) {
|
||||
// block_sz != block_sz_, invalid signing block format, abort
|
||||
LOGE("cert: invalid signing block format\n");
|
||||
return {};
|
||||
}
|
||||
|
||||
// Finally, we are now at the beginning of the id-value pair sequence
|
||||
|
||||
for (;;) {
|
||||
read(fd, &size8, sizeof(size8)); // id-value pair length
|
||||
if (size8 == signing_blk_sz) {
|
||||
xxread(fd, &u64, sizeof(u64)); // id-value pair length
|
||||
if (u64 == signing_blk_sz) {
|
||||
// Outside of the id-value pair sequence; actually reading block_sz_
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t id;
|
||||
read(fd, &id, sizeof(id));
|
||||
xxread(fd, &id, sizeof(id));
|
||||
if (id == SIGNATURE_SCHEME_V2_MAGIC) {
|
||||
read(fd, &size4, sizeof(size4)); // signer sequence length
|
||||
// Skip [signer sequence length] + [1st signer length] + [signed data length]
|
||||
xlseek(fd, sizeof(uint32_t) * 3, SEEK_CUR);
|
||||
|
||||
read(fd, &size4, sizeof(size4)); // signer length
|
||||
read(fd, &size4, sizeof(size4)); // signed data length
|
||||
xxread(fd, &u32, sizeof(u32)); // digest sequence length
|
||||
xlseek(fd, u32, SEEK_CUR); // skip all digests
|
||||
|
||||
read(fd, &size4, sizeof(size4)); // digest sequence length
|
||||
lseek(fd, (off_t) (size4), SEEK_CUR); // skip all digests
|
||||
|
||||
read(fd, &size4, sizeof(size4)); // cert sequence length
|
||||
read(fd, &size4, sizeof(size4)); // cert length
|
||||
xlseek(fd, sizeof(uint32_t), SEEK_CUR); // cert sequence length
|
||||
xxread(fd, &u32, sizeof(u32)); // 1st cert length
|
||||
|
||||
string cert;
|
||||
cert.resize(size4);
|
||||
read(fd, cert.data(), size4);
|
||||
cert.resize(u32);
|
||||
xxread(fd, cert.data(), u32);
|
||||
|
||||
return cert;
|
||||
} else {
|
||||
// Skip this id-value pair
|
||||
lseek(fd, (off_t) (size8 - sizeof(id)), SEEK_CUR);
|
||||
xlseek(fd, u64 - sizeof(id), SEEK_CUR);
|
||||
}
|
||||
}
|
||||
|
||||
LOGE("cert: cannot find certificate\n");
|
||||
return {};
|
||||
}
|
||||
|
@@ -21,7 +21,7 @@ void reboot();
|
||||
void start_log_daemon();
|
||||
void setup_logfile(bool reset);
|
||||
void magisk_logging();
|
||||
std::string read_certificate(int fd);
|
||||
std::string read_certificate(int fd, int version = -1);
|
||||
|
||||
// Module stuffs
|
||||
void handle_modules();
|
||||
|
@@ -105,7 +105,7 @@ int get_manager(int user_id, string *pkg, bool install) {
|
||||
int dyn = open(app_path, O_RDONLY | O_CLOEXEC);
|
||||
if (dyn < 0)
|
||||
return false;
|
||||
bool mismatch = default_cert && read_certificate(dyn) != *default_cert;
|
||||
bool mismatch = default_cert && read_certificate(dyn, MAGISK_VER_CODE) != *default_cert;
|
||||
close(dyn);
|
||||
if (mismatch) {
|
||||
LOGE("pkg: dyn APK signature mismatch: %s\n", app_path);
|
||||
@@ -226,7 +226,7 @@ int get_manager(int user_id, string *pkg, bool install) {
|
||||
#if ENFORCE_SIGNATURE
|
||||
string apk = find_apk_path(JAVA_PACKAGE_NAME);
|
||||
int fd = xopen(apk.data(), O_RDONLY | O_CLOEXEC);
|
||||
string cert = read_certificate(fd);
|
||||
string cert = read_certificate(fd, MAGISK_VER_CODE);
|
||||
close(fd);
|
||||
if (default_cert && cert != *default_cert) {
|
||||
// Found APK with invalid signature, force replace with stub
|
||||
|
@@ -72,7 +72,7 @@ static void load_overlay_rc(const char *overlay) {
|
||||
if (str_ends(entry->d_name, ".rc")) {
|
||||
LOGD("Found rc script [%s]\n", entry->d_name);
|
||||
int rc = xopenat(dfd, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
rc_list.push_back(fd_full_read(rc));
|
||||
rc_list.push_back(full_read(rc));
|
||||
close(rc);
|
||||
unlinkat(dfd, entry->d_name, 0);
|
||||
}
|
||||
|
@@ -64,7 +64,7 @@ void LegacySARInit::first_stage_prep() {
|
||||
xmkdir("/data/.backup", 0);
|
||||
xmkdir("/data/overlay.d", 0);
|
||||
restore_folder("/data/overlay.d", overlays);
|
||||
int cfg = xopen("/data/.backup/config", O_WRONLY | O_CREAT, 0);
|
||||
int cfg = xopen("/data/.backup/.magisk", O_WRONLY | O_CREAT, 0);
|
||||
xwrite(cfg, magisk_cfg.buf, magisk_cfg.sz);
|
||||
close(cfg);
|
||||
}
|
||||
@@ -80,9 +80,15 @@ bool SecondStageInit::prepare() {
|
||||
argv[0] = (char *) INIT_PATH;
|
||||
|
||||
// Some weird devices like meizu, uses 2SI but still have legacy rootfs
|
||||
// Check if root and system are on the same filesystem
|
||||
// Check if root and system are on different filesystems
|
||||
struct stat root{}, system{};
|
||||
xstat("/", &root);
|
||||
xstat("/system", &system);
|
||||
return root.st_dev != system.st_dev;
|
||||
if (root.st_dev != system.st_dev) {
|
||||
// We are still on rootfs, so make sure we will execute the init of the 2nd stage
|
||||
unlink("/init");
|
||||
xsymlink(INIT_PATH, "/init");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@@ -17,10 +17,11 @@ exe, "/system/bin", "com.android.commands.content.Content", \
|
||||
#define START_ACTIVITY \
|
||||
exe, "/system/bin", "com.android.commands.am.Am", \
|
||||
"start", "-p", target, "--user", user, "-a", "android.intent.action.VIEW", \
|
||||
"-f", "0x58000020", "--es", "action", action
|
||||
"-f", "0x58800020", "--es", "action", action
|
||||
|
||||
// 0x58000020 = FLAG_ACTIVITY_NEW_TASK|FLAG_ACTIVITY_MULTIPLE_TASK|
|
||||
// FLAG_ACTIVITY_NO_HISTORY|FLAG_INCLUDE_STOPPED_PACKAGES
|
||||
// 0x58800020 = FLAG_ACTIVITY_NEW_TASK|FLAG_ACTIVITY_MULTIPLE_TASK|
|
||||
// FLAG_ACTIVITY_NO_HISTORY|FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS|
|
||||
// FLAG_INCLUDE_STOPPED_PACKAGES
|
||||
|
||||
#define get_cmd(to) \
|
||||
((to).command.empty() ? \
|
||||
|
@@ -415,14 +415,13 @@ void su_daemon_handler(int client, const sock_cred *cred) {
|
||||
if (realpath(path, cwd))
|
||||
chdir(cwd);
|
||||
snprintf(path, sizeof(path), "/proc/%d/environ", ctx.pid);
|
||||
char buf[4096] = { 0 };
|
||||
int fd = xopen(path, O_RDONLY);
|
||||
read(fd, buf, sizeof(buf));
|
||||
close(fd);
|
||||
auto env = full_read(path);
|
||||
clearenv();
|
||||
for (size_t pos = 0; buf[pos];) {
|
||||
putenv(buf + pos);
|
||||
pos += strlen(buf + pos) + 1;
|
||||
for (size_t pos = 0; pos < env.size(); ++pos) {
|
||||
putenv(env.data() + pos);
|
||||
pos = env.find_first_of('\0', pos);
|
||||
if (pos == std::string::npos)
|
||||
break;
|
||||
}
|
||||
if (!ctx.req.keepenv) {
|
||||
struct passwd *pw;
|
||||
|
@@ -271,6 +271,7 @@ def gen_jni_hook():
|
||||
|
||||
with open('jni_hooks.hpp', 'w') as f:
|
||||
f.write('// Generated by gen_jni_hooks.py\n')
|
||||
f.write('\nnamespace {\n')
|
||||
|
||||
zygote = 'com/android/internal/os/Zygote'
|
||||
|
||||
@@ -285,4 +286,4 @@ with open('jni_hooks.hpp', 'w') as f:
|
||||
|
||||
f.write(gen_jni_hook())
|
||||
|
||||
f.write('\n')
|
||||
f.write('\n\n} // namespace\n')
|
||||
|
@@ -79,6 +79,8 @@ HookContext *g_ctx;
|
||||
const JNINativeInterface *old_functions;
|
||||
JNINativeInterface *new_functions;
|
||||
|
||||
} // namespace
|
||||
|
||||
#define HOOK_JNI(method) \
|
||||
if (methods[i].name == #method##sv) { \
|
||||
int j = 0; \
|
||||
@@ -103,6 +105,8 @@ if (methods[i].name == #method##sv) {
|
||||
|
||||
#undef HOOK_JNI
|
||||
|
||||
namespace {
|
||||
|
||||
jclass gClassRef;
|
||||
jmethodID class_getName;
|
||||
string get_class_name(JNIEnv *env, jclass clazz) {
|
||||
|
@@ -1,5 +1,7 @@
|
||||
// Generated by gen_jni_hooks.py
|
||||
|
||||
namespace {
|
||||
|
||||
void *nativeForkAndSpecialize_orig = nullptr;
|
||||
jint nativeForkAndSpecialize_l(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jstring instruction_set, jstring app_data_dir) {
|
||||
AppSpecializeArgs_v3 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
|
||||
@@ -318,3 +320,5 @@ unique_ptr<JNINativeMethod[]> hookAndSaveJNIMethods(const char *className, const
|
||||
}
|
||||
return newMethods;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@@ -9,8 +9,8 @@
|
||||
#define INJECT_ENV_2 "MAGISK_INJ_2"
|
||||
#define MAGISKTMP_ENV "MAGISKTMP"
|
||||
|
||||
#define HIJACK_BIN64 "/system/bin/bootanimation"
|
||||
#define HIJACK_BIN32 "/system/bin/screencap"
|
||||
#define HIJACK_BIN64 "/system/bin/appwidget"
|
||||
#define HIJACK_BIN32 "/system/bin/bu"
|
||||
|
||||
namespace ZygiskRequest {
|
||||
enum : int {
|
||||
|
@@ -183,7 +183,15 @@ rm -f ramdisk.cpio.orig config magisk*.xz
|
||||
#################
|
||||
|
||||
for dt in dtb kernel_dtb extra; do
|
||||
[ -f $dt ] && ./magiskboot dtb $dt patch && ui_print "- Patch fstab in $dt"
|
||||
if [ -f $dt ]; then
|
||||
if ! ./magiskboot dtb $dt test; then
|
||||
ui_print "! Unsupported boot image $dt"
|
||||
abort "! Please restore back to stock boot image"
|
||||
fi
|
||||
if ./magiskboot dtb $dt patch; then
|
||||
ui_print "- Patch fstab in boot image $dt"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -f kernel ]; then
|
||||
|
Binary file not shown.
@@ -1,27 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEqDCCA5CgAwIBAgIJAJNurL4H8gHfMA0GCSqGSIb3DQEBBQUAMIGUMQswCQYD
|
||||
VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g
|
||||
VmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UE
|
||||
AxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe
|
||||
Fw0wODAyMjkwMTMzNDZaFw0zNTA3MTcwMTMzNDZaMIGUMQswCQYDVQQGEwJVUzET
|
||||
MBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4G
|
||||
A1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9p
|
||||
ZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZI
|
||||
hvcNAQEBBQADggENADCCAQgCggEBANaTGQTexgskse3HYuDZ2CU+Ps1s6x3i/waM
|
||||
qOi8qM1r03hupwqnbOYOuw+ZNVn/2T53qUPn6D1LZLjk/qLT5lbx4meoG7+yMLV4
|
||||
wgRDvkxyGLhG9SEVhvA4oU6Jwr44f46+z4/Kw9oe4zDJ6pPQp8PcSvNQIg1QCAcy
|
||||
4ICXF+5qBTNZ5qaU7Cyz8oSgpGbIepTYOzEJOmc3Li9kEsBubULxWBjf/gOBzAzU
|
||||
RNps3cO4JFgZSAGzJWQTT7/emMkod0jb9WdqVA2BVMi7yge54kdVMxHEa5r3b97s
|
||||
zI5p58ii0I54JiCUP5lyfTwE/nKZHZnfm644oLIXf6MdW2r+6R8CAQOjgfwwgfkw
|
||||
HQYDVR0OBBYEFEhZAFY9JyxGrhGGBaR0GawJyowRMIHJBgNVHSMEgcEwgb6AFEhZ
|
||||
AFY9JyxGrhGGBaR0GawJyowRoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UE
|
||||
CBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMH
|
||||
QW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAG
|
||||
CSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJAJNurL4H8gHfMAwGA1Ud
|
||||
EwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHqvlozrUMRBBVEY0NqrrwFbinZa
|
||||
J6cVosK0TyIUFf/azgMJWr+kLfcHCHJsIGnlw27drgQAvilFLAhLwn62oX6snb4Y
|
||||
LCBOsVMR9FXYJLZW2+TcIkCRLXWG/oiVHQGo/rWuWkJgU134NDEFJCJGjDbiLCpe
|
||||
+ZTWHdcwauTJ9pUbo8EvHRkU3cYfGmLaLfgn9gP+pWA7LFQNvXwBnDa6sppCccEX
|
||||
31I828XzgXpJ4O+mDL1/dBd+ek8ZPUP0IgdyZm5MTYPhvVqGCHzzTy3sIeJFymwr
|
||||
sBbmg2OAUNLEMO6nwmocSdN2ClirfxqCzJOLSDE4QyS9BAH6EhY6UFcOaE0=
|
||||
-----END CERTIFICATE-----
|
Reference in New Issue
Block a user