1
mirror of https://github.com/topjohnwu/Magisk synced 2025-11-03 15:52:30 +01:00

Compare commits

..

132 Commits

Author SHA1 Message Date
topjohnwu
b62835cbeb Release new canary build 2025-01-31 02:36:58 +08:00
Wang Han
6ea740b5ab Skip clearing install dir if not needed 2025-01-27 12:14:55 +08:00
topjohnwu
7ab98dd5ac Make ioctl not a special token 2025-01-27 03:01:00 +08:00
anonymix007
fc8b3400fc Fix sterm parsing logic for ioctl 2025-01-27 03:01:00 +08:00
topjohnwu
54428ba415 Fix Android Studio C++ indexing 2025-01-26 02:24:35 +08:00
topjohnwu
95d1e69d8e Update to ONDK r28.2 2025-01-21 18:50:12 +08:00
topjohnwu
a0f13ab49f Move lambda to static function 2025-01-19 18:59:17 +08:00
topjohnwu
c3e8405020 Update libcxx 2025-01-19 18:51:17 +08:00
Pesh4waaa
a93593ea66 Kurdish Language For Magisk 2025-01-19 15:24:03 +08:00
Wang Han
23eff70883 Fix repeated binding of first argument
Co-authored-by: LoveSy <shana@zju.edu.cn>
2025-01-19 11:57:09 +08:00
vvb2060
110dd4a8b9 Update dependencies 2025-01-19 11:55:44 +08:00
Wang Han
d9c2bffc9f Avoid hardcoding max fd size
Android changed max fd limit to 32768 since Android 9:
cb5fccc83c

Co-authored-by: LoveSy <shana@zju.edu.cn>
2025-01-19 11:54:26 +08:00
topjohnwu
049db49dc8 Use preprocessor for 64bit detection 2025-01-11 00:15:10 +08:00
Wang Han
7c1d2ec61e zygisk: Let client send arch info 2025-01-11 00:15:10 +08:00
topjohnwu
a1b2830c06 Address clippy warnings 2025-01-11 00:11:48 +08:00
topjohnwu
82d1d19267 Migrate uid_granted_root to Rust 2025-01-11 00:11:48 +08:00
topjohnwu
4d4195c02d Migrate prune_su_access to Rust 2025-01-11 00:11:48 +08:00
topjohnwu
5637a258fc Migrate package detection to Rust 2025-01-11 00:11:48 +08:00
topjohnwu
ee6810f417 Rewrite magisk logging implementation 2025-01-11 00:11:48 +08:00
topjohnwu
7098248c64 Add more functionality into Rust 2025-01-11 00:11:48 +08:00
topjohnwu
0d31d356ef Use SQLite's internal time function 2025-01-05 05:04:04 -08:00
topjohnwu
b782e7dcb7 Fetch policy table from Rust 2025-01-05 05:04:04 -08:00
Softastur
a4671b4698 Update Asturian translations
Fixing and updating
2025-01-05 03:15:31 -08:00
topjohnwu
7edd8be169 Minor changes 2025-01-04 02:24:08 -08:00
topjohnwu
24650eefe4 Bind SQLite to Rust 2025-01-04 02:24:08 -08:00
topjohnwu
8e1a44e7eb Use argument binding for query 2025-01-04 02:24:08 -08:00
topjohnwu
2722875190 Use better C++ SQL APIs 2025-01-04 02:24:08 -08:00
topjohnwu
3ca6d06f69 Cleanup database code 2025-01-04 02:24:08 -08:00
topjohnwu
10e47248de Use finer grain sqlite3 APIs 2025-01-04 02:24:08 -08:00
Pzqqt
e73ff679ac scripts: flash_script.sh: Avoid overly dangerous code 2024-12-27 16:02:24 -08:00
Wang Han
53e401fa2d Perform authentication if needed for AutomaticResponse setting 2024-12-27 16:00:02 -08:00
LoveSy
d2768357da Support systemlessly deleting files or folders
After we refactor the magic mount and always mount folder as tmpfs,
we can easily support deleting files or folders now. We recognize
dummy devices with major number 0 and minor number 0 as an indicator
for removing files and folders. This indicator is borrowed from
overlayfs.
2024-12-27 15:57:54 -08:00
LoveSy
a6c2ba7c1e Allow kernel to relabel 2024-12-27 12:35:29 -08:00
LoveSy
aae5b466fb Use rust to implement collect/reset overlay context 2024-12-27 12:35:29 -08:00
5ec1cff
2b7be8b949 init: reset overlay.d files context after sepolicy loaded 2024-12-27 12:35:29 -08:00
5ec1cff
b6511a510d Revert "Allow all domains to access tmpfs files"
This reverts commit da43ac89a0.
2024-12-27 12:35:29 -08:00
Wang Han
704541aef2 Use /metadata/watchdog as preinit dir if exists
Since Android 15, all domains are allowed to search /metadata so preinit
dir will be exposed. Use /metadata/watchdog when /metadata is chosen as
preinit device, and the dir is available (since Android 11).
2024-12-27 10:35:05 -08:00
topjohnwu
005560a4c5 Always rescan manager APK when database is updated 2024-12-26 12:18:38 -08:00
topjohnwu
231a5d1853 Cleanup test code 2024-12-25 22:26:30 -08:00
topjohnwu
9e2b59060d Drive app migration tests through instrumentation
Make tests less flaky
2024-12-25 22:26:30 -08:00
topjohnwu
08ea937f7c Test su request via instrumentation 2024-12-25 22:26:30 -08:00
topjohnwu
2baedf74d1 Install and test LSPosed through test app 2024-12-25 22:26:30 -08:00
topjohnwu
32faa4ced6 Redesign test APK architecture
The test APK and the main APK share the same process and classloader,
and in the non-hidden case, the test APK's classes take precedence over
the ones in the main APK. This causes issues because the test APK and
main APK share some dependencies, but don't always use the same
version. This is especially problematic for the Kotlin stdlib and
AndroidX dependencies.

The solution here is to rely on R8's obfuscation feature and repackage
all potentially conflicting classes into a separate package in the test
APK. To ensure that the test classes are always using the same classes
as the main APK, we have to directly implement all tests inside the main
APK, making the test APK purely a "test runner with test dependencies".

As a result, the test APK can only be used when built in release mode,
because R8 no longer allow class obfuscation to be enabled when building
for debug versions.
2024-12-25 20:17:57 -08:00
topjohnwu
ccdb0b5d13 Add ability to skip certain test variants 2024-12-25 20:11:21 -08:00
topjohnwu
8506b672ad Update CI operating system 2024-12-23 22:52:30 -08:00
topjohnwu
ce2e33bb20 Cleanup test scripts 2024-12-23 20:42:54 -08:00
topjohnwu
6707b72260 Fix AVD tests 2024-12-23 20:41:42 -08:00
topjohnwu
5885b8c20d Add new tests for app hiding 2024-12-18 17:22:31 -08:00
topjohnwu
820710c086 Fix incorrect SQLite syntax 2024-12-18 17:22:31 -08:00
topjohnwu
51cf196bf7 Always use root to hide/restore app 2024-12-18 17:22:31 -08:00
Wang Han
dadba44cf9 Update module installer guide about META-INF 2024-12-17 16:36:40 -08:00
topjohnwu
2ce4a5543b Make ndk-build happy when Rust libs are missing 2024-12-13 17:00:40 -08:00
topjohnwu
9112a3a4f5 Introduce instrumentation tests 2024-12-13 12:07:42 -08:00
topjohnwu
24615afda1 Remove usage of ProcessLifecycle 2024-12-13 12:07:42 -08:00
topjohnwu
c5778f398b Cleanup imports 2024-12-13 12:07:42 -08:00
topjohnwu
4eb4097b9b Split file processing into its own class 2024-12-13 12:07:42 -08:00
vvb2060
c512496847 install_module: simplify script 2024-12-12 10:08:09 -08:00
vvb2060
506961a10d flash module: ignore META-INF 2024-12-12 10:07:47 -08:00
topjohnwu
3414415907 Support zip files with unsupported compresssion method 2024-12-12 02:50:19 -08:00
topjohnwu
dc2ae7cfd1 Disable CI on master push
Changes should be done through PRs for CI
2024-12-12 02:50:19 -08:00
topjohnwu
2e86d21c29 16k pages only work on Android B on x64 2024-12-09 20:13:27 -08:00
topjohnwu
2654382c43 Address clippy warnings 2024-12-09 18:26:39 -08:00
topjohnwu
9e26b73813 Update rust dependencies 2024-12-09 18:26:39 -08:00
topjohnwu
10cd13bf80 Update ONDK r28.1 2024-12-09 18:26:39 -08:00
topjohnwu
f10ee5f887 Test 16K pages with AVD instead of Cuttlefish 2024-12-09 14:16:08 -08:00
topjohnwu
47cc532d96 Release new canary build 2024-12-06 18:19:06 -08:00
topjohnwu
218327f92b Release Magisk v28.1 2024-12-06 17:45:41 -08:00
topjohnwu
4eae66a1a7 Add v28.1 release notes 2024-12-06 17:38:43 -08:00
vvb2060
b09ceeb43c scripts: sync avd_magisk.sh with mgiskinit 2024-12-06 02:21:17 -08:00
vvb2060
4fb539c110 core: use a new tmpfs as worker 2024-12-06 02:19:43 -08:00
vvb2060
849b284da5 core: insert symlink magisk_node 2024-12-06 02:19:32 -08:00
topjohnwu
895b5f6cbf Release new canary build 2024-12-04 01:28:31 -08:00
SonyaMedved
cb3d4ea514 strings.xml
The strings have been translated into Ukrainian.
2024-12-04 01:26:39 -08:00
topjohnwu
0d89a2a97d Update AGP 2024-12-04 01:25:44 -08:00
nedokaka
3ca5913055 Update Russian Translation 2024-12-03 19:52:53 -08:00
topjohnwu
df6b808f49 Cleanup DesugarClassVisitorFactory 2024-12-03 19:52:39 -08:00
topjohnwu
09c7ac754b Simplify MagiskD Rust/C++ FFI 2024-12-03 15:51:17 -08:00
topjohnwu
805da67c23 Update cxx-rs 2024-12-03 14:16:14 -08:00
topjohnwu
3c6889505b Stop using polymorphism in magiskinit 2024-12-03 02:18:22 -08:00
topjohnwu
c8e9ce7627 Cleanup mount code in magiskinit 2024-12-03 02:18:22 -08:00
topjohnwu
837c679a31 Update avd_test API versions 2024-12-03 02:18:22 -08:00
LoveSy
06616659b8 Only desugar ZipEntry's methods 2024-12-02 19:55:28 -08:00
topjohnwu
a34c04f999 Release new canary build 2024-12-01 14:59:57 -08:00
topjohnwu
da43ac89a0 Allow all domains to access tmpfs files
Fix #8457
2024-11-30 23:21:33 -08:00
vvb2060
830fc758b9 init: Use apex dir to determine whether 2SI 2024-11-30 23:03:29 -08:00
vvb2060
0f3cfef278 Revert "init: support 2SI devices with skip_initramfs"
This reverts commit b38fd1ca5f.
2024-11-30 23:03:29 -08:00
topjohnwu
b32d7bfafd Update gradle version 2024-11-21 21:05:35 -08:00
topjohnwu
c0899f2939 Update dependencies 2024-11-19 20:29:15 -08:00
topjohnwu
082330808f Fix building APK 2024-11-19 20:25:10 -08:00
topjohnwu
024da05888 Move several stuff into buildSrc 2024-11-09 20:08:12 -08:00
LoveSy
377b6d0cc2 avoid desugar the Desugar class 2024-11-09 19:41:06 -08:00
Georgi Boiko
c661009b31 docs(ci): update setup action to state correct jdk version 2024-11-04 11:12:01 -08:00
vvb2060
613f2d31c5 app: auto close action fragment only when focus lost 2024-11-04 11:11:41 -08:00
vvb2060
7dbb973db5 daemon: some samsung devices using incorrect mediatek-res path 2024-11-04 11:09:53 -08:00
topjohnwu
f4502f8be8 Add our own API desugaring
Fix #8452
2024-10-29 12:13:22 -07:00
topjohnwu
455b13b83c Fix download URL in stub.apk 2024-10-17 19:42:10 -07:00
topjohnwu
8b98709743 Update dependencies 2024-10-17 13:17:46 -07:00
tzagim
1b12f45f39 Update Hebrew Translation 2024-10-15 15:23:21 -07:00
vvb2060
a5cad532ff ui: fix lock screen orientation 2024-10-12 01:16:24 -07:00
topjohnwu
070719db50 Release new canary build 2024-10-10 02:10:50 -07:00
topjohnwu
28cccdf7aa Release Magisk v28.0 2024-10-10 01:47:00 -07:00
topjohnwu
b7e0986a5c Add v28.0 changelog 2024-10-10 01:40:14 -07:00
topjohnwu
da709745dd Revert #8245 2024-10-09 15:40:23 -07:00
topjohnwu
8b6771d487 Update dependencies 2024-10-08 01:40:09 -07:00
topjohnwu
e1b847fbc5 Find boot image with MagiskInstaller
Fix #8211
2024-10-07 16:52:35 -07:00
topjohnwu
7188de1205 Support unaligned boot image file
Fix #7733
2024-10-06 03:01:08 -07:00
topjohnwu
44fb7dbcbe Update Busybox
Fix #8403
2024-10-06 01:47:13 -07:00
topjohnwu
8086b5933c Update crt0
Fix #8424
2024-10-02 16:37:07 -07:00
topjohnwu
7f675f4bf7 Update dependencies 2024-09-27 14:38:32 -07:00
vvb2060
5e6b53e0da AppMigration: put suManager after installation 2024-09-25 12:34:21 -07:00
残页
5b29fefc65 Replace LOGE with LOGW so the process don't abort
Co-authored-by: 南宫雪珊 <vvb2060@gmail.com>
2024-09-25 11:59:58 -07:00
残页
16a168535d Check sepolicy database version in add_xperm_rule
Fix #8344
2024-09-25 11:59:58 -07:00
Wang Han
33f70f8f6d Update zh-rCN strings 2024-09-17 15:01:10 -07:00
topjohnwu
4f18a66d73 Release new canary build 2024-09-17 01:46:04 -07:00
Wang Han
250dc16007 Fix post-fs-data blocking time in doc
f7d3d1eeaf.
2024-09-17 01:28:01 -07:00
Wang Han
7af273e047 Don't append "start logd" in post-fs-data
This was first done in b13eb3f because magiskd was started in
post-fs stage that time. Among all android versions, logd was all
started before post-fs-data.
2024-09-16 00:30:36 -07:00
Arbri çoçka
e381aebaa0 Update strings.xml Albania (sq) 2024-09-16 00:24:47 -07:00
LoveSy
45d91c9658 Upgrade Gradle 2024-09-15 00:14:15 -07:00
igor
4a9158f667 Update Portuguese translation 2024-09-14 23:08:40 -07:00
niels
0d9ee89e7f magiskboot: cleanup bootconfig and vendor ramdisk dir 2024-09-14 23:08:22 -07:00
topjohnwu
abaff72304 Enable core library desugaring
Fix #8343
2024-09-09 01:59:32 -07:00
topjohnwu
b828e2d0b2 Update dependencies 2024-09-08 03:02:09 -07:00
Wang Han
53d7cbc11b Clarify magiskboot requirements for repacking img 2024-09-08 01:13:46 -07:00
LoveSy
310be7ab47 Return exit value of action.sh 2024-09-08 01:12:30 -07:00
LoveSy
60894e458f Automatically close action fragment when action exits successfully 2024-09-08 01:12:30 -07:00
LoveSy
fbebb6ac10 Add action.sh for user to manually trigger modules' functionality from app 2024-09-08 01:12:30 -07:00
LoveSy
a9f8c20703 Upgrade AGP 2024-09-05 21:50:56 -07:00
vvb2060
ae0b15d197 deps: update gradle to 8.10 2024-09-05 21:50:46 -07:00
vvb2060
869aa62328 ci: fix build 2024-09-05 21:50:46 -07:00
vvb2060
dcd3bc58a3 app: target api 35 2024-09-05 21:50:46 -07:00
Salvo Giangreco
a82f17c594 Disable Samsung PROCA
Signed-off-by: Salvo Giangreco <giangrecosalvo9@gmail.com>
2024-09-04 01:49:02 -07:00
vvb2060
b38fd1ca5f init: support 2SI devices with skip_initramfs 2024-09-03 16:33:57 -07:00
153 changed files with 4757 additions and 2754 deletions

View File

@@ -6,11 +6,11 @@ inputs:
runs:
using: "composite"
steps:
- name: Set up JDK 17
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
distribution: "temurin"
java-version: "17"
java-version: "21"
- name: Set up Python 3
uses: actions/setup-python@v5

View File

@@ -1,15 +1,6 @@
name: Magisk Build
on:
push:
branches: [master]
paths:
- "app/**"
- "native/**"
- "buildSrc/**"
- "build.py"
- "gradle.properties"
- ".github/workflows/build.yml"
pull_request:
branches: [master]
workflow_dispatch:
@@ -17,7 +8,7 @@ on:
jobs:
build:
name: Build Magisk artifacts
runs-on: macos-14
runs-on: macos-15
strategy:
fail-fast: false
steps:
@@ -60,7 +51,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [windows-latest, ubuntu-latest]
os: [windows-2025, ubuntu-24.04]
steps:
- name: Check out
uses: actions/checkout@v4
@@ -78,16 +69,18 @@ jobs:
avd-test:
name: Test API ${{ matrix.version }} (x86_64)
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
needs: build
strategy:
fail-fast: false
matrix:
version: [23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34]
version: [23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35]
type: [""]
include:
- version: 35
- version: "Baklava"
type: "google_apis"
- version: "Baklava"
type: "google_apis_ps16k"
steps:
- name: Check out
@@ -122,7 +115,7 @@ jobs:
avd-test-32:
name: Test API ${{ matrix.version }} (x86)
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
needs: build
strategy:
fail-fast: false
@@ -161,7 +154,7 @@ jobs:
kernel.log
logcat.log
cf_test:
cf-test:
name: Test ${{ matrix.device }}
runs-on: ubuntu-24.04
needs: build
@@ -173,8 +166,6 @@ jobs:
include:
- branch: "aosp-main"
device: "aosp_cf_x86_64_phone"
- branch: "aosp-main-throttled"
device: "aosp_cf_x86_64_phone_pgagnostic"
steps:
- name: Check out

1
.gitignore vendored
View File

@@ -14,6 +14,7 @@ native/out
*.iml
.gradle
.idea
.kotlin
/local.properties
/build
/captures

View File

@@ -20,9 +20,9 @@ Some highlight features:
Click the icon below to download Magisk apk.
[![](https://img.shields.io/badge/Magisk-v27.0-blue)](https://github.com/topjohnwu/Magisk/releases/tag/v27.0)
[![](https://img.shields.io/badge/Magisk%20Beta-v27.0-blue)](https://github.com/topjohnwu/Magisk/releases/tag/v27.0)
[![](https://img.shields.io/badge/Magisk-Canary-red)](https://github.com/topjohnwu/Magisk/releases/tag/canary-27007)
[![](https://img.shields.io/badge/Magisk-v28.1-blue)](https://github.com/topjohnwu/Magisk/releases/tag/v28.1)
[![](https://img.shields.io/badge/Magisk%20Beta-v28.1-blue)](https://github.com/topjohnwu/Magisk/releases/tag/v28.1)
[![](https://img.shields.io/badge/Magisk-Canary-red)](https://github.com/topjohnwu/Magisk/releases/tag/canary-28102)
## Useful Links

View File

@@ -6,46 +6,37 @@ plugins {
id("androidx.navigation.safeargs.kotlin")
}
setupAppCommon()
setupMainApk()
kapt {
correctErrorTypes = true
useBuildCache = true
mapDiagnosticLocations = true
javacOptions {
option("-Xmaxerrs", 1000)
option("-Xmaxerrs", "1000")
}
}
android {
namespace = "com.topjohnwu.magisk"
buildFeatures {
dataBinding = true
}
defaultConfig {
applicationId = "com.topjohnwu.magisk"
vectorDrawables.useSupportLibrary = true
versionName = Config.version
versionCode = Config.versionCode
ndk {
abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64", "riscv64")
debugSymbolLevel = "FULL"
}
compileOptions {
isCoreLibraryDesugaringEnabled = true
}
buildTypes {
release {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles("proguard-rules.pro")
}
}
buildFeatures {
dataBinding = true
}
}
dependencies {
implementation(project(":app:core"))
coreLibraryDesugaring(libs.jdk.libs)
implementation(libs.indeterminate.checkbox)
implementation(libs.rikka.layoutinflater)

View File

@@ -1,13 +1,18 @@
package com.topjohnwu.magisk.dialog
import android.widget.Toast
import androidx.core.os.postDelayed
import androidx.lifecycle.lifecycleScope
import com.topjohnwu.magisk.core.BuildConfig
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.R
import com.topjohnwu.magisk.core.ktx.reboot
import com.topjohnwu.magisk.core.ktx.toast
import com.topjohnwu.magisk.core.tasks.MagiskInstaller
import com.topjohnwu.magisk.events.DialogBuilder
import com.topjohnwu.magisk.ui.home.HomeViewModel
import com.topjohnwu.magisk.view.MagiskDialog
import com.topjohnwu.superuser.internal.UiThreadHandler
import kotlinx.coroutines.launch
class EnvFixDialog(private val vm: HomeViewModel, private val code: Int) : DialogBuilder {
@@ -27,9 +32,15 @@ class EnvFixDialog(private val vm: HomeViewModel, private val code: Int) : Dialo
setCancelable(false)
}
dialog.activity.lifecycleScope.launch {
MagiskInstaller.FixEnv {
MagiskInstaller.FixEnv().exec { success ->
dialog.dismiss()
}.exec()
context.toast(
if (success) R.string.reboot_delay_toast else R.string.setup_fail,
Toast.LENGTH_LONG
)
if (success)
UiThreadHandler.handler.postDelayed(5000) { reboot() }
}
}
}
}

View File

@@ -1,15 +1,17 @@
package com.topjohnwu.magisk.dialog
import android.app.ProgressDialog
import android.content.Context
import android.widget.Toast
import androidx.lifecycle.lifecycleScope
import com.topjohnwu.magisk.arch.NavigationActivity
import com.topjohnwu.magisk.arch.UIActivity
import com.topjohnwu.magisk.core.R
import com.topjohnwu.magisk.core.ktx.toast
import com.topjohnwu.magisk.core.tasks.MagiskInstaller
import com.topjohnwu.magisk.events.DialogBuilder
import com.topjohnwu.magisk.ui.flash.FlashFragment
import com.topjohnwu.magisk.view.MagiskDialog
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.launch
class UninstallDialog : DialogBuilder {
@@ -19,7 +21,7 @@ class UninstallDialog : DialogBuilder {
setMessage(R.string.uninstall_magisk_msg)
setButton(MagiskDialog.ButtonType.POSITIVE) {
text = R.string.restore_img
onClick { restore(dialog.context) }
onClick { restore(dialog.activity) }
}
setButton(MagiskDialog.ButtonType.NEGATIVE) {
text = R.string.complete_uninstall
@@ -29,18 +31,20 @@ class UninstallDialog : DialogBuilder {
}
@Suppress("DEPRECATION")
private fun restore(context: Context) {
val dialog = ProgressDialog(context).apply {
setMessage(context.getString(R.string.restore_img_msg))
private fun restore(activity: UIActivity<*>) {
val dialog = ProgressDialog(activity).apply {
setMessage(activity.getString(R.string.restore_img_msg))
show()
}
Shell.cmd("restore_imgs").submit { result ->
dialog.dismiss()
if (result.isSuccess) {
context.toast(R.string.restore_done, Toast.LENGTH_SHORT)
} else {
context.toast(R.string.restore_fail, Toast.LENGTH_LONG)
activity.lifecycleScope.launch {
MagiskInstaller.Restore().exec { success ->
dialog.dismiss()
if (success) {
activity.toast(R.string.restore_done, Toast.LENGTH_SHORT)
} else {
activity.toast(R.string.restore_fail, Toast.LENGTH_LONG)
}
}
}
}

View File

@@ -71,7 +71,7 @@ class FlashFragment : BaseFragment<FragmentFlashMd2Binding>(), MenuProvider {
super.onViewCreated(view, savedInstanceState)
defaultOrientation = activity?.requestedOrientation ?: -1
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LOCKED
if (savedInstanceState == null) {
viewModel.startFlashing()
}

View File

@@ -0,0 +1,108 @@
package com.topjohnwu.magisk.ui.module
import android.annotation.SuppressLint
import android.content.pm.ActivityInfo
import android.os.Bundle
import android.view.KeyEvent
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewTreeObserver
import android.widget.Toast
import androidx.core.view.MenuProvider
import androidx.core.view.isVisible
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.arch.BaseFragment
import com.topjohnwu.magisk.arch.viewModel
import com.topjohnwu.magisk.core.ktx.toast
import com.topjohnwu.magisk.databinding.FragmentActionMd2Binding
import com.topjohnwu.magisk.core.R as CoreR
class ActionFragment : BaseFragment<FragmentActionMd2Binding>(), MenuProvider {
override val layoutRes = R.layout.fragment_action_md2
override val viewModel by viewModel<ActionViewModel>()
override val snackbarView: View get() = binding.snackbarContainer
private var defaultOrientation = -1
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.args = ActionFragmentArgs.fromBundle(requireArguments())
}
override fun onStart() {
super.onStart()
activity?.setTitle(viewModel.args.name)
binding.closeBtn.setOnClickListener {
activity?.onBackPressed()
}
viewModel.state.observe(this) {
if (it != ActionViewModel.State.RUNNING) {
binding.closeBtn.apply {
if (!this.isVisible) this.show()
if (!this.isFocused) this.requestFocus()
}
}
if (it != ActionViewModel.State.SUCCESS) return@observe
view?.viewTreeObserver?.addOnWindowFocusChangeListener(
object : ViewTreeObserver.OnWindowFocusChangeListener {
override fun onWindowFocusChanged(hasFocus: Boolean) {
if (hasFocus) return
view?.viewTreeObserver?.removeOnWindowFocusChangeListener(this)
view?.context?.apply {
toast(
getString(CoreR.string.done_action, viewModel.args.name),
Toast.LENGTH_SHORT
)
}
viewModel.back()
}
}
)
}
}
override fun onCreateMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_flash, menu)
}
override fun onMenuItemSelected(item: MenuItem): Boolean {
return viewModel.onMenuItemClicked(item)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
defaultOrientation = activity?.requestedOrientation ?: -1
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LOCKED
if (savedInstanceState == null) {
viewModel.startRunAction()
}
}
@SuppressLint("WrongConstant")
override fun onDestroyView() {
if (defaultOrientation != -1) {
activity?.requestedOrientation = defaultOrientation
}
super.onDestroyView()
}
override fun onKeyEvent(event: KeyEvent): Boolean {
return when (event.keyCode) {
KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_VOLUME_DOWN -> true
else -> false
}
}
override fun onBackPressed(): Boolean {
if (viewModel.state.value == ActionViewModel.State.RUNNING) return true
return super.onBackPressed()
}
override fun onPreBind(binding: FragmentActionMd2Binding) = Unit
}

View File

@@ -0,0 +1,88 @@
package com.topjohnwu.magisk.ui.module
import android.view.MenuItem
import androidx.databinding.ObservableArrayList
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.arch.BaseViewModel
import com.topjohnwu.magisk.core.ktx.synchronized
import com.topjohnwu.magisk.core.ktx.timeFormatStandard
import com.topjohnwu.magisk.core.ktx.toTime
import com.topjohnwu.magisk.core.utils.MediaStoreUtils
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
import com.topjohnwu.magisk.events.SnackbarEvent
import com.topjohnwu.magisk.ui.flash.ConsoleItem
import com.topjohnwu.superuser.CallbackList
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.io.IOException
class ActionViewModel : BaseViewModel() {
enum class State {
RUNNING, SUCCESS, FAILED
}
private val _state = MutableLiveData(State.RUNNING)
val state: LiveData<State> get() = _state
val items = ObservableArrayList<ConsoleItem>()
lateinit var args: ActionFragmentArgs
private val logItems = mutableListOf<String>().synchronized()
private val outItems = object : CallbackList<String>() {
override fun onAddElement(e: String?) {
e ?: return
items.add(ConsoleItem(e))
logItems.add(e)
}
}
fun startRunAction() = viewModelScope.launch {
onResult(withContext(Dispatchers.IO) {
try {
Shell.cmd("run_action \'${args.id}\'")
.to(outItems, logItems)
.exec().isSuccess
} catch (e: IOException) {
Timber.e(e)
false
}
})
}
private fun onResult(success: Boolean) {
_state.value = if (success) State.SUCCESS else State.FAILED
}
fun onMenuItemClicked(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_save -> savePressed()
}
return true
}
private fun savePressed() = withExternalRW {
viewModelScope.launch(Dispatchers.IO) {
val name = "%s_action_log_%s.log".format(
args.name,
System.currentTimeMillis().toTime(timeFormatStandard)
)
val file = MediaStoreUtils.getFile(name)
file.uri.outputStream().bufferedWriter().use { writer ->
synchronized(logItems) {
logItems.forEach {
writer.write(it)
writer.newLine()
}
}
}
SnackbarEvent(file.toString()).publish()
}
}
}

View File

@@ -25,6 +25,7 @@ class LocalModuleRvItem(
override val layoutRes = R.layout.item_module_md2
val showNotice: Boolean
val showAction: Boolean
val noticeText: TextHolder
init {
@@ -35,6 +36,7 @@ class LocalModuleRvItem(
showNotice = zygiskUnloaded ||
(Info.isZygiskEnabled && isRiru) ||
(!Info.isZygiskEnabled && isZygisk)
showAction = item.hasAction && !showNotice
noticeText =
when {
zygiskUnloaded -> CoreR.string.zygisk_module_unloaded.asText()

View File

@@ -4,8 +4,10 @@ import android.net.Uri
import androidx.databinding.Bindable
import androidx.lifecycle.MutableLiveData
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.MainDirections
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.arch.AsyncLoadViewModel
import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.base.ContentResultCallback
import com.topjohnwu.magisk.core.model.module.LocalModule
@@ -96,6 +98,10 @@ class ModuleViewModel : AsyncLoadViewModel() {
}
}
fun runAction(id: String, name: String) {
MainDirections.actionActionFragment(id, name).navigate()
}
companion object {
private val uri = MutableLiveData<Uri?>()
}

View File

@@ -13,6 +13,7 @@ import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.arch.BaseViewModel
import com.topjohnwu.magisk.core.AppContext
import com.topjohnwu.magisk.core.BuildConfig
import com.topjohnwu.magisk.core.Config
import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.R
@@ -92,7 +93,7 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
DownloadPath -> withExternalRW(doAction)
UpdateChecker -> withPostNotificationPermission(doAction)
Authentication -> AuthEvent(doAction).publish()
Hide, Restore -> withInstallPermission(doAction)
AutomaticResponse -> if (Config.suAuth) AuthEvent(doAction).publish() else doAction()
else -> doAction()
}
}

View File

@@ -78,8 +78,8 @@ class SuperuserViewModel(
this@SuperuserViewModel, policy,
info.packageName,
info.sharedUserId != null,
info.applicationInfo.loadIcon(pm),
info.applicationInfo.getLabel(pm)
info.applicationInfo?.loadIcon(pm) ?: pm.defaultActivityIcon,
info.applicationInfo?.getLabel(pm) ?: info.packageName
)
} catch (e: PackageManager.NameNotFoundException) {
null

View File

@@ -111,7 +111,7 @@ class SuRequestViewModel(
// shared UID. We have no way to know where this request comes from.
icon = pm.defaultActivityIcon
title = "[SharedUID] ${info.sharedUserId}"
packageName = info.sharedUserId
packageName = info.sharedUserId.toString()
} else {
val prefix = if (info.sharedUserId == null) "" else "[SharedUID] "
icon = app.loadIcon(pm)

View File

@@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M8,5v14l11,-7z"/>
</vector>

View File

@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.topjohnwu.magisk.ui.module.ActionViewModel" />
</data>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/internal_action_bar_size"
app:layout_fitsSystemWindowsInsets="top"
tools:layout_marginTop="@dimen/internal_action_bar_size">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/flash_content"
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" />
</HorizontalScrollView>
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/close_btn"
android:visibility="gone"
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="true"
android:enabled="true"
android:focusable="true"
android:text="@string/close"
android:textAllCaps="false"
android:textColor="?colorOnPrimary"
android:textStyle="bold"
app:backgroundTint="?colorPrimary"
app:icon="@drawable/ic_close_md2"
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"
app:fitsSystemWindowsInsets="top|bottom" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>

View File

@@ -189,12 +189,32 @@
android:textColor="?colorError"
app:layout_constraintBottom_toBottomOf="@+id/module_remove"
app:layout_constraintEnd_toStartOf="@+id/bottom_bar_barrier"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintStart_toEndOf="@id/module_config"
app:layout_constraintTop_toTopOf="@+id/module_remove"
tools:lines="2"
tools:text="@tools:sample/lorem/random"
tools:visibility="visible" />
<Button
android:id="@+id/module_config"
style="@style/WidgetFoundation.Button.Text"
goneUnless="@{item.showAction}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:enabled="@{item.enabled}"
android:focusable="true"
android:onClick="@{() -> viewModel.runAction(item.item.id, item.item.name)}"
android:text="@string/module_action"
android:textAllCaps="false"
android:visibility="gone"
app:icon="@drawable/ic_action_md2"
app:iconGravity="textStart"
app:layout_constraintBottom_toBottomOf="@+id/module_remove"
app:layout_constraintTop_toTopOf="@+id/module_remove"
app:layout_constraintStart_toStartOf="parent"
app:srcCompat="@drawable/ic_download_md2" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>

View File

@@ -53,6 +53,21 @@
</fragment>
<fragment
android:id="@+id/actionFragment"
android:name="com.topjohnwu.magisk.ui.module.ActionFragment"
android:label="ActionFragment"
tools:layout="@layout/fragment_action_md2" >
<argument
android:name="id"
app:argType="string" />
<argument
android:name="name"
app:argType="string" />
</fragment>
<fragment
android:id="@+id/installFragment"
android:name="com.topjohnwu.magisk.ui.install.InstallFragment"
@@ -152,4 +167,12 @@
app:popEnterAnim="@anim/fragment_enter_pop"
app:popExitAnim="@anim/fragment_exit_pop" />
<action
android:id="@+id/action_actionFragment"
app:destination="@id/actionFragment"
app:enterAnim="@anim/fragment_enter"
app:exitAnim="@anim/fragment_exit"
app:popEnterAnim="@anim/fragment_enter_pop"
app:popExitAnim="@anim/fragment_exit_pop" />
</navigation>

View File

@@ -19,6 +19,7 @@ android {
buildConfigField("int", "APP_VERSION_CODE", "${Config.versionCode}")
buildConfigField("String", "APP_VERSION_NAME", "\"${Config.version}\"")
buildConfigField("int", "STUB_VERSION", Config.stubVersion)
consumerProguardFile("proguard-rules.pro")
}
buildFeatures {
@@ -59,5 +60,10 @@ dependencies {
implementation(libs.activity)
implementation(libs.collection.ktx)
implementation(libs.profileinstaller)
implementation(libs.lifecycle.process)
// We also implement all our tests in this module.
// However, we don't want to bundle test dependencies.
// That's why we make it compileOnly.
compileOnly(libs.test.junit)
compileOnly(libs.test.uiautomator)
}

View File

@@ -22,42 +22,19 @@
int mActivityHandlesConfigFlags;
}
# main
-keep,allowoptimization public class com.topjohnwu.magisk.signing.SignBoot {
public static void main(java.lang.String[]);
}
# Strip Timber verbose and debug logging
-assumenosideeffects class timber.log.Timber$Tree {
public void v(**);
public void d(**);
}
# https://github.com/square/retrofit/issues/3751#issuecomment-1192043644
# Keep generic signature of Call, Response (R8 full mode strips signatures from non-kept items).
-keep,allowobfuscation,allowshrinking interface retrofit2.Call
-keep,allowobfuscation,allowshrinking class retrofit2.Response
# With R8 full mode generic signatures are stripped for classes that are not
# kept. Suspend functions are wrapped in continuations where the type argument
# is used.
-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation
# Excessive obfuscation
-repackageclasses 'a'
-flattenpackagehierarchy
-allowaccessmodification
-obfuscationdictionary ../dict.txt
-classobfuscationdictionary ../dict.txt
-packageobfuscationdictionary ../dict.txt
-dontwarn org.bouncycastle.jsse.BCSSLParameters
-dontwarn org.bouncycastle.jsse.BCSSLSocket
-dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
-dontwarn org.commonmark.ext.gfm.strikethrough.Strikethrough
-dontwarn org.conscrypt.Conscrypt*
-dontwarn org.conscrypt.ConscryptHostnameVerifier
-dontwarn org.openjsse.javax.net.ssl.SSLParameters
-dontwarn org.openjsse.javax.net.ssl.SSLSocket
-dontwarn org.openjsse.net.ssl.OpenJSSE
-dontwarn org.junit.**

View File

@@ -16,7 +16,6 @@ import com.topjohnwu.magisk.StubApk
import com.topjohnwu.magisk.core.base.UntrackedActivity
import com.topjohnwu.magisk.core.utils.LocaleSetting
import com.topjohnwu.magisk.core.utils.NetworkObserver
import com.topjohnwu.magisk.core.utils.ProcessLifecycle
import com.topjohnwu.magisk.core.utils.RootUtils
import com.topjohnwu.magisk.core.utils.ShellInit
import com.topjohnwu.superuser.Shell
@@ -40,6 +39,7 @@ object AppContext : ContextWrapper(null),
private var ref = WeakReference<Activity>(null)
private lateinit var application: Application
private lateinit var networkObserver: NetworkObserver
init {
// Always log full stack trace with Timber
@@ -56,6 +56,10 @@ object AppContext : ContextWrapper(null),
LocaleSetting.instance.updateResource(resources)
}
override fun onActivityStarted(activity: Activity) {
networkObserver.postCurrentState()
}
override fun onActivityResumed(activity: Activity) {
if (activity is UntrackedActivity) return
ref = WeakReference(activity)
@@ -102,8 +106,7 @@ object AppContext : ContextWrapper(null),
val lm = getSystemService(LocaleManager::class.java)
lm.overrideLocaleConfig = LocaleSetting.localeConfig
}
ProcessLifecycle.init(this)
NetworkObserver.init(this)
networkObserver = NetworkObserver.init(this)
if (!BuildConfig.DEBUG && !isRunningAsStub) {
GlobalScope.launch(Dispatchers.IO) {
ProfileInstaller.writeProfile(this@AppContext)
@@ -120,7 +123,6 @@ object AppContext : ContextWrapper(null),
}
override fun onActivityCreated(activity: Activity, bundle: Bundle?) {}
override fun onActivityStarted(activity: Activity) {}
override fun onActivityStopped(activity: Activity) {}
override fun onActivitySaveInstanceState(activity: Activity, bundle: Bundle) {}
override fun onActivityDestroyed(activity: Activity) {}

View File

@@ -83,7 +83,7 @@ object Config : PreferenceConfig, DBConfig {
const val SU_AUTO_ALLOW = 2
// su timeout
val TIMEOUT_LIST = intArrayOf(0, -1, 10, 20, 30, 60)
val TIMEOUT_LIST = longArrayOf(0, -1, 10, 20, 30, 60)
}
private val defaultChannel =

View File

@@ -11,6 +11,7 @@ import androidx.core.content.getSystemService
import com.topjohnwu.magisk.core.base.BaseJobService
import com.topjohnwu.magisk.core.di.ServiceLocator
import com.topjohnwu.magisk.core.download.DownloadEngine
import com.topjohnwu.magisk.core.download.DownloadSession
import com.topjohnwu.magisk.core.download.Subject
import com.topjohnwu.magisk.view.Notifications
import kotlinx.coroutines.Dispatchers
@@ -25,7 +26,7 @@ class JobService : BaseJobService() {
@TargetApi(value = 34)
inner class Session(
private var params: JobParameters
) : DownloadEngine.Session {
) : DownloadSession {
override val context get() = this@JobService
val engine = DownloadEngine(this)

View File

@@ -3,7 +3,6 @@ package com.topjohnwu.magisk.core
import android.os.Bundle
import com.topjohnwu.magisk.core.base.BaseProvider
import com.topjohnwu.magisk.core.su.SuCallbackHandler
import com.topjohnwu.magisk.core.su.TestHandler
class Provider : BaseProvider() {
@@ -13,7 +12,7 @@ class Provider : BaseProvider() {
SuCallbackHandler.run(context!!, method, extras)
Bundle.EMPTY
}
else -> TestHandler.run(method)
else -> Bundle.EMPTY
}
}
}

View File

@@ -7,9 +7,10 @@ import androidx.core.app.ServiceCompat
import androidx.core.content.IntentCompat
import com.topjohnwu.magisk.core.base.BaseService
import com.topjohnwu.magisk.core.download.DownloadEngine
import com.topjohnwu.magisk.core.download.DownloadSession
import com.topjohnwu.magisk.core.download.Subject
class Service : BaseService(), DownloadEngine.Session {
class Service : BaseService(), DownloadSession {
private var mEngine: DownloadEngine? = null
override val context get() = this

View File

@@ -7,9 +7,13 @@ import kotlinx.coroutines.withContext
open class MagiskDB {
suspend fun <R> exec(
class Literal(
val str: String
)
suspend inline fun <R> exec(
query: String,
mapper: suspend (Map<String, String>) -> R
crossinline mapper: (Map<String, String>) -> R
): List<R> {
return withContext(Dispatchers.IO) {
val out = Shell.cmd("magisk --sqlite '$query'").await().out
@@ -18,13 +22,15 @@ open class MagiskDB {
.map { it.split("=", limit = 2) }
.filter { it.size == 2 }
.associate { it[0] to it[1] }
.let { mapper(it) }
.let(mapper)
}
}
}
suspend inline fun exec(query: String) {
exec(query) {}
suspend fun exec(query: String) {
withContext(Dispatchers.IO) {
Shell.cmd("magisk --sqlite '$query'").await()
}
}
fun Map<String, Any>.toQuery(): String {
@@ -33,6 +39,7 @@ open class MagiskDB {
when (it) {
is Boolean -> if (it) "1" else "0"
is Number -> it.toString()
is Literal -> it.str
else -> "\"$it\""
}
}

View File

@@ -3,24 +3,24 @@ package com.topjohnwu.magisk.core.data.magiskdb
import com.topjohnwu.magisk.core.AppContext
import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.model.su.SuPolicy
import java.util.concurrent.TimeUnit
private const val SELECT_QUERY = "SELECT (until - strftime(\"%s\", \"now\")) AS remain, *"
class PolicyDao : MagiskDB() {
suspend fun deleteOutdated() {
val nowSeconds = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())
val query = "DELETE FROM ${Table.POLICY} WHERE " +
"(until > 0 AND until < $nowSeconds) OR until < 0"
"(until > 0 AND until < strftime(\"%s\", \"now\")) OR until < 0"
exec(query)
}
suspend fun delete(uid: Int) {
val query = "DELETE FROM ${Table.POLICY} WHERE uid == $uid"
val query = "DELETE FROM ${Table.POLICY} WHERE uid=$uid"
exec(query)
}
suspend fun fetch(uid: Int): SuPolicy? {
val query = "SELECT * FROM ${Table.POLICY} WHERE uid == $uid LIMIT = 1"
val query = "$SELECT_QUERY FROM ${Table.POLICY} WHERE uid=$uid LIMIT 1"
return exec(query, ::toPolicy).firstOrNull()
}
@@ -35,7 +35,7 @@ class PolicyDao : MagiskDB() {
}
suspend fun fetchAll(): List<SuPolicy> {
val query = "SELECT * FROM ${Table.POLICY} WHERE uid/100000 == ${Const.USER_ID}"
val query = "$SELECT_QUERY FROM ${Table.POLICY} WHERE uid/100000=${Const.USER_ID}"
return exec(query, ::toPolicy).filterNotNull()
}
@@ -43,8 +43,15 @@ class PolicyDao : MagiskDB() {
val uid = map["uid"]?.toInt() ?: return null
val policy = SuPolicy(uid)
map["until"]?.toLong()?.let { until ->
if (until <= 0) {
policy.remain = until
} else {
map["remain"]?.toLong()?.let { policy.remain = it }
}
}
map["policy"]?.toInt()?.let { policy.policy = it }
map["until"]?.toLong()?.let { policy.until = it }
map["logging"]?.toInt()?.let { policy.logging = it != 0 }
map["notification"]?.toInt()?.let { policy.notification = it != 0 }
return policy

View File

@@ -3,7 +3,7 @@ package com.topjohnwu.magisk.core.data.magiskdb
class SettingsDao : MagiskDB() {
suspend fun delete(key: String) {
val query = "DELETE FROM ${Table.SETTINGS} WHERE key == \"$key\""
val query = "DELETE FROM ${Table.SETTINGS} WHERE key=\"$key\""
exec(query)
}
@@ -14,7 +14,7 @@ class SettingsDao : MagiskDB() {
}
suspend fun fetch(key: String, default: Int = -1): Int {
val query = "SELECT value FROM ${Table.SETTINGS} WHERE key == \"$key\" LIMIT 1"
val query = "SELECT value FROM ${Table.SETTINGS} WHERE key=\"$key\" LIMIT 1"
return exec(query) { it["value"]?.toInt() }.firstOrNull() ?: default
}
}

View File

@@ -3,7 +3,7 @@ package com.topjohnwu.magisk.core.data.magiskdb
class StringDao : MagiskDB() {
suspend fun delete(key: String) {
val query = "DELETE FROM ${Table.STRINGS} WHERE key == \"$key\""
val query = "DELETE FROM ${Table.STRINGS} WHERE key=\"$key\""
exec(query)
}
@@ -14,7 +14,7 @@ class StringDao : MagiskDB() {
}
suspend fun fetch(key: String, default: String = ""): String {
val query = "SELECT value FROM ${Table.STRINGS} WHERE key == \"$key\" LIMIT 1"
val query = "SELECT value FROM ${Table.STRINGS} WHERE key=\"$key\" LIMIT 1"
return exec(query) { it["value"] }.firstOrNull() ?: default
}
}

View File

@@ -0,0 +1,122 @@
package com.topjohnwu.magisk.core.download
import android.net.Uri
import com.topjohnwu.magisk.StubApk
import com.topjohnwu.magisk.core.R
import com.topjohnwu.magisk.core.isRunningAsStub
import com.topjohnwu.magisk.core.ktx.cachedFile
import com.topjohnwu.magisk.core.ktx.copyAll
import com.topjohnwu.magisk.core.ktx.copyAndClose
import com.topjohnwu.magisk.core.ktx.withInOut
import com.topjohnwu.magisk.core.ktx.writeTo
import com.topjohnwu.magisk.core.tasks.AppMigration
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
import com.topjohnwu.magisk.utils.APKInstall
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream
import org.apache.commons.compress.archivers.zip.ZipFile
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
class DownloadProcessor(notifier: DownloadNotifier) : DownloadNotifier by notifier {
suspend fun handle(stream: InputStream, subject: Subject) {
when (subject) {
is Subject.App -> handleApp(stream, subject)
is Subject.Module -> handleModule(stream, subject.file)
else -> stream.copyAndClose(subject.file.outputStream())
}
}
suspend fun handleApp(stream: InputStream, subject: Subject.App) {
val external = subject.file.outputStream()
if (isRunningAsStub) {
val updateApk = StubApk.update(context)
try {
// Download full APK to stub update path
stream.copyAndClose(TeeOutputStream(external, updateApk.outputStream()))
// Also upgrade stub
notifyUpdate(subject.notifyId) {
it.setProgress(0, 0, true)
.setContentTitle(context.getString(R.string.hide_app_title))
.setContentText("")
}
// Extract stub
val apk = context.cachedFile("stub.apk")
ZipFile.Builder().setFile(updateApk).get().use { zf ->
apk.delete()
zf.getInputStream(zf.getEntry("assets/stub.apk")).writeTo(apk)
}
// Patch and install
subject.intent = AppMigration.upgradeStub(context, apk)
?: throw IOException("HideAPK patch error")
apk.delete()
} catch (e: Exception) {
// If any error occurred, do not let stub load the new APK
updateApk.delete()
throw e
}
} else {
val session = APKInstall.startSession(context)
stream.copyAndClose(TeeOutputStream(external, session.openStream(context)))
subject.intent = session.waitIntent()
}
}
suspend fun handleModule(src: InputStream, file: Uri) {
val tmp = context.cachedFile("module.zip")
try {
// First download the entire zip into cache so we can process it
src.writeTo(tmp)
val input = ZipFile.Builder().setFile(tmp).get()
val output = ZipArchiveOutputStream(file.outputStream())
withInOut(input, output) { zin, zout ->
zout.putArchiveEntry(ZipArchiveEntry("META-INF/"))
zout.closeArchiveEntry()
zout.putArchiveEntry(ZipArchiveEntry("META-INF/com/"))
zout.closeArchiveEntry()
zout.putArchiveEntry(ZipArchiveEntry("META-INF/com/google/"))
zout.closeArchiveEntry()
zout.putArchiveEntry(ZipArchiveEntry("META-INF/com/google/android/"))
zout.closeArchiveEntry()
zout.putArchiveEntry(ZipArchiveEntry("META-INF/com/google/android/update-binary"))
context.assets.open("module_installer.sh").use { it.copyAll(zout) }
zout.closeArchiveEntry()
zout.putArchiveEntry(ZipArchiveEntry("META-INF/com/google/android/updater-script"))
zout.write("#MAGISK\n".toByteArray())
zout.closeArchiveEntry()
// Then simply copy all entries to output
zin.copyRawEntries(zout) { entry -> !entry.name.startsWith("META-INF") }
}
} finally {
tmp.delete()
}
}
private class TeeOutputStream(
private val o1: OutputStream,
private val o2: OutputStream
) : OutputStream() {
override fun write(b: Int) {
o1.write(b)
o2.write(b)
}
override fun write(b: ByteArray?, off: Int, len: Int) {
o1.write(b, off, len)
o2.write(b, off, len)
}
override fun close() {
o1.close()
o2.close()
}
}
}

Some files were not shown because too many files have changed in this diff Show More