mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-08-14 15:27:25 +00:00
Compare commits
71 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6066b5cf86 | ||
![]() |
5cdf95a4d0 | ||
![]() |
910a36fdc1 | ||
![]() |
8331206acb | ||
![]() |
8423dc8d63 | ||
![]() |
6077c989a7 | ||
![]() |
c97d1044fa | ||
![]() |
f42c089b26 | ||
![]() |
1f8c063dc6 | ||
![]() |
4874520d65 | ||
![]() |
5e53639969 | ||
![]() |
83ab0ca6cd | ||
![]() |
70fd03d5fc | ||
![]() |
2e52875b50 | ||
![]() |
fd9b990ad7 | ||
![]() |
69978a9442 | ||
![]() |
d155da52ce | ||
![]() |
9c5b131913 | ||
![]() |
9d740cec1a | ||
![]() |
c2978eb9c3 | ||
![]() |
38abad1e44 | ||
![]() |
b4863eb51b | ||
![]() |
3817167ba1 | ||
![]() |
d1a35dd2ba | ||
![]() |
26116ac414 | ||
![]() |
0b26882fce | ||
![]() |
a2495fb5fb | ||
![]() |
0beb3bf16a | ||
![]() |
b68658e974 | ||
![]() |
3ae7344747 | ||
![]() |
4eb71830b3 | ||
![]() |
9183a0a6ea | ||
![]() |
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 |
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -40,3 +40,6 @@
|
||||
[submodule "zopfli"]
|
||||
path = native/jni/external/zopfli
|
||||
url = https://github.com/google/zopfli.git
|
||||
[submodule "cxx-rs"]
|
||||
path = native/jni/external/cxx-rs
|
||||
url = https://github.com/topjohnwu/cxx.git
|
||||
|
@@ -18,8 +18,8 @@ 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.1)
|
||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v25.1)
|
||||
[](https://raw.githubusercontent.com/topjohnwu/magisk-files/canary/app-release.apk)
|
||||
[](https://raw.githubusercontent.com/topjohnwu/magisk-files/canary/app-debug.apk)
|
||||
|
||||
@@ -51,7 +51,7 @@ For Magisk app crashes, record and upload the logcat when the crash occurs.
|
||||
- Run `./build.py ndk` to let the script download and install NDK for you
|
||||
- To start building, run `build.py` to see your options. \
|
||||
For each action, use `-h` to access help (e.g. `./build.py all -h`)
|
||||
- To start development, open the project with Android Studio. The IDE can be used for both app (Kotlin/Java) and native (C++/C) sources.
|
||||
- To start development, open the project with Android Studio. The IDE can be used for both app (Kotlin/Java) and native sources.
|
||||
- Optionally, set custom configs with `config.prop`. A sample `config.prop.sample` is provided.
|
||||
|
||||
## Signing and Distribution
|
||||
|
@@ -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,13 @@ class HomeViewModel(
|
||||
("${magisk.version} (${magisk.versionCode}) (${stub.versionCode})" +
|
||||
if (isDebug) " (D)" else "").asText()
|
||||
} ?: run {
|
||||
state = State.LOADING_FAILED
|
||||
appState = State.INVALID
|
||||
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 +113,14 @@ class HomeViewModel(
|
||||
|
||||
fun onDeletePressed() = UninstallDialog().publish()
|
||||
|
||||
fun onManagerPressed() = when (state) {
|
||||
State.LOADED -> withExternalRW {
|
||||
fun onManagerPressed() = when (appState) {
|
||||
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 +133,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 +141,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()
|
||||
}
|
||||
}
|
||||
|
@@ -54,8 +54,8 @@ public class ConcealableBottomNavigationView extends BottomNavigationView {
|
||||
toHidden.setDuration(175);
|
||||
toHidden.setInterpolator(new FastOutLinearInInterpolator());
|
||||
Animator toUnhidden = ObjectAnimator.ofFloat(this, "translationY", 0);
|
||||
toHidden.setDuration(225);
|
||||
toHidden.setInterpolator(new FastOutLinearInInterpolator());
|
||||
toUnhidden.setDuration(225);
|
||||
toUnhidden.setInterpolator(new FastOutLinearInInterpolator());
|
||||
|
||||
StateListAnimator animator = new StateListAnimator();
|
||||
|
||||
|
@@ -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>
|
||||
|
240
app/src/main/res/values-bn/strings.xml
Normal file
240
app/src/main/res/values-bn/strings.xml
Normal file
@@ -0,0 +1,240 @@
|
||||
<resources>
|
||||
|
||||
<!--Sections-->
|
||||
<string name="modules">মডিউল</string>
|
||||
<string name="superuser">সুপার ইউজার</string>
|
||||
<string name="logs">লগ</string>
|
||||
<string name="settings">সেটিংস</string>
|
||||
<string name="install">ইনস্টল করুন</string>
|
||||
<string name="section_home">বাড়ি</string>
|
||||
<string name="section_theme">থিম</string>
|
||||
<string name="denylist">তালিকা অস্বীকার করুন</string>
|
||||
|
||||
<!--Home-->
|
||||
<string name="no_connection">কোন সংযোগ উপলব্ধ নেই</string>
|
||||
<string name="app_changelog">চেঞ্জলগ</string>
|
||||
<string name="loading">লোড হচ্ছে…</string>
|
||||
<string name="update">হালনাগাদ</string>
|
||||
<string name="not_available">N/A</string>
|
||||
<string name="hide">লুকান</string>
|
||||
<string name="home_package">প্যাকেজ</string>
|
||||
<string name="home_app_title">অ্যাপ</string>
|
||||
|
||||
<string name="home_notice_content">শুধুমাত্র অফিসিয়াল গিটহাব পৃষ্ঠা থেকে ম্যাজিস্ক ডাউনলোড করুন। অজানা উৎস থেকে ফাইল দূষিত হতে পারে!</string>
|
||||
<string name="home_support_title">আমাদের সমর্থন</string>
|
||||
<string name="home_follow_title">আমাদের অনুসরণ করো</string>
|
||||
<string name="home_item_source">সূত্র</string>
|
||||
<string name="home_support_content">ম্যাজিস্ক হল, এবং সবসময় থাকবে, বিনামূল্যে, এবং ওপেন সোর্স। তবে আপনি দান করার মাধ্যমে আমাদের দেখাতে পারেন যে আপনি যত্নশীল.</string>
|
||||
<string name="home_installed_version">ইনস্টল করা হয়েছে</string>
|
||||
<string name="home_latest_version">সর্বশেষ</string>
|
||||
<string name="invalid_update_channel">অবৈধ আপডেট চ্যানেল</string>
|
||||
<string name="uninstall_magisk_title">ম্যাজিস্ক আনইনস্টল করুন</string>
|
||||
<string name="uninstall_magisk_msg">সমস্ত মডিউল নিষ্ক্রিয়/মুছে ফেলা হবে!\nরুট সরানো হবে!\nম্যাজিস্ক ব্যবহারের মাধ্যমে এনক্রিপ্ট করা যে কোনও অভ্যন্তরীণ স্টোরেজ পুনরায় এনক্রিপ্ট করা হবে!</string>
|
||||
|
||||
<!--Install-->
|
||||
<string name="keep_force_encryption">বল এনক্রিপশন সংরক্ষণ করুন</string>
|
||||
<string name="keep_dm_verity">AVB 2.0/dm-verity সংরক্ষণ করুন</string>
|
||||
<string name="patch_vbmeta">বুট ইমেজে vbmeta প্যাচ করুন</string>
|
||||
<string name="recovery_mode">পুনরুদ্ধার অবস্থা</string>
|
||||
<string name="install_options_title">অপশন</string>
|
||||
<string name="install_method_title">পদ্ধতি</string>
|
||||
<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">Iনিষ্ক্রিয় স্লটে ইনস্টল করুন (OTA পরে)</string>
|
||||
<string name="install_inactive_slot_msg">রিবুট করার পরে আপনার ডিভাইসটিকে বর্তমান নিষ্ক্রিয় স্লটে বুট করতে বাধ্য করা হবে!\It হয়ে গেলেই এই বিকল্পটি ব্যবহার করুন।\চালিয়ে রাখবেন?</string>
|
||||
<string name="setup_title">অতিরিক্ত সেটআপ</string>
|
||||
<string name="select_patch_file">একটি ফাইল নির্বাচন করুন এবং প্যাচ করুন</string>
|
||||
<string name="patch_file_msg">একটি কাঁচা চিত্র (*.img) বা একটি ODIN টারফাইল (*.tar) নির্বাচন করুন</string>
|
||||
<string name="reboot_delay_toast">5 সেকেন্ডের মধ্যে রিবুট হচ্ছে...</string>
|
||||
<string name="flash_screen_title">স্থাপন</string>
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">সুপার ইউজার অনুরোধ/স্ট্রিং</string>
|
||||
<string name="touch_filtered_warning">যেহেতু একটি অ্যাপ সুপার ব্যবহারকারীর অনুরোধকে অস্পষ্ট করছে, ম্যাজিস্ক আপনার প্রতিক্রিয়া যাচাই করতে পারে না</string>
|
||||
<string name="deny">অস্বীকার করুন</string>
|
||||
<string name="prompt">শীঘ্র</string>
|
||||
<string name="grant">প্রদান</string>
|
||||
<string name="su_warning">আপনার ডিভাইসে সম্পূর্ণ অ্যাক্সেস মঞ্জুর করে৷\nআপনি নিশ্চিত না হলে অস্বীকার করুন!</string>
|
||||
<string name="forever">চিরতরে</string>
|
||||
<string name="once">একদা</string>
|
||||
<string name="tenmin">১০ মিনিট</string>
|
||||
<string name="twentymin">২০ মিনিট</string>
|
||||
<string name="thirtymin">৩০ মিনিট</string>
|
||||
<string name="sixtymin">৬০ মিনিট</string>
|
||||
<string name="su_allow_toast">%1$s সুপার ইউজার অধিকার দেওয়া হয়েছিল</string>
|
||||
<string name="su_deny_toast">%1$s সুপার ইউজার অধিকার অস্বীকার করা হয়েছিল</string>
|
||||
<string name="su_snack_grant">%1$s-এর সুপার ব্যবহারকারীর অধিকার মঞ্জুর করা হয়েছে</string>
|
||||
<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_revoke_title">প্রত্যাহার করুন?</string>
|
||||
<string name="su_revoke_msg">%1$s সুপার ব্যবহারকারীর অধিকার প্রত্যাহার করতে নিশ্চিত করুন৷</string>
|
||||
<string name="toast">টোস্ট</string>
|
||||
<string name="none">কোনোটিই নয়</string>
|
||||
|
||||
<string name="superuser_toggle_notification">বিজ্ঞপ্তি</string>
|
||||
<string name="superuser_toggle_revoke">প্রত্যাহার করুন</string>
|
||||
<string name="superuser_policy_none">কোনো অ্যাপ এখনও সুপার ইউজারের অনুমতি চায়নি।</string>
|
||||
|
||||
<!--Logs-->
|
||||
<string name="log_data_none">আপনি লগ-মুক্ত, আপনার রুট অ্যাপগুলি আরও ব্যবহার করার চেষ্টা করুন৷</string>
|
||||
<string name="log_data_magisk_none">ম্যাজিস্ক লগগুলি খালি, এটি অদ্ভুত</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-->
|
||||
|
||||
<!--MagiskHide-->
|
||||
<string name="show_system_app">সিস্টেম অ্যাপ দেখান</string>
|
||||
<string name="show_os_app">ওএস অ্যাপ দেখান</string>
|
||||
<string name="hide_filter_hint">নাম অনুসারে ফিল্টার করুন</string>
|
||||
<string name="hide_search">অনুসন্ধান করুন</string>
|
||||
|
||||
<!--Module-->
|
||||
<string name="no_info_provided">(কোন তথ্য প্রদান করা হয়</string>
|
||||
<string name="reboot_userspace">নরম রিবুট</string>
|
||||
<string name="reboot_recovery">রিকভারিতে রিবুট করুন</string>
|
||||
<string name="reboot_bootloader">বুটলোডারে রিবুট করুন</string>
|
||||
<string name="reboot_download">ডাউনলোড করতে রিবুট করুন</string>
|
||||
<string name="reboot_edl">EDL এ রিবুট করুন</string>
|
||||
<string name="module_version_author">%1$s by %2$s</string>
|
||||
<string name="module_state_remove">অপসারণ</string>
|
||||
<string name="module_state_restore">পুনরুদ্ধার করুন</string>
|
||||
<string name="module_action_install_external">স্টোরেজ থেকে ইনস্টল করুন</string>
|
||||
<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">অসামঞ্জস্যতার কারণে জাইগিস্ক মডিউল লোড করা হয়নি</string>
|
||||
<string name="module_empty">কোন মডিউল ইনস্টল করা নেই</string>
|
||||
|
||||
<!--Settings-->
|
||||
<string name="settings_dark_mode_title">থিম মোড</string>
|
||||
<string name="settings_dark_mode_message">আপনার শৈলী সবচেয়ে উপযুক্ত মোড নির্বাচন করুন!</string>
|
||||
<string name="settings_dark_mode_light">সবসময় আলো</string>
|
||||
<string name="settings_dark_mode_system">সিস্টেম অনুসরণ করুন</string>
|
||||
<string name="settings_dark_mode_dark">সবসময় অন্ধকার</string>
|
||||
<string name="settings_download_path_title">পাথ ডাউনলোড করুন</string>
|
||||
<string name="settings_download_path_message">ফাইলগুলি %1$s এ সংরক্ষণ করা হবে৷</string>
|
||||
<string name="settings_hide_app_title">ম্যাজিস্ক অ্যাপটি লুকান</string>
|
||||
<string name="settings_hide_app_summary">একটি র্যান্ডম প্যাকেজ আইডি এবং কাস্টম অ্যাপ লেব সহ একটি প্রক্সি অ্যাপ ইনস্টল করুনl</string>
|
||||
<string name="settings_restore_app_title">ম্যাজিস্ক অ্যাপটি পুনরুদ্ধার করুন</string>
|
||||
<string name="settings_restore_app_summary">অ্যাপটি আড়াল করুন এবং আসলটি পুনরুদ্ধার করুন</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>
|
||||
<string name="settings_update_stable">স্থিতিশীল</string>
|
||||
<string name="settings_update_beta">বেটা</string>
|
||||
<string name="settings_update_custom">কাস্টম</string>
|
||||
<string name="settings_update_custom_msg">একটি কাস্টম চ্যানেল URL সন্নিবেশ করুন</string>
|
||||
<string name="settings_zygisk_summary">জাইগোট ডেমনে ম্যাজিস্কের অংশগুলি চালান</string>
|
||||
<string name="settings_denylist_title">তালিকা অস্বীকার করুন প্রয়োগ করুন</string>
|
||||
<string name="settings_denylist_summary">ডিনালিস্টের প্রসেসগুলিতে সমস্ত ম্যাজিস্ক পরিবর্তনগুলি ফিরিয়ে দেওয়া হবে</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">সিস্টেমহীন হোস্ট</string>
|
||||
<string name="settings_hosts_summary">বিজ্ঞাপন ব্লকিং অ্যাপের জন্য সিস্টেমলেস হোস্ট সমর্থন</string>
|
||||
<string name="settings_hosts_toast">সিস্টেমহীন হোস্ট মডিউল যোগ করা হয়েছে</string>
|
||||
<string name="settings_app_name_hint">নতুন নাম</string>
|
||||
<string name="settings_app_name_helper">অ্যাপটি এই নামের সাথে পুনরায় প্যাকেজ করা হবে</string>
|
||||
<string name="settings_app_name_error">ভুল ফরম্যাট</string>
|
||||
<string name="settings_su_app_adb">অ্যাপস এবং এডিবি</string>
|
||||
<string name="settings_su_app">শুধুমাত্র অ্যাপস</string>
|
||||
<string name="settings_su_adb">শুধুমাত্র এডিবি</string>
|
||||
<string name="settings_su_disable">অক্ষম</string>
|
||||
<string name="settings_su_request_10">১০ সেকেন্ড</string>
|
||||
<string name="settings_su_request_15">১৫ সেকেন্ড</string>
|
||||
<string name="settings_su_request_20">২০ সেকেন্ড</string>
|
||||
<string name="settings_su_request_30">৩০ সেকেন্ড</string>
|
||||
<string name="settings_su_request_45">৪৫ সেকেন্ড</string>
|
||||
<string name="settings_su_request_60">৬০ সেকেন্ড</string>
|
||||
<string name="superuser_access">সুপার ইউজার অ্যাক্সেস</string>
|
||||
<string name="auto_response">স্বয়ংক্রিয় প্রতিক্রিয়া</string>
|
||||
<string name="request_timeout">অনুরোধের সময়সীমা শেষ</string>
|
||||
<string name="superuser_notification">সুপার ইউজার বিজ্ঞপ্তি</string>
|
||||
<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_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">HTTPS এর উপর ডিএনএস</string>
|
||||
<string name="settings_doh_description">কিছু দেশে ডিএনএস বিষক্রিয়ার সমাধান</string>
|
||||
|
||||
<string name="multiuser_mode">মাল্টিউজার মোড</string>
|
||||
<string name="settings_owner_only">শুধুমাত্র ডিভাইসের মালিক</string>
|
||||
<string name="settings_owner_manage">ডিভাইস মালিক পরিচালিত</string>
|
||||
<string name="settings_user_independent">ব্যবহারকারী-স্বাধীন</string>
|
||||
<string name="owner_only_summary">শুধুমাত্র মালিকের রুট অ্যাক্সেস আছে</string>
|
||||
<string name="owner_manage_summary">শুধুমাত্র মালিকই রুট অ্যাক্সেস পরিচালনা করতে পারে এবং অনুরোধ প্রম্পট গ্রহণ করতে পারে</string>
|
||||
<string name="user_independent_summary">প্রতিটি ব্যবহারকারীর নিজস্ব পৃথক রুট নিয়ম আছে</string>
|
||||
|
||||
<string name="mount_namespace_mode">মাউন্ট নেমস্পেস মোড</string>
|
||||
<string name="settings_ns_global">গ্লোবাল নেমস্পেস</string>
|
||||
<string name="settings_ns_requester">নেমস্পেস ইনহেরিট করুন</string>
|
||||
<string name="settings_ns_isolate">বিচ্ছিন্ন নামস্থান</string>
|
||||
<string name="global_summary">সমস্ত রুট সেশন গ্লোবাল মাউন্ট নেমস্পেস ব্যবহার করে</string>
|
||||
<string name="requester_summary">রুট সেশন অনুরোধকারীর নামস্থান উত্তরাধিকারী হবে</string>
|
||||
<string name="isolate_summary">প্রতিটি রুট সেশনের নিজস্ব বিচ্ছিন্ন নামস্থান থাকবে</string>
|
||||
|
||||
<!--Notifications-->
|
||||
<string name="update_channel">ম্যাজিস্ক আপডেট</string>
|
||||
<string name="progress_channel">অগ্রগতি বিজ্ঞপ্তি</string>
|
||||
<string name="updated_channel">আপডেট সম্পূর্ণ</string>
|
||||
<string name="download_complete">ডাউনলোড শেষ</string>
|
||||
<string name="download_file_error">ফাইল ডাউনলোড করার সময় ত্রুটি</string>
|
||||
<string name="magisk_update_title">ম্যাজিস্ক আপডেট উপলব্ধ!</string>
|
||||
<string name="updated_title">ম্যাজিস্ক আপডেট</string>
|
||||
<string name="updated_text">অ্যাপ খুলতে আলতো চাপুন</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="yes">হ্যাঁ</string>
|
||||
<string name="no">না</string>
|
||||
<string name="repo_install_title">ইনস্টল করুন %1$s %2$s(%3$d)</string>
|
||||
<string name="download">ডাউনলোড করুন</string>
|
||||
<string name="reboot">রিবুট করুন</string>
|
||||
<string name="release_notes">অব্যাহতি পত্র</string>
|
||||
<string name="flashing">ঝলকানি…</string>
|
||||
<string name="done">সম্পন্ন!</string>
|
||||
<string name="failure">ব্যর্থ হয়েছে!</string>
|
||||
<string name="hide_app_title">ম্যাজিস্ক অ্যাপটি লুকিয়ে রাখছে…</string>
|
||||
<string name="open_link_failed_toast">লিঙ্ক খোলার জন্য কোনো অ্যাপ পাওয়া যায়নি</string>
|
||||
<string name="complete_uninstall">সম্পূর্ণ আনইনস্টল করুন</string>
|
||||
<string name="restore_img">ছবি পুনরুদ্ধার করুন</string>
|
||||
<string name="restore_img_msg">পুনরুদ্ধার করা হচ্ছে…</string>
|
||||
<string name="restore_done">পুনরুদ্ধার করা হয়েছে!</string>
|
||||
<string name="restore_fail">স্টক ব্যাকআপ বিদ্যমান নেই!</string>
|
||||
<string name="setup_fail">সেটআপ ব্যর্থ হয়েছে৷</string>
|
||||
<string name="env_fix_title">অতিরিক্ত সেটআপ প্রয়োজন</string>
|
||||
<string name="env_fix_msg">ম্যাজিস্ক সঠিকভাবে কাজ করার জন্য আপনার ডিভাইসের অতিরিক্ত সেটআপ প্রয়োজন। আপনি কি এগিয়ে যেতে এবং রিবুট করতে চান??</string>
|
||||
<string name="setup_msg">চলমান পরিবেশ সেটআপ…</string>
|
||||
<string name="authenticate">প্রমাণীকরণ</string>
|
||||
<string name="unsupport_magisk_title">অসমর্থিত ম্যাজিস্ক সংস্করণ</string>
|
||||
<string name="unsupport_magisk_msg">অ্যাপটির এই সংস্করণটি %1$s-এর চেয়ে কম ম্যাগিস্ক সংস্করণগুলিকে সমর্থন করে না৷\n\nঅ্যাপটি এমন আচরণ করবে যেন কোনও ম্যাজিস্ক ইনস্টল করা নেই, দয়া করে যত তাড়াতাড়ি সম্ভব ম্যাজিস্ক আপগ্রেড করুন.</string>
|
||||
<string name="unsupport_general_title">অস্বাভাবিক অবস্থা</string>
|
||||
<string name="unsupport_system_app_msg">এই অ্যাপটিকে একটি সিস্টেম অ্যাপ হিসেবে চালানো সমর্থিত নয়। অনুগ্রহ করে অ্যাপটিকে একটি ব্যবহারকারী অ্যাপে ফিরিয়ে দিন.</string>
|
||||
<string name="unsupport_other_su_msg">ম্যাজিস্ক থেকে নয় একটি \"su\" বাইনারি সনাক্ত করা হয়েছে। অনুগ্রহ করে কোনো প্রতিযোগী রুট সমাধান সরান এবং/অথবা ম্যাজিস্ক পুনরায় ইনস্টল করুন।</string>
|
||||
<string name="unsupport_external_storage_msg">ম্যাজিস্ক বহিরাগত স্টোরেজ ইনস্টল করা হয়. অনুগ্রহ করে অ্যাপটিকে অভ্যন্তরীণ সঞ্চয়স্থানে সরান৷</string>
|
||||
<string name="unsupport_nonroot_stub_msg">লুকানো ম্যাজিস্ক অ্যাপটি কাজ চালিয়ে যেতে পারে না কারণ রুট হারিয়ে গেছে। অনুগ্রহ করে আসল APK পুনরুদ্ধার করুন।</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="add_shortcut_title">হোম স্ক্রিনে শর্টকাট যোগ করুন</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>
|
||||
|
||||
</resources>
|
@@ -22,6 +22,7 @@
|
||||
|
||||
<string name="home_notice_content">Laden Sie Magisk NUR von der offiziellen Github Seite herunter. Dateien aus unbekannten Quellen können bösartig sein!</string>
|
||||
<string name="home_support_title">Unterstützen Sie uns</string>
|
||||
<string name="home_follow_title">Folgen Sie uns</string>
|
||||
<string name="home_item_source">Quelle</string>
|
||||
<string name="home_support_content">Magisk ist und wird immer frei und quelloffen sein. Sie können uns jedoch jederzeit mit einer kleinen Spende unterstützen.</string>
|
||||
<string name="home_installed_version">Installiert</string>
|
||||
@@ -111,6 +112,7 @@
|
||||
<string name="suspend_text_riru">Modul ausgesetzt, weil %1$s aktiviert ist</string>
|
||||
<string name="suspend_text_zygisk">Modul ausgesetzt, weil %1$s nicht aktiviert ist</string>
|
||||
<string name="zygisk_module_unloaded">Zygisk-Modul aufgrund von Inkompatibilität nicht geladen</string>
|
||||
<string name="module_empty">Kein Modul installiert</string>
|
||||
|
||||
<!--Settings-->
|
||||
<string name="settings_dark_mode_title">Themen-Modus</string>
|
||||
@@ -190,9 +192,12 @@
|
||||
<!--Notifications-->
|
||||
<string name="update_channel">Magisk Updates</string>
|
||||
<string name="progress_channel">Fortschrittsmitteilungen</string>
|
||||
<string name="updated_channel">Update abgeschlossen</string>
|
||||
<string name="download_complete">Download abgeschlossen</string>
|
||||
<string name="download_file_error">Fehler beim Herunterladen der Datei</string>
|
||||
<string name="magisk_update_title">Magisk Update verfügbar!</string>
|
||||
<string name="updated_title">Magisk aktualisiert</string>
|
||||
<string name="updated_text">Tippen, um die App zu öffnen</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="yes">Ja</string>
|
||||
@@ -225,6 +230,8 @@
|
||||
<string name="unsupport_nonroot_stub_msg">App kann im versteckten Zustand nicht weiter funktionieren, da Root verloren gegangen ist. Ursprüngliche APK bitte wiederherstellen.</string>
|
||||
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
||||
<string name="external_rw_permission_denied">Speichererlaubnis erteilen, um diese Funktion zu aktivieren</string>
|
||||
<string name="install_unknown_denied">Unbekannte Apps installieren erlauben, um diese Funktion zu aktivieren</string>
|
||||
|
||||
<string name="add_shortcut_title">Verknüpfung zum Startbildschirm hinzufügen</string>
|
||||
<string name="add_shortcut_msg">Nachdem Sie diese App verstecken, wird der Name und das Symbol eventuell schwer zu erkennen sein. Möchten Sie zum Startbildschirm eine schöne Verknüpfung hinzufügen?</string>
|
||||
<string name="app_not_found">Keine App gefunden, die diese Aktion verarbeitet</string>
|
||||
|
@@ -8,6 +8,7 @@
|
||||
<string name="install">Εγκατάσταση</string>
|
||||
<string name="section_home">Αρχική</string>
|
||||
<string name="section_theme">Θέματα</string>
|
||||
<string name="denylist">Λίστα Απορρίψεων</string>
|
||||
|
||||
<!--Home-->
|
||||
<string name="no_connection">Δεν υπάρχει διαθέσιμη σύνδεση</string>
|
||||
@@ -21,6 +22,7 @@
|
||||
|
||||
<string name="home_notice_content">Κάντε λήψη του Magisk ΜΟΝΟ από την επίσημη σελίδα του GitHub. Τα αρχεία από άγνωστες πηγές μπορεί να είναι κακόβουλα!</string>
|
||||
<string name="home_support_title">Υποστήριξη</string>
|
||||
<string name="home_follow_title">Ακολούθησε μας</string>
|
||||
<string name="home_item_source">Πηγή</string>
|
||||
<string name="home_support_content">Το Magisk είναι, και θα είναι για πάντα, δωρεάν και ανοιχτού κώδικα. Μπορείτε ωστόσο να μας δείξετε ότι ενδιαφέρεστε στέλνοντας μια μικρή δωρεά.</string>
|
||||
<string name="home_installed_version">Τρέχουσα έκδοση</string>
|
||||
@@ -32,6 +34,7 @@
|
||||
<!--Install-->
|
||||
<string name="keep_force_encryption">Διατήρηση επιβεβλημένης κρυπτογράφησης</string>
|
||||
<string name="keep_dm_verity">Διατήρηση AVB 2.0/dm-verity</string>
|
||||
<string name="patch_vbmeta">Patch vbmeta στην εικόνα του boot</string>
|
||||
<string name="recovery_mode">Μέσω Recovery mode</string>
|
||||
<string name="install_options_title">Επιλογές</string>
|
||||
<string name="install_method_title">Μέθοδος</string>
|
||||
@@ -76,6 +79,7 @@
|
||||
<string name="superuser_toggle_notification">Ειδοποιήσεις</string>
|
||||
<string name="superuser_toggle_revoke">Ανακάλεσε</string>
|
||||
<string name="superuser_policy_none">Καμία εφαρμογή δεν έχει ζητήσει άδεια υπερχρήστη.</string>
|
||||
|
||||
<!--Logs-->
|
||||
<string name="log_data_none">Δεν υπάρχουν αρχεία καταγραφής, δοκιμάστε να χρησιμοποιήσετε περισσότερο τις εφαρμογές με δυνατότητα SU</string>
|
||||
<string name="log_data_magisk_none">Τα αρχεία καταγραφής του Magisk είναι κενά, αυτό είναι περίεργο</string>
|
||||
@@ -105,6 +109,10 @@
|
||||
<string name="module_state_restore">Επαναφορά</string>
|
||||
<string name="module_action_install_external">Εγκατάσταση από χώρο αποθήκευσης</string>
|
||||
<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 module not loaded due to incompatibility</string>
|
||||
<string name="module_empty">Δεν υπάρχουν εγκατεστημένες επεκτάσεις</string>
|
||||
|
||||
<!--Settings-->
|
||||
<string name="settings_dark_mode_title">Λειτουργία θέματος</string>
|
||||
@@ -127,6 +135,12 @@
|
||||
<string name="settings_update_beta">Δοκιμαστικό</string>
|
||||
<string name="settings_update_custom">Προσαρμοσμένο</string>
|
||||
<string name="settings_update_custom_msg">Εισαγωγή ενός custom URL</string>
|
||||
<string name="settings_zygisk_summary">Εκτέλεση μέρους του Magisk μέσα στο zygote daemon</string>
|
||||
<string name="settings_denylist_title">Επιβολή Λίστας Απορρίψεων</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">Systemless hosts</string>
|
||||
<string name="settings_hosts_summary">Υποστήριξη Systemless hosts για εφαρμογές Adblock</string>
|
||||
<string name="settings_hosts_toast">Προσθήκη αρθρώματος systemless hosts</string>
|
||||
@@ -178,9 +192,12 @@
|
||||
<!--Notifications-->
|
||||
<string name="update_channel">Ενημερώσεις Magisk</string>
|
||||
<string name="progress_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>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="yes">Ναι</string>
|
||||
@@ -206,10 +223,18 @@
|
||||
<string name="authenticate">Αυθεντικοποίηση</string>
|
||||
<string name="unsupport_magisk_title">Μη υποστηριζόμενη έκδοση Magisk</string>
|
||||
<string name="unsupport_magisk_msg">Αυτή η έκδοση της εφαρμογής δεν υποστηρίζει έκδοση Magisk χαμηλότερη από %1$s.\n\nΗ εφαρμογή θα συμπεριφέρεται σαν να μην έχει εγκατασταθεί το Magisk, αναβαθμίστε το Magisk το συντομότερο δυνατό.</string>
|
||||
<string name="external_rw_permission_denied">Παραχωρήστε άδεια αποθήκευσης για να ενεργοποιήσετε αυτήν τη λειτουργία</string>
|
||||
<string name="unsupport_general_title">Μη Φυσιολογική Κατάσταση</string>
|
||||
<string name="unsupport_system_app_msg">Η εκτέλεση αυτής της εφαρμογής ως εφαρμογής συστήματος δεν υποστηρίζεται. Επαναφέρετε την εφαρμογή σε εφαρμογή χρήστη.</string>
|
||||
<string name="unsupport_other_su_msg">Εντοπίστηκε ένα \"su\" binary που δεν είναι από το Magisk. Καταργήστε οποιαδήποτε άλλη root μέθοδο και/ή επανεγκαταστήστε το Magisk.</string>
|
||||
<string name="unsupport_external_storage_msg">Το Magisk είναι εγκατεστημένο σε εξωτερικό χώρο αποθήκευσης. Μετακινήστε την εφαρμογή στον εσωτερικό χώρο αποθήκευσης.</string>
|
||||
<string name="unsupport_nonroot_stub_msg">Η κρυφή εφαρμογή Magisk δεν μπορεί να συνεχίσει να λειτουργεί επειδή χάθηκε το root. Παρακαλώ επαναφέρετε το αρχικό APK.</string>
|
||||
|
||||
<string name="external_rw_permission_denied">Παραχωρήστε άδεια αποθήκευσης για να ενεργοποιήσετε αυτήν τη λειτουργία</string>
|
||||
<string name="install_unknown_denied">Επιτρέψτε την "εγκατάσταση άγνωστων εφαρμογών" για να ενεργοποιήσετε αυτήν τη λειτουργία</string>
|
||||
<string name="add_shortcut_title">Προσθέστε συντόμευση στην αρχική οθόνη</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>
|
||||
|
||||
</resources>
|
||||
|
@@ -22,6 +22,7 @@
|
||||
|
||||
<string name="home_notice_content">Descarga Magisk SÓLO de la página de GitHub oficial. Archivos de fuentes desconocidas pueden resultar maliciosos!</string>
|
||||
<string name="home_support_title">Apóyenos</string>
|
||||
<string name="home_follow_title">Síganos</string>
|
||||
<string name="home_item_source">Código fuente</string>
|
||||
<string name="home_support_content">Magisk es, y siempre será gratuito y de código abierto. Sin embargo, puede apoyarnos enviando una pequeña donación.</string>
|
||||
<string name="home_installed_version">Instalado</string>
|
||||
@@ -33,6 +34,7 @@
|
||||
<!--Install-->
|
||||
<string name="keep_force_encryption">Mantener cifrado forzado</string>
|
||||
<string name="keep_dm_verity">Mantener AVB 2.0/dm-verity</string>
|
||||
<string name="patch_vbmeta">Parche vbmeta en la imagen de arranque</string>
|
||||
<string name="recovery_mode">Modo recovery</string>
|
||||
<string name="install_options_title">Opciones</string>
|
||||
<string name="install_method_title">Método</string>
|
||||
@@ -109,6 +111,8 @@
|
||||
<string name="update_available">Actualización disponible</string>
|
||||
<string name="suspend_text_riru">Módulo suspendido porque %1$s está habilitado</string>
|
||||
<string name="suspend_text_zygisk">Módulo suspendido porque %1$s no está habilitado</string>
|
||||
<string name="zygisk_module_unloaded">Módulo Zygisk no cargado debido a incompatibilidad</string>
|
||||
<string name="module_empty">Ningún módulo instalado</string>
|
||||
|
||||
<!--Settings -->
|
||||
<string name="settings_dark_mode_title">Elegir modo</string>
|
||||
@@ -188,9 +192,12 @@
|
||||
<!--Notifications-->
|
||||
<string name="update_channel">Actualización de Magisk</string>
|
||||
<string name="progress_channel">Notificaciones de progreso</string>
|
||||
<string name="updated_channel">Actualización completa</string>
|
||||
<string name="download_complete">Descarga Completa</string>
|
||||
<string name="download_file_error">Error descargando archivo</string>
|
||||
<string name="magisk_update_title">¡Actualización de Magisk disponible!</string>
|
||||
<string name="updated_title">Magisk Actualizado</string>
|
||||
<string name="updated_text">Toca para abrir la aplicación</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="yes">Sí</string>
|
||||
@@ -216,10 +223,18 @@
|
||||
<string name="authenticate">Autenticar</string>
|
||||
<string name="unsupport_magisk_title">Versión de Magisk no soportada</string>
|
||||
<string name="unsupport_magisk_msg">Esta versión de la app no soporta versiones de Magisk inferiores a la %1$s.\n\nLa aplicación se comportará como si no tuvieses Magisk instalado, por favor actualiza Magisk tan pronto como sea posible.</string>
|
||||
<string name="unsupport_general_title">Estado anormal</string>
|
||||
<string name="unsupport_system_app_msg">No se admite la ejecución de esta aplicación como una aplicación del sistema. Revierta la aplicación a una aplicación de usuario.</string>
|
||||
<string name="unsupport_other_su_msg">Se ha detectado un binario \"su\" que no es de Magisk. Elimine cualquier solución raíz de la competencia y/o reinstale Magisk.</string>
|
||||
<string name="unsupport_external_storage_msg">Magisk está instalado en el almacenamiento externo. Mueva la aplicación al almacenamiento interno.</string>
|
||||
<string name="unsupport_nonroot_stub_msg">La aplicación Magisk oculta no puede seguir funcionando porque se perdió la raíz. Restaura el APK original.</string>
|
||||
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
||||
<string name="external_rw_permission_denied">Conceder permiso de almacenamiento para habilitar esta funcionalidad</string>
|
||||
<string name="install_unknown_denied">Permitir "instalar aplicaciones desconocidas" para habilitar esta funcionalidad</string>
|
||||
<string name="add_shortcut_title">Añadir un atajo a la pantalla de inicio</string>
|
||||
<string name="add_shortcut_msg">Tras esconder la app, su nombre e icono pueden resultar difíciles de reconocer. ¿Quieres añadir un acceso directo más bonito a la pantalla principal?</string>
|
||||
<string name="app_not_found">No se ha encontrado ninguna app para manejar esta acción</string>
|
||||
<string name="reboot_apply_change">Reiniciar para aplicar cambios</string>
|
||||
<string name="restore_app_confirmation">Esto restaurará la aplicación oculta a la aplicación original. ¿Realmente quieres hacer esto?</string>
|
||||
|
||||
</resources>
|
||||
|
@@ -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>
|
||||
|
@@ -20,16 +20,16 @@
|
||||
<string name="home_package">パッケージ ID </string>
|
||||
<string name="home_app_title">アプリ</string>
|
||||
|
||||
<string name="home_notice_content">Magisk は必ず公式 GitHub ページからダウンロードしてください。その他の提供元では悪意あるコードを含んでいる可能性があります!</string>
|
||||
<string name="home_notice_content">Magisk は必ず公式の GitHub ページからダウンロードしてください。その他の提供元では悪意あるコードを含んでいる可能性があります!</string>
|
||||
<string name="home_support_title">サポートする</string>
|
||||
<string name="home_follow_title">フォローする</string>
|
||||
<string name="home_item_source">ソース</string>
|
||||
<string name="home_support_content">Magisk はフリーでオープンソースです。寄付を送ることで、 Magisk の開発を支援できます。</string>
|
||||
<string name="home_support_content">Magisk はフリーでオープンソースです。寄付を送ることで、 Magisk の開発を支援することができます。</string>
|
||||
<string name="home_installed_version">インストール済</string>
|
||||
<string name="home_latest_version">最新版</string>
|
||||
<string name="invalid_update_channel">不正な更新チャンネルです</string>
|
||||
<string name="uninstall_magisk_title">Magisk のアンインストール</string>
|
||||
<string name="uninstall_magisk_msg">すべてのモジュールが無効化/削除されます。\nアンインストール後は root が無効化され、ストレージが暗号化されていない場合、暗号化される場合があります。</string>
|
||||
<string name="uninstall_magisk_msg">すべてのモジュールが無効化/削除されます。\nアンインストール後は root が削除され、ストレージが暗号化されていない場合は暗号化される場合があります。</string>
|
||||
|
||||
<!--Install-->
|
||||
<string name="keep_force_encryption">暗号化を維持する</string>
|
||||
@@ -40,7 +40,7 @@
|
||||
<string name="install_method_title">方法</string>
|
||||
<string name="install_next">次へ</string>
|
||||
<string name="install_start">はじめる</string>
|
||||
<string name="manager_download_install">タップでダウンロードしてインストールします</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>
|
||||
@@ -52,7 +52,7 @@
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">スーパーユーザー権限の要求</string>
|
||||
<string name="touch_filtered_warning">スーパーユーザー権限の要求ダイアログが他のアプリで隠れているため、Magisk はあなたの応答を確認できません。</string>
|
||||
<string name="touch_filtered_warning">他のアプリがスーパーユーザー権限の要求ダイアログを隠しているため、Magisk はあなたの応答を確認できません。</string>
|
||||
<string name="deny">拒否</string>
|
||||
<string name="prompt">尋ねる</string>
|
||||
<string name="grant">許可</string>
|
||||
@@ -206,7 +206,7 @@
|
||||
<string name="download">ダウンロード</string>
|
||||
<string name="reboot">再起動</string>
|
||||
<string name="release_notes">更新履歴</string>
|
||||
<string name="flashing">書き込み中…</string>
|
||||
<string name="flashing">書込中…</string>
|
||||
<string name="done">完了!</string>
|
||||
<string name="failure">失敗!</string>
|
||||
<string name="hide_app_title">Magisk アプリを隠しています…</string>
|
||||
@@ -218,7 +218,7 @@
|
||||
<string name="restore_fail">Stock のバックアップがありません!</string>
|
||||
<string name="setup_fail">セットアップに失敗しました</string>
|
||||
<string name="env_fix_title">追加のセットアップが必要です</string>
|
||||
<string name="env_fix_msg">Magisk を正常に動作させるために追加のセットアップが必要です。今すぐ再起動しますか?</string>
|
||||
<string name="env_fix_msg">Magisk を正常に動作させるためには追加のセットアップが必要です。今すぐ再起動しますか?</string>
|
||||
<string name="setup_msg">追加セットアップを実行中…</string>
|
||||
<string name="authenticate">認証</string>
|
||||
<string name="unsupport_magisk_title">対応していない Magisk バージョンです</string>
|
||||
@@ -227,7 +227,7 @@
|
||||
<string name="unsupport_system_app_msg">このアプリをシステムアプリとして起動することはサポートされていません。ユーザーアプリに戻してください。</string>
|
||||
<string name="unsupport_other_su_msg">Magisk 以外の \"su\" バイナリが検出されました。競合する root 権限取得ソフトを削除し、Magisk をインストールし直してください。</string>
|
||||
<string name="unsupport_external_storage_msg">Magisk が外部ストレージにインストールされています。内部ストレージに移動してください。</string>
|
||||
<string name="unsupport_nonroot_stub_msg">root 権限が失われたため、Magisk の隠しアプリが動作できなくなりました。元の APK を復元してください。</string>
|
||||
<string name="unsupport_nonroot_stub_msg">root 権限が失われたため、隠し Magisk アプリが動作できなくなりました。元の APK を復元してください。</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>
|
||||
|
@@ -119,7 +119,7 @@
|
||||
<string name="settings_download_path_title">Caminho para Baixar</string>
|
||||
<string name="settings_download_path_message">Os arquivos serão salvos em %1$s</string>
|
||||
<string name="settings_hide_app_title">Ocultar o app do Magisk</string>
|
||||
<string name="settings_hide_app_summary">Instala o app ooulto com ID aleatório e nome personalizado</string>
|
||||
<string name="settings_hide_app_summary">Instala o app oculto com ID aleatório e nome personalizado</string>
|
||||
<string name="settings_restore_app_title">Restaurar o App do Magisk</string>
|
||||
<string name="settings_restore_app_summary">Desoculta o app do Magisk e restaura o APK original</string>
|
||||
<string name="language">Idioma</string>
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
<!--Sections-->
|
||||
<string name="modules">Модули</string>
|
||||
<string name="superuser">Суперпользователь</string>
|
||||
<string name="superuser">Superuser</string>
|
||||
<string name="logs">Лог</string>
|
||||
<string name="settings">Настройки</string>
|
||||
<string name="install">Установка</string>
|
||||
@@ -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>
|
||||
|
140
build.py
140
build.py
@@ -37,10 +37,10 @@ def vprint(str):
|
||||
|
||||
is_windows = os.name == 'nt'
|
||||
is_ci = 'CI' in os.environ and os.environ['CI'] == 'true'
|
||||
EXE_EXT = '.exe' if is_windows else ''
|
||||
|
||||
if not is_ci and is_windows:
|
||||
import colorama
|
||||
|
||||
colorama.init()
|
||||
|
||||
# Environment checks
|
||||
@@ -58,16 +58,20 @@ except FileNotFoundError:
|
||||
|
||||
cpu_count = multiprocessing.cpu_count()
|
||||
archs = ['armeabi-v7a', 'x86', 'arm64-v8a', 'x86_64']
|
||||
triples = ['armv7a-linux-androideabi', 'i686-linux-android', 'aarch64-linux-android', 'x86_64-linux-android']
|
||||
default_targets = ['magisk', 'magiskinit', 'magiskboot', 'magiskpolicy', 'busybox']
|
||||
support_targets = default_targets + ['resetprop', 'test']
|
||||
rust_targets = ['magisk', 'magiskinit', 'magiskboot', 'magiskpolicy']
|
||||
|
||||
sdk_path = os.environ['ANDROID_SDK_ROOT']
|
||||
ndk_root = op.join(sdk_path, 'ndk')
|
||||
ndk_path = op.join(ndk_root, 'magisk')
|
||||
ndk_build = op.join(ndk_path, 'ndk-build')
|
||||
rust_bin = op.join(ndk_path, 'toolchains', 'rust', 'bin')
|
||||
cargo = op.join(rust_bin, 'cargo' + EXE_EXT)
|
||||
gradlew = op.join('.', 'gradlew' + ('.bat' if is_windows else ''))
|
||||
adb_path = op.join(sdk_path, 'platform-tools', 'adb' + ('.exe' if is_windows else ''))
|
||||
native_gen_path = op.join('native', 'out', 'generated')
|
||||
adb_path = op.join(sdk_path, 'platform-tools', 'adb' + EXE_EXT)
|
||||
native_gen_path = op.realpath(op.join('native', 'out', 'generated'))
|
||||
|
||||
# Global vars
|
||||
config = {}
|
||||
@@ -123,16 +127,17 @@ def mkdir_p(path, mode=0o755):
|
||||
os.makedirs(path, mode, exist_ok=True)
|
||||
|
||||
|
||||
def execv(cmd):
|
||||
return subprocess.run(cmd, stdout=STDOUT)
|
||||
def execv(cmd, env=None):
|
||||
return subprocess.run(cmd, stdout=STDOUT, env=env)
|
||||
|
||||
|
||||
def system(cmd):
|
||||
return subprocess.run(cmd, shell=True, stdout=STDOUT)
|
||||
|
||||
|
||||
def cmd_out(cmd):
|
||||
return subprocess.check_output(cmd).strip().decode('utf-8')
|
||||
def cmd_out(cmd, env=None):
|
||||
return subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, env=env) \
|
||||
.stdout.strip().decode('utf-8')
|
||||
|
||||
|
||||
def xz(data):
|
||||
@@ -180,15 +185,6 @@ def load_config(args):
|
||||
STDOUT = None if args.verbose else subprocess.DEVNULL
|
||||
|
||||
|
||||
def collect_binary():
|
||||
for arch in archs:
|
||||
mkdir_p(op.join('native', 'out', arch))
|
||||
for bin in support_targets + ['libpreload.so']:
|
||||
source = op.join('native', 'libs', arch, bin)
|
||||
target = op.join('native', 'out', arch, bin)
|
||||
mv(source, target)
|
||||
|
||||
|
||||
def clean_elf():
|
||||
if is_windows:
|
||||
elf_cleaner = op.join('tools', 'elf-cleaner.exe')
|
||||
@@ -203,48 +199,6 @@ def clean_elf():
|
||||
execv(args)
|
||||
|
||||
|
||||
def find_build_tools():
|
||||
global build_tools
|
||||
if build_tools:
|
||||
return build_tools
|
||||
build_tools_root = op.join(os.environ['ANDROID_SDK_ROOT'], 'build-tools')
|
||||
ls = os.listdir(build_tools_root)
|
||||
# Use the latest build tools available
|
||||
ls.sort()
|
||||
build_tools = op.join(build_tools_root, ls[-1])
|
||||
return build_tools
|
||||
|
||||
|
||||
# Unused but keep this code
|
||||
def sign_zip(unsigned):
|
||||
if 'keyStore' not in config:
|
||||
return
|
||||
|
||||
msg = '* Signing APK'
|
||||
apksigner = op.join(find_build_tools(), 'apksigner' + ('.bat' if is_windows else ''))
|
||||
|
||||
exec_args = [apksigner, 'sign',
|
||||
'--ks', config['keyStore'],
|
||||
'--ks-pass', f'pass:{config["keyStorePass"]}',
|
||||
'--ks-key-alias', config['keyAlias'],
|
||||
'--key-pass', f'pass:{config["keyPass"]}',
|
||||
'--v1-signer-name', 'CERT',
|
||||
'--v4-signing-enabled', 'false']
|
||||
|
||||
if unsigned.endswith('.zip'):
|
||||
msg = '* Signing zip'
|
||||
exec_args.extend(['--min-sdk-version', '17',
|
||||
'--v2-signing-enabled', 'false',
|
||||
'--v3-signing-enabled', 'false'])
|
||||
|
||||
exec_args.append(unsigned)
|
||||
|
||||
header(msg)
|
||||
proc = execv(exec_args)
|
||||
if proc.returncode != 0:
|
||||
error('Signing failed!')
|
||||
|
||||
|
||||
def binary_dump(src, var_name):
|
||||
out_str = f'constexpr unsigned char {var_name}[] = {{'
|
||||
for i, c in enumerate(xz(src.read())):
|
||||
@@ -261,7 +215,70 @@ def run_ndk_build(flags):
|
||||
if proc.returncode != 0:
|
||||
error('Build binary failed!')
|
||||
os.chdir('..')
|
||||
collect_binary()
|
||||
for arch in archs:
|
||||
for tgt in support_targets + ['libpreload.so']:
|
||||
source = op.join('native', 'libs', arch, tgt)
|
||||
target = op.join('native', 'out', arch, tgt)
|
||||
mv(source, target)
|
||||
|
||||
|
||||
def run_cargo_build(args):
|
||||
os.chdir(op.join('native', 'rust'))
|
||||
targets = set(args.target) & set(rust_targets)
|
||||
|
||||
env = os.environ.copy()
|
||||
env['CARGO_BUILD_RUSTC'] = op.join(rust_bin, 'rustc' + EXE_EXT)
|
||||
|
||||
# Install cxxbridge and generate C++ bindings
|
||||
native_out = op.join('..', '..', 'native', 'out')
|
||||
local_cargo_root = op.join(native_out, '.cargo')
|
||||
mkdir_p(local_cargo_root)
|
||||
cmds = [cargo, 'install', '--root', local_cargo_root, 'cxxbridge-cmd']
|
||||
if not args.verbose:
|
||||
cmds.append('-q')
|
||||
proc = execv(cmds, env)
|
||||
if proc.returncode != 0:
|
||||
error('cxxbridge-cmd installation failed!')
|
||||
cxxbridge = op.join(local_cargo_root, 'bin', 'cxxbridge' + EXE_EXT)
|
||||
mkdir(native_gen_path)
|
||||
for p in ['base', 'boot', 'core', 'init', 'sepolicy']:
|
||||
text = cmd_out([cxxbridge, op.join(p, 'src', 'lib.rs')])
|
||||
write_if_diff(op.join(native_gen_path, f'{p}-rs.cpp'), text)
|
||||
text = cmd_out([cxxbridge, '--header', op.join(p, 'src', 'lib.rs')])
|
||||
write_if_diff(op.join(native_gen_path, f'{p}-rs.hpp'), text)
|
||||
|
||||
# Start building the actual build commands
|
||||
cmds = [cargo, 'build', '-Z', 'build-std=std,panic_abort',
|
||||
'-Z', 'build-std-features=panic_immediate_abort']
|
||||
for target in targets:
|
||||
cmds.append('-p')
|
||||
cmds.append(target)
|
||||
rust_out = 'debug'
|
||||
if args.release:
|
||||
cmds.append('-r')
|
||||
rust_out = 'release'
|
||||
if not args.verbose:
|
||||
cmds.append('-q')
|
||||
|
||||
os_name = platform.system().lower()
|
||||
llvm_bin = op.join(ndk_path, 'toolchains', 'llvm', 'prebuilt', f'{os_name}-x86_64', 'bin')
|
||||
env['TARGET_CC'] = op.join(llvm_bin, 'clang' + EXE_EXT)
|
||||
env['RUSTFLAGS'] = '-Clinker-plugin-lto'
|
||||
for (arch, triple) in zip(archs, triples):
|
||||
env['TARGET_CFLAGS'] = f'--target={triple}21'
|
||||
rust_triple = 'thumbv7neon-linux-androideabi' if triple.startswith('armv7') else triple
|
||||
proc = execv([*cmds, '--target', rust_triple], env)
|
||||
if proc.returncode != 0:
|
||||
error('Build binary failed!')
|
||||
|
||||
arch_out = op.join(native_out, arch)
|
||||
mkdir(arch_out)
|
||||
for tgt in targets:
|
||||
source = op.join('target', rust_triple, rust_out, f'lib{tgt}.a')
|
||||
target = op.join(arch_out, f'lib{tgt}-rs.a')
|
||||
mv(source, target)
|
||||
|
||||
os.chdir(op.join('..', '..'))
|
||||
|
||||
|
||||
def write_if_diff(file_name, text):
|
||||
@@ -326,6 +343,8 @@ def build_binary(args):
|
||||
|
||||
header('* Building binaries: ' + ' '.join(args.target))
|
||||
|
||||
run_cargo_build(args)
|
||||
|
||||
dump_flag_header()
|
||||
|
||||
flag = ''
|
||||
@@ -402,10 +421,11 @@ def cleanup(args):
|
||||
rm_rf(op.join('native', 'out'))
|
||||
rm_rf(op.join('native', 'libs'))
|
||||
rm_rf(op.join('native', 'obj'))
|
||||
rm_rf(op.join('native', 'rust', 'target'))
|
||||
|
||||
if 'java' in args.target:
|
||||
header('* Cleaning java')
|
||||
execv([gradlew, 'clean'])
|
||||
execv([gradlew, 'app:clean', 'app:shared:clean', 'stub:clean'])
|
||||
|
||||
|
||||
def setup_ndk(args):
|
||||
|
@@ -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")
|
||||
|
@@ -11,7 +11,8 @@ import javax.crypto.spec.SecretKeySpec
|
||||
import kotlin.random.asKotlinRandom
|
||||
|
||||
// Set non-zero value here to fix the random seed for reproducible builds
|
||||
const val RAND_SEED = 0
|
||||
// CI builds are always reproducible
|
||||
val RAND_SEED = if (System.getenv("CI") != null) 42 else 0
|
||||
private lateinit var RANDOM: Random
|
||||
private val kRANDOM get() = RANDOM.asKotlinRandom()
|
||||
|
||||
@@ -64,8 +65,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 +126,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,7 +21,10 @@ 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.jar.JarFile
|
||||
import java.util.zip.*
|
||||
|
||||
private fun Project.androidBase(configure: Action<BaseExtension>) =
|
||||
@@ -55,6 +63,38 @@ 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()
|
||||
it.get(JarFile.MANIFEST_NAME)?.delete()
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.setupAppCommon() {
|
||||
setupCommon()
|
||||
|
||||
@@ -91,6 +131,16 @@ private fun Project.setupAppCommon() {
|
||||
includeInApk = false
|
||||
}
|
||||
}
|
||||
|
||||
android.applicationVariants.all {
|
||||
val projectName = project.name.toLowerCase(Locale.ROOT)
|
||||
val variantCapped = name.capitalize(Locale.ROOT)
|
||||
tasks.getByPath(":$projectName:package$variantCapped").doLast {
|
||||
val apk = outputs.files.asFileTree.filter { it.name.endsWith(".apk") }.singleFile
|
||||
val comment = "version=${Config.version}\nversionCode=${Config.versionCode}"
|
||||
addComment(apk, signingConfig, android.defaultConfig.minSdk!!, comment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.setupApp() {
|
||||
@@ -219,27 +269,20 @@ fun Project.setupStub() {
|
||||
commandLine(aapt, "optimize", "-o", apkTmp, "--collapse-resource-names", apk)
|
||||
}
|
||||
|
||||
val buffer = ByteArrayOutputStream()
|
||||
apkTmp.inputStream().use {
|
||||
object : GZIPOutputStream(buffer) {
|
||||
init {
|
||||
def.setLevel(Deflater.BEST_COMPRESSION)
|
||||
}
|
||||
}.use { o ->
|
||||
it.transferTo(o)
|
||||
val bos = ByteArrayOutputStream()
|
||||
ZipFile(apkTmp).use { src ->
|
||||
ZipOutputStream(apk.outputStream()).use {
|
||||
it.setLevel(Deflater.BEST_COMPRESSION)
|
||||
it.putNextEntry(ZipEntry("AndroidManifest.xml"))
|
||||
src.getInputStream(src.getEntry("AndroidManifest.xml")).transferTo(it)
|
||||
it.closeEntry()
|
||||
}
|
||||
}
|
||||
ZipFile(apkTmp).use { o ->
|
||||
ZipOutputStream(apk.outputStream()).use { n ->
|
||||
n.setLevel(Deflater.BEST_COMPRESSION)
|
||||
n.putNextEntry(ZipEntry("AndroidManifest.xml"))
|
||||
o.getInputStream(o.getEntry("AndroidManifest.xml")).transferTo(n)
|
||||
n.closeEntry()
|
||||
n.finish()
|
||||
DeflaterOutputStream(bos, Deflater(Deflater.BEST_COMPRESSION)).use {
|
||||
src.getInputStream(src.getEntry("resources.arsc")).transferTo(it)
|
||||
}
|
||||
}
|
||||
apkTmp.delete()
|
||||
genEncryptedResources(ByteArrayInputStream(buffer.toByteArray()), outSrcDir)
|
||||
genEncryptedResources(ByteArrayInputStream(bos.toByteArray()), outSrcDir)
|
||||
}
|
||||
}
|
||||
registerJavaGeneratingTask(genSrcTask, outSrcDir)
|
||||
|
@@ -1,5 +1,22 @@
|
||||
# Magisk Changelog
|
||||
|
||||
### v25.2
|
||||
|
||||
- [MagiskInit] Fix a potential issue when stub cpio is used
|
||||
- [MagiskInit] Fix reboot to recovery when stub cpio is used
|
||||
- [MagiskInit] Fix sepolicy.rules symlink for rootfs devices
|
||||
- [General] Better data encryption detection
|
||||
- [General] Move the whole logging infrastructure into Rust
|
||||
|
||||
### 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)
|
11
docs/releases/25200.md
Normal file
11
docs/releases/25200.md
Normal file
@@ -0,0 +1,11 @@
|
||||
## 2022.7.20 Magisk v25.2
|
||||
|
||||
Maintenance release fixing various issues.
|
||||
|
||||
- [MagiskInit] Fix a potential issue when stub cpio is used
|
||||
- [MagiskInit] Fix reboot to recovery when stub cpio is used
|
||||
- [MagiskInit] Fix sepolicy.rules symlink for rootfs devices
|
||||
- [General] Better data encryption detection
|
||||
- [General] Move the whole logging infrastructure into Rust
|
||||
|
||||
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)
|
@@ -1,5 +1,7 @@
|
||||
# Release Notes
|
||||
|
||||
- [v25.2](25200.md)
|
||||
- [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.ondkVersion=r24.1
|
||||
magisk.stubVersion=33
|
||||
magisk.versionCode=25200
|
||||
magisk.ondkVersion=r24.2
|
||||
|
34
native/README.md
Normal file
34
native/README.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Native Development
|
||||
|
||||
## Prerequisite
|
||||
|
||||
Install the NDK required to build and develop Magisk with `./build.py ndk`. The NDK will be installed to `$ANDROID_SDK_ROOT/ndk/magisk`. You don't need to manually install a Rust toolchain with `rustup`, as the NDK installed already has a Rust toolchain bundled.
|
||||
|
||||
## Code Paths
|
||||
|
||||
- `jni`: Magisk's code in C++
|
||||
- `jni/external`: external dependencies, mostly submodules
|
||||
- `rust`: Magisk's code in Rust
|
||||
- `src`: irrelevant, only exists to setup a native Android Studio project
|
||||
|
||||
## Build Configs
|
||||
|
||||
All C/C++ code and its dependencies are built with [`ndk-build`](https://developer.android.com/ndk/guides/ndk-build) and configured with several `*.mk` files scatterred in many places.
|
||||
|
||||
The `rust` folder is a proper Cargo workspace, and all Rust code is built with `cargo` just like any other Rust projects.
|
||||
|
||||
## Rust + C/C++
|
||||
|
||||
To reduce complexity involved in linking, all Rust code is built as `staticlib` and linked to C++ targets to ensure our final product is built with an officially supported NDK build system. Each C++ target can at most link to **one** Rust `staticlib` or else multiple definitions error will occur.
|
||||
|
||||
We use the [`cxx`](https://cxx.rs) project for interop between Rust and C++. Although cxx supports interop in both directions, for Magisk, it is strongly advised to avoid calling C++ functions in Rust; if some functionality required in Rust is already implemented in C++, the desired solution is to port the C++ implementation into Rust and export the migrated function back to C++.
|
||||
|
||||
## Development / IDE
|
||||
|
||||
All C++ code should be recognized and properly indexed by Android Studio out of the box. For Rust:
|
||||
|
||||
- Install the [Rust plugin](https://www.jetbrains.com/rust/) in Android Studio
|
||||
- In Preferences > Languages & Frameworks > Rust, set `$ANDROID_SDK_ROOT/ndk/magisk/toolchains/rust/bin` as the toolchain location
|
||||
- Open `native/rust/Cargo.toml`, and select "Attach" in the "No Cargo projects found" banner
|
||||
|
||||
Note: run `./build.py binary` before developing to make sure generated code is created.
|
25
native/jni/Android-rs.mk
Normal file
25
native/jni/Android-rs.mk
Normal file
@@ -0,0 +1,25 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
###########################
|
||||
# Rust compilation outputs
|
||||
###########################
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := magisk-rs
|
||||
LOCAL_SRC_FILES := ../out/$(TARGET_ARCH_ABI)/libmagisk-rs.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := boot-rs
|
||||
LOCAL_SRC_FILES := ../out/$(TARGET_ARCH_ABI)/libmagiskboot-rs.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := init-rs
|
||||
LOCAL_SRC_FILES := ../out/$(TARGET_ARCH_ABI)/libmagiskinit-rs.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := policy-rs
|
||||
LOCAL_SRC_FILES := ../out/$(TARGET_ARCH_ABI)/libmagiskpolicy-rs.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
@@ -14,7 +14,8 @@ LOCAL_STATIC_LIBRARIES := \
|
||||
libsystemproperties \
|
||||
libphmap \
|
||||
libxhook \
|
||||
libmincrypt
|
||||
libmincrypt \
|
||||
libmagisk-rs
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
core/applets.cpp \
|
||||
@@ -68,7 +69,8 @@ LOCAL_STATIC_LIBRARIES := \
|
||||
libbase \
|
||||
libcompat \
|
||||
libpolicy \
|
||||
libxz
|
||||
libxz \
|
||||
libinit-rs
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
init/init.cpp \
|
||||
@@ -95,7 +97,8 @@ LOCAL_STATIC_LIBRARIES := \
|
||||
libbz2 \
|
||||
libfdt \
|
||||
libz \
|
||||
libzopfli
|
||||
libzopfli \
|
||||
libboot-rs
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
boot/main.cpp \
|
||||
@@ -119,7 +122,8 @@ LOCAL_MODULE := magiskpolicy
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libbase \
|
||||
libbase \
|
||||
libpolicy
|
||||
libpolicy \
|
||||
libpolicy-rs
|
||||
|
||||
LOCAL_SRC_FILES := sepolicy/main.cpp
|
||||
|
||||
@@ -135,7 +139,8 @@ LOCAL_STATIC_LIBRARIES := \
|
||||
libbase \
|
||||
libcompat \
|
||||
libnanopb \
|
||||
libsystemproperties
|
||||
libsystemproperties \
|
||||
libmagisk-rs
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
core/applet_stub.cpp \
|
||||
@@ -181,6 +186,7 @@ LOCAL_SRC_FILES := \
|
||||
sepolicy/statement.cpp
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
include jni/Android-rs.mk
|
||||
include jni/base/Android.mk
|
||||
include jni/external/Android.mk
|
||||
|
||||
|
@@ -15,7 +15,8 @@ LOCAL_SRC_FILES := \
|
||||
selinux.cpp \
|
||||
logging.cpp \
|
||||
xwrap.cpp \
|
||||
stream.cpp
|
||||
stream.cpp \
|
||||
../external/cxx-rs/src/cxx.cc
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
# All static executables should link with libcompat
|
||||
|
@@ -6,7 +6,7 @@
|
||||
|
||||
#undef _FORTIFY_SOURCE
|
||||
|
||||
extern int __vloge(const char* fmt, va_list ap);
|
||||
extern void __vloge(const char* fmt, va_list ap);
|
||||
|
||||
static inline __noreturn __printflike(1, 2) void __fortify_fatal(const char* fmt, ...) {
|
||||
va_list args;
|
||||
|
@@ -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);
|
||||
|
@@ -5,3 +5,4 @@
|
||||
#include "../files.hpp"
|
||||
#include "../misc.hpp"
|
||||
#include "../logging.hpp"
|
||||
#include <base-rs.hpp>
|
||||
|
@@ -1,62 +1,75 @@
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <flags.h>
|
||||
#include <android/log.h>
|
||||
|
||||
#include "logging.hpp"
|
||||
#include <flags.h>
|
||||
#include <base.hpp>
|
||||
|
||||
// Just need to include it somewhere
|
||||
#include <base-rs.cpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
int nop_log(const char *, va_list) { return 0; }
|
||||
|
||||
void nop_ex(int) {}
|
||||
|
||||
log_callback log_cb = {
|
||||
.d = nop_log,
|
||||
.i = nop_log,
|
||||
.w = nop_log,
|
||||
.e = nop_log,
|
||||
.ex = nop_ex
|
||||
};
|
||||
|
||||
void no_logging() {
|
||||
log_cb.d = nop_log;
|
||||
log_cb.i = nop_log;
|
||||
log_cb.w = nop_log;
|
||||
log_cb.e = nop_log;
|
||||
log_cb.ex = nop_ex;
|
||||
static int fmt_and_log_with_rs(LogLevel level, const char *fmt, va_list ap) {
|
||||
char buf[4096];
|
||||
int ret = vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
log_with_rs(level, rust::Str(buf, ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vprintfe(const char *fmt, va_list ap) {
|
||||
return vfprintf(stderr, fmt, ap);
|
||||
int (*cpp_logger)(LogLevel level, const char *fmt, va_list ap) = fmt_and_log_with_rs;
|
||||
|
||||
// Used to override external C library logging
|
||||
extern "C" int magisk_log_print(int prio, const char *tag, const char *fmt, ...) {
|
||||
LogLevel level;
|
||||
switch (prio) {
|
||||
case ANDROID_LOG_DEBUG:
|
||||
level = LogLevel::Debug;
|
||||
break;
|
||||
case ANDROID_LOG_INFO:
|
||||
level = LogLevel::Info;
|
||||
break;
|
||||
case ANDROID_LOG_WARN:
|
||||
level = LogLevel::Warn;
|
||||
break;
|
||||
case ANDROID_LOG_ERROR:
|
||||
level = LogLevel::Error;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
char fmt_buf[4096];
|
||||
auto len = strlcpy(fmt_buf, tag, sizeof(fmt_buf));
|
||||
// Prevent format specifications in the tag
|
||||
std::replace(fmt_buf, fmt_buf + len, '%', '_');
|
||||
snprintf(fmt_buf + len, sizeof(fmt_buf) - len, ": %s", fmt);
|
||||
va_list argv;
|
||||
va_start(argv, fmt);
|
||||
int ret = cpp_logger(level, fmt_buf, argv);
|
||||
va_end(argv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void cmdline_logging() {
|
||||
log_cb.d = vprintfe;
|
||||
log_cb.i = vprintf;
|
||||
log_cb.w = vprintfe;
|
||||
log_cb.e = vprintfe;
|
||||
log_cb.ex = exit;
|
||||
}
|
||||
|
||||
#define LOG_BODY(prio) { \
|
||||
#define LOG_BODY(level) { \
|
||||
va_list argv; \
|
||||
va_start(argv, fmt); \
|
||||
log_cb.prio(fmt, argv); \
|
||||
cpp_logger(LogLevel::level, fmt, argv); \
|
||||
va_end(argv); \
|
||||
}
|
||||
|
||||
// LTO will optimize out the NOP function
|
||||
#if MAGISK_DEBUG
|
||||
void LOGD(const char *fmt, ...) { LOG_BODY(d) }
|
||||
void LOGD(const char *fmt, ...) { LOG_BODY(Debug) }
|
||||
#else
|
||||
void LOGD(const char *fmt, ...) {}
|
||||
#endif
|
||||
void LOGI(const char *fmt, ...) { LOG_BODY(i) }
|
||||
void LOGW(const char *fmt, ...) { LOG_BODY(w) }
|
||||
void LOGE(const char *fmt, ...) { LOG_BODY(e); log_cb.ex(EXIT_FAILURE); }
|
||||
void LOGI(const char *fmt, ...) { LOG_BODY(Info) }
|
||||
void LOGW(const char *fmt, ...) { LOG_BODY(Warn) }
|
||||
void LOGE(const char *fmt, ...) { LOG_BODY(Error) }
|
||||
|
||||
// Export raw symbol to fortify compat
|
||||
extern "C" int __vloge(const char* fmt, va_list ap) {
|
||||
return log_cb.e(fmt, ap);
|
||||
extern "C" void __vloge(const char* fmt, va_list ap) {
|
||||
cpp_logger(LogLevel::Error, fmt, ap);
|
||||
}
|
||||
|
@@ -3,24 +3,12 @@
|
||||
#include <cerrno>
|
||||
#include <cstdarg>
|
||||
|
||||
struct log_callback {
|
||||
int (*d)(const char* fmt, va_list ap);
|
||||
int (*i)(const char* fmt, va_list ap);
|
||||
int (*w)(const char* fmt, va_list ap);
|
||||
int (*e)(const char* fmt, va_list ap);
|
||||
void (*ex)(int code);
|
||||
};
|
||||
#include <base-rs.hpp>
|
||||
|
||||
extern log_callback log_cb;
|
||||
extern int (*cpp_logger)(LogLevel level, const char *fmt, va_list ap);
|
||||
|
||||
void LOGD(const char *fmt, ...) __printflike(1, 2);
|
||||
void LOGI(const char *fmt, ...) __printflike(1, 2);
|
||||
void LOGW(const char *fmt, ...) __printflike(1, 2);
|
||||
void LOGE(const char *fmt, ...) __printflike(1, 2);
|
||||
#define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s\n", ##args, errno, std::strerror(errno))
|
||||
|
||||
int nop_log(const char *, va_list);
|
||||
void nop_ex(int);
|
||||
|
||||
void no_logging();
|
||||
void cmdline_logging();
|
||||
|
@@ -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);
|
||||
|
@@ -188,7 +188,7 @@ static bool check_data() {
|
||||
return false;
|
||||
auto crypto = getprop("ro.crypto.state");
|
||||
if (!crypto.empty()) {
|
||||
if (crypto == "unencrypted") {
|
||||
if (crypto != "encrypted") {
|
||||
// Unencrypted, we can directly access data
|
||||
return true;
|
||||
} else {
|
||||
|
@@ -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 {};
|
||||
}
|
||||
|
@@ -20,8 +20,7 @@ void unlock_blocks();
|
||||
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();
|
||||
|
@@ -11,6 +11,8 @@
|
||||
#include <resetprop.hpp>
|
||||
#include <flags.h>
|
||||
|
||||
#include <core-rs.cpp>
|
||||
|
||||
#include "core.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
@@ -35,7 +35,7 @@ void setup_logfile(bool reset) {
|
||||
}
|
||||
|
||||
// Maximum message length for pipes to transfer atomically
|
||||
#define MAX_MSG_LEN (PIPE_BUF - sizeof(log_meta))
|
||||
#define MAX_MSG_LEN (int) (PIPE_BUF - sizeof(log_meta))
|
||||
|
||||
static void *logfile_writer(void *arg) {
|
||||
int pipefd = (long) arg;
|
||||
@@ -124,11 +124,11 @@ static void *logfile_writer(void *arg) {
|
||||
}
|
||||
}
|
||||
|
||||
int magisk_log(int prio, const char *fmt, va_list ap) {
|
||||
char buf[MAX_MSG_LEN + 1];
|
||||
int len = vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
|
||||
void magisk_log_write(int prio, const char *msg, int len) {
|
||||
if (logd_fd >= 0) {
|
||||
// Truncate
|
||||
len = std::min(MAX_MSG_LEN, len);
|
||||
|
||||
log_meta meta = {
|
||||
.prio = prio,
|
||||
.len = len,
|
||||
@@ -139,7 +139,7 @@ int magisk_log(int prio, const char *fmt, va_list ap) {
|
||||
iovec iov[2];
|
||||
iov[0].iov_base = &meta;
|
||||
iov[0].iov_len = sizeof(meta);
|
||||
iov[1].iov_base = buf;
|
||||
iov[1].iov_base = (void *) msg;
|
||||
iov[1].iov_len = len;
|
||||
|
||||
if (writev(logd_fd, iov, 2) < 0) {
|
||||
@@ -147,41 +147,6 @@ int magisk_log(int prio, const char *fmt, va_list ap) {
|
||||
close(logd_fd.exchange(-1));
|
||||
}
|
||||
}
|
||||
__android_log_write(prio, "Magisk", buf);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
// Used to override external C library logging
|
||||
extern "C" int magisk_log_print(int prio, const char *tag, const char *fmt, ...) {
|
||||
char buf[4096];
|
||||
auto len = strlcpy(buf, tag, sizeof(buf));
|
||||
// Prevent format specifications in the tag
|
||||
std::replace(buf, buf + len, '%', '_');
|
||||
snprintf(buf + len, sizeof(buf) - len, ": %s", fmt);
|
||||
va_list argv;
|
||||
va_start(argv, fmt);
|
||||
int ret = magisk_log(prio, buf, argv);
|
||||
va_end(argv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define mlog(prio) [](auto fmt, auto ap){ return magisk_log(ANDROID_LOG_##prio, fmt, ap); }
|
||||
void magisk_logging() {
|
||||
log_cb.d = mlog(DEBUG);
|
||||
log_cb.i = mlog(INFO);
|
||||
log_cb.w = mlog(WARN);
|
||||
log_cb.e = mlog(ERROR);
|
||||
log_cb.ex = nop_ex;
|
||||
}
|
||||
|
||||
#define alog(prio) [](auto fmt, auto ap){ return __android_log_vprint(ANDROID_LOG_##prio, "Magisk", fmt, ap); }
|
||||
void android_logging() {
|
||||
log_cb.d = alog(DEBUG);
|
||||
log_cb.i = alog(INFO);
|
||||
log_cb.w = alog(WARN);
|
||||
log_cb.e = alog(ERROR);
|
||||
log_cb.ex = nop_ex;
|
||||
}
|
||||
|
||||
void start_log_daemon() {
|
||||
|
@@ -127,6 +127,7 @@ int magisk_main(int argc, char *argv[]) {
|
||||
#if 0
|
||||
/* Entry point for testing stuffs */
|
||||
else if (argv[1] == "--test"sv) {
|
||||
rust_test_entry();
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
@@ -103,9 +103,11 @@ int get_manager(int user_id, string *pkg, bool install) {
|
||||
snprintf(app_path, sizeof(app_path),
|
||||
"%s/%d/%s/dyn/current.apk", APP_DATA_DIR, u, mgr_pkg->data());
|
||||
int dyn = open(app_path, O_RDONLY | O_CLOEXEC);
|
||||
if (dyn < 0)
|
||||
if (dyn < 0) {
|
||||
LOGW("pkg: no dyn APK, ignore\n");
|
||||
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 +228,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
|
||||
@@ -270,7 +272,8 @@ int get_manager(int user_id, string *pkg, bool install) {
|
||||
install_stub();
|
||||
|
||||
not_found:
|
||||
LOGW("pkg: cannot find manager for user=[%d]\n", user_id);
|
||||
const char *name = mgr_pkg->empty() ? JAVA_PACKAGE_NAME : mgr_pkg->data();
|
||||
LOGW("pkg: cannot find %s for user=[%d]\n", name, user_id);
|
||||
if (pkg) pkg->clear();
|
||||
return -1;
|
||||
}
|
||||
|
1
native/jni/external/cxx-rs
vendored
Submodule
1
native/jni/external/cxx-rs
vendored
Submodule
Submodule native/jni/external/cxx-rs added at 650e64bc39
@@ -8,6 +8,7 @@
|
||||
#include <functional>
|
||||
|
||||
#include <socket.hpp>
|
||||
#include <core-rs.hpp>
|
||||
|
||||
#define AID_ROOT 0
|
||||
#define AID_SHELL 2000
|
||||
@@ -80,8 +81,7 @@ void exec_task(std::function<void()> &&task);
|
||||
|
||||
// Logging
|
||||
extern std::atomic<int> logd_fd;
|
||||
int magisk_log(int prio, const char *fmt, va_list ap);
|
||||
void android_logging();
|
||||
extern "C" void magisk_log_write(int prio, const char *msg, int len);
|
||||
|
||||
// Daemon handlers
|
||||
void post_fs_data(int client);
|
||||
|
@@ -118,11 +118,18 @@ static bool check_key_combo() {
|
||||
}
|
||||
|
||||
static FILE *kmsg;
|
||||
static char kmsg_buf[4096];
|
||||
static int vprintk(const char *fmt, va_list ap) {
|
||||
vsnprintf(kmsg_buf + 12, sizeof(kmsg_buf) - 12, fmt, ap);
|
||||
return fprintf(kmsg, "%s", kmsg_buf);
|
||||
extern "C" void klog_write(const char *msg, int len) {
|
||||
fprintf(kmsg, "%.*s", len, msg);
|
||||
}
|
||||
|
||||
static int klog_with_rs(LogLevel level, const char *fmt, va_list ap) {
|
||||
char buf[4096];
|
||||
strlcpy(buf, "magiskinit: ", sizeof(buf));
|
||||
int len = vsnprintf(buf + 12, sizeof(buf) - 12, fmt, ap) + 12;
|
||||
log_with_rs(level, rust::Str(buf, len));
|
||||
return len;
|
||||
}
|
||||
|
||||
void setup_klog() {
|
||||
// Shut down first 3 fds
|
||||
int fd;
|
||||
@@ -149,9 +156,8 @@ void setup_klog() {
|
||||
|
||||
kmsg = fdopen(fd, "w");
|
||||
setbuf(kmsg, nullptr);
|
||||
log_cb.d = log_cb.i = log_cb.w = log_cb.e = vprintk;
|
||||
log_cb.ex = nop_ex;
|
||||
strcpy(kmsg_buf, "magiskinit: ");
|
||||
rust::setup_klog();
|
||||
cpp_logger = klog_with_rs;
|
||||
|
||||
// Disable kmsg rate limiting
|
||||
if (FILE *rate = fopen("/proc/sys/kernel/printk_devkmsg", "w")) {
|
||||
|
@@ -22,6 +22,8 @@
|
||||
|
||||
#include "init.hpp"
|
||||
|
||||
#include <init-rs.cpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
bool unxz(int fd, const uint8_t *buf, size_t size) {
|
||||
@@ -57,6 +59,20 @@ static int dump_bin(const uint8_t *buf, size_t sz, const char *path, mode_t mode
|
||||
return 0;
|
||||
}
|
||||
|
||||
void restore_ramdisk_init() {
|
||||
unlink("/init");
|
||||
|
||||
const char *orig_init = backup_init();
|
||||
if (access(orig_init, F_OK) == 0) {
|
||||
xrename(orig_init, "/init");
|
||||
} else {
|
||||
// If the backup init is missing, this means that the boot ramdisk
|
||||
// was created from scratch, and the real init is in a separate CPIO,
|
||||
// which is guaranteed to be placed at /system/bin/init.
|
||||
xsymlink(INIT_PATH, "/init");
|
||||
}
|
||||
}
|
||||
|
||||
int dump_manager(const char *path, mode_t mode) {
|
||||
return dump_bin(manager_xz, sizeof(manager_xz), path, mode);
|
||||
}
|
||||
@@ -70,7 +86,7 @@ public:
|
||||
using BaseInit::BaseInit;
|
||||
void start() override {
|
||||
LOGD("Ramdisk is recovery, abort\n");
|
||||
rename(backup_init(), "/init");
|
||||
restore_ramdisk_init();
|
||||
rm_rf("/.backup");
|
||||
exec_init();
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#include <base.hpp>
|
||||
#include <init-rs.hpp>
|
||||
|
||||
using kv_pairs = std::vector<std::pair<std::string, std::string>>;
|
||||
|
||||
@@ -23,6 +24,7 @@ struct BootConfig {
|
||||
};
|
||||
|
||||
#define DEFAULT_DT_DIR "/proc/device-tree/firmware/android"
|
||||
#define INIT_PATH "/system/bin/init"
|
||||
|
||||
extern std::vector<std::string> mount_list;
|
||||
|
||||
@@ -32,6 +34,7 @@ void load_kernel_info(BootConfig *config);
|
||||
bool check_two_stage();
|
||||
void setup_klog();
|
||||
const char *backup_init();
|
||||
void restore_ramdisk_init();
|
||||
int dump_manager(const char *path, mode_t mode);
|
||||
int dump_preload(const char *path, mode_t mode);
|
||||
|
||||
|
@@ -202,13 +202,25 @@ persist:
|
||||
success:
|
||||
// Create symlinks so we don't need to go through this logic again
|
||||
strcpy(p, "/sepolicy.rules");
|
||||
xsymlink(custom_rules_dir.data(), path);
|
||||
if (char *rel = strstr(custom_rules_dir.data(), MIRRDIR)) {
|
||||
// Create symlink with relative path
|
||||
char s[128];
|
||||
s[0] = '.';
|
||||
strlcpy(s + 1, rel + sizeof(MIRRDIR) - 1, sizeof(s) - 1);
|
||||
xsymlink(s, path);
|
||||
} else {
|
||||
xsymlink(custom_rules_dir.data(), path);
|
||||
}
|
||||
}
|
||||
|
||||
bool LegacySARInit::mount_system_root() {
|
||||
backup_files();
|
||||
|
||||
LOGD("Mounting system_root\n");
|
||||
|
||||
// there's no /dev in stub cpio
|
||||
xmkdir("/dev", 0777);
|
||||
|
||||
strcpy(blk_info.block_dev, "/dev/root");
|
||||
|
||||
do {
|
||||
@@ -250,7 +262,6 @@ mount_root:
|
||||
switch_root("/system_root");
|
||||
|
||||
// Make dev writable
|
||||
xmkdir("/dev", 0755);
|
||||
xmount("tmpfs", "/dev", "tmpfs", 0, "mode=755");
|
||||
mount_list.emplace_back("/dev");
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -8,7 +8,6 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define INIT_PATH "/system/bin/init"
|
||||
#define REDIR_PATH "/data/magiskinit"
|
||||
|
||||
void FirstStageInit::prepare() {
|
||||
@@ -16,16 +15,7 @@ void FirstStageInit::prepare() {
|
||||
xmount("tmpfs", "/data", "tmpfs", 0, "mode=755");
|
||||
cp_afc("/init" /* magiskinit */, REDIR_PATH);
|
||||
|
||||
unlink("/init");
|
||||
const char *orig_init = backup_init();
|
||||
if (access(orig_init, F_OK) == 0) {
|
||||
xrename(orig_init, "/init");
|
||||
} else {
|
||||
// If the backup init is missing, this means that the boot ramdisk
|
||||
// was created from scratch, and the real init is in a separate CPIO,
|
||||
// which is guaranteed to be placed at /system/bin/init.
|
||||
xsymlink(INIT_PATH, "/init");
|
||||
}
|
||||
restore_ramdisk_init();
|
||||
|
||||
{
|
||||
auto init = mmap_data("/init", true);
|
||||
@@ -39,6 +29,7 @@ void FirstStageInit::prepare() {
|
||||
}
|
||||
|
||||
void LegacySARInit::first_stage_prep() {
|
||||
xmkdir("/data", 0755);
|
||||
xmount("tmpfs", "/data", "tmpfs", 0, "mode=755");
|
||||
|
||||
// Patch init binary
|
||||
@@ -64,7 +55,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 +71,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;
|
||||
}
|
||||
|
@@ -75,8 +75,6 @@ PB_BIND(PersistentProperties_PersistentPropertyRecord, PersistentProperties_Pers
|
||||
* End of auto generated code
|
||||
* ***************************/
|
||||
|
||||
bool use_pb = false;
|
||||
|
||||
static bool name_decode(pb_istream_t *stream, const pb_field_t *field, void **arg) {
|
||||
string &name = *static_cast<string *>(*arg);
|
||||
name.resize(stream->bytes_left);
|
||||
@@ -154,8 +152,18 @@ static bool file_getprop(const char *name, char *value) {
|
||||
return value[0] != '\0';
|
||||
}
|
||||
|
||||
static bool check_pb() {
|
||||
static bool checked = false;
|
||||
static bool use_pb = false;
|
||||
if (!checked) {
|
||||
checked = true;
|
||||
use_pb = access(PERSISTENT_PROPERTY_DIR "/persistent_properties", R_OK) == 0;
|
||||
}
|
||||
return use_pb;
|
||||
}
|
||||
|
||||
void persist_getprops(prop_cb *prop_cb) {
|
||||
if (use_pb) {
|
||||
if (check_pb()) {
|
||||
pb_getprop(prop_cb);
|
||||
} else {
|
||||
auto dir = open_dir(PERSISTENT_PROPERTY_DIR);
|
||||
@@ -180,7 +188,7 @@ private:
|
||||
};
|
||||
|
||||
string persist_getprop(const char *name) {
|
||||
if (use_pb) {
|
||||
if (check_pb()) {
|
||||
auto prop = match_prop_name(name);
|
||||
pb_getprop(&prop);
|
||||
if (prop.value[0]) {
|
||||
@@ -199,7 +207,7 @@ string persist_getprop(const char *name) {
|
||||
}
|
||||
|
||||
bool persist_deleteprop(const char *name) {
|
||||
if (use_pb) {
|
||||
if (check_pb()) {
|
||||
prop_list list;
|
||||
prop_collector collector(list);
|
||||
persist_getprops(&collector);
|
||||
|
@@ -16,8 +16,6 @@ struct prop_cb {
|
||||
}
|
||||
};
|
||||
|
||||
extern bool use_pb;
|
||||
|
||||
using prop_list = std::map<std::string, std::string>;
|
||||
|
||||
struct prop_collector : prop_cb {
|
||||
|
@@ -13,8 +13,6 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
static bool verbose = false;
|
||||
|
||||
#ifdef APPLET_STUB_MAIN
|
||||
#define system_property_set __system_property_set
|
||||
#define system_property_find __system_property_find
|
||||
@@ -240,7 +238,6 @@ struct resetprop : public sysprop {
|
||||
static sysprop_stub *get_impl() {
|
||||
static sysprop_stub *impl = nullptr;
|
||||
if (impl == nullptr) {
|
||||
use_pb = access(PERSISTENT_PROPERTY_DIR "/persistent_properties", R_OK) == 0;
|
||||
#ifdef APPLET_STUB_MAIN
|
||||
if (__system_properties_init()) {
|
||||
LOGE("resetprop: __system_properties_init error\n");
|
||||
@@ -297,10 +294,9 @@ void load_prop_file(const char *filename, bool prop_svc) {
|
||||
}
|
||||
|
||||
int resetprop_main(int argc, char *argv[]) {
|
||||
log_cb.d = [](auto fmt, auto ap) -> int { return verbose ? vfprintf(stderr, fmt, ap) : 0; };
|
||||
|
||||
bool prop_svc = true;
|
||||
bool persist = false;
|
||||
bool verbose = false;
|
||||
char *argv0 = argv[0];
|
||||
|
||||
--argc;
|
||||
@@ -340,6 +336,8 @@ int resetprop_main(int argc, char *argv[]) {
|
||||
++argv;
|
||||
}
|
||||
|
||||
set_log_level_state(LogLevel::Debug, verbose);
|
||||
|
||||
switch (argc) {
|
||||
case 0:
|
||||
print_props(persist);
|
||||
|
@@ -6,8 +6,7 @@ using namespace std;
|
||||
|
||||
void sepolicy::magisk_rules() {
|
||||
// Temp suppress warnings
|
||||
auto bak = log_cb.w;
|
||||
log_cb.w = nop_log;
|
||||
set_log_level_state(LogLevel::Warn, false);
|
||||
|
||||
// This indicates API 26+
|
||||
bool new_rules = exists("untrusted_app_25");
|
||||
@@ -196,5 +195,5 @@ void sepolicy::magisk_rules() {
|
||||
impl->strip_dontaudit();
|
||||
#endif
|
||||
|
||||
log_cb.w = bak;
|
||||
set_log_level_state(LogLevel::Warn, true);
|
||||
}
|
||||
|
@@ -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() ? \
|
||||
|
@@ -311,7 +311,7 @@ void su_daemon_handler(int client, const sock_cred *cred) {
|
||||
LOGD("su: fork handler\n");
|
||||
|
||||
// Abort upon any error occurred
|
||||
log_cb.ex = exit;
|
||||
exit_on_error(true);
|
||||
|
||||
// ack
|
||||
write_int(client, 0);
|
||||
@@ -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;
|
||||
|
@@ -17,17 +17,6 @@ using namespace std;
|
||||
|
||||
void *self_handle = nullptr;
|
||||
|
||||
static int zygisk_log(int prio, const char *fmt, va_list ap);
|
||||
|
||||
#define zlog(prio) [](auto fmt, auto ap){ return zygisk_log(ANDROID_LOG_##prio, fmt, ap); }
|
||||
static void zygisk_logging() {
|
||||
log_cb.d = zlog(DEBUG);
|
||||
log_cb.i = zlog(INFO);
|
||||
log_cb.w = zlog(WARN);
|
||||
log_cb.e = zlog(ERROR);
|
||||
log_cb.ex = nop_ex;
|
||||
}
|
||||
|
||||
// Make sure /proc/self/environ is sanitized
|
||||
// Filter env and reset MM_ENV_END
|
||||
static void sanitize_environ() {
|
||||
@@ -114,7 +103,7 @@ static void zygisk_init() {
|
||||
|
||||
// The following code runs in zygote/app process
|
||||
|
||||
static int zygisk_log(int prio, const char *fmt, va_list ap) {
|
||||
extern "C" void zygisk_log_write(int prio, const char *msg, int len) {
|
||||
// If we don't have log pipe set, ask magiskd for it
|
||||
// This could happen multiple times in zygote because it was closed to prevent crashing
|
||||
if (logd_fd < 0) {
|
||||
@@ -139,13 +128,12 @@ static int zygisk_log(int prio, const char *fmt, va_list ap) {
|
||||
sigaddset(&mask, SIGPIPE);
|
||||
pthread_sigmask(SIG_BLOCK, &mask, &orig_mask);
|
||||
}
|
||||
int ret = magisk_log(prio, fmt, ap);
|
||||
magisk_log_write(prio, msg, len);
|
||||
if (sig) {
|
||||
timespec ts{};
|
||||
sigtimedwait(&mask, nullptr, &ts);
|
||||
pthread_sigmask(SIG_SETMASK, &orig_mask, nullptr);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline bool should_load_modules(uint32_t flags) {
|
||||
|
@@ -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 {
|
||||
|
1
native/rust/.gitignore
vendored
Normal file
1
native/rust/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
target/
|
103
native/rust/Cargo.lock
generated
Normal file
103
native/rust/Cargo.lock
generated
Normal file
@@ -0,0 +1,103 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "base"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cxx",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.69"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-flags",
|
||||
"cxxbridge-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.69"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "magisk"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"base",
|
||||
"cxx",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "magiskboot"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"base",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "magiskinit"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"base",
|
||||
"cxx",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "magiskpolicy"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"base",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
|
26
native/rust/Cargo.toml
Normal file
26
native/rust/Cargo.toml
Normal file
@@ -0,0 +1,26 @@
|
||||
[workspace]
|
||||
|
||||
members = [
|
||||
"base",
|
||||
"boot",
|
||||
"core",
|
||||
"init",
|
||||
"sepolicy",
|
||||
]
|
||||
|
||||
[profile.dev]
|
||||
opt-level = "z"
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
strip = true
|
||||
|
||||
[profile.release]
|
||||
opt-level = "z"
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
strip = true
|
||||
|
||||
[patch.crates-io]
|
||||
cxx = { path = "../jni/external/cxx-rs" }
|
7
native/rust/base/Cargo.toml
Normal file
7
native/rust/base/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "base"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
cxx = "1.0.69"
|
25
native/rust/base/src/lib.rs
Normal file
25
native/rust/base/src/lib.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
#![feature(format_args_nl)]
|
||||
|
||||
pub use logging::*;
|
||||
pub use misc::*;
|
||||
|
||||
mod logging;
|
||||
mod misc;
|
||||
|
||||
#[cxx::bridge]
|
||||
pub mod ffi {
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum LogLevel {
|
||||
Error,
|
||||
Warn,
|
||||
Info,
|
||||
Debug,
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
fn log_with_rs(level: LogLevel, msg: &str);
|
||||
fn exit_on_error(b: bool);
|
||||
fn set_log_level_state(level: LogLevel, enabled: bool);
|
||||
fn cmdline_logging();
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user